From 3cb7301eedf33bcdc01b14ddd7e8ceda28fd8269 Mon Sep 17 00:00:00 2001 From: Brian Lai <51336873+brianjlai@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:43:03 -0500 Subject: [PATCH 01/45] Instantiate ConcurrentDeclarativeSource with args for remote manifest sources (#48373) Co-authored-by: Ben Church --- .../source-declarative-manifest/metadata.yaml | 2 +- .../pyproject.toml | 2 +- .../source_declarative_manifest/run.py | 67 +++++++++++++------ .../test_source_declarative_local_manifest.py | 16 +++-- ...test_source_declarative_remote_manifest.py | 6 +- docs/integrations/sources/low-code.md | 1 + 6 files changed, 65 insertions(+), 29 deletions(-) diff --git a/airbyte-integrations/connectors/source-declarative-manifest/metadata.yaml b/airbyte-integrations/connectors/source-declarative-manifest/metadata.yaml index 95c37b1e2e40..53039337afab 100644 --- a/airbyte-integrations/connectors/source-declarative-manifest/metadata.yaml +++ b/airbyte-integrations/connectors/source-declarative-manifest/metadata.yaml @@ -8,7 +8,7 @@ data: connectorType: source definitionId: 64a2f99c-542f-4af8-9a6f-355f1217b436 # This version should not be updated manually - it is updated by the CDK release workflow. - dockerImageTag: 6.2.0 + dockerImageTag: 6.2.1 dockerRepository: airbyte/source-declarative-manifest # This page is hidden from the docs for now, since the connector is not in any Airbyte registries. documentationUrl: https://docs.airbyte.com/integrations/sources/low-code diff --git a/airbyte-integrations/connectors/source-declarative-manifest/pyproject.toml b/airbyte-integrations/connectors/source-declarative-manifest/pyproject.toml index d90d10ca93b8..d44f0c01b34a 100644 --- a/airbyte-integrations/connectors/source-declarative-manifest/pyproject.toml +++ b/airbyte-integrations/connectors/source-declarative-manifest/pyproject.toml @@ -3,7 +3,7 @@ requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.poetry] -version = "6.2.0" +version = "6.2.1" name = "source-declarative-manifest" description = "Base source implementation for low-code sources." authors = ["Airbyte "] diff --git a/airbyte-integrations/connectors/source-declarative-manifest/source_declarative_manifest/run.py b/airbyte-integrations/connectors/source-declarative-manifest/source_declarative_manifest/run.py index b7303e9c6927..2dfd888804b8 100644 --- a/airbyte-integrations/connectors/source-declarative-manifest/source_declarative_manifest/run.py +++ b/airbyte-integrations/connectors/source-declarative-manifest/source_declarative_manifest/run.py @@ -1,6 +1,7 @@ # # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # + from __future__ import annotations import json @@ -11,21 +12,21 @@ from pathlib import Path from typing import Any, List, Mapping, Optional -from airbyte_cdk import TState -from airbyte_cdk.connector import BaseConnector from airbyte_cdk.entrypoint import AirbyteEntrypoint, launch from airbyte_cdk.models import ( AirbyteErrorTraceMessage, AirbyteMessage, AirbyteMessageSerializer, + AirbyteStateMessage, AirbyteTraceMessage, ConfiguredAirbyteCatalog, ConnectorSpecificationSerializer, TraceType, Type, ) -from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource +from airbyte_cdk.sources.declarative.concurrent_declarative_source import ConcurrentDeclarativeSource from airbyte_cdk.sources.declarative.yaml_declarative_source import YamlDeclarativeSource +from airbyte_cdk.sources.source import TState from orjson import orjson @@ -61,16 +62,10 @@ def handle_command(args: List[str]) -> None: handle_remote_manifest_command(args) -def _get_local_yaml_source(args: List[str]): - catalog_path = AirbyteEntrypoint.extract_catalog(args) - config_path = AirbyteEntrypoint.extract_config(args) - state_path = AirbyteEntrypoint.extract_state(args) +def _get_local_yaml_source(args: List[str]) -> SourceLocalYaml: try: - return SourceLocalYaml( - SourceLocalYaml.read_catalog(catalog_path) if catalog_path else None, - SourceLocalYaml.read_config(config_path) if config_path else None, - SourceLocalYaml.read_state(state_path) if state_path else None, - ) + config, catalog, state = _parse_inputs_into_config_catalog_state(args) + return SourceLocalYaml(config=config, catalog=catalog, state=state) except Exception as error: print( orjson.dumps( @@ -112,24 +107,56 @@ def handle_remote_manifest_command(args: List[str]) -> None: message = AirbyteMessage(type=Type.SPEC, spec=spec) print(AirbyteEntrypoint.airbyte_message_to_string(message)) else: - source = create_manifest(args) + source = create_declarative_source(args) launch(source, args) -def create_manifest(args: List[str]) -> ManifestDeclarativeSource: +def create_declarative_source(args: List[str]) -> ConcurrentDeclarativeSource: """Creates the source with the injected config. This essentially does what other low-code sources do at build time, but at runtime, with a user-provided manifest in the config. This better reflects what happens in the connector builder. """ - parsed_args = AirbyteEntrypoint.parse_args(args) - config = BaseConnector.read_config(parsed_args.config) - if "__injected_declarative_manifest" not in config: - raise ValueError( - f"Invalid config: `__injected_declarative_manifest` should be provided at the root of the config but config only has keys {list(config.keys())}" + try: + config, catalog, state = _parse_inputs_into_config_catalog_state(args) + if "__injected_declarative_manifest" not in config: + raise ValueError( + f"Invalid config: `__injected_declarative_manifest` should be provided at the root of the config but config only has keys {list(config.keys())}" + ) + return ConcurrentDeclarativeSource( + config=config, catalog=catalog, state=state, source_config=config.get("__injected_declarative_manifest") ) - return ManifestDeclarativeSource(config.get("__injected_declarative_manifest")) + except Exception as error: + print( + orjson.dumps( + AirbyteMessageSerializer.dump( + AirbyteMessage( + type=Type.TRACE, + trace=AirbyteTraceMessage( + type=TraceType.ERROR, + emitted_at=int(datetime.now().timestamp() * 1000), + error=AirbyteErrorTraceMessage( + message=f"Error starting the sync. This could be due to an invalid configuration or catalog. Please contact Support for assistance. Error: {error}", + stack_trace=traceback.format_exc(), + ), + ), + ) + ) + ).decode() + ) + raise error + + +def _parse_inputs_into_config_catalog_state( + args: List[str], +) -> (Optional[Mapping[str, Any]], Optional[ConfiguredAirbyteCatalog], List[AirbyteStateMessage]): + parsed_args = AirbyteEntrypoint.parse_args(args) + config = ConcurrentDeclarativeSource.read_config(parsed_args.config) if hasattr(parsed_args, "config") else None + catalog = ConcurrentDeclarativeSource.read_catalog(parsed_args.catalog) if hasattr(parsed_args, "catalog") else None + state = ConcurrentDeclarativeSource.read_state(parsed_args.state) if hasattr(parsed_args, "state") else [] + + return config, catalog, state def run(): diff --git a/airbyte-integrations/connectors/source-declarative-manifest/unit_tests/test_source_declarative_local_manifest.py b/airbyte-integrations/connectors/source-declarative-manifest/unit_tests/test_source_declarative_local_manifest.py index dddd710bdbee..e2f03e369f8d 100644 --- a/airbyte-integrations/connectors/source-declarative-manifest/unit_tests/test_source_declarative_local_manifest.py +++ b/airbyte-integrations/connectors/source-declarative-manifest/unit_tests/test_source_declarative_local_manifest.py @@ -2,6 +2,7 @@ # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # +import json from unittest.mock import patch import pytest @@ -10,6 +11,8 @@ POKEAPI_JSON_SPEC_SUBSTRING = '"required":["pokemon_name"]' SUCCESS_CHECK_SUBSTRING = '"connectionStatus":{"status":"SUCCEEDED"}' +FAILED_CHECK_SUBSTRING = '"connectionStatus":{"status":"FAILED"}' + @pytest.fixture(autouse=True) def setup(valid_local_manifest_yaml): @@ -17,21 +20,26 @@ def setup(valid_local_manifest_yaml): with patch('source_declarative_manifest.run.YamlDeclarativeSource._read_and_parse_yaml_file', return_value=valid_local_manifest_yaml): yield + def test_spec_is_poke_api(capsys): run.handle_command(["spec"]) stdout = capsys.readouterr() assert POKEAPI_JSON_SPEC_SUBSTRING in stdout.out + def test_invalid_yaml_throws(capsys, invalid_local_manifest_yaml): with patch('source_declarative_manifest.run.YamlDeclarativeSource._read_and_parse_yaml_file', return_value=invalid_local_manifest_yaml): with pytest.raises(ValidationError): run.handle_command(["spec"]) -def test_given_invalid_config_then_raise_value_error(invalid_local_config_file): - with pytest.raises(ValueError): - run.create_manifest(["check", "--config", str(invalid_local_config_file)]) -def test_given_invalid_config_then_raise_value_error(capsys, valid_local_config_file): +def test_given_invalid_config_then_unsuccessful_check(capsys, invalid_local_config_file): + run.handle_command(["check", "--config", str(invalid_local_config_file)]) + stdout = capsys.readouterr() + assert json.loads(stdout.out).get("connectionStatus").get("status") == "FAILED" + + +def test_given_valid_config_with_successful_check(capsys, valid_local_config_file): run.handle_command(["check", "--config", str(valid_local_config_file)]) stdout = capsys.readouterr() assert SUCCESS_CHECK_SUBSTRING in stdout.out diff --git a/airbyte-integrations/connectors/source-declarative-manifest/unit_tests/test_source_declarative_remote_manifest.py b/airbyte-integrations/connectors/source-declarative-manifest/unit_tests/test_source_declarative_remote_manifest.py index 35091ac0785b..52a363a298c1 100644 --- a/airbyte-integrations/connectors/source-declarative-manifest/unit_tests/test_source_declarative_remote_manifest.py +++ b/airbyte-integrations/connectors/source-declarative-manifest/unit_tests/test_source_declarative_remote_manifest.py @@ -4,7 +4,7 @@ import pytest from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource -from source_declarative_manifest.run import create_manifest, handle_command +from source_declarative_manifest.run import create_declarative_source, handle_command REMOTE_MANIFEST_SPEC_SUBSTRING = '"required":["__injected_declarative_manifest"]' @@ -17,9 +17,9 @@ def test_spec_does_not_raise_value_error(capsys): def test_given_no_injected_declarative_manifest_then_raise_value_error(invalid_remote_config): with pytest.raises(ValueError): - create_manifest(["check", "--config", str(invalid_remote_config)]) + create_declarative_source(["check", "--config", str(invalid_remote_config)]) def test_given_injected_declarative_manifest_then_return_declarative_manifest(valid_remote_config): - source = create_manifest(["check", "--config", str(valid_remote_config)]) + source = create_declarative_source(["check", "--config", str(valid_remote_config)]) assert isinstance(source, ManifestDeclarativeSource) diff --git a/docs/integrations/sources/low-code.md b/docs/integrations/sources/low-code.md index a14575a9a378..273b561b3f01 100644 --- a/docs/integrations/sources/low-code.md +++ b/docs/integrations/sources/low-code.md @@ -9,6 +9,7 @@ The changelog below is automatically updated by the `bump_version` command as pa | Version | Date | Pull Request | Subject | | :------ | :--------- | :------------------------------------------------------- | :------------------------------------------------------------------- | +| 6.2.1 | 2024-11-05 | [48373](https://github.com/airbytehq/airbyte/pull/48373) | Add CDK v6 support to remote manifest mode | | 6.2.0 | 2024-11-05 | [36501](https://github.com/airbytehq/airbyte/pull/36501) | Bump CDK version to 6.2.0 | | 6.1.2 | 2024-11-05 | [48344](https://github.com/airbytehq/airbyte/pull/48344) | Fix discover failing on new CDK | | 6.1.1 | 2024-11-04 | [36501](https://github.com/airbytehq/airbyte/pull/36501) | Bump CDK version to 6.1.1 | From 9e01e5ab634458269924a81acba3e6235040b8e8 Mon Sep 17 00:00:00 2001 From: Natalie Kwong <38087517+nataliekwong@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:07:53 -0800 Subject: [PATCH 02/45] docs: Remove Doc link (#48334) --- docs/integrations/sources/salesforce.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integrations/sources/salesforce.md b/docs/integrations/sources/salesforce.md index 2e97104ea4cc..a28f916e0c08 100644 --- a/docs/integrations/sources/salesforce.md +++ b/docs/integrations/sources/salesforce.md @@ -83,7 +83,7 @@ To obtain these credentials, follow [this walkthrough](https://medium.com/@bpmme 1. If your Salesforce URL is not in the `X.salesforce.com` format, use your Salesforce domain name. For example, if your Salesforce URL is `awesomecompany.force.com` then use that instead of `awesomecompany.salesforce.com`. 2. When running a curl command, run it with the `-L` option to follow any redirects. -3. If you [created a read-only user](https://docs.google.com/document/d/1wZR8pz4MRdc2zUculc9IqoF8JxN87U40IqVnTtcqdrI/edit#heading=h.w5v6h7b2a9y4), use the user credentials when logging in to generate OAuth tokens. +3. If you created a read-only user, use the user credentials when logging in to generate OAuth tokens. From 2279bb0c908eb64a0b39ea2662739d1cd9c39cc2 Mon Sep 17 00:00:00 2001 From: "Pedro S. Lopez" Date: Tue, 5 Nov 2024 20:13:39 -0400 Subject: [PATCH 03/45] docs: add warning about applications being deleted when migrating to SSO on Cloud (#48376) --- docs/access-management/sso-providers/azure-entra-id.md | 4 ++++ docs/access-management/sso-providers/okta.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/docs/access-management/sso-providers/azure-entra-id.md b/docs/access-management/sso-providers/azure-entra-id.md index a04b8a40d5de..5bbef3341258 100644 --- a/docs/access-management/sso-providers/azure-entra-id.md +++ b/docs/access-management/sso-providers/azure-entra-id.md @@ -61,6 +61,10 @@ You'll need to pass your Airbyte contact the following information of the create Once we've received this information from you, We'll setup SSO for you and let you know once it's ready to be used. +:::warning +For security purposes, existing [Applications](https://reference.airbyte.com/reference/authentication) used to access the Airbyte API that were created before enabling SSO **will be disabled** once the user that owns the Application signs in via SSO for the first time. After enabling SSO, please make sure to replace any Application secrets that were previously in use. +::: + diff --git a/docs/access-management/sso-providers/okta.md b/docs/access-management/sso-providers/okta.md index e6937d897b1a..e331d207a8ec 100644 --- a/docs/access-management/sso-providers/okta.md +++ b/docs/access-management/sso-providers/okta.md @@ -65,6 +65,10 @@ On the following screen you'll need to configure all parameters for your Okta ap * **Client Secret** * **Email Domain** (users signing in from this domain will be required to sign in via SSO) + :::warning + For security purposes, existing [Applications](https://reference.airbyte.com/reference/authentication) used to access the Airbyte API that were created before enabling SSO **will be disabled** once the user that owns the Application signs in via SSO for the first time. After enabling SSO, please make sure to replace any Application secrets that were previously in use. + ::: + Create the application with the following parameters: From 7129f2163ed8e24f2371c921a798f58695cb33f3 Mon Sep 17 00:00:00 2001 From: Balaji Seetharaman Date: Wed, 6 Nov 2024 20:45:13 +0530 Subject: [PATCH 04/45] source-finnhub contribution from bala-ceg (#47343) Co-authored-by: Marcos Marx --- .../connectors/source-finnhub/README.md | 33 + .../source-finnhub/acceptance-test-config.yml | 17 + .../connectors/source-finnhub/icon.svg | 8 + .../connectors/source-finnhub/manifest.yaml | 992 ++++++++++++++++++ .../connectors/source-finnhub/metadata.yaml | 35 + docs/integrations/sources/finnhub.md | 38 + 6 files changed, 1123 insertions(+) create mode 100644 airbyte-integrations/connectors/source-finnhub/README.md create mode 100644 airbyte-integrations/connectors/source-finnhub/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-finnhub/icon.svg create mode 100644 airbyte-integrations/connectors/source-finnhub/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-finnhub/metadata.yaml create mode 100644 docs/integrations/sources/finnhub.md diff --git a/airbyte-integrations/connectors/source-finnhub/README.md b/airbyte-integrations/connectors/source-finnhub/README.md new file mode 100644 index 000000000000..bb95fee010c1 --- /dev/null +++ b/airbyte-integrations/connectors/source-finnhub/README.md @@ -0,0 +1,33 @@ +# Finnhub +This directory contains the manifest-only connector for `source-finnhub`. + +Finnhub is a financial data platform that provides real-time stock market, forex, and cryptocurrency data, along with extensive fundamental data, economic indicators, and alternative data for global markets. With its powerful API, Finnhub delivers actionable insights, enabling developers, financial institutions, and traders to integrate market intelligence into their applications, build trading algorithms, and track investment performance. It supports a wide range of financial metrics, including earnings reports, company profiles, and news, making it a comprehensive solution for financial analysis and market research + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-finnhub:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-finnhub build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-finnhub test +``` + diff --git a/airbyte-integrations/connectors/source-finnhub/acceptance-test-config.yml b/airbyte-integrations/connectors/source-finnhub/acceptance-test-config.yml new file mode 100644 index 000000000000..cab576f482a1 --- /dev/null +++ b/airbyte-integrations/connectors/source-finnhub/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-finnhub:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-finnhub/icon.svg b/airbyte-integrations/connectors/source-finnhub/icon.svg new file mode 100644 index 000000000000..1201a237d741 --- /dev/null +++ b/airbyte-integrations/connectors/source-finnhub/icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/airbyte-integrations/connectors/source-finnhub/manifest.yaml b/airbyte-integrations/connectors/source-finnhub/manifest.yaml new file mode 100644 index 000000000000..fd5ce52390d8 --- /dev/null +++ b/airbyte-integrations/connectors/source-finnhub/manifest.yaml @@ -0,0 +1,992 @@ +version: 5.16.0 + +type: DeclarativeSource + +description: >- + Finnhub is a financial data platform that provides real-time stock market, + forex, and cryptocurrency data, along with extensive fundamental data, + economic indicators, and alternative data for global markets. With its + powerful API, Finnhub delivers actionable insights, enabling developers, + financial institutions, and traders to integrate market intelligence into + their applications, build trading algorithms, and track investment + performance. It supports a wide range of financial metrics, including earnings + reports, company profiles, and news, making it a comprehensive solution for + financial analysis and market research +check: + type: CheckStream + stream_names: + - marketnews + +definitions: + streams: + marketnews: + type: DeclarativeStream + name: marketnews + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /news?category={{config['market_new_category']}} + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/marketnews" + stock_symbols: + type: DeclarativeStream + name: stock_symbols + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /stock/symbol?exchange={{ config['exchange'] }} + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/stock_symbols" + basic_financial_report: + type: DeclarativeStream + name: basic_financial_report + primary_key: + - accessNumber + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /stock/financials-reported + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + partition_router: + type: ListPartitionRouter + values: "{{ config.symbols }}" + cursor_field: symbol + request_option: + type: RequestOption + inject_into: request_parameter + field_name: symbol + incremental_sync: + type: DatetimeBasedCursor + cursor_field: startDate + cursor_datetime_formats: + - "%Y-%m-%d %H:%M:%S" + datetime_format: "%Y-%m-%d %H:%M:%S" + start_datetime: + type: MinMaxDatetime + datetime: "{{ config[\"start_date_2\"] }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + start_time_option: + type: RequestOption + inject_into: request_parameter + field_name: from + end_time_option: + type: RequestOption + inject_into: request_parameter + field_name: to + end_datetime: + type: MinMaxDatetime + datetime: "{{ now_utc().strftime('%Y-%m-%dT%H:%M:%SZ') }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/basic_financial_report" + company_profile: + type: DeclarativeStream + name: company_profile + primary_key: + - ticker + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /stock/profile2 + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + partition_router: + type: ListPartitionRouter + values: "{{ config.symbols }}" + cursor_field: symbol + request_option: + type: RequestOption + inject_into: request_parameter + field_name: symbol + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/company_profile" + sec_filings: + type: DeclarativeStream + name: sec_filings + primary_key: + - accessNumber + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /stock/filings + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + partition_router: + type: ListPartitionRouter + values: "{{ config.symbols }}" + cursor_field: symbol + request_option: + type: RequestOption + inject_into: request_parameter + field_name: symbol + incremental_sync: + type: DatetimeBasedCursor + cursor_field: filedDate + cursor_datetime_formats: + - "%Y-%m-%d %H:%M:%S" + datetime_format: "%Y-%m-%d %H:%M:%S" + start_datetime: + type: MinMaxDatetime + datetime: "{{ config[\"start_date_2\"] }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + start_time_option: + type: RequestOption + inject_into: request_parameter + field_name: from + end_time_option: + type: RequestOption + inject_into: request_parameter + field_name: to + end_datetime: + type: MinMaxDatetime + datetime: "{{ now_utc().strftime('%Y-%m-%dT%H:%M:%SZ') }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/sec_filings" + insider_transactions: + type: DeclarativeStream + name: insider_transactions + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /stock/insider-transactions + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + partition_router: + type: ListPartitionRouter + values: "{{ config.symbols }}" + cursor_field: symbol + request_option: + type: RequestOption + inject_into: request_parameter + field_name: symbol + incremental_sync: + type: DatetimeBasedCursor + cursor_field: transactionDate + cursor_datetime_formats: + - "%Y-%m-%d" + datetime_format: "%Y-%m-%d" + start_datetime: + type: MinMaxDatetime + datetime: "{{ config[\"start_date_2\"] }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + start_time_option: + type: RequestOption + inject_into: request_parameter + field_name: from + end_time_option: + type: RequestOption + inject_into: request_parameter + field_name: to + end_datetime: + type: MinMaxDatetime + datetime: "{{ now_utc().strftime('%Y-%m-%dT%H:%M:%SZ') }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/insider_transactions" + insider_sentiment: + type: DeclarativeStream + name: insider_sentiment + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /stock/insider-sentiment + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + partition_router: + type: ListPartitionRouter + values: "{{ config.symbols }}" + cursor_field: symbol + request_option: + type: RequestOption + inject_into: request_parameter + field_name: symbol + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/insider_sentiment" + company_news: + type: DeclarativeStream + name: company_news + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /company-news + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + partition_router: + type: ListPartitionRouter + values: "{{ config.symbols }}" + cursor_field: symbol + request_option: + type: RequestOption + inject_into: request_parameter + field_name: symbol + incremental_sync: + type: DatetimeBasedCursor + cursor_field: datetime + cursor_datetime_formats: + - "%s" + datetime_format: "%Y-%m-%d" + start_datetime: + type: MinMaxDatetime + datetime: "{{ config[\"start_date_2\"] }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + start_time_option: + type: RequestOption + inject_into: request_parameter + field_name: from + end_time_option: + type: RequestOption + inject_into: request_parameter + field_name: to + end_datetime: + type: MinMaxDatetime + datetime: "{{ now_utc().strftime('%Y-%m-%dT%H:%M:%SZ') }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/company_news" + stock_recommendations: + type: DeclarativeStream + name: stock_recommendations + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /stock/recommendation + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + partition_router: + type: ListPartitionRouter + values: "{{ config.symbols }}" + cursor_field: symbol + request_option: + type: RequestOption + inject_into: request_parameter + field_name: symbol + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/stock_recommendations" + earnings_surprises: + type: DeclarativeStream + name: earnings_surprises + primary_key: + - symbol + - period + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /stock/earnings + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + partition_router: + type: ListPartitionRouter + values: "{{ config.symbols }}" + cursor_field: symbol + request_option: + type: RequestOption + inject_into: request_parameter + field_name: symbol + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/earnings_surprises" + stock_quote: + type: DeclarativeStream + name: stock_quote + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /quote + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + partition_router: + type: ListPartitionRouter + values: "{{ config.symbols }}" + cursor_field: symbol + request_option: + type: RequestOption + inject_into: request_parameter + field_name: symbol + transformations: + - type: AddFields + fields: + - path: + - symbol + value: "{{ stream_partition.symbol }}" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/stock_quote" + base_requester: + type: HttpRequester + url_base: https://finnhub.io/api/v1 + authenticator: + type: ApiKeyAuthenticator + api_token: "{{ config[\"api_key\"] }}" + inject_into: + type: RequestOption + field_name: token + inject_into: request_parameter + +streams: + - $ref: "#/definitions/streams/marketnews" + - $ref: "#/definitions/streams/stock_symbols" + - $ref: "#/definitions/streams/basic_financial_report" + - $ref: "#/definitions/streams/company_profile" + - $ref: "#/definitions/streams/sec_filings" + - $ref: "#/definitions/streams/insider_transactions" + - $ref: "#/definitions/streams/insider_sentiment" + - $ref: "#/definitions/streams/company_news" + - $ref: "#/definitions/streams/stock_recommendations" + - $ref: "#/definitions/streams/earnings_surprises" + - $ref: "#/definitions/streams/stock_quote" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - api_key + - symbols + - market_news_category + - exchange + - start_date_2 + properties: + api_key: + type: string + description: The API key to use for authentication + name: api_key + order: 0 + title: API Key + airbyte_secret: true + symbols: + type: array + name: company_symbol + order: 1 + title: Companies + market_news_category: + type: string + description: >- + This parameter can be 1 of the following values general, forex, + crypto, merger. + title: Market News Category + default: general + enum: + - general + - forex + - crypto + - merger + order: 2 + exchange: + type: string + description: "More info: https://finnhub.io/docs/api/stock-symbols" + title: Exchange + default: US + order: 3 + start_date_2: + type: string + title: Start date + format: date-time + pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ + order: 4 + additionalProperties: true + +metadata: + autoImportSchema: + marketnews: true + stock_symbols: true + basic_financial_report: false + company_profile: true + sec_filings: true + insider_transactions: false + insider_sentiment: false + company_news: true + stock_recommendations: false + earnings_surprises: false + stock_quote: true + testedStreams: + marketnews: + streamHash: f1c6821d2ad17da19c15be9a29b964c95fc72b3d + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + stock_symbols: + streamHash: a481b061b2ebb223a5e94e71275cf121b53ae457 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + basic_financial_report: + streamHash: 7fb2565f8171add436ff06128d2bddc65f39948f + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + company_profile: + streamHash: 093d488e2794fae4274cc8beed808b021cdd30b7 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + sec_filings: + streamHash: 5ccfc67778f56038004767e0c879fe9699ba8909 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + insider_transactions: + streamHash: d3e80f2b01eb399e15aaa95c0a645aa795eb4737 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + insider_sentiment: + streamHash: 68f53090b913d3c23afe895d102f2009a19d45a5 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + company_news: + streamHash: 28d15b6953fe6b944ec6bef610f6c69a6aac2c84 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + stock_recommendations: + streamHash: 190cf9650126e66328ec0ebac0f9a324c91fd346 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + earnings_surprises: + streamHash: b89237a3ccb7a1991700822a1cb3c784e6b525bf + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + stock_quote: + streamHash: 59936a915d2fdab55efa6613756c64504578ccf1 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + assist: + docsUrl: https://finnhub.io/docs/api/ + +schemas: + marketnews: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + category: + type: + - string + - "null" + datetime: + type: + - number + - "null" + headline: + type: + - string + - "null" + id: + type: number + image: + type: + - string + - "null" + related: + type: + - string + - "null" + source: + type: + - string + - "null" + summary: + type: + - string + - "null" + url: + type: + - string + - "null" + required: + - id + stock_symbols: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + type: + type: + - string + - "null" + description: + type: + - string + - "null" + currency: + type: + - string + - "null" + displaySymbol: + type: + - string + - "null" + figi: + type: + - string + - "null" + mic: + type: + - string + - "null" + shareClassFIGI: + type: + - string + - "null" + symbol: + type: + - string + - "null" + symbol2: + type: + - string + - "null" + basic_financial_report: + type: object + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: true + properties: {} + company_profile: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + country: + type: + - string + - "null" + currency: + type: + - string + - "null" + estimateCurrency: + type: + - string + - "null" + exchange: + type: + - string + - "null" + finnhubIndustry: + type: + - string + - "null" + ipo: + type: + - string + - "null" + logo: + type: + - string + - "null" + marketCapitalization: + type: + - number + - "null" + name: + type: + - string + - "null" + phone: + type: + - string + - "null" + shareOutstanding: + type: + - number + - "null" + ticker: + type: string + weburl: + type: + - string + - "null" + required: + - ticker + sec_filings: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + acceptedDate: + type: + - string + - "null" + accessNumber: + type: string + cik: + type: + - string + - "null" + filedDate: + type: string + filingUrl: + type: + - string + - "null" + form: + type: + - string + - "null" + reportUrl: + type: + - string + - "null" + symbol: + type: + - string + - "null" + required: + - accessNumber + - filedDate + insider_transactions: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + change: + type: + - number + - "null" + currency: + type: + - string + - "null" + filingDate: + type: + - string + - "null" + id: + type: string + isDerivative: + type: + - boolean + - "null" + name: + type: + - string + - "null" + share: + type: + - number + - "null" + source: + type: + - string + - "null" + symbol: + type: + - string + - "null" + transactionCode: + type: + - string + - "null" + transactionDate: + type: + - string + - "null" + transactionPrice: + type: + - number + - "null" + required: + - id + insider_sentiment: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + data: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + change: + type: + - number + - "null" + month: + type: + - number + - "null" + mspr: + type: + - number + - "null" + symbol: + type: + - string + - "null" + year: + type: + - number + - "null" + symbol: + type: + - string + - "null" + company_news: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + category: + type: + - string + - "null" + datetime: + type: number + headline: + type: + - string + - "null" + id: + type: + - number + - "null" + image: + type: + - string + - "null" + related: + type: + - string + - "null" + source: + type: + - string + - "null" + summary: + type: + - string + - "null" + url: + type: + - string + - "null" + required: + - datetime + stock_recommendations: + type: object + $schema: http://json-schema.org/schema# + properties: + buy: + type: + - number + - "null" + hold: + type: + - number + - "null" + period: + type: + - string + - "null" + sell: + type: + - number + - "null" + strongBuy: + type: + - number + - "null" + strongSell: + type: + - number + - "null" + symbol: + type: + - string + - "null" + additionalProperties: true + earnings_surprises: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + actual: + type: + - number + - "null" + estimate: + type: + - number + - "null" + period: + type: + - string + - "null" + quarter: + type: + - number + - "null" + surprise: + type: + - number + - "null" + surprisePercent: + type: + - number + - "null" + symbol: + type: + - string + - "null" + year: + type: + - number + - "null" + stock_quote: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + c: + type: + - number + - "null" + d: + type: + - number + - "null" + dp: + type: + - number + - "null" + h: + type: + - number + - "null" + l: + type: + - number + - "null" + o: + type: + - number + - "null" + pc: + type: + - number + - "null" + symbol: + type: + - string + - "null" + t: + type: + - number + - "null" diff --git a/airbyte-integrations/connectors/source-finnhub/metadata.yaml b/airbyte-integrations/connectors/source-finnhub/metadata.yaml new file mode 100644 index 000000000000..0d191ed394fd --- /dev/null +++ b/airbyte-integrations/connectors/source-finnhub/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "finnhub.io" + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-finnhub + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.16.0@sha256:6800f806944ee4fccf24ae01f6b8fbefb12d952c3b3da338f51f732b55de51f2 + connectorSubtype: api + connectorType: source + definitionId: ec96d68b-e32c-4f0a-ba28-0b01f176a3bb + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-finnhub + githubIssueLabel: source-finnhub + icon: icon.svg + license: MIT + name: Finnhub + releaseDate: 2024-10-24 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/finnhub + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/finnhub.md b/docs/integrations/sources/finnhub.md new file mode 100644 index 000000000000..9e3b8249e143 --- /dev/null +++ b/docs/integrations/sources/finnhub.md @@ -0,0 +1,38 @@ +# Finnhub +Finnhub is a financial data platform that provides real-time stock market, forex, and cryptocurrency data, along with extensive fundamental data, economic indicators, and alternative data for global markets. With its powerful API, Finnhub delivers actionable insights, enabling developers, financial institutions, and traders to integrate market intelligence into their applications, build trading algorithms, and track investment performance. It supports a wide range of financial metrics, including earnings reports, company profiles, and news, making it a comprehensive solution for financial analysis and market research + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `api_key` | `string` | API Key. The API key to use for authentication | | +| `symbols` | `array` | Companies. | | +| `market_news_category` | `string` | Market News Category. This parameter can be 1 of the following values general, forex, crypto, merger. | general | +| `exchange` | `string` | Exchange. More info: https://finnhub.io/docs/api/stock-symbols | US | +| `start_date_2` | `string` | Start date. | | + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| marketnews | id | No pagination | ✅ | ❌ | +| stock_symbols | | No pagination | ✅ | ❌ | +| basic_financial_report | accessNumber | No pagination | ✅ | ✅ | +| company_profile | ticker | No pagination | ✅ | ❌ | +| sec_filings | accessNumber | No pagination | ✅ | ✅ | +| insider_transactions | | No pagination | ✅ | ✅ | +| insider_sentiment | | No pagination | ✅ | ❌ | +| company_news | | No pagination | ✅ | ✅ | +| stock_recommendations | | No pagination | ✅ | ❌ | +| earnings_surprises | symbol.period | No pagination | ✅ | ❌ | +| stock_quote | | No pagination | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-11-06 | | Initial release by [@marcosmarxm](https://github.com/marcosmarxm) via Connector Builder | + +
From 803658c549ab50e0787acfcca8f3090c49e6a6f3 Mon Sep 17 00:00:00 2001 From: Biplab Bera <123734227+bishalbera@users.noreply.github.com> Date: Wed, 6 Nov 2024 20:54:11 +0530 Subject: [PATCH 05/45] source-zoho-expense contribution from bishalbera (#47406) Co-authored-by: Marcos Marx --- .../connectors/source-zoho-expense/README.md | 33 + .../acceptance-test-config.yml | 17 + .../connectors/source-zoho-expense/icon.svg | 319 +++ .../source-zoho-expense/manifest.yaml | 2240 +++++++++++++++++ .../source-zoho-expense/metadata.yaml | 35 + docs/integrations/sources/zoho-expense.md | 36 + 6 files changed, 2680 insertions(+) create mode 100644 airbyte-integrations/connectors/source-zoho-expense/README.md create mode 100644 airbyte-integrations/connectors/source-zoho-expense/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-zoho-expense/icon.svg create mode 100644 airbyte-integrations/connectors/source-zoho-expense/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-zoho-expense/metadata.yaml create mode 100644 docs/integrations/sources/zoho-expense.md diff --git a/airbyte-integrations/connectors/source-zoho-expense/README.md b/airbyte-integrations/connectors/source-zoho-expense/README.md new file mode 100644 index 000000000000..2ebed0abc0a1 --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-expense/README.md @@ -0,0 +1,33 @@ +# Zoho Expense +This directory contains the manifest-only connector for `source-zoho-expense`. + +Zoho Expense connector enables seamless data synchronization between Zoho Expense and various destinations. This connector automates expense tracking workflows by extracting financial data efficiently, ensuring accurate reporting and streamlined operations. + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-zoho-expense:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-zoho-expense build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-zoho-expense test +``` + diff --git a/airbyte-integrations/connectors/source-zoho-expense/acceptance-test-config.yml b/airbyte-integrations/connectors/source-zoho-expense/acceptance-test-config.yml new file mode 100644 index 000000000000..e02c43eea04b --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-expense/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-zoho-expense:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-zoho-expense/icon.svg b/airbyte-integrations/connectors/source-zoho-expense/icon.svg new file mode 100644 index 000000000000..764c698ecbc1 --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-expense/icon.svg @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + diff --git a/airbyte-integrations/connectors/source-zoho-expense/manifest.yaml b/airbyte-integrations/connectors/source-zoho-expense/manifest.yaml new file mode 100644 index 000000000000..704651381914 --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-expense/manifest.yaml @@ -0,0 +1,2240 @@ +version: 5.16.0 + +type: DeclarativeSource + +description: >- + Zoho Expense connector enables seamless data synchronization between Zoho + Expense and various destinations. This connector automates expense tracking + workflows by extracting financial data efficiently, ensuring accurate + reporting and streamlined operations. + +check: + type: CheckStream + stream_names: + - users + +definitions: + streams: + users: + type: DeclarativeStream + name: users + primary_key: + - user_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /users + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - users + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/users" + trips: + type: DeclarativeStream + name: trips + primary_key: + - trip_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /trips + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - trips + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/trips" + expense_reports: + type: DeclarativeStream + name: expense_reports + primary_key: + - report_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /expensereports + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - expense_reports + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/expense_reports" + projects: + type: DeclarativeStream + name: projects + primary_key: + - project_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /projects + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - projects + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/projects" + customers: + type: DeclarativeStream + name: customers + primary_key: + - contact_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /contacts + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - contacts + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/customers" + organizations: + type: DeclarativeStream + name: organizations + primary_key: + - organization_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /organizations + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - organizations + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/organizations" + expense_categories: + type: DeclarativeStream + name: expense_categories + primary_key: + - category_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /expensecategories + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - expense_accounts + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/expense_categories" + currencies: + type: DeclarativeStream + name: currencies + primary_key: + - currency_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /settings/currencies + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - currencies + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/currencies" + taxes: + type: DeclarativeStream + name: taxes + primary_key: + - tax_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /settings/taxes + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - taxes + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/taxes" + base_requester: + type: HttpRequester + url_base: https://www.zohoapis.{{ config['data_center'] }}/expense/v1 + authenticator: + type: OAuthAuthenticator + client_id: "{{ config[\"client_id\"] }}" + grant_type: refresh_token + client_secret: "{{ config[\"client_secret\"] }}" + refresh_token: "{{ config[\"refresh_token\"] }}" + expires_in_name: expires_in + access_token_name: access_token + refresh_request_body: {} + token_refresh_endpoint: https://accounts.zoho.{{ config["data_center"] }}/oauth/v2/token + +streams: + - $ref: "#/definitions/streams/users" + - $ref: "#/definitions/streams/trips" + - $ref: "#/definitions/streams/expense_reports" + - $ref: "#/definitions/streams/projects" + - $ref: "#/definitions/streams/customers" + - $ref: "#/definitions/streams/organizations" + - $ref: "#/definitions/streams/expense_categories" + - $ref: "#/definitions/streams/currencies" + - $ref: "#/definitions/streams/taxes" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - client_id + - client_secret + - refresh_token + properties: + data_center: + type: string + description: >- + The domain suffix for the Zoho Expense API based on your data center + location (e.g., 'com', 'eu', 'in', etc.) + enum: + - com + - in + - jp + - ca + - com.cn + - sa + - com.au + - eu + name: domain + order: 0 + title: Data Center + default: com + client_id: + type: string + name: client_id + order: 1 + title: OAuth Client ID + airbyte_secret: true + client_secret: + type: string + name: client_secret + order: 2 + title: OAuth Client Secret + airbyte_secret: true + refresh_token: + type: string + name: refresh_token + order: 3 + title: OAuth Refresh Token + airbyte_secret: true + additionalProperties: true + +metadata: + autoImportSchema: + users: true + trips: true + expense_reports: true + projects: true + customers: true + organizations: true + expense_categories: true + currencies: true + taxes: true + testedStreams: + users: + streamHash: 7ebe190ed5cf36fc0eb352e4256418f7e9d82062 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + trips: + streamHash: 3006b5339f6cc116109cbe2fb8aa7029f2933813 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + expense_reports: + streamHash: 44dac027120534cf64c0f9ac9ea701178b7cd86c + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + projects: + streamHash: 5d1a97e0aead1cee724fc6686b5796628c41abd5 + hasResponse: true + responsesAreSuccessful: true + hasRecords: false + primaryKeysArePresent: true + primaryKeysAreUnique: true + customers: + streamHash: 58c100803c4f10d6389885509b2f532d2c81e529 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + organizations: + streamHash: ced9ef3dbf607306d9c45f8f70d05a757e52ae33 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + expense_categories: + streamHash: 94fa40a0885845e1586ebe063c16e460b8d8336d + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + currencies: + streamHash: 97bcca96af464e0f54335cab614c20a0336d1d4b + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + taxes: + streamHash: 9c86b3cad1cd8a0b8d065a8508387e16756ea44a + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + assist: + docsUrl: https://www.zoho.com/expense/api/v1/introduction/#overview + +schemas: + users: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + accounting_app_name: + type: + - string + - "null" + accounting_reference_id: + type: + - string + - "null" + accounting_vendor_reference_id: + type: + - string + - "null" + approval_amount_limit: + type: + - number + - "null" + approver_photo_url: + type: + - string + - "null" + approves_to_email: + type: + - string + - "null" + approves_to_id: + type: + - string + - "null" + approves_to_name: + type: + - string + - "null" + bank_name: + type: + - string + - "null" + can_approve: + type: + - boolean + - "null" + created_time: + type: + - string + - "null" + default_approver_email: + type: + - string + - "null" + default_approver_id: + type: + - string + - "null" + default_approver_name: + type: + - string + - "null" + department_id: + type: + - string + - "null" + department_name: + type: + - string + - "null" + display_name: + type: + - string + - "null" + email: + type: + - string + - "null" + employee_number: + type: + - string + - "null" + getthere_access: + type: + - string + - "null" + getthere_profile_created: + type: + - boolean + - "null" + hr_url: + type: + - string + - "null" + invitation_type: + type: + - string + - "null" + is_current_user: + type: + - boolean + - "null" + is_from_crm: + type: + - boolean + - "null" + is_from_zoho_people: + type: + - boolean + - "null" + is_super_admin: + type: + - boolean + - "null" + last_four_digits: + type: + - string + - "null" + last_modified_time: + type: + - string + - "null" + mobile: + type: + - string + - "null" + name: + type: + - string + - "null" + photo_url: + type: + - string + - "null" + policy_id: + type: + - string + - "null" + policy_name: + type: + - string + - "null" + role_id: + type: + - string + - "null" + status: + type: + - string + - "null" + submission_amount_limit: + type: + - number + - "null" + travel_policy_display_name: + type: + - string + - "null" + travel_policy_id: + type: + - string + - "null" + travel_policy_name: + type: + - string + - "null" + user_id: + type: string + user_mail_in_id: + type: + - string + - "null" + user_role: + type: + - string + - "null" + user_type: + type: + - string + - "null" + required: + - user_id + trips: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + description: + type: + - string + - "null" + approved_date: + type: + - string + - "null" + approver_email: + type: + - string + - "null" + approver_employee_no: + type: + - string + - "null" + approver_id: + type: + - string + - "null" + approver_name: + type: + - string + - "null" + approver_photo_url: + type: + - string + - "null" + approver_zuid: + type: + - string + - "null" + bcy_budget_amount: + type: + - number + - "null" + booking_summary: + type: + - array + - "null" + budget_amount: + type: + - number + - "null" + business_purpose: + type: + - string + - "null" + can_other_travelers_associate_report: + type: + - boolean + - "null" + created_by_email: + type: + - string + - "null" + created_by_id: + type: + - string + - "null" + created_by_name: + type: + - string + - "null" + created_date: + type: + - string + - "null" + created_time: + type: + - string + - "null" + creator_employee_no: + type: + - string + - "null" + creator_photo_url: + type: + - string + - "null" + currency_code: + type: + - string + - "null" + currency_id: + type: + - string + - "null" + custom_fields: + type: + - array + - "null" + customer_id: + type: + - string + - "null" + customer_name: + type: + - string + - "null" + department_id: + type: + - string + - "null" + department_name: + type: + - string + - "null" + departure: + type: + - string + - "null" + destination_city: + type: + - string + - "null" + destination_country: + type: + - string + - "null" + destination_state: + type: + - string + - "null" + employee_number: + type: + - string + - "null" + end_date: + type: + - string + - "null" + exchange_rate: + type: + - number + - "null" + from_source: + type: + - string + - "null" + is_archived: + type: + - boolean + - "null" + is_billable: + type: + - boolean + - "null" + is_international: + type: + - boolean + - "null" + is_round_trip: + type: + - boolean + - "null" + is_visa_required: + type: + - boolean + - "null" + last_modified_time: + type: + - string + - "null" + last_submitted_date: + type: + - string + - "null" + meal_preference: + type: + - string + - "null" + notes: + type: + - string + - "null" + policy_display_name: + type: + - string + - "null" + policy_id: + type: + - string + - "null" + policy_name: + type: + - string + - "null" + price_precision: + type: + - number + - "null" + project_id: + type: + - string + - "null" + project_name: + type: + - string + - "null" + reports_count: + type: + - number + - "null" + seat_preference: + type: + - string + - "null" + start_date: + type: + - string + - "null" + status: + type: + - string + - "null" + sub_status: + type: + - string + - "null" + submitted_by: + type: + - number + - "null" + submitted_date: + type: + - string + - "null" + submitted_to_department_name: + type: + - string + - "null" + submitted_to_email: + type: + - string + - "null" + submitted_to_employee_no: + type: + - string + - "null" + submitted_to_id: + type: + - string + - "null" + submitted_to_name: + type: + - string + - "null" + submitter_department_name: + type: + - string + - "null" + submitter_email: + type: + - string + - "null" + submitter_employee_no: + type: + - string + - "null" + submitter_name: + type: + - string + - "null" + submitter_photo_url: + type: + - string + - "null" + submitter_zuid: + type: + - string + - "null" + trip_id: + type: string + trip_name: + type: + - string + - "null" + trip_number: + type: + - string + - "null" + user_custom_fields: + type: + - array + - "null" + user_email: + type: + - string + - "null" + user_id: + type: + - string + - "null" + user_name: + type: + - string + - "null" + required: + - trip_id + expense_reports: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + description: + type: + - string + - "null" + accounting_app_name: + type: + - string + - "null" + accounting_reference_id: + type: + - string + - "null" + accounting_sync_time: + type: + - string + - "null" + amount_policy_violation_count: + type: + - number + - "null" + amount_to_be_reimbursed: + type: + - number + - "null" + approved_date: + type: + - string + - "null" + approver_email: + type: + - string + - "null" + approver_employee_no: + type: + - string + - "null" + approver_id: + type: + - string + - "null" + approver_name: + type: + - string + - "null" + approver_photo_url: + type: + - string + - "null" + approver_zuid: + type: + - string + - "null" + audit_status: + type: + - string + - "null" + can_push_to_accounting_app: + type: + - boolean + - "null" + claim_type_id: + type: + - string + - "null" + claim_type_name: + type: + - string + - "null" + comments_count: + type: + - number + - "null" + created_by_id: + type: + - string + - "null" + created_by_name: + type: + - string + - "null" + created_by_zuid: + type: + - string + - "null" + created_date: + type: + - string + - "null" + created_time: + type: + - string + - "null" + creator_employee_no: + type: + - string + - "null" + creator_photo_url: + type: + - string + - "null" + currency_code: + type: + - string + - "null" + currency_id: + type: + - string + - "null" + custom_fields: + type: + - array + - "null" + custom_policy_violation_count: + type: + - number + - "null" + customer_id: + type: + - string + - "null" + customer_name: + type: + - string + - "null" + department_id: + type: + - string + - "null" + department_name: + type: + - string + - "null" + description_policy_violation_count: + type: + - number + - "null" + due_date: + type: + - string + - "null" + due_days: + type: + - string + - "null" + employee_number: + type: + - string + - "null" + end_date: + type: + - string + - "null" + expense_count: + type: + - number + - "null" + imprest_account_id: + type: + - string + - "null" + imprest_account_name: + type: + - string + - "null" + is_ach_reimbursement: + type: + - boolean + - "null" + is_archived: + type: + - boolean + - "null" + is_international_trip: + type: + - string + - "null" + is_shared_report: + type: + - boolean + - "null" + last_modified_date: + type: + - string + - "null" + last_modified_time: + type: + - string + - "null" + last_submitted_date: + type: + - string + - "null" + non_reimbursable_total: + type: + - number + - "null" + payrun_name: + type: + - string + - "null" + policy_display_name: + type: + - string + - "null" + policy_id: + type: + - string + - "null" + policy_name: + type: + - string + - "null" + policy_violated: + type: + - boolean + - "null" + project_id: + type: + - string + - "null" + project_name: + type: + - string + - "null" + rcy_currency_code: + type: + - string + - "null" + rcy_currency_id: + type: + - string + - "null" + rcy_currency_symbol: + type: + - string + - "null" + rcy_exchange_rate: + type: + - number + - "null" + rcy_non_reimbursable_total: + type: + - number + - "null" + rcy_price_precision: + type: + - number + - "null" + rcy_reimbursable_total: + type: + - number + - "null" + rcy_total: + type: + - number + - "null" + receipt_policy_violation_count: + type: + - number + - "null" + reimbursable_total: + type: + - number + - "null" + reimbursement_date: + type: + - string + - "null" + report_id: + type: string + report_name: + type: + - string + - "null" + report_number: + type: + - string + - "null" + start_date: + type: + - string + - "null" + status: + type: + - string + - "null" + sub_status: + type: + - string + - "null" + submitted_by: + type: + - string + - "null" + submitted_date: + type: + - string + - "null" + submitted_to_email: + type: + - string + - "null" + submitted_to_employee_no: + type: + - string + - "null" + submitted_to_id: + type: + - string + - "null" + submitted_to_name: + type: + - string + - "null" + submitted_to_zuid: + type: + - string + - "null" + submitter_email: + type: + - string + - "null" + submitter_employee_no: + type: + - string + - "null" + submitter_name: + type: + - string + - "null" + submitter_photo_url: + type: + - string + - "null" + submitter_zuid: + type: + - string + - "null" + total: + type: + - number + - "null" + total_policy_violation_count: + type: + - number + - "null" + trip_budget_amount: + type: + - number + - "null" + trip_budget_amount_currency_code: + type: + - string + - "null" + trip_budget_amount_currency_id: + type: + - string + - "null" + trip_departure: + type: + - string + - "null" + trip_destination_city: + type: + - string + - "null" + trip_destination_country: + type: + - string + - "null" + trip_end_date: + type: + - string + - "null" + trip_id: + type: + - string + - "null" + trip_name: + type: + - string + - "null" + trip_number: + type: + - string + - "null" + trip_start_date: + type: + - string + - "null" + trip_status: + type: + - string + - "null" + uncategorized_expense_count: + type: + - number + - "null" + user_custom_fields: + type: + - array + - "null" + user_email: + type: + - string + - "null" + user_id: + type: + - string + - "null" + user_name: + type: + - string + - "null" + user_photo_url: + type: + - string + - "null" + zcrm_potential_id: + type: + - string + - "null" + zcrm_potential_name: + type: + - string + - "null" + required: + - report_id + projects: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + code: + type: + - number + - "null" + message: + type: + - string + - "null" + page_context: + type: + - object + - "null" + properties: + applied_filter: + type: + - string + - "null" + custom_fields: + type: + - array + - "null" + has_more_page: + type: + - boolean + - "null" + page: + type: + - number + - "null" + per_page: + type: + - number + - "null" + report_name: + type: + - string + - "null" + sort_column: + type: + - string + - "null" + sort_order: + type: + - string + - "null" + projects: + type: + - array + - "null" + customers: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + ach_supported: + type: + - boolean + - "null" + company_name: + type: + - string + - "null" + contact_id: + type: string + contact_name: + type: + - string + - "null" + contact_type: + type: + - string + - "null" + contact_type_formatted: + type: + - string + - "null" + created_time: + type: + - string + - "null" + created_time_formatted: + type: + - string + - "null" + currency_code: + type: + - string + - "null" + currency_id: + type: + - string + - "null" + custom_field_hash: + type: + - object + - "null" + custom_fields: + type: + - array + - "null" + customer_name: + type: + - string + - "null" + customer_sub_type: + type: + - string + - "null" + email: + type: + - string + - "null" + facebook: + type: + - string + - "null" + first_name: + type: + - string + - "null" + has_attachment: + type: + - boolean + - "null" + is_linked_with_zohocrm: + type: + - boolean + - "null" + language_code: + type: + - string + - "null" + language_code_formatted: + type: + - string + - "null" + last_modified_time: + type: + - string + - "null" + last_modified_time_formatted: + type: + - string + - "null" + last_name: + type: + - string + - "null" + mobile: + type: + - string + - "null" + outstanding_payable_amount: + type: + - number + - "null" + outstanding_payable_amount_bcy: + type: + - number + - "null" + outstanding_receivable_amount: + type: + - number + - "null" + outstanding_receivable_amount_bcy: + type: + - number + - "null" + payment_terms: + type: + - number + - "null" + payment_terms_label: + type: + - string + - "null" + phone: + type: + - string + - "null" + portal_status: + type: + - string + - "null" + primary_contact_id: + type: + - string + - "null" + source: + type: + - string + - "null" + status: + type: + - string + - "null" + twitter: + type: + - string + - "null" + unused_credits_payable_amount: + type: + - number + - "null" + unused_credits_payable_amount_bcy: + type: + - number + - "null" + unused_credits_receivable_amount: + type: + - number + - "null" + unused_credits_receivable_amount_bcy: + type: + - number + - "null" + vendor_name: + type: + - string + - "null" + website: + type: + - string + - "null" + required: + - contact_id + organizations: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + version: + type: + - string + - "null" + AppList: + type: + - array + - "null" + items: + type: + - string + - "null" + account_created_date: + type: + - string + - "null" + account_created_date_formatted: + type: + - string + - "null" + can_change_timezone: + type: + - boolean + - "null" + can_sign_invoice: + type: + - boolean + - "null" + contact_name: + type: + - string + - "null" + country: + type: + - string + - "null" + country_code: + type: + - string + - "null" + currency_code: + type: + - string + - "null" + currency_format: + type: + - string + - "null" + currency_id: + type: + - string + - "null" + currency_symbol: + type: + - string + - "null" + custom_field_type: + type: + - number + - "null" + custom_fields: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + index: + type: + - number + - "null" + label: + type: + - string + - "null" + value: + type: + - string + - "null" + digital_signature_mode: + type: + - string + - "null" + email: + type: + - string + - "null" + field_separator: + type: + - string + - "null" + fiscal_year_start_month: + type: + - number + - "null" + isOrgActive: + type: + - boolean + - "null" + isOrgNotSupported: + type: + - boolean + - "null" + is_advance_approval_enabled: + type: + - boolean + - "null" + is_auto_scan_allowed: + type: + - boolean + - "null" + is_default_org: + type: + - boolean + - "null" + is_designated_zone: + type: + - boolean + - "null" + is_dsign_required: + type: + - boolean + - "null" + is_export_with_payment_enabled: + type: + - boolean + - "null" + is_free_zone: + type: + - boolean + - "null" + is_gst_india_version: + type: + - boolean + - "null" + is_hsn_or_sac_enabled: + type: + - boolean + - "null" + is_international_trade_enabled: + type: + - boolean + - "null" + is_invoice_pmt_tds_allowed: + type: + - boolean + - "null" + is_org_active: + type: + - boolean + - "null" + is_quick_setup_completed: + type: + - boolean + - "null" + is_registered_for_composite_scheme: + type: + - boolean + - "null" + is_registered_for_gst: + type: + - boolean + - "null" + is_registered_for_tax: + type: + - boolean + - "null" + is_sales_inclusive_tax_enabled: + type: + - boolean + - "null" + is_sales_reverse_charge_enabled: + type: + - boolean + - "null" + is_search360_enabled: + type: + - string + - "null" + is_sku_enabled: + type: + - boolean + - "null" + is_solo_org: + type: + - boolean + - "null" + is_tax_registered: + type: + - boolean + - "null" + is_trial_expired: + type: + - boolean + - "null" + is_trial_period_extended: + type: + - boolean + - "null" + is_trip_enabled: + type: + - boolean + - "null" + is_user_dsign_mandatory: + type: + - boolean + - "null" + is_ziedition: + type: + - boolean + - "null" + is_zpayroll_grid: + type: + - boolean + - "null" + language_code: + type: + - string + - "null" + mode: + type: + - string + - "null" + name: + type: + - string + - "null" + org_action: + type: + - string + - "null" + org_created_app_source: + type: + - number + - "null" + org_joined_app_list: + type: + - array + - "null" + items: + type: + - string + - "null" + org_settings: + type: + - boolean + - "null" + org_type: + type: + - string + - "null" + organization_id: + type: string + partners_domain: + type: + - string + - "null" + phone: + type: + - string + - "null" + plan_name: + type: + - string + - "null" + plan_period: + type: + - string + - "null" + plan_type: + type: + - number + - "null" + price_precision: + type: + - number + - "null" + sales_tax_type: + type: + - string + - "null" + source: + type: + - number + - "null" + state: + type: + - string + - "null" + state_code: + type: + - string + - "null" + tax_group_enabled: + type: + - boolean + - "null" + time_zone: + type: + - string + - "null" + time_zone_formatted: + type: + - string + - "null" + user_status: + type: + - number + - "null" + user_status_formatted: + type: + - string + - "null" + version_formatted: + type: + - string + - "null" + zi_zb_client: + type: + - number + - "null" + zi_zb_edition: + type: + - number + - "null" + zoho_one_org: + type: + - string + - "null" + required: + - organization_id + expense_categories: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + description: + type: + - string + - "null" + attachment_file_name: + type: + - string + - "null" + can_delete: + type: + - boolean + - "null" + can_override_settings: + type: + - boolean + - "null" + can_show: + type: + - boolean + - "null" + category_hint: + type: + - string + - "null" + category_icon: + type: + - string + - "null" + category_id: + type: string + category_name: + type: + - string + - "null" + category_name_with_gl_code: + type: + - string + - "null" + category_type: + type: + - string + - "null" + child_count: + type: + - string + - "null" + depth: + type: + - number + - "null" + expense_type_id: + type: + - string + - "null" + expense_type_name: + type: + - string + - "null" + flat_amount: + type: + - number + - "null" + gl_code: + type: + - string + - "null" + is_child_present: + type: + - boolean + - "null" + is_custom: + type: + - boolean + - "null" + is_description_required: + type: + - boolean + - "null" + is_disabled_master: + type: + - boolean + - "null" + is_enabled: + type: + - boolean + - "null" + is_maximum_amount_required: + type: + - boolean + - "null" + is_mileage: + type: + - boolean + - "null" + is_perdiem_account: + type: + - boolean + - "null" + is_receipt_required: + type: + - boolean + - "null" + is_super_parent: + type: + - boolean + - "null" + maximum_allowed_amount: + type: + - number + - "null" + page_layout_id: + type: + - string + - "null" + page_layout_name: + type: + - string + - "null" + parent_account_id: + type: + - string + - "null" + parent_account_name: + type: + - string + - "null" + receipt_required_amount: + type: + - number + - "null" + status: + type: + - string + - "null" + required: + - category_id + currencies: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + currency_code: + type: + - string + - "null" + currency_format: + type: + - string + - "null" + currency_id: + type: string + currency_name: + type: + - string + - "null" + currency_name_formatted: + type: + - string + - "null" + currency_symbol: + type: + - string + - "null" + effective_date: + type: + - string + - "null" + exchange_rate: + type: + - number + - "null" + is_base_currency: + type: + - boolean + - "null" + price_precision: + type: + - number + - "null" + rcy_configured_count: + type: + - number + - "null" + required: + - currency_id + taxes: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + accounting_app_name: + type: + - string + - "null" + accounting_reference_id: + type: + - string + - "null" + diff_rate_reason: + type: + - string + - "null" + end_date: + type: + - string + - "null" + is_editable: + type: + - boolean + - "null" + is_inactive: + type: + - boolean + - "null" + is_value_added: + type: + - number + - "null" + last_modified_time: + type: + - string + - "null" + output_tax_account_name: + type: + - string + - "null" + start_date: + type: + - string + - "null" + status: + type: + - string + - "null" + tax_account_id: + type: + - string + - "null" + tax_id: + type: string + tax_name: + type: + - string + - "null" + tax_percentage: + type: + - number + - "null" + tax_type: + type: + - string + - "null" + required: + - tax_id diff --git a/airbyte-integrations/connectors/source-zoho-expense/metadata.yaml b/airbyte-integrations/connectors/source-zoho-expense/metadata.yaml new file mode 100644 index 000000000000..7853e294d11b --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-expense/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "zohoapis." + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-zoho-expense + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.16.0@sha256:6800f806944ee4fccf24ae01f6b8fbefb12d952c3b3da338f51f732b55de51f2 + connectorSubtype: api + connectorType: source + definitionId: 9acfeefc-dad6-4887-a928-6c15ce5737b6 + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-zoho-expense + githubIssueLabel: source-zoho-expense + icon: icon.svg + license: MIT + name: Zoho Expense + releaseDate: 2024-10-26 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/zoho-expense + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/zoho-expense.md b/docs/integrations/sources/zoho-expense.md new file mode 100644 index 000000000000..e55a1cfd21d0 --- /dev/null +++ b/docs/integrations/sources/zoho-expense.md @@ -0,0 +1,36 @@ +# Zoho Expense +Zoho Expense connector enables seamless data synchronization between Zoho Expense and various destinations. This connector automates expense tracking workflows by extracting financial data efficiently, ensuring accurate reporting and streamlined operations. + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `client_id` | `string` | OAuth Client ID. | | +| `client_secret` | `string` | OAuth Client Secret. | | +| `refresh_token` | `string` | OAuth Refresh Token. | | +| `data_center` | `string` | Data Center. The domain suffix for the Zoho Expense API based on your data center location (e.g., `com`, `in`, `jp` etc.) | `com` | + + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| users | user_id | DefaultPaginator | ✅ | ❌ | +| trips | trip_id | DefaultPaginator | ✅ | ❌ | +| expense_reports | report_id | DefaultPaginator | ✅ | ❌ | +| projects | | DefaultPaginator | ✅ | ❌ | +| customers | contact_id | DefaultPaginator | ✅ | ❌ | +| organizations | organization_id | DefaultPaginator | ✅ | ❌ | +| expense_categories | category_id | DefaultPaginator | ✅ | ❌ | +| currencies | currency_id | DefaultPaginator | ✅ | ❌ | +| taxes | tax_id | DefaultPaginator | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-10-26 | | Initial release by [@bishalbera](https://github.com/bishalbera) via Connector Builder | + +
From 5e17bbc604fc20b3d18e7205d437a85d8c419755 Mon Sep 17 00:00:00 2001 From: Biplab Bera <123734227+bishalbera@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:08:41 +0530 Subject: [PATCH 06/45] source-zoho-bigin contribution from bishalbera (#47414) Co-authored-by: Marcos Marx --- .../connectors/source-zoho-bigin/README.md | 33 + .../acceptance-test-config.yml | 17 + .../connectors/source-zoho-bigin/icon.svg | 369 +++++ .../source-zoho-bigin/manifest.yaml | 1347 +++++++++++++++++ .../source-zoho-bigin/metadata.yaml | 35 + docs/integrations/sources/zoho-bigin.md | 37 + 6 files changed, 1838 insertions(+) create mode 100644 airbyte-integrations/connectors/source-zoho-bigin/README.md create mode 100644 airbyte-integrations/connectors/source-zoho-bigin/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-zoho-bigin/icon.svg create mode 100644 airbyte-integrations/connectors/source-zoho-bigin/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-zoho-bigin/metadata.yaml create mode 100644 docs/integrations/sources/zoho-bigin.md diff --git a/airbyte-integrations/connectors/source-zoho-bigin/README.md b/airbyte-integrations/connectors/source-zoho-bigin/README.md new file mode 100644 index 000000000000..1374efcd7c4c --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-bigin/README.md @@ -0,0 +1,33 @@ +# Zoho Bigin +This directory contains the manifest-only connector for `source-zoho-bigin`. + + Zoho Bigin connector enables seamless data sync between Zoho Bigin and other platforms. This connector automates CRM data integration, improving workflows and ensuring real-time access to customer insights across tools. + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-zoho-bigin:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-zoho-bigin build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-zoho-bigin test +``` + diff --git a/airbyte-integrations/connectors/source-zoho-bigin/acceptance-test-config.yml b/airbyte-integrations/connectors/source-zoho-bigin/acceptance-test-config.yml new file mode 100644 index 000000000000..09b318895df0 --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-bigin/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-zoho-bigin:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-zoho-bigin/icon.svg b/airbyte-integrations/connectors/source-zoho-bigin/icon.svg new file mode 100644 index 000000000000..38409d2e747d --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-bigin/icon.svg @@ -0,0 +1,369 @@ + + + + + + + + + + + + + + + + + + + diff --git a/airbyte-integrations/connectors/source-zoho-bigin/manifest.yaml b/airbyte-integrations/connectors/source-zoho-bigin/manifest.yaml new file mode 100644 index 000000000000..e503b92d7e04 --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-bigin/manifest.yaml @@ -0,0 +1,1347 @@ +version: 5.15.0 + +type: DeclarativeSource + +description: " Zoho Bigin connector enables seamless data sync between Zoho Bigin and other platforms. This connector automates CRM data integration, improving workflows and ensuring real-time access to customer insights across tools." + +check: + type: CheckStream + stream_names: + - users + +definitions: + streams: + users: + type: DeclarativeStream + name: users + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /users + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - users + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/users" + modules: + type: DeclarativeStream + name: modules + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /settings/modules + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - modules + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/modules" + organizations: + type: DeclarativeStream + name: organizations + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /org + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - org + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/organizations" + roles: + type: DeclarativeStream + name: roles + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /settings/roles + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - roles + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/roles" + notes: + type: DeclarativeStream + name: notes + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /Notes + http_method: GET + request_parameters: + fields: Note_Title,Note_Content,Parent_Id + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: PageIncrement + page_size: 200 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/notes" + tags: + type: DeclarativeStream + name: tags + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /settings/tags + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - tags + partition_router: + type: ListPartitionRouter + values: + - Contacts + - Pipelines + - Companies + - Products + - Tasks + - Events + - Calls + cursor_field: module + request_option: + type: RequestOption + inject_into: request_parameter + field_name: module + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/tags" + companies: + type: DeclarativeStream + name: companies + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /Accounts + http_method: GET + request_parameters: + fields: Account_Name,Phone,Website + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page_token + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: per_page + pagination_strategy: + type: CursorPagination + page_size: 200 + cursor_value: "{{ response.get('info', {}).get('next_page_token') }}" + stop_condition: "{{ not response.get('info', {}).get('more_records', False) }}" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/companies" + contacts: + type: DeclarativeStream + name: contacts + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /Contacts + http_method: GET + request_parameters: + fields: Last_Name,Email + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page_token + pagination_strategy: + type: CursorPagination + cursor_value: "{{ response.get('info', {}).get('next_page_token', '') }}" + stop_condition: "{{ not response.get('info', {}).get('more_records', False) }}" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/contacts" + tasks: + type: DeclarativeStream + name: tasks + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /Tasks + http_method: GET + request_parameters: + fields: Owner,Subject,Due Date,Remind_At + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page_token + pagination_strategy: + type: CursorPagination + cursor_value: "{{ response.get('info', {}).get('next_page_token', '') }}" + stop_condition: "{{ not response.get('info', {}).get('more_records', False) }}" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/tasks" + events: + type: DeclarativeStream + name: events + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /Events + http_method: GET + request_parameters: + fields: Owner,Event_Title,Start_DateTime,End_DateTime + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: per_page + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + start_from_page: 0 + page_size: 200 + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/events" + products: + type: DeclarativeStream + name: products + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /Products + http_method: GET + request_parameters: + fields: >- + Owner,Product_Name,Product_Category,Unit_Price,Description,Product_Active + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page_token + pagination_strategy: + type: CursorPagination + cursor_value: "{{ response.get('info', {}).get('next_page_token', '') }}" + stop_condition: "{{ not response.get('info', {}).get('more_records', False) }}" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/products" + base_requester: + type: HttpRequester + url_base: https://www.zohoapis.{{ config["data_center"] }}/bigin/v2 + authenticator: + type: OAuthAuthenticator + client_id: "{{ config[\"client_id\"] }}" + grant_type: refresh_token + client_secret: "{{ config[\"client_secret\"] }}" + refresh_token: "{{ config[\"client_refresh_token\"] }}" + expires_in_name: expires_in + access_token_name: access_token + refresh_request_body: {} + token_refresh_endpoint: https://accounts.zoho.{{ config["data_center"] }}/oauth/v2/token + +streams: + - $ref: "#/definitions/streams/users" + - $ref: "#/definitions/streams/modules" + - $ref: "#/definitions/streams/organizations" + - $ref: "#/definitions/streams/roles" + - $ref: "#/definitions/streams/notes" + - $ref: "#/definitions/streams/tags" + - $ref: "#/definitions/streams/companies" + - $ref: "#/definitions/streams/contacts" + - $ref: "#/definitions/streams/tasks" + - $ref: "#/definitions/streams/events" + - $ref: "#/definitions/streams/products" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - client_id + - data_center + - client_secret + - client_refresh_token + - module_name + properties: + client_id: + type: string + name: client_id + order: 0 + title: OAuth Client ID + airbyte_secret: true + data_center: + type: string + description: The data center where the Bigin account's resources are hosted + enum: + - com + - com.au + - eu + - in + - com.cn + - jp + name: data_center + order: 1 + title: Data Center + default: com + client_secret: + type: string + name: client_secret + order: 2 + title: OAuth Client Secret + airbyte_secret: true + client_refresh_token: + type: string + order: 3 + title: Refresh token + airbyte_secret: true + module_name: + type: string + order: 4 + title: Module Name + additionalProperties: true + +metadata: + autoImportSchema: + users: true + modules: true + organizations: true + roles: true + notes: true + tags: true + companies: true + contacts: true + tasks: true + events: true + products: true + testedStreams: + users: + streamHash: f9c8fc4a62114f362eb6a984c79ba314d934be17 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + modules: + streamHash: 7027ed4afe12e9a9c1cb9528ed5f1d5bf99c8e76 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + organizations: + streamHash: 2db7dbec4301f64e9296f9d3150a26fcf9f97f3a + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + roles: + streamHash: 36e98e146a92bc49dc30c62dd6ba105598e962c8 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + notes: + streamHash: 751e00f2202ac83eb64ee8ad4c2321287834f2e9 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + tags: + streamHash: 1f8fce21c3a7316d352fa01f7bc49213556e74e2 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + companies: + streamHash: f41b79d9489ccffd58e8de6fa8029eb8feeadeb0 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + contacts: + streamHash: fb71d11b5a32e69fbcbf527c82e98bc8c3149a49 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + tasks: + streamHash: f28271e7d149c1f1590199c0835fdf32831fb20b + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + events: + streamHash: f96faf2540b727a7e1cf24134f8f6cbec82133e3 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + products: + hasRecords: true + streamHash: 8517b62f6a46b2eefaab27dc2a7ba6f6c7f48c9d + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + assist: + docsUrl: https://www.bigin.com/developer/docs/apis/v2/modules-api.html + +schemas: + users: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + Isonline: + type: + - boolean + - "null" + Modified_By: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + name: + type: + - string + - "null" + Modified_Time: + type: + - string + - "null" + category: + type: + - string + - "null" + confirm: + type: + - boolean + - "null" + country: + type: + - string + - "null" + country_locale: + type: + - string + - "null" + created_by: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + name: + type: + - string + - "null" + created_time: + type: + - string + - "null" + customize_info: + type: + - object + - "null" + properties: + show_detail_view: + type: + - boolean + - "null" + show_home: + type: + - boolean + - "null" + date_format: + type: + - string + - "null" + decimal_separator: + type: + - string + - "null" + default_tab_group: + type: + - string + - "null" + email: + type: + - string + - "null" + first_name: + type: + - string + - "null" + full_name: + type: + - string + - "null" + id: + type: string + language: + type: + - string + - "null" + last_name: + type: + - string + - "null" + locale: + type: + - string + - "null" + microsoft: + type: + - boolean + - "null" + name_format: + type: + - string + - "null" + number_separator: + type: + - string + - "null" + offset: + type: + - number + - "null" + personal_account: + type: + - boolean + - "null" + profile: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + name: + type: + - string + - "null" + role: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + name: + type: + - string + - "null" + sandboxDeveloper: + type: + - boolean + - "null" + sort_order_preference: + type: + - string + - "null" + state: + type: + - string + - "null" + status: + type: + - string + - "null" + theme: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + background: + type: + - string + - "null" + normal_tab: + type: + - object + - "null" + properties: + background: + type: + - string + - "null" + font_color: + type: + - string + - "null" + screen: + type: + - string + - "null" + selected_tab: + type: + - object + - "null" + properties: + background: + type: + - string + - "null" + font_color: + type: + - string + - "null" + time_format: + type: + - string + - "null" + time_zone: + type: + - string + - "null" + zuid: + type: + - string + - "null" + required: + - id + modules: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + access_type: + type: + - string + - "null" + actual_plural_label: + type: + - string + - "null" + actual_singular_label: + type: + - string + - "null" + api_name: + type: + - string + - "null" + api_supported: + type: + - boolean + - "null" + arguments: + type: + - array + - "null" + business_card_field_limit: + type: + - number + - "null" + convertable: + type: + - boolean + - "null" + creatable: + type: + - boolean + - "null" + deletable: + type: + - boolean + - "null" + editable: + type: + - boolean + - "null" + emailTemplate_support: + type: + - boolean + - "null" + email_parser_supported: + type: + - boolean + - "null" + feeds_required: + type: + - boolean + - "null" + filter_supported: + type: + - boolean + - "null" + generated_type: + type: + - string + - "null" + global_search_supported: + type: + - boolean + - "null" + has_more_profiles: + type: + - boolean + - "null" + id: + type: string + inventory_template_supported: + type: + - boolean + - "null" + isBlueprintSupported: + type: + - boolean + - "null" + modified_by: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + name: + type: + - string + - "null" + modified_time: + type: + - string + - "null" + module_name: + type: + - string + - "null" + parent_module: + type: + - object + - "null" + properties: + api_name: + type: + - string + - "null" + id: + type: + - string + - "null" + plural_label: + type: + - string + - "null" + presence_sub_menu: + type: + - boolean + - "null" + profile_count: + type: + - number + - "null" + profiles: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + name: + type: + - string + - "null" + public_fields_configured: + type: + - boolean + - "null" + quick_create: + type: + - boolean + - "null" + scoring_supported: + type: + - boolean + - "null" + sequence_number: + type: + - number + - "null" + show_as_tab: + type: + - boolean + - "null" + singular_label: + type: + - string + - "null" + triggers_supported: + type: + - boolean + - "null" + viewable: + type: + - boolean + - "null" + visibility: + type: + - number + - "null" + visible: + type: + - boolean + - "null" + webform_supported: + type: + - boolean + - "null" + required: + - id + organizations: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + company_name: + type: + - string + - "null" + country_code: + type: + - string + - "null" + currency: + type: + - string + - "null" + currency_locale: + type: + - string + - "null" + currency_symbol: + type: + - string + - "null" + deletable_org_account: + type: + - boolean + - "null" + domain_name: + type: + - string + - "null" + gapps_enabled: + type: + - boolean + - "null" + hierarchy_preferences: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + hipaa_compliance_enabled: + type: + - boolean + - "null" + id: + type: string + iso_code: + type: + - string + - "null" + license_details: + type: + - object + - "null" + properties: + paid: + type: + - boolean + - "null" + paid_type: + type: + - string + - "null" + portal_users_license_purchased: + type: + - number + - "null" + trial_expiry: + type: + - string + - "null" + trial_type: + type: + - string + - "null" + users_license_purchased: + type: + - number + - "null" + lite_users_enabled: + type: + - boolean + - "null" + mc_status: + type: + - boolean + - "null" + phone: + type: + - string + - "null" + primary_email: + type: + - string + - "null" + primary_zuid: + type: + - string + - "null" + privacy_settings: + type: + - boolean + - "null" + time_zone: + type: + - string + - "null" + translation_enabled: + type: + - boolean + - "null" + zgid: + type: + - string + - "null" + required: + - id + roles: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + description: + type: + - string + - "null" + display_label: + type: + - string + - "null" + id: + type: string + name: + type: + - string + - "null" + reporting_to: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + name: + type: + - string + - "null" + share_with_peers: + type: + - boolean + - "null" + required: + - id + notes: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + Note_Content: + type: + - string + - "null" + Note_Title: + type: + - string + - "null" + Parent_Id: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + name: + type: + - string + - "null" + id: + type: string + required: + - id + tags: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + created_by: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + name: + type: + - string + - "null" + created_time: + type: + - string + - "null" + id: + type: string + modified_by: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + name: + type: + - string + - "null" + modified_time: + type: + - string + - "null" + name: + type: + - string + - "null" + required: + - id + companies: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + Account_Name: + type: + - string + - "null" + Phone: + type: + - string + - "null" + Website: + type: + - string + - "null" + id: + type: string + required: + - id + contacts: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + Email: + type: + - string + - "null" + Last_Name: + type: + - string + - "null" + id: + type: string + required: + - id + tasks: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + Owner: + type: + - object + - "null" + properties: + email: + type: + - string + - "null" + id: + type: + - string + - "null" + name: + type: + - string + - "null" + Subject: + type: + - string + - "null" + id: + type: string + required: + - id + events: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + End_DateTime: + type: + - string + - "null" + Event_Title: + type: + - string + - "null" + Owner: + type: + - object + - "null" + properties: + email: + type: + - string + - "null" + id: + type: + - string + - "null" + name: + type: + - string + - "null" + Start_DateTime: + type: + - string + - "null" + id: + type: string + required: + - id + products: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + Description: + type: + - string + - "null" + Owner: + type: + - object + - "null" + properties: + email: + type: + - string + - "null" + id: + type: + - string + - "null" + name: + type: + - string + - "null" + Product_Active: + type: + - boolean + - "null" + Product_Category: + type: + - string + - "null" + Product_Name: + type: + - string + - "null" + Unit_Price: + type: + - number + - "null" + id: + type: string + required: + - id diff --git a/airbyte-integrations/connectors/source-zoho-bigin/metadata.yaml b/airbyte-integrations/connectors/source-zoho-bigin/metadata.yaml new file mode 100644 index 000000000000..641f597f4ef6 --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-bigin/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "zohoapis." + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-zoho-bigin + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.16.0@sha256:6800f806944ee4fccf24ae01f6b8fbefb12d952c3b3da338f51f732b55de51f2 + connectorSubtype: api + connectorType: source + definitionId: 30e47b8d-5132-4b24-a204-2493bda33c8d + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-zoho-bigin + githubIssueLabel: source-zoho-bigin + icon: icon.svg + license: MIT + name: Zoho Bigin + releaseDate: 2024-10-27 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/zoho-bigin + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/zoho-bigin.md b/docs/integrations/sources/zoho-bigin.md new file mode 100644 index 000000000000..7ff4d314898e --- /dev/null +++ b/docs/integrations/sources/zoho-bigin.md @@ -0,0 +1,37 @@ +# Zoho Bigin + Zoho Bigin connector enables seamless data sync between Zoho Bigin and other platforms. This connector automates CRM data integration, improving workflows and ensuring real-time access to customer insights across tools. + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `data_center` | `string` | Data Center. The data center where the Bigin account's resources are hosted | com | +| `client_id` | `string` | OAuth Client ID. | | +| `client_secret` | `string` | OAuth Client Secret. | | +| `client_refresh_token` | `string` | Refresh token. | | + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| users | id | DefaultPaginator | ✅ | ❌ | +| modules | id | No pagination | ✅ | ❌ | +| organizations | id | No pagination | ✅ | ❌ | +| roles | id | No pagination | ✅ | ❌ | +| notes | id | DefaultPaginator | ✅ | ❌ | +| tags | id | No pagination | ✅ | ❌ | +| companies | id | DefaultPaginator | ✅ | ❌ | +| contacts | id | DefaultPaginator | ✅ | ❌ | +| tasks | | DefaultPaginator | ✅ | ❌ | +| events | id | DefaultPaginator | ✅ | ❌ | +| products | id | DefaultPaginator | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-10-27 | | Initial release by [@bishalbera](https://github.com/bishalbera) via Connector Builder | + +
From 961657375171129f01c474b14f29800bd52c8823 Mon Sep 17 00:00:00 2001 From: Om Bhardwaj <115864495+ombhardwajj@users.noreply.github.com> Date: Wed, 6 Nov 2024 23:04:39 +0530 Subject: [PATCH 07/45] source-openaq contribution from ombhardwajj (#47021) Co-authored-by: Marcos Marx --- .../connectors/source-openaq/README.md | 36 + .../source-openaq/acceptance-test-config.yml | 17 + .../connectors/source-openaq/icon.svg | 1 + .../connectors/source-openaq/manifest.yaml | 2060 +++++++++++++++++ .../connectors/source-openaq/metadata.yaml | 35 + docs/integrations/sources/openaq.md | 43 + 6 files changed, 2192 insertions(+) create mode 100644 airbyte-integrations/connectors/source-openaq/README.md create mode 100644 airbyte-integrations/connectors/source-openaq/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-openaq/icon.svg create mode 100644 airbyte-integrations/connectors/source-openaq/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-openaq/metadata.yaml create mode 100644 docs/integrations/sources/openaq.md diff --git a/airbyte-integrations/connectors/source-openaq/README.md b/airbyte-integrations/connectors/source-openaq/README.md new file mode 100644 index 000000000000..fe90cdf7aea4 --- /dev/null +++ b/airbyte-integrations/connectors/source-openaq/README.md @@ -0,0 +1,36 @@ +# OpenAQ +This directory contains the manifest-only connector for `source-openaq`. + +The OpenAQ API provides open access to global air quality data. +This connector enables you to fetch data from all the streams listed on their website such as Locations , Sensors , Measurements and much more. + +Docs : https://docs.openaq.org/using-the-api/quick-start + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-openaq:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-openaq build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-openaq test +``` + diff --git a/airbyte-integrations/connectors/source-openaq/acceptance-test-config.yml b/airbyte-integrations/connectors/source-openaq/acceptance-test-config.yml new file mode 100644 index 000000000000..e4d77afa656b --- /dev/null +++ b/airbyte-integrations/connectors/source-openaq/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-openaq:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-openaq/icon.svg b/airbyte-integrations/connectors/source-openaq/icon.svg new file mode 100644 index 000000000000..58bac52844b4 --- /dev/null +++ b/airbyte-integrations/connectors/source-openaq/icon.svg @@ -0,0 +1 @@ + diff --git a/airbyte-integrations/connectors/source-openaq/manifest.yaml b/airbyte-integrations/connectors/source-openaq/manifest.yaml new file mode 100644 index 000000000000..0b26cfa0d889 --- /dev/null +++ b/airbyte-integrations/connectors/source-openaq/manifest.yaml @@ -0,0 +1,2060 @@ +version: 5.14.0 + +type: DeclarativeSource + +description: >- + The OpenAQ API provides open access to global air quality data. + + This connector enables you to fetch data from all the streams listed on their + website such as Locations , Sensors , Measurements and much more. + + + Docs : https://docs.openaq.org/using-the-api/quick-start + +check: + type: CheckStream + stream_names: + - instruments + +definitions: + streams: + instruments: + type: DeclarativeStream + name: instruments + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: instruments + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/instruments" + manufacturers: + type: DeclarativeStream + name: manufacturers + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: manufacturers + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/manufacturers" + manufacturer_instruments: + type: DeclarativeStream + name: manufacturer_instruments + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: manufacturers/{{ stream_partition.manufacturer_id }}/instruments + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: id + partition_field: manufacturer_id + stream: + $ref: "#/definitions/streams/manufacturers" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/manufacturer_instruments" + locations: + type: DeclarativeStream + name: locations + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: locations + http_method: GET + error_handler: + type: CompositeErrorHandler + error_handlers: + - type: DefaultErrorHandler + max_retries: 2 + backoff_strategies: + - type: ConstantBackoffStrategy + backoff_time_in_seconds: 5 + response_filters: + - type: HttpResponseFilter + action: RETRY + http_codes: + - 500 + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + partition_router: + type: ListPartitionRouter + values: "{{ config.country_ids }}" + cursor_field: co_id + request_option: + type: RequestOption + inject_into: request_parameter + field_name: countries_id + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/locations" + licenses: + type: DeclarativeStream + name: licenses + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: licenses + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/licenses" + license_instrument: + type: DeclarativeStream + name: license_instrument + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: licenses/{{ stream_partition.licenses_id }} + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: id + partition_field: licenses_id + stream: + $ref: "#/definitions/streams/licenses" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/license_instrument" + parameters: + type: DeclarativeStream + name: parameters + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: parameters + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/parameters" + countries: + type: DeclarativeStream + name: countries + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: countries + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/countries" + latest_parameters: + type: DeclarativeStream + name: latest_parameters + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: parameters/{{ stream_partition.parameters_id }}/latest + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: id + partition_field: parameters_id + stream: + $ref: "#/definitions/streams/parameters" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/latest_parameters" + sensors: + type: DeclarativeStream + name: sensors + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: locations/{{ stream_partition.location_id }}/sensors + http_method: GET + error_handler: + type: CompositeErrorHandler + error_handlers: + - type: DefaultErrorHandler + max_retries: 10 + backoff_strategies: + - type: ConstantBackoffStrategy + backoff_time_in_seconds: 5 + response_filters: + - type: HttpResponseFilter + action: RETRY + http_codes: + - 500 + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: id + partition_field: location_id + stream: + $ref: "#/definitions/streams/locations" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/sensors" + providers: + type: DeclarativeStream + name: providers + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: providers + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/providers" + owners: + type: DeclarativeStream + name: owners + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: owners + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/owners" + location_latest_measure: + type: DeclarativeStream + name: location_latest_measure + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: locations/{{ stream_partition.location_id }}/latest + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: id + partition_field: location_id + stream: + $ref: "#/definitions/streams/locations" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/location_latest_measure" + sensor_measurements: + type: DeclarativeStream + name: sensor_measurements + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: sensors/{{ stream_partition.sensor_id }}/measurements + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: id + partition_field: sensor_id + stream: + $ref: "#/definitions/streams/sensors" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/sensor_measurements" + measurements_daily: + type: DeclarativeStream + name: measurements_daily + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: sensors/{{ stream_partition.sensor_id }}/days + http_method: GET + error_handler: + type: CompositeErrorHandler + error_handlers: + - type: DefaultErrorHandler + max_retries: 10 + backoff_strategies: + - type: ConstantBackoffStrategy + backoff_time_in_seconds: 10 + response_filters: + - type: HttpResponseFilter + action: SUCCESS + http_codes: + - 500 + error_message: Internal Server Error + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: PageIncrement + page_size: 1000 + start_from_page: 1 + inject_on_first_request: true + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: id + partition_field: sensor_id + stream: + $ref: "#/definitions/streams/sensors" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/measurements_daily" + measurements_yearly: + type: DeclarativeStream + name: measurements_yearly + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: sensors/{{ stream_partition.sensor_id }}/years + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: id + partition_field: sensor_id + stream: + $ref: "#/definitions/streams/sensors" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/measurements_yearly" + base_requester: + type: HttpRequester + url_base: https://api.openaq.org/v3/ + authenticator: + type: ApiKeyAuthenticator + api_token: "{{ config[\"api_key\"] }}" + inject_into: + type: RequestOption + field_name: X-API-Key + inject_into: header + +streams: + - $ref: "#/definitions/streams/instruments" + - $ref: "#/definitions/streams/manufacturers" + - $ref: "#/definitions/streams/manufacturer_instruments" + - $ref: "#/definitions/streams/locations" + - $ref: "#/definitions/streams/licenses" + - $ref: "#/definitions/streams/license_instrument" + - $ref: "#/definitions/streams/parameters" + - $ref: "#/definitions/streams/countries" + - $ref: "#/definitions/streams/latest_parameters" + - $ref: "#/definitions/streams/sensors" + - $ref: "#/definitions/streams/providers" + - $ref: "#/definitions/streams/owners" + - $ref: "#/definitions/streams/location_latest_measure" + - $ref: "#/definitions/streams/sensor_measurements" + - $ref: "#/definitions/streams/measurements_daily" + - $ref: "#/definitions/streams/measurements_yearly" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - api_key + - country_ids + properties: + api_key: + type: string + order: 0 + title: API Key + airbyte_secret: true + country_ids: + type: array + description: >- + The list of IDs of countries (comma separated) you need the data for, + check more: https://docs.openaq.org/resources/countries + order: 1 + title: Countries + additionalProperties: true + +metadata: + autoImportSchema: + instruments: true + manufacturers: true + manufacturer_instruments: true + locations: true + licenses: true + license_instrument: true + parameters: true + countries: true + latest_parameters: true + sensors: true + providers: true + owners: true + location_latest_measure: true + sensor_measurements: true + measurements_daily: true + measurements_yearly: true + testedStreams: + instruments: + streamHash: 37ced606dd4d1ee203685c7dfff05e26f1c6fbe0 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + manufacturers: + streamHash: 758a158a89e8f45543eb8c22c99ea8643ea462ab + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + manufacturer_instruments: + streamHash: 1d7607c726b0ca392e558ae0b2244c6696b67a0e + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + locations: + streamHash: 19ade0d6f041647c45cc191e69b2d4fb6c4a5203 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + licenses: + streamHash: 186c2b5653b20ef06c281f6aef755eee7b1c050f + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + license_instrument: + streamHash: 1ba49590965222df97cb14b486c66eddbffd3253 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + parameters: + streamHash: 2957351af4367680bc00dee0b089bb0b44ab51f1 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + countries: + streamHash: 8d9861715c30528ea96f98e4cb461a87ab3fbef0 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + latest_parameters: + streamHash: 5c9ed8694a093960cd9fa4a5a5821d4b2ccd9282 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + sensors: + streamHash: 32b6ebb6a4882b0e0b46b458aa2ad685df21be9e + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + providers: + streamHash: d4d7e6b2db844501fb3a3a8b004071d0be10c22a + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + owners: + streamHash: f39546df1a5c47d70a3c98d020dae113f1bcd6f5 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + location_latest_measure: + streamHash: d55e14b3570d202bb19b85675d5de26b634a8597 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + sensor_measurements: + streamHash: b1a0f502086b51e638bc82d67731db75b05d2392 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + measurements_daily: + streamHash: 1c3febd96e12e486b4a47d61cff3e6b6026a2157 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + measurements_yearly: + streamHash: 713432a65dbf7b74fa621638c524aa2c3def25b2 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + assist: {} + +schemas: + instruments: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + id: + type: number + isMonitor: + type: + - boolean + - "null" + manufacturer: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + name: + type: + - string + - "null" + required: + - id + manufacturers: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + id: + type: number + instruments: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + name: + type: + - string + - "null" + required: + - id + manufacturer_instruments: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + id: + type: number + isMonitor: + type: + - boolean + - "null" + manufacturer: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + name: + type: + - string + - "null" + required: + - id + locations: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + bounds: + type: + - array + - "null" + items: + type: + - number + - "null" + coordinates: + type: + - object + - "null" + properties: + latitude: + type: + - number + - "null" + longitude: + type: + - number + - "null" + country: + type: + - object + - "null" + properties: + code: + type: + - string + - "null" + id: + type: + - number + - "null" + name: + type: + - string + - "null" + datetimeFirst: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + datetimeLast: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + id: + type: number + instruments: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + isMobile: + type: + - boolean + - "null" + isMonitor: + type: + - boolean + - "null" + licenses: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + attribution: + type: + - object + - "null" + properties: + name: + type: + - string + - "null" + dateFrom: + type: + - string + - "null" + id: + type: + - number + - "null" + name: + type: + - string + - "null" + locality: + type: + - string + - "null" + name: + type: + - string + - "null" + owner: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + provider: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + sensors: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + parameter: + type: + - object + - "null" + properties: + displayName: + type: + - string + - "null" + id: + type: + - number + - "null" + name: + type: + - string + - "null" + units: + type: + - string + - "null" + timezone: + type: + - string + - "null" + required: + - id + licenses: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + attributionRequired: + type: + - boolean + - "null" + commercialUseAllowed: + type: + - boolean + - "null" + id: + type: + - number + - "null" + modificationAllowed: + type: + - boolean + - "null" + name: + type: + - string + - "null" + redistributionAllowed: + type: + - boolean + - "null" + shareAlikeRequired: + type: + - boolean + - "null" + sourceUrl: + type: + - string + - "null" + license_instrument: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + attributionRequired: + type: + - boolean + - "null" + commercialUseAllowed: + type: + - boolean + - "null" + id: + type: number + modificationAllowed: + type: + - boolean + - "null" + name: + type: + - string + - "null" + redistributionAllowed: + type: + - boolean + - "null" + shareAlikeRequired: + type: + - boolean + - "null" + sourceUrl: + type: + - string + - "null" + required: + - id + parameters: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + description: + type: + - string + - "null" + displayName: + type: + - string + - "null" + id: + type: number + name: + type: + - string + - "null" + units: + type: + - string + - "null" + required: + - id + countries: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + code: + type: + - string + - "null" + datetimeFirst: + type: + - string + - "null" + datetimeLast: + type: + - string + - "null" + id: + type: number + name: + type: + - string + - "null" + parameters: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + units: + type: + - string + - "null" + required: + - id + latest_parameters: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + coordinates: + type: + - object + - "null" + properties: + latitude: + type: + - number + - "null" + longitude: + type: + - number + - "null" + datetime: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + locationsId: + type: + - number + - "null" + sensorsId: + type: + - number + - "null" + value: + type: + - number + - "null" + sensors: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + coverage: + type: + - object + - "null" + properties: + datetimeFrom: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + datetimeTo: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + expectedCount: + type: + - number + - "null" + expectedInterval: + type: + - string + - "null" + observedCount: + type: + - number + - "null" + observedInterval: + type: + - string + - "null" + percentComplete: + type: + - number + - "null" + percentCoverage: + type: + - number + - "null" + datetimeFirst: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + datetimeLast: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + id: + type: number + latest: + type: + - object + - "null" + properties: + coordinates: + type: + - object + - "null" + properties: + latitude: + type: + - number + - "null" + longitude: + type: + - number + - "null" + datetime: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + value: + type: + - number + - "null" + name: + type: + - string + - "null" + parameter: + type: + - object + - "null" + properties: + displayName: + type: + - string + - "null" + id: + type: + - number + - "null" + name: + type: + - string + - "null" + units: + type: + - string + - "null" + summary: + type: + - object + - "null" + properties: + avg: + type: + - number + - "null" + max: + type: + - number + - "null" + min: + type: + - number + - "null" + required: + - id + providers: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + bbox: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + coordinates: + type: + - array + - "null" + items: + anyOf: + - type: number + - type: array + items: + type: array + items: + type: number + datetimeAdded: + type: + - string + - "null" + datetimeFirst: + type: + - string + - "null" + datetimeLast: + type: + - string + - "null" + entitiesId: + type: + - number + - "null" + exportPrefix: + type: + - string + - "null" + id: + type: number + name: + type: + - string + - "null" + parameters: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + units: + type: + - string + - "null" + sourceName: + type: + - string + - "null" + required: + - id + owners: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + id: + type: number + name: + type: + - string + - "null" + required: + - id + location_latest_measure: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + coordinates: + type: + - object + - "null" + properties: + latitude: + type: + - number + - "null" + longitude: + type: + - number + - "null" + datetime: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + locationsId: + type: + - number + - "null" + sensorsId: + type: + - number + - "null" + value: + type: + - number + - "null" + sensor_measurements: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + coverage: + type: + - object + - "null" + properties: + datetimeFrom: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + datetimeTo: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + expectedCount: + type: + - number + - "null" + expectedInterval: + type: + - string + - "null" + observedCount: + type: + - number + - "null" + observedInterval: + type: + - string + - "null" + percentComplete: + type: + - number + - "null" + percentCoverage: + type: + - number + - "null" + flagInfo: + type: + - object + - "null" + properties: + hasFlags: + type: + - boolean + - "null" + parameter: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + units: + type: + - string + - "null" + period: + type: + - object + - "null" + properties: + datetimeFrom: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + datetimeTo: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + interval: + type: + - string + - "null" + label: + type: + - string + - "null" + value: + type: + - number + - "null" + measurements_daily: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + coverage: + type: + - object + - "null" + properties: + datetimeFrom: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + datetimeTo: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + expectedCount: + type: + - number + - "null" + expectedInterval: + type: + - string + - "null" + observedCount: + type: + - number + - "null" + observedInterval: + type: + - string + - "null" + percentComplete: + type: + - number + - "null" + percentCoverage: + type: + - number + - "null" + flagInfo: + type: + - object + - "null" + properties: + hasFlags: + type: + - boolean + - "null" + parameter: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + units: + type: + - string + - "null" + period: + type: + - object + - "null" + properties: + datetimeFrom: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + datetimeTo: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + interval: + type: + - string + - "null" + label: + type: + - string + - "null" + summary: + type: + - object + - "null" + properties: + avg: + type: + - number + - "null" + max: + type: + - number + - "null" + median: + type: + - number + - "null" + min: + type: + - number + - "null" + q02: + type: + - number + - "null" + q25: + type: + - number + - "null" + q75: + type: + - number + - "null" + q98: + type: + - number + - "null" + sd: + type: + - number + - "null" + value: + type: + - number + - "null" + measurements_yearly: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + coverage: + type: + - object + - "null" + properties: + datetimeFrom: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + datetimeTo: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + expectedCount: + type: + - number + - "null" + expectedInterval: + type: + - string + - "null" + observedCount: + type: + - number + - "null" + observedInterval: + type: + - string + - "null" + percentComplete: + type: + - number + - "null" + percentCoverage: + type: + - number + - "null" + flagInfo: + type: + - object + - "null" + properties: + hasFlags: + type: + - boolean + - "null" + parameter: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + units: + type: + - string + - "null" + period: + type: + - object + - "null" + properties: + datetimeFrom: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + datetimeTo: + type: + - object + - "null" + properties: + local: + type: + - string + - "null" + utc: + type: + - string + - "null" + interval: + type: + - string + - "null" + label: + type: + - string + - "null" + summary: + type: + - object + - "null" + properties: + avg: + type: + - number + - "null" + max: + type: + - number + - "null" + median: + type: + - number + - "null" + min: + type: + - number + - "null" + q02: + type: + - number + - "null" + q25: + type: + - number + - "null" + q75: + type: + - number + - "null" + q98: + type: + - number + - "null" + sd: + type: + - number + - "null" + value: + type: + - number + - "null" diff --git a/airbyte-integrations/connectors/source-openaq/metadata.yaml b/airbyte-integrations/connectors/source-openaq/metadata.yaml new file mode 100644 index 000000000000..52233430c448 --- /dev/null +++ b/airbyte-integrations/connectors/source-openaq/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "api.openaq.org" + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-openaq + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.14.0@sha256:accdf6c1bbcabd45b40f836692e4f3b1a1e1f0b28267973802ee212cd9c2c16a + connectorSubtype: api + connectorType: source + definitionId: 5b7216ca-1d6d-4a50-89c3-067746806586 + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-openaq + githubIssueLabel: source-openaq + icon: icon.svg + license: MIT + name: OpenAQ + releaseDate: 2024-10-19 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/openaq + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/openaq.md b/docs/integrations/sources/openaq.md new file mode 100644 index 000000000000..7e18a3142eb7 --- /dev/null +++ b/docs/integrations/sources/openaq.md @@ -0,0 +1,43 @@ +# OpenAQ +The OpenAQ API provides open access to global air quality data. +This connector enables you to fetch data from all the streams listed on their website such as Locations , Sensors , Measurements and much more. + +Docs : https://docs.openaq.org/using-the-api/quick-start + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `api_key` | `string` | API Key. | | +| `country_ids` | `array` | Countries. The list of IDs of countries (comma separated) you need the data for, check more: https://docs.openaq.org/resources/countries | | + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| instruments | id | DefaultPaginator | ✅ | ❌ | +| manufacturers | id | DefaultPaginator | ✅ | ❌ | +| manufacturer_instruments | id | No pagination | ✅ | ❌ | +| locations | id | DefaultPaginator | ✅ | ❌ | +| licenses | | DefaultPaginator | ✅ | ❌ | +| license_instrument | id | No pagination | ✅ | ❌ | +| parameters | id | DefaultPaginator | ✅ | ❌ | +| countries | id | DefaultPaginator | ✅ | ❌ | +| latest_parameters | | No pagination | ✅ | ❌ | +| sensors | id | DefaultPaginator | ✅ | ❌ | +| providers | id | DefaultPaginator | ✅ | ❌ | +| owners | id | DefaultPaginator | ✅ | ❌ | +| location_latest_measure | | No pagination | ✅ | ❌ | +| sensor_measurements | | DefaultPaginator | ✅ | ❌ | +| measurements_daily | | DefaultPaginator | ✅ | ❌ | +| measurements_yearly | | No pagination | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-11-06 | | Initial release by [@marcosmarxm](https://github.com/marcosmarxm) via Connector Builder | + +
From 8e3b5a037dd85144733e395a9249461fe0b13890 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Wed, 6 Nov 2024 23:32:21 +0530 Subject: [PATCH 08/45] Source Gorgias: Fix incremental sync date format (#48378) Co-authored-by: Natik Gadzhi --- .../connectors/source-gorgias/manifest.yaml | 238 +++++++++++++++--- .../connectors/source-gorgias/metadata.yaml | 2 +- docs/integrations/sources/gorgias.md | 1 + 3 files changed, 212 insertions(+), 29 deletions(-) diff --git a/airbyte-integrations/connectors/source-gorgias/manifest.yaml b/airbyte-integrations/connectors/source-gorgias/manifest.yaml index be154ceb9b9f..4d8ed27bd736 100644 --- a/airbyte-integrations/connectors/source-gorgias/manifest.yaml +++ b/airbyte-integrations/connectors/source-gorgias/manifest.yaml @@ -50,6 +50,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -114,6 +116,8 @@ definitions: cursor_field: updated_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -180,6 +184,8 @@ definitions: cursor_field: updated_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -244,6 +250,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -308,6 +316,8 @@ definitions: cursor_field: updated_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -372,6 +382,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -436,6 +448,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -500,6 +514,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -564,6 +580,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -628,6 +646,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -692,6 +712,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -756,6 +778,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -820,6 +844,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -884,6 +910,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -948,6 +976,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -1020,6 +1050,8 @@ definitions: cursor_field: created_datetime cursor_datetime_formats: - "%Y-%m-%dT%H:%M:%S.%f%z" + - "%Y-%m-%d %H:%M:%S.%f+00:00" + - "%Y-%m-%d %H:%M:%S+00:00" datetime_format: "%Y-%m-%dT%H:%M:%S.%f%z" start_datetime: type: MinMaxDatetime @@ -1112,114 +1144,148 @@ metadata: messages: true users: true views_items: true + yamlComponents: + streams: + account: + - incrementalSync + customers: + - incrementalSync + custom-fields: + - incrementalSync + events: + - incrementalSync + integrations: + - incrementalSync + jobs: + - incrementalSync + macros: + - incrementalSync + views: + - incrementalSync + rules: + - incrementalSync + satisfaction-surveys: + - incrementalSync + tags: + - incrementalSync + teams: + - incrementalSync + tickets: + - incrementalSync + messages: + - incrementalSync + users: + - incrementalSync + views_items: + - incrementalSync testedStreams: account: - streamHash: 35c0cadba84dbdeff763b400c40d63332063f0ca + streamHash: d744624c7484b6696d00b448a4873d44577fe6d4 hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true customers: - streamHash: f4fa57dc21e535ec9b5b8b772ca89d3c7587e2e9 + streamHash: ca559007a6bf263aeaf11dbb9b96295e64555c2f hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true custom-fields: - hasRecords: true - streamHash: c19cf92c5bde4806aba375c664479a9d36a61896 + streamHash: 158d74d4ec15ddb393fcd93410df78b504b07144 hasResponse: true - primaryKeysAreUnique: true - primaryKeysArePresent: true responsesAreSuccessful: true - events: hasRecords: true - streamHash: 49ac78abe633a9c4fce0778e183d9bba24473308 - hasResponse: true - primaryKeysAreUnique: true primaryKeysArePresent: true + primaryKeysAreUnique: true + events: + streamHash: 180fd5e4dcc2a7c6c78011249aa2a8bdc8910c4f + hasResponse: true responsesAreSuccessful: true - integrations: hasRecords: true - streamHash: d160fd9e3e9e1a62d7d8c789126191c998476934 - hasResponse: true - primaryKeysAreUnique: true primaryKeysArePresent: true + primaryKeysAreUnique: true + integrations: + streamHash: d7d971ef58a4eab715c2a3609213d5d65e8fefca + hasResponse: true responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true jobs: - streamHash: e705479af8c394a6a042d19943fde3c7c574df91 + streamHash: ed6c3e3ed40bb137f8ef8748435bd2c83b9e3d74 hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true macros: - hasRecords: true - streamHash: bebe6bd207f5507cc2b92614e82608fe5488acd7 + streamHash: 955fe8be382283bd12e9b461f8c6dea96d521480 hasResponse: true - primaryKeysAreUnique: true - primaryKeysArePresent: true responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true views: - streamHash: fd8ae05b480c0312b7780da2d0ecc1831779db78 + streamHash: f6f2fec4decd9c5657d47007cf7ca50c74798f5d hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true rules: - streamHash: 5df2be309fb2e0d9ddc5903d1f1177e8ddb0e551 + streamHash: 1bbb2ded40d6ec6ac514a7a4d031824fb72c8671 hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true satisfaction-surveys: - streamHash: 83a105ff8474fa6f58bb39fa09006938444f10ce + streamHash: 78392e54d6a852c07c6b68f1268167a523a1fed8 hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true tags: - streamHash: 3715b9466b20abc4f5bdc222cd4dde77d65b0344 + streamHash: d08f102f1d1d60817d0dc073daebc13c9589d2fc hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true teams: - streamHash: 4c753176b11815ff3da55d6806ee16839b9c0c7d + streamHash: 0ef08ffb72fa8e5404c7ceb3d350be90cf39cbcb hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true tickets: - streamHash: 5086078a027afddc63a8a323be7c7208b47b109c + streamHash: b735a8354cf6d5ad5034612614d137a60a0420d8 hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true messages: - streamHash: d57f0f0ff7940081368628d20293453fd5807e0a + streamHash: 5fb8111396993c073be7704df24dac5c8bdb3446 hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true users: - streamHash: 4c462adb8d9112b0f7d54e967939cdb8627ba15f + streamHash: 5c44f43aba3bdbdddb0bce2483d1a2baf7591307 hasResponse: true responsesAreSuccessful: true hasRecords: true primaryKeysArePresent: true primaryKeysAreUnique: true views_items: - streamHash: 95d5e0af74d9233ab6a07c00834b2d3b993b4496 + streamHash: 3fb81032865e8878b9d3d811f070b8e09c2d82cf hasResponse: true responsesAreSuccessful: true hasRecords: true @@ -1420,6 +1486,19 @@ schemas: - object - "null" properties: + notification: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + message: + type: + - string + - "null" status: type: - string @@ -1440,6 +1519,10 @@ schemas: type: - string - "null" + custom_fields: + type: + - object + - "null" email: type: - string @@ -1693,12 +1776,103 @@ schemas: - "null" created_datetime: type: string + ended_datetime: + type: + - string + - "null" id: type: number info: type: - object - "null" + properties: + _composed_files: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + absolute_path: + type: + - string + - "null" + content_type: + type: + - string + - "null" + name: + type: + - string + - "null" + size: + type: + - number + - "null" + url: + type: + - string + - "null" + _tickets_files: + type: + - array + - "null" + archive: + type: + - object + - "null" + properties: + absolute_path: + type: + - string + - "null" + content_type: + type: + - string + - "null" + name: + type: + - string + - "null" + size: + type: + - number + - "null" + url: + type: + - string + - "null" + custom_fields_mapping: + type: + - object + - "null" + custom_fields_order: + type: + - array + - "null" + items: + type: + - string + - "null" + iteration_over: + type: + - boolean + - "null" + progress_count: + type: + - number + - "null" + progress_cursor: + type: + - number + - "null" + timezone: + type: + - string + - "null" params: type: - object @@ -2053,6 +2227,10 @@ schemas: type: - string - "null" + started_datetime: + type: + - string + - "null" status: type: - string @@ -7371,6 +7549,10 @@ schemas: - "null" created_datetime: type: string + custom_fields: + type: + - object + - "null" email: type: - string diff --git a/airbyte-integrations/connectors/source-gorgias/metadata.yaml b/airbyte-integrations/connectors/source-gorgias/metadata.yaml index dd2adedb848c..54a9e5182256 100644 --- a/airbyte-integrations/connectors/source-gorgias/metadata.yaml +++ b/airbyte-integrations/connectors/source-gorgias/metadata.yaml @@ -17,7 +17,7 @@ data: connectorSubtype: api connectorType: source definitionId: 9fdc3733-c51a-4129-9be5-7e9ad6eab6ac - dockerImageTag: 0.0.3 + dockerImageTag: 0.0.4 dockerRepository: airbyte/source-gorgias githubIssueLabel: source-gorgias icon: icon.svg diff --git a/docs/integrations/sources/gorgias.md b/docs/integrations/sources/gorgias.md index a56688418f0e..c11684d36d9c 100644 --- a/docs/integrations/sources/gorgias.md +++ b/docs/integrations/sources/gorgias.md @@ -43,6 +43,7 @@ Visit `https://developers.gorgias.com/reference/introduction` for API documentat | Version | Date | Pull Request | Subject | | ------------------ | ------------ | --- | ---------------- | +| 0.0.4 | 2024-11-06 | [48378](https://github.com/airbytehq/airbyte/pull/48378) | Fix incremental sync format, Auto update schema with additional fields | | 0.0.3 | 2024-10-29 | [47923](https://github.com/airbytehq/airbyte/pull/47923) | Update dependencies | | 0.0.2 | 2024-10-28 | [47459](https://github.com/airbytehq/airbyte/pull/47459) | Update dependencies | | 0.0.1 | 2024-09-29 | [46221](https://github.com/airbytehq/airbyte/pull/46221) | Initial release by [@btkcodedev](https://github.com/btkcodedev) via Connector Builder | From 6294144c169da7bea246d5954e719dd958325972 Mon Sep 17 00:00:00 2001 From: Stephane Geneix <147216312+stephane-airbyte@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:45:15 -0800 Subject: [PATCH 09/45] source-sftp-bulk: make the private key an airbyte secret (#46739) simply making the private key an airbyte secret. I also tested this change in cloud on an existing connection where the field was not an airbyteSecret, and everything worked smoothly --- .../connectors/source-sftp-bulk/integration_tests/spec.json | 1 + airbyte-integrations/connectors/source-sftp-bulk/metadata.yaml | 2 +- airbyte-integrations/connectors/source-sftp-bulk/pyproject.toml | 2 +- .../connectors/source-sftp-bulk/source_sftp_bulk/spec.py | 2 +- docs/integrations/sources/sftp-bulk.md | 1 + 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/airbyte-integrations/connectors/source-sftp-bulk/integration_tests/spec.json b/airbyte-integrations/connectors/source-sftp-bulk/integration_tests/spec.json index dd8d9b09f717..eef7da5cce32 100644 --- a/airbyte-integrations/connectors/source-sftp-bulk/integration_tests/spec.json +++ b/airbyte-integrations/connectors/source-sftp-bulk/integration_tests/spec.json @@ -524,6 +524,7 @@ "private_key": { "title": "Private key", "description": "The Private key", + "airbyte_secret": true, "multiline": true, "order": 4, "type": "string" diff --git a/airbyte-integrations/connectors/source-sftp-bulk/metadata.yaml b/airbyte-integrations/connectors/source-sftp-bulk/metadata.yaml index b4f71cbb1c51..4de3a174c45e 100644 --- a/airbyte-integrations/connectors/source-sftp-bulk/metadata.yaml +++ b/airbyte-integrations/connectors/source-sftp-bulk/metadata.yaml @@ -7,7 +7,7 @@ data: connectorSubtype: file connectorType: source definitionId: 31e3242f-dee7-4cdc-a4b8-8e06c5458517 - dockerImageTag: 1.3.0 + dockerImageTag: 1.4.0 dockerRepository: airbyte/source-sftp-bulk documentationUrl: https://docs.airbyte.com/integrations/sources/sftp-bulk githubIssueLabel: source-sftp-bulk diff --git a/airbyte-integrations/connectors/source-sftp-bulk/pyproject.toml b/airbyte-integrations/connectors/source-sftp-bulk/pyproject.toml index 78b1d5b189dc..5bb8477d6395 100644 --- a/airbyte-integrations/connectors/source-sftp-bulk/pyproject.toml +++ b/airbyte-integrations/connectors/source-sftp-bulk/pyproject.toml @@ -3,7 +3,7 @@ requires = [ "poetry-core>=1.0.0",] build-backend = "poetry.core.masonry.api" [tool.poetry] -version = "1.3.0" +version = "1.4.0" name = "source-sftp-bulk" description = "Source implementation for SFTP Bulk." authors = [ "Airbyte ",] diff --git a/airbyte-integrations/connectors/source-sftp-bulk/source_sftp_bulk/spec.py b/airbyte-integrations/connectors/source-sftp-bulk/source_sftp_bulk/spec.py index 5de6ce689963..06863da1ae01 100644 --- a/airbyte-integrations/connectors/source-sftp-bulk/source_sftp_bulk/spec.py +++ b/airbyte-integrations/connectors/source-sftp-bulk/source_sftp_bulk/spec.py @@ -23,7 +23,7 @@ class Config(OneOfOptionConfig): discriminator = "auth_type" auth_type: Literal["private_key"] = Field("private_key", const=True) - private_key: str = Field(title="Private key", description="The Private key", multiline=True, order=4) + private_key: str = Field(title="Private key", description="The Private key", multiline=True, order=4, airbyte_secret=True) class SourceSFTPBulkSpec(AbstractFileBasedSpec): diff --git a/docs/integrations/sources/sftp-bulk.md b/docs/integrations/sources/sftp-bulk.md index ca50b008701a..a3dad5cf1a56 100644 --- a/docs/integrations/sources/sftp-bulk.md +++ b/docs/integrations/sources/sftp-bulk.md @@ -156,6 +156,7 @@ This source provides a single stream per file with a dynamic schema. The current | Version | Date | Pull Request | Subject | |:--------|:-----------|:---------------------------------------------------------|:------------------------------------------------------------| +| 1.4.0 | 2024-10-31 | [46739](https://github.com/airbytehq/airbyte/pull/46739) | make private key an airbyte secret. | | 1.3.0 | 2024-10-31 | [47703](https://github.com/airbytehq/airbyte/pull/47703) | Update dependency to CDK v6 with ability to transfer files. | | 1.2.0 | 2024-09-03 | [46323](https://github.com/airbytehq/airbyte/pull/46323) | Update dependency to CDK v5 | | 1.1.0 | 2024-08-14 | [44028](https://github.com/airbytehq/airbyte/pull/44028) | Update dependency to CDK v4 | From e6d3cde64a35a8708eeb917959ce3e9a19201c32 Mon Sep 17 00:00:00 2001 From: Johnny Schmidt Date: Wed, 6 Nov 2024 11:58:31 -0800 Subject: [PATCH 10/45] Bulk Load CDK: Preliminary Refactor before adding mappers (#48350) --- .../cdk/load/check/DestinationChecker.kt | 4 +- .../cdk/load/command/DestinationStream.kt | 59 ---- .../load/data/AirbyteSchemaIdentityMapper.kt | 10 +- .../io/airbyte/cdk/load/data/AirbyteType.kt | 2 - .../data/AirbyteTypeToAirbyteTypeWithMeta.kt | 64 ++++ .../load/data/AirbyteValueIdentityMapper.kt | 1 - ...DestinationRecordToAirbyteValueWithMeta.kt | 24 +- .../cdk/load/data/NullableToUnionNull.kt | 14 - .../load/data/UnionTypeToDisjointRecord.kt | 16 +- .../load/data/json/AirbyteTypeToJsonSchema.kt | 15 +- .../load/data/json/JsonSchemaToAirbyteType.kt | 87 +++--- .../cdk/load/data/json/JsonToAirbyteValue.kt | 2 - .../data/AirbyteSchemaIdentityMapperTest.kt | 4 +- .../data/AirbyteValueIdentityMapperTest.kt | 1 - .../cdk/load/data/NullableToUnionNullTest.kt | 49 --- .../data/UnionTypeToDisjointRecordTest.kt | 12 +- .../json/AirbyteSchemaTypeToJsonSchemaTest.kt | 291 ++++++++++-------- .../load/data/json/AirbyteValueToJsonTest.kt | 14 +- .../json/JsonSchemaToAirbyteSchemaTypeTest.kt | 8 - .../load/data/json/JsonToAirbyteValueTest.kt | 7 - .../load/data/avro/AirbyteTypeToAvroSchema.kt | 2 - .../data/avro/AvroRecordToAirbyteValue.kt | 3 - .../ObjectStorageFormattingWriter.kt | 35 +-- .../cdk/load/ObjectStorageDataDumper.kt | 19 +- 24 files changed, 320 insertions(+), 423 deletions(-) create mode 100644 airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteTypeToAirbyteTypeWithMeta.kt delete mode 100644 airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/NullableToUnionNull.kt delete mode 100644 airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/NullableToUnionNullTest.kt diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/check/DestinationChecker.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/check/DestinationChecker.kt index de44f79fd389..e46ef32658d3 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/check/DestinationChecker.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/check/DestinationChecker.kt @@ -7,7 +7,7 @@ package io.airbyte.cdk.load.check import io.airbyte.cdk.load.command.Append import io.airbyte.cdk.load.command.DestinationConfiguration import io.airbyte.cdk.load.command.DestinationStream -import io.airbyte.cdk.load.data.NullType +import io.airbyte.cdk.load.data.ObjectTypeWithoutSchema /** * A check operation that is run before the destination is used. @@ -25,7 +25,7 @@ interface DestinationChecker { DestinationStream( descriptor = DestinationStream.Descriptor("testing", "test"), importType = Append, - schema = NullType, + schema = ObjectTypeWithoutSchema, generationId = 1, minimumGenerationId = 0, syncId = 1, diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/command/DestinationStream.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/command/DestinationStream.kt index bd14ada6491e..04044cbc6e41 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/command/DestinationStream.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/command/DestinationStream.kt @@ -5,14 +5,8 @@ package io.airbyte.cdk.load.command import io.airbyte.cdk.load.data.AirbyteType -import io.airbyte.cdk.load.data.ArrayType -import io.airbyte.cdk.load.data.FieldType -import io.airbyte.cdk.load.data.IntegerType -import io.airbyte.cdk.load.data.ObjectType -import io.airbyte.cdk.load.data.StringType import io.airbyte.cdk.load.data.json.AirbyteTypeToJsonSchema import io.airbyte.cdk.load.data.json.JsonSchemaToAirbyteType -import io.airbyte.cdk.load.message.DestinationRecord import io.airbyte.protocol.models.v0.AirbyteStream import io.airbyte.protocol.models.v0.ConfiguredAirbyteStream import io.airbyte.protocol.models.v0.DestinationSyncMode @@ -51,59 +45,6 @@ data class DestinationStream( * what actually exists, as many destinations have legacy data from before this schema was * adopted. */ - val schemaWithMeta: ObjectType - get() = - ObjectType( - linkedMapOf( - DestinationRecord.Meta.COLUMN_NAME_AB_RAW_ID to - FieldType(StringType, nullable = false), - DestinationRecord.Meta.COLUMN_NAME_AB_EXTRACTED_AT to - FieldType(IntegerType, nullable = false), - DestinationRecord.Meta.COLUMN_NAME_AB_META to - FieldType( - nullable = false, - type = - ObjectType( - linkedMapOf( - "sync_id" to FieldType(IntegerType, nullable = false), - "changes" to - FieldType( - nullable = false, - type = - ArrayType( - FieldType( - nullable = false, - type = - ObjectType( - linkedMapOf( - "field" to - FieldType( - StringType, - nullable = false - ), - "change" to - FieldType( - StringType, - nullable = false - ), - "reason" to - FieldType( - StringType, - nullable = false - ), - ) - ) - ) - ) - ) - ) - ) - ), - DestinationRecord.Meta.COLUMN_NAME_AB_GENERATION_ID to - FieldType(IntegerType, nullable = false), - DestinationRecord.Meta.COLUMN_NAME_DATA to FieldType(schema, nullable = false), - ) - ) /** * This is not fully round-trippable. Destinations don't care about most of the stuff in an diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteSchemaIdentityMapper.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteSchemaIdentityMapper.kt index a36c286a03a5..8e6936639d42 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteSchemaIdentityMapper.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteSchemaIdentityMapper.kt @@ -4,10 +4,13 @@ package io.airbyte.cdk.load.data -interface AirbyteSchemaIdentityMapper { - fun map(schema: AirbyteType): AirbyteType = +interface AirbyteSchemaMapper { + fun map(schema: AirbyteType): AirbyteType +} + +interface AirbyteSchemaIdentityMapper : AirbyteSchemaMapper { + override fun map(schema: AirbyteType): AirbyteType = when (schema) { - is NullType -> mapNull(schema) is StringType -> mapString(schema) is BooleanType -> mapBoolean(schema) is IntegerType -> mapInteger(schema) @@ -26,7 +29,6 @@ interface AirbyteSchemaIdentityMapper { is UnknownType -> mapUnknown(schema) } - fun mapNull(schema: NullType): AirbyteType = schema fun mapString(schema: StringType): AirbyteType = schema fun mapBoolean(schema: BooleanType): AirbyteType = schema fun mapInteger(schema: IntegerType): AirbyteType = schema diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteType.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteType.kt index 5093f0597077..f0356dd8cb51 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteType.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteType.kt @@ -6,8 +6,6 @@ package io.airbyte.cdk.load.data sealed interface AirbyteType -data object NullType : AirbyteType - data object StringType : AirbyteType data object BooleanType : AirbyteType diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteTypeToAirbyteTypeWithMeta.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteTypeToAirbyteTypeWithMeta.kt new file mode 100644 index 000000000000..d7d399e85ca6 --- /dev/null +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteTypeToAirbyteTypeWithMeta.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024 Airbyte, Inc., all rights reserved. + */ + +package io.airbyte.cdk.load.data + +import io.airbyte.cdk.load.message.DestinationRecord + +class AirbyteTypeToAirbyteTypeWithMeta { + fun convert(schema: AirbyteType): ObjectType = + ObjectType( + linkedMapOf( + DestinationRecord.Meta.COLUMN_NAME_AB_RAW_ID to + FieldType(StringType, nullable = false), + DestinationRecord.Meta.COLUMN_NAME_AB_EXTRACTED_AT to + FieldType(IntegerType, nullable = false), + DestinationRecord.Meta.COLUMN_NAME_AB_META to + FieldType( + nullable = false, + type = + ObjectType( + linkedMapOf( + "sync_id" to FieldType(IntegerType, nullable = false), + "changes" to + FieldType( + nullable = false, + type = + ArrayType( + FieldType( + nullable = false, + type = + ObjectType( + linkedMapOf( + "field" to + FieldType( + StringType, + nullable = false + ), + "change" to + FieldType( + StringType, + nullable = false + ), + "reason" to + FieldType( + StringType, + nullable = false + ), + ) + ) + ) + ) + ) + ) + ) + ), + DestinationRecord.Meta.COLUMN_NAME_AB_GENERATION_ID to + FieldType(IntegerType, nullable = false), + DestinationRecord.Meta.COLUMN_NAME_DATA to FieldType(schema, nullable = false), + ) + ) +} + +fun AirbyteType.withAirbyteMeta(): ObjectType = AirbyteTypeToAirbyteTypeWithMeta().convert(this) diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapper.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapper.kt index eaab8330f276..f72d908425fd 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapper.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapper.kt @@ -45,7 +45,6 @@ open class AirbyteValueIdentityMapper( mapTimestampWithTimezone(value as TimestampValue, path) is TimestampTypeWithoutTimezone -> mapTimestampWithoutTimezone(value as TimestampValue, path) - is NullType -> mapNull(path) is UnknownType -> { collectFailure(path) mapNull(path) diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/DestinationRecordToAirbyteValueWithMeta.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/DestinationRecordToAirbyteValueWithMeta.kt index 84392b1503c3..8343c158ce09 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/DestinationRecordToAirbyteValueWithMeta.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/DestinationRecordToAirbyteValueWithMeta.kt @@ -4,29 +4,24 @@ package io.airbyte.cdk.load.data -import io.airbyte.cdk.load.command.DestinationCatalog +import io.airbyte.cdk.load.command.DestinationStream import io.airbyte.cdk.load.message.DestinationRecord import io.airbyte.cdk.load.message.DestinationRecord.Meta -import io.micronaut.context.annotation.Secondary -import jakarta.inject.Singleton import java.util.* -@Singleton -@Secondary -class DestinationRecordToAirbyteValueWithMeta(private val catalog: DestinationCatalog) { - fun decorate(record: DestinationRecord): ObjectValue { - val streamActual = catalog.getStream(record.stream.name, record.stream.namespace) +class DestinationRecordToAirbyteValueWithMeta(val stream: DestinationStream) { + fun convert(data: AirbyteValue, emittedAtMs: Long, meta: DestinationRecord.Meta?): ObjectValue { return ObjectValue( linkedMapOf( Meta.COLUMN_NAME_AB_RAW_ID to StringValue(UUID.randomUUID().toString()), - Meta.COLUMN_NAME_AB_EXTRACTED_AT to IntegerValue(record.emittedAtMs), + Meta.COLUMN_NAME_AB_EXTRACTED_AT to IntegerValue(emittedAtMs), Meta.COLUMN_NAME_AB_META to ObjectValue( linkedMapOf( - "sync_id" to IntegerValue(streamActual.syncId), + "sync_id" to IntegerValue(stream.syncId), "changes" to ArrayValue( - record.meta?.changes?.map { + meta?.changes?.map { ObjectValue( linkedMapOf( "field" to StringValue(it.field), @@ -39,9 +34,12 @@ class DestinationRecordToAirbyteValueWithMeta(private val catalog: DestinationCa ) ) ), - Meta.COLUMN_NAME_AB_GENERATION_ID to IntegerValue(streamActual.generationId), - Meta.COLUMN_NAME_DATA to record.data + Meta.COLUMN_NAME_AB_GENERATION_ID to IntegerValue(stream.generationId), + Meta.COLUMN_NAME_DATA to data ) ) } } + +fun DestinationRecord.dataWithAirbyteMeta(stream: DestinationStream) = + DestinationRecordToAirbyteValueWithMeta(stream).convert(data, emittedAtMs, meta) diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/NullableToUnionNull.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/NullableToUnionNull.kt deleted file mode 100644 index 8b1ea3daa35a..000000000000 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/NullableToUnionNull.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2024 Airbyte, Inc., all rights reserved. - */ - -package io.airbyte.cdk.load.data - -class NullableToUnionNull : AirbyteSchemaIdentityMapper { - override fun mapField(field: FieldType): FieldType { - if (field.nullable) { - return FieldType(UnionType(listOf(field.type, NullType)), nullable = false) - } - return field - } -} diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecord.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecord.kt index 7217f8f32097..c6af8baad53f 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecord.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecord.kt @@ -8,30 +8,24 @@ import io.airbyte.cdk.load.message.DestinationRecord class UnionTypeToDisjointRecord : AirbyteSchemaIdentityMapper { override fun mapUnion(schema: UnionType): AirbyteType { - val (nullOptions, nonNullOptions) = schema.options.partition { it is NullType } - if (nonNullOptions.size < 2) { + if (schema.options.size < 2) { return schema } /* Create a schema of { "type": "string", "": , etc... } */ val properties = linkedMapOf("type" to FieldType(StringType, nullable = false)) - nonNullOptions.forEach { + schema.options.forEach { val name = typeName(it) if (name in properties) { throw IllegalArgumentException("Union of types with same name: $name") } properties[typeName(it)] = FieldType(it, nullable = true) } - val obj = ObjectType(properties) - if (nullOptions.isEmpty()) { - return obj - } - return UnionType(nullOptions + obj) + return ObjectType(properties) } companion object { fun typeName(type: AirbyteType): String = when (type) { - is NullType -> "null" is StringType -> "string" is BooleanType -> "boolean" is IntegerType -> "integer" @@ -58,8 +52,7 @@ class UnionValueToDisjointRecord(meta: DestinationRecord.Meta) : AirbyteValueIde schema: UnionType, path: List ): AirbyteValue { - val nNonNullOptions = schema.options.filter { it !is NullType }.size - if (nNonNullOptions < 2) { + if (schema.options.size < 2) { return value } @@ -82,7 +75,6 @@ class UnionValueToDisjointRecord(meta: DestinationRecord.Meta) : AirbyteValueIde is BooleanType -> value is BooleanValue is DateType -> value is DateValue is IntegerType -> value is IntegerValue - is NullType -> value is NullValue is NumberType -> value is NumberValue is ObjectType, is ObjectTypeWithoutSchema, diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/AirbyteTypeToJsonSchema.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/AirbyteTypeToJsonSchema.kt index c1f290a6f894..a55eb19183b6 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/AirbyteTypeToJsonSchema.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/AirbyteTypeToJsonSchema.kt @@ -16,7 +16,6 @@ class AirbyteTypeToJsonSchema { fun convert(airbyteType: AirbyteType): JsonNode { return when (airbyteType) { - is NullType -> ofType("null") is StringType -> ofType("string") is BooleanType -> ofType("boolean") is IntegerType -> ofType("integer") @@ -25,13 +24,13 @@ class AirbyteTypeToJsonSchema { JsonNodeFactory.instance .objectNode() .put("type", "array") - .set("items", fromFieldType(airbyteType.items)) + .set("items", convert(airbyteType.items.type)) is ArrayTypeWithoutSchema -> ofType("array") is ObjectType -> { val objNode = ofType("object") val properties = objNode.putObject("properties") airbyteType.properties.forEach { (name, field) -> - properties.replace(name, fromFieldType(field)) + properties.replace(name, convert(field.type)) } objNode } @@ -68,14 +67,4 @@ class AirbyteTypeToJsonSchema { is UnknownType -> JsonNodeFactory.instance.objectNode() } } - - private fun fromFieldType(field: FieldType): JsonNode { - if (field.nullable) { - if (field.type is UnionType) { - return convert(UnionType(options = field.type.options + NullType)) - } - return convert(UnionType(options = listOf(field.type, NullType))) - } - return convert(field.type) - } } diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/JsonSchemaToAirbyteType.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/JsonSchemaToAirbyteType.kt index 7653c70e467c..163cff0358dc 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/JsonSchemaToAirbyteType.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/JsonSchemaToAirbyteType.kt @@ -10,7 +10,9 @@ import com.fasterxml.jackson.databind.node.ObjectNode import io.airbyte.cdk.load.data.* class JsonSchemaToAirbyteType { - fun convert(schema: JsonNode): AirbyteType { + fun convert(schema: JsonNode): AirbyteType = convertInner(schema)!! + + private fun convertInner(schema: JsonNode): AirbyteType? { // try { if (schema.isObject && schema.has("type")) { // Normal json object with {"type": ..., ...} @@ -24,33 +26,33 @@ class JsonSchemaToAirbyteType { "number" -> fromNumber(schema) "array" -> fromArray(schema) "object" -> fromObject(schema) - "null" -> NullType + "null" -> null else -> throw IllegalArgumentException( "Unknown type: ${ - schema.get("type").asText() - }" + schema.get("type").asText() + }" ) } } else if (schemaType.isArray) { // {"type": [...], ...} unionFromCombinedTypes(schemaType.toList(), schema) } else { - UnknownType("unspported type for 'type' field: $schemaType") + UnknownType("unsupported type for 'type' field: $schemaType") } } else if (schema.isObject) { // {"oneOf": [...], ...} or {"anyOf": [...], ...} or {"allOf": [...], ...} val options = schema.get("oneOf") ?: schema.get("anyOf") ?: schema.get("allOf") return if (options != null) { - UnionType(options.map { convert(it as ObjectNode) }) + optionsToUnionOrSingleType(options.mapNotNull { convertInner(it as ObjectNode) }) } else { // Default to object if no type and not a union type - convert((schema as ObjectNode).put("type", "object")) + convertInner((schema as ObjectNode).put("type", "object")) } } else if (schema.isTextual) { // "" val typeSchema = JsonNodeFactory.instance.objectNode().put("type", schema.asText()) - return convert(typeSchema) + return convertInner(typeSchema) } else { return UnknownType("Unknown schema type: $schema") } @@ -78,8 +80,8 @@ class JsonSchemaToAirbyteType { else -> throw IllegalArgumentException( "Unknown string format: ${ - schema.get("format").asText() - }" + schema.get("format").asText() + }" ) } @@ -96,8 +98,9 @@ class JsonSchemaToAirbyteType { if (items.isEmpty) { return ArrayTypeWithoutSchema } - val itemOptions = UnionType(items.map { convert(it) }) - return ArrayType(fieldFromUnion(itemOptions)) + val itemOptions = UnionType(items.mapNotNull { convertInner(it) }) + val itemType = optionsToUnionOrSingleType(itemOptions.options) + return ArrayType(FieldType(itemType, true)) } return ArrayType(fieldFromSchema(items as ObjectNode)) } @@ -107,60 +110,48 @@ class JsonSchemaToAirbyteType { if (properties.isEmpty) { return ObjectTypeWithEmptySchema } - val requiredFields = schema.get("required")?.map { it.asText() } ?: emptyList() - return objectFromProperties(properties as ObjectNode, requiredFields) + val propertiesMapped = + properties + .fields() + .asSequence() + .map { (name, node) -> name to fieldFromSchema(node as ObjectNode) } + .toMap(LinkedHashMap()) + return ObjectType(propertiesMapped) } private fun fieldFromSchema( fieldSchema: ObjectNode, - onRequiredList: Boolean = false ): FieldType { - val markedRequired = fieldSchema.get("required")?.asBoolean() ?: false - val nullable = !(onRequiredList || markedRequired) - val airbyteType = convert(fieldSchema) - if (airbyteType is UnionType) { - return fieldFromUnion(airbyteType, nullable) - } else { - return FieldType(airbyteType, nullable) - } - } - - private fun fieldFromUnion(unionType: UnionType, nullable: Boolean = false): FieldType { - if (unionType.options.contains(NullType)) { - val filtered = unionType.options.filter { it != NullType } - return FieldType(UnionType(filtered), nullable = true) - } - return FieldType(unionType, nullable = nullable) - } - - private fun objectFromProperties(schema: ObjectNode, requiredFields: List): ObjectType { - val properties = - schema - .fields() - .asSequence() - .map { (name, node) -> - name to fieldFromSchema(node as ObjectNode, requiredFields.contains(name)) - } - .toMap(LinkedHashMap()) - return ObjectType(properties) + val airbyteType = + convertInner(fieldSchema) ?: UnknownType("Illegal null type as field type") + return FieldType(airbyteType, nullable = true) } private fun unionFromCombinedTypes( options: List, parentSchema: ObjectNode - ): UnionType { + ): AirbyteType { // Denormalize the properties across each type (the converter only checks what matters // per type). val unionOptions = - options.map { + options.mapNotNull { if (it.isTextual) { val schema = parentSchema.deepCopy() schema.put("type", it.textValue()) - convert(schema) + convertInner(schema) } else { - convert(it) + convertInner(it) } } - return UnionType(unionOptions) + return optionsToUnionOrSingleType(unionOptions) } + + private fun optionsToUnionOrSingleType(options: List): AirbyteType = + if (options.isEmpty()) { + UnionType(listOf(UnknownType("No valid options in union"))) + } else if (options.size == 1) { + options.first() + } else { + UnionType(options) + } } diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/JsonToAirbyteValue.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/JsonToAirbyteValue.kt index aada67af68ee..96557f74ef78 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/JsonToAirbyteValue.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/json/JsonToAirbyteValue.kt @@ -30,7 +30,6 @@ class JsonToAirbyteValue { is BooleanType -> toBoolean(json) is DateType -> DateValue(json.asText()) is IntegerType -> toInteger(json) - is NullType -> toNull(json) is NumberType -> toNumber(json) is ObjectType -> toObject(json, schema) is ObjectTypeWithoutSchema, @@ -185,7 +184,6 @@ class JsonToAirbyteValue { is BooleanType -> json.isBoolean is DateType -> json.isTextual is IntegerType -> json.isIntegralNumber - is NullType -> json.isNull is NumberType -> json.isNumber is ObjectType, is ObjectTypeWithoutSchema, diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteSchemaIdentityMapperTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteSchemaIdentityMapperTest.kt index f91d2f9680a6..3338f3e7edcb 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteSchemaIdentityMapperTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteSchemaIdentityMapperTest.kt @@ -19,9 +19,8 @@ class AirbyteSchemaIdentityMapperTest { .with(IntegerType) .with(BooleanType) .with(NumberType) - .with(NullType) .with(ArrayType(FieldType(StringType, true))) - .with(UnionType(listOf(StringType, IntegerType, NullType))) + .with(UnionType(listOf(StringType, IntegerType))) .withRecord() .with(TimeTypeWithTimezone) .with(TimeTypeWithoutTimezone) @@ -33,7 +32,6 @@ class AirbyteSchemaIdentityMapperTest { .with(ArrayTypeWithoutSchema) .endRecord() .endRecord() - .with(NullType) .build() val mapper = object : AirbyteSchemaIdentityMapper {} diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapperTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapperTest.kt index 6adcad2dadd4..f162f8bfc630 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapperTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapperTest.kt @@ -35,7 +35,6 @@ class AirbyteValueIdentityMapperTest { ArrayTypeWithoutSchema ) .withRecord() - .with(NullValue, NullType) .endRecord() .endRecord() .build() diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/NullableToUnionNullTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/NullableToUnionNullTest.kt deleted file mode 100644 index 7832c3628232..000000000000 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/NullableToUnionNullTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2024 Airbyte, Inc., all rights reserved. - */ - -package io.airbyte.cdk.load.data - -import io.airbyte.cdk.load.test.util.Root -import io.airbyte.cdk.load.test.util.SchemaRecordBuilder -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test - -class NullableToUnionNullTest { - @Test - fun testBasicBehavior() { - val (inputSchema, expectedOutput) = - SchemaRecordBuilder() - .with(FieldType(StringType, nullable = false)) - .with( - FieldType(IntegerType, nullable = true), - FieldType(UnionType(listOf(IntegerType, NullType)), nullable = false) - ) - .build() - Assertions.assertEquals(NullableToUnionNull().map(inputSchema), expectedOutput) - } - - @Test - fun testWackyBehavior() { - val (inputSchema, expectedOutput) = - SchemaRecordBuilder() - .with(FieldType(UnionType(listOf(StringType, IntegerType)), nullable = false)) - .with( - FieldType(UnionType(listOf(StringType, IntegerType)), nullable = true), - FieldType( - UnionType(listOf(UnionType(listOf(StringType, IntegerType)), NullType)), - nullable = false - ) - ) - .with(FieldType(UnionType(listOf(StringType, NullType)), nullable = false)) - .with( - FieldType(UnionType(listOf(StringType, NullType)), nullable = true), - FieldType( - UnionType(listOf(UnionType(listOf(StringType, NullType)), NullType)), - nullable = false - ) - ) - .build() - Assertions.assertEquals(NullableToUnionNull().map(inputSchema), expectedOutput) - } -} diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecordTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecordTest.kt index dee9cc9e95a8..4e601b50f5cb 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecordTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecordTest.kt @@ -12,7 +12,7 @@ import org.junit.jupiter.api.Test class UnionTypeToDisjointRecordTest { @Test fun testBasicSchemaBehavior() { - val disjoinRecord = + val disjointRecord = ObjectType( linkedMapOf( "type" to FieldType(StringType, nullable = false), @@ -23,15 +23,7 @@ class UnionTypeToDisjointRecordTest { val (inputSchema, expectedOutput) = SchemaRecordBuilder() .with(UnionType(listOf(StringType))) // union of 1 => ignore - .with(UnionType(listOf(StringType, NullType))) // union of 1 w/ null => ignore - .with( - UnionType(listOf(StringType, IntegerType)), - expected = disjoinRecord - ) // union of 2 => disjoint - .with( - UnionType(listOf(StringType, IntegerType, NullType)), - expected = UnionType(listOf(NullType, disjoinRecord)) - ) // union of 2 w/ null => disjoint + .with(UnionType(listOf(StringType, IntegerType)), expected = disjointRecord) .build() val output = UnionTypeToDisjointRecord().map(inputSchema) Assertions.assertEquals(expectedOutput, output) diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/AirbyteSchemaTypeToJsonSchemaTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/AirbyteSchemaTypeToJsonSchemaTest.kt index b4139df4af5e..6f4f1bc8fc91 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/AirbyteSchemaTypeToJsonSchemaTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/AirbyteSchemaTypeToJsonSchemaTest.kt @@ -4,145 +4,172 @@ package io.airbyte.cdk.load.data.json -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.node.JsonNodeFactory -import com.fasterxml.jackson.databind.node.ObjectNode +import io.airbyte.cdk.load.data.ArrayType +import io.airbyte.cdk.load.data.BooleanType +import io.airbyte.cdk.load.data.DateType +import io.airbyte.cdk.load.data.FieldType +import io.airbyte.cdk.load.data.IntegerType +import io.airbyte.cdk.load.data.NumberType +import io.airbyte.cdk.load.data.ObjectType +import io.airbyte.cdk.load.data.StringType +import io.airbyte.cdk.load.data.TimeTypeWithTimezone +import io.airbyte.cdk.load.data.TimeTypeWithoutTimezone +import io.airbyte.cdk.load.data.TimestampTypeWithTimezone +import io.airbyte.cdk.load.data.TimestampTypeWithoutTimezone +import io.airbyte.cdk.load.data.UnionType +import io.airbyte.cdk.load.util.deserializeToNode +import io.airbyte.cdk.load.util.serializeToString import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test class AirbyteSchemaTypeToJsonSchemaTest { - @Test - fun testRoundTrip() { - val schema = JsonNodeFactory.instance.objectNode() - val props = schema.putObject("properties") - props.putObject("name").put("type", "string").put("required", true) - props.putObject("age").put("type", "integer") - props.putObject("is_cool").put("type", "boolean") - props.putObject("height").put("type", "number") - props.putObject("friends").put("type", "array").putObject("items").put("type", "string") - val subProps = props.putObject("address").put("type", "object").putObject("properties") - subProps.putObject("street").put("type", "string") - subProps.putObject("city").put("type", "string") - props.putObject("null_field").put("type", "null") - val union = props.putObject("nullable_union").putArray("oneOf") - union.add(JsonNodeFactory.instance.objectNode().put("type", "string")) - union.add(JsonNodeFactory.instance.objectNode().put("type", "integer")) - union.add(JsonNodeFactory.instance.objectNode().put("type", "null")) - - val union2 = props.putObject("nonnullable_union") - val union2opts = union2.putArray("oneOf") - union2opts.add(JsonNodeFactory.instance.objectNode().put("type", "string")) - union2opts.add(JsonNodeFactory.instance.objectNode().put("type", "integer")) - union2.put("required", true) - - props.putObject("combined_null").putArray("type").add("string").add("null") - - val combinedDenormalized = props.putObject("combined_denormalized") - combinedDenormalized.putArray("type").add("string").add("object") - combinedDenormalized.putObject("properties").putObject("name").put("type", "string") - - props - .putObject("union_array") - .put("type", "array") - .putArray("items") - .add("string") - .add("integer") - - props.putObject("date").put("type", "string").put("format", "date") - props.putObject("time").put("type", "string").put("format", "time") - props.putObject("timestamp").put("type", "string").put("format", "date-time") - props - .putObject("time_without_timezone") - .put("type", "string") - .put("format", "time") - .put("airbyte_type", "time_without_timezone") - props - .putObject("timestamp_without_timezone") - .put("type", "string") - .put("format", "date-time") - .put("airbyte_type", "timestamp_without_timezone") - - val converted = JsonSchemaToAirbyteType().convert(schema) - val unconverted = AirbyteTypeToJsonSchema().convert(converted) - - val propsOut = unconverted.get("properties") - Assertions.assertEquals(ofType("string", false), propsOut.get("name")) - Assertions.assertEquals(ofType("integer", true), propsOut.get("age")) - Assertions.assertEquals(ofType("boolean", true), propsOut.get("is_cool")) - Assertions.assertEquals(ofType("number", true), propsOut.get("height")) - - val friends = JsonNodeFactory.instance.objectNode() - friends.put("type", "array").replace("items", ofType("string", true)) - Assertions.assertEquals(ofNullable(friends), propsOut.get("friends")) - - val address = JsonNodeFactory.instance.objectNode() - val addressProps = address.put("type", "object").putObject("properties") - addressProps.replace("street", ofType("string", true)) - addressProps.replace("city", ofType("string", true)) - Assertions.assertEquals(ofNullable(address), propsOut.get("address")) - - Assertions.assertEquals(ofType("null", true), propsOut.get("null_field")) - - val nullableUnion = JsonNodeFactory.instance.objectNode() - nullableUnion - .putArray("oneOf") - .add(ofType("string", false)) - .add(ofType("integer", false)) - .add(ofType("null", false)) - Assertions.assertEquals(nullableUnion, propsOut.get("nullable_union")) - - val nonnullableUnion = JsonNodeFactory.instance.objectNode() - nonnullableUnion - .putArray("oneOf") - .add(ofType("string", false)) - .add(ofType("integer", false)) - Assertions.assertEquals(nonnullableUnion, propsOut.get("nonnullable_union")) - - Assertions.assertEquals(ofType("string", true), propsOut.get("combined_null")) - - val combinedDenormed = JsonNodeFactory.instance.objectNode() - val cdObj = ofType("object", false) - cdObj.putObject("properties").replace("name", ofType("string", true)) - combinedDenormed - .putArray("oneOf") - .add(ofType("string", false)) - .add(cdObj) - .add(ofType("null", false)) - Assertions.assertEquals(combinedDenormed, propsOut.get("combined_denormalized")) - - val unionArrayOut = JsonNodeFactory.instance.objectNode() - unionArrayOut - .put("type", "array") - .putObject("items") - .putArray("oneOf") - .add(ofType("string", false)) - .add(ofType("integer", false)) - Assertions.assertEquals(ofNullable(unionArrayOut), propsOut.get("union_array")) - - val timeTypeFieldNames = - listOf("time", "timestamp", "time_without_timezone", "timestamp_without_timezone") - timeTypeFieldNames.forEach { fieldName -> - val expected = props.get(fieldName) as ObjectNode - if (listOf("date-time", "time").contains(expected.get("format").asText())) { - val formatName = expected.get("format").asText().replace("date-time", "timestamp") - if (!expected.has("airbyte_type")) { - expected.put("airbyte_type", "${formatName}_with_timezone") + // A json of every supported type + private val airbyteType = + ObjectType( + mapOf( + "name" to StringType, + "age" to IntegerType, + "is_cool" to BooleanType, + "height" to NumberType, + "alt_integer" to IntegerType, + "friends" to ArrayType(FieldType(StringType, true)), + "mixed_array" to + ArrayType( + FieldType(UnionType(listOf(StringType, IntegerType)), nullable = true) + ), + "address" to + ObjectType( + linkedMapOf( + "street" to FieldType(StringType, true), + "city" to FieldType(StringType, true) + ) + ), + "nonnullable_union" to UnionType(listOf(StringType)), + "combined_denormalized" to + ObjectType(linkedMapOf("name" to FieldType(StringType, true))), + "union_array" to + ArrayType(FieldType(UnionType(listOf(StringType, IntegerType)), true)), + "date" to DateType, + "time" to TimeTypeWithTimezone, + "time_without_timezone" to TimeTypeWithoutTimezone, + "timestamp" to TimestampTypeWithTimezone, + "timestamp_without_timezone" to TimestampTypeWithoutTimezone + ) + .map { it.key to FieldType(it.value, nullable = true) } + .let { linkedMapOf(*it.toTypedArray()) } + ) + + // the json equivalent + private val json = + """ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "age": { + "type": "integer" + }, + "is_cool": { + "type": "boolean" + }, + "height": { + "type": "number" + }, + "alt_integer": { + "type": "integer" + }, + "friends": { + "type": "array", + "items": { + "type": "string" + } + }, + "mixed_array": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + } + }, + "address": { + "type": "object", + "properties": { + "street": { + "type": "string" + }, + "city": { + "type": "string" } + } + }, + "nonnullable_union": { + "oneOf": [ + { + "type": "string" + } + ] + }, + "combined_denormalized": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "union_array": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + } + }, + "date": { + "type": "string", + "format": "date" + }, + "time": { + "type": "string", + "format": "time", + "airbyte_type": "time_with_timezone" + }, + "time_without_timezone": { + "type": "string", + "format": "time", + "airbyte_type": "time_without_timezone" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "airbyte_type": "timestamp_with_timezone" + }, + "timestamp_without_timezone": { + "type": "string", + "format": "date-time", + "airbyte_type": "timestamp_without_timezone" } - Assertions.assertEquals(ofNullable(expected), propsOut.get(fieldName)) - } - } - - private fun ofType(type: String, nullable: Boolean = true): ObjectNode = - if (nullable) { - ofNullable(ofType(type, false)) - } else { - JsonNodeFactory.instance.objectNode().put("type", type) + } } + """.trimIndent() - private fun ofNullable(typeNode: JsonNode): ObjectNode { - val schema = JsonNodeFactory.instance.objectNode() - schema.putArray("oneOf").add(typeNode).add(ofType("null", false)) - return schema + @Test + fun testToJsonSchema() { + val expected = json.deserializeToNode().serializeToString() + val actual = AirbyteTypeToJsonSchema().convert(airbyteType).serializeToString() + Assertions.assertEquals(expected, actual) } } diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/AirbyteValueToJsonTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/AirbyteValueToJsonTest.kt index 4d2bede2c25a..76ca3ad817cf 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/AirbyteValueToJsonTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/AirbyteValueToJsonTest.kt @@ -13,8 +13,6 @@ import io.airbyte.cdk.load.data.DateValue import io.airbyte.cdk.load.data.FieldType import io.airbyte.cdk.load.data.IntegerType import io.airbyte.cdk.load.data.IntegerValue -import io.airbyte.cdk.load.data.NullType -import io.airbyte.cdk.load.data.NullValue import io.airbyte.cdk.load.data.NumberType import io.airbyte.cdk.load.data.NumberValue import io.airbyte.cdk.load.data.ObjectType @@ -49,10 +47,7 @@ class AirbyteValueToJsonTest { "city" to StringValue("San Francisco") ) ), - "null_field" to NullValue, - "nullable_union" to IntegerValue(42), - "nonnullable_union" to StringValue("hello"), - "combined_null" to StringValue("hello"), + "union" to StringValue("hello"), "combined_denormalized" to ObjectValue(linkedMapOf("name" to StringValue("hello"))), "union_array" to ArrayValue(listOf(StringValue("hello"), IntegerValue(42))), @@ -81,12 +76,7 @@ class AirbyteValueToJsonTest { ), false ), - "null_field" to FieldType(NullType, false), - "nullable_union" to - FieldType(UnionType(listOf(StringType, IntegerType, NullType)), false), - "nonnullable_union" to - FieldType(UnionType(listOf(StringType, IntegerType)), true), - "combined_null" to FieldType(UnionType(listOf(StringType, NullType)), false), + "union" to FieldType(UnionType(listOf(StringType, IntegerType)), true), "combined_denormalized" to FieldType( ObjectType(linkedMapOf("name" to FieldType(StringType, true))), diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/JsonSchemaToAirbyteSchemaTypeTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/JsonSchemaToAirbyteSchemaTypeTest.kt index 6264dea2dca4..5a291518d38a 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/JsonSchemaToAirbyteSchemaTypeTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/JsonSchemaToAirbyteSchemaTypeTest.kt @@ -12,7 +12,6 @@ import io.airbyte.cdk.load.data.BooleanType import io.airbyte.cdk.load.data.DateType import io.airbyte.cdk.load.data.FieldType import io.airbyte.cdk.load.data.IntegerType -import io.airbyte.cdk.load.data.NullType import io.airbyte.cdk.load.data.NumberType import io.airbyte.cdk.load.data.ObjectType import io.airbyte.cdk.load.data.ObjectTypeWithEmptySchema @@ -32,13 +31,6 @@ class JsonSchemaToAirbyteSchemaTypeTest { return JsonNodeFactory.instance.objectNode().put("type", type) } - @Test - fun testNull() { - val nullType = ofType("null") - val airbyteType = JsonSchemaToAirbyteType().convert(nullType) - Assertions.assertTrue(airbyteType is NullType) - } - @Test fun testString() { val stringType = ofType("string") diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/JsonToAirbyteValueTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/JsonToAirbyteValueTest.kt index 515099026352..b5d309481f11 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/JsonToAirbyteValueTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/json/JsonToAirbyteValueTest.kt @@ -15,8 +15,6 @@ import io.airbyte.cdk.load.data.DateValue import io.airbyte.cdk.load.data.FieldType import io.airbyte.cdk.load.data.IntegerType import io.airbyte.cdk.load.data.IntegerValue -import io.airbyte.cdk.load.data.NullType -import io.airbyte.cdk.load.data.NullValue import io.airbyte.cdk.load.data.NumberType import io.airbyte.cdk.load.data.NumberValue import io.airbyte.cdk.load.data.ObjectType @@ -36,11 +34,6 @@ import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test class JsonToAirbyteValueTest { - @Test - fun testNull() { - val value = JsonToAirbyteValue().convert(JsonNodeFactory.instance.nullNode(), NullType) - Assertions.assertTrue(value is NullValue) - } @Test fun testString() { diff --git a/airbyte-cdk/bulk/toolkits/load-avro/src/main/kotlin/io/airbyte/cdk/load/data/avro/AirbyteTypeToAvroSchema.kt b/airbyte-cdk/bulk/toolkits/load-avro/src/main/kotlin/io/airbyte/cdk/load/data/avro/AirbyteTypeToAvroSchema.kt index a4c8797db572..8cedca5ffb42 100644 --- a/airbyte-cdk/bulk/toolkits/load-avro/src/main/kotlin/io/airbyte/cdk/load/data/avro/AirbyteTypeToAvroSchema.kt +++ b/airbyte-cdk/bulk/toolkits/load-avro/src/main/kotlin/io/airbyte/cdk/load/data/avro/AirbyteTypeToAvroSchema.kt @@ -11,7 +11,6 @@ import io.airbyte.cdk.load.data.ArrayTypeWithoutSchema import io.airbyte.cdk.load.data.BooleanType import io.airbyte.cdk.load.data.DateType import io.airbyte.cdk.load.data.IntegerType -import io.airbyte.cdk.load.data.NullType import io.airbyte.cdk.load.data.NumberType import io.airbyte.cdk.load.data.ObjectType import io.airbyte.cdk.load.data.ObjectTypeWithEmptySchema @@ -54,7 +53,6 @@ class AirbyteTypeToAvroSchema { LogicalTypes.date().addToSchema(schema) } is IntegerType -> SchemaBuilder.builder().longType() - is NullType -> SchemaBuilder.builder().nullType() is NumberType -> SchemaBuilder.builder().doubleType() is ObjectTypeWithEmptySchema -> throw IllegalArgumentException("Object type with empty schema is not supported") diff --git a/airbyte-cdk/bulk/toolkits/load-avro/src/testFixtures/kotlin/io/airbyte/cdk/load/data/avro/AvroRecordToAirbyteValue.kt b/airbyte-cdk/bulk/toolkits/load-avro/src/testFixtures/kotlin/io/airbyte/cdk/load/data/avro/AvroRecordToAirbyteValue.kt index 0cd0ba1f680d..1ba25b45e78e 100644 --- a/airbyte-cdk/bulk/toolkits/load-avro/src/testFixtures/kotlin/io/airbyte/cdk/load/data/avro/AvroRecordToAirbyteValue.kt +++ b/airbyte-cdk/bulk/toolkits/load-avro/src/testFixtures/kotlin/io/airbyte/cdk/load/data/avro/AvroRecordToAirbyteValue.kt @@ -14,8 +14,6 @@ import io.airbyte.cdk.load.data.BooleanValue import io.airbyte.cdk.load.data.DateType import io.airbyte.cdk.load.data.IntegerType import io.airbyte.cdk.load.data.IntegerValue -import io.airbyte.cdk.load.data.NullType -import io.airbyte.cdk.load.data.NullValue import io.airbyte.cdk.load.data.NumberType import io.airbyte.cdk.load.data.NumberValue import io.airbyte.cdk.load.data.ObjectType @@ -55,7 +53,6 @@ class AvroRecordToAirbyteValue { is BooleanType -> return BooleanValue(avroValue as Boolean) is DateType -> throw UnsupportedOperationException("DateType is not supported") is IntegerType -> return IntegerValue(avroValue as Long) - is NullType -> return NullValue is NumberType -> return NumberValue((avroValue as Double).toBigDecimal()) is ObjectTypeWithEmptySchema -> throw UnsupportedOperationException("ObjectTypeWithEmptySchema is not supported") diff --git a/airbyte-cdk/bulk/toolkits/load-object-storage/src/main/kotlin/io/airbyte/cdk/load/file/object_storage/ObjectStorageFormattingWriter.kt b/airbyte-cdk/bulk/toolkits/load-object-storage/src/main/kotlin/io/airbyte/cdk/load/file/object_storage/ObjectStorageFormattingWriter.kt index bc70149ebc3c..e1aec13d8798 100644 --- a/airbyte-cdk/bulk/toolkits/load-object-storage/src/main/kotlin/io/airbyte/cdk/load/file/object_storage/ObjectStorageFormattingWriter.kt +++ b/airbyte-cdk/bulk/toolkits/load-object-storage/src/main/kotlin/io/airbyte/cdk/load/file/object_storage/ObjectStorageFormattingWriter.kt @@ -10,11 +10,12 @@ import io.airbyte.cdk.load.command.object_storage.CSVFormatConfiguration import io.airbyte.cdk.load.command.object_storage.JsonFormatConfiguration import io.airbyte.cdk.load.command.object_storage.ObjectStorageFormatConfigurationProvider import io.airbyte.cdk.load.command.object_storage.ParquetFormatConfiguration -import io.airbyte.cdk.load.data.DestinationRecordToAirbyteValueWithMeta import io.airbyte.cdk.load.data.avro.toAvroRecord import io.airbyte.cdk.load.data.avro.toAvroSchema import io.airbyte.cdk.load.data.csv.toCsvRecord +import io.airbyte.cdk.load.data.dataWithAirbyteMeta import io.airbyte.cdk.load.data.json.toJson +import io.airbyte.cdk.load.data.withAirbyteMeta import io.airbyte.cdk.load.file.avro.toAvroWriter import io.airbyte.cdk.load.file.csv.toCsvPrinterWithHeader import io.airbyte.cdk.load.file.parquet.toParquetWriter @@ -33,7 +34,6 @@ interface ObjectStorageFormattingWriter : Closeable { @Singleton @Secondary class ObjectStorageFormattingWriterFactory( - private val recordDecorator: DestinationRecordToAirbyteValueWithMeta, private val formatConfigProvider: ObjectStorageFormatConfigurationProvider, ) { fun create( @@ -41,14 +41,13 @@ class ObjectStorageFormattingWriterFactory( outputStream: OutputStream ): ObjectStorageFormattingWriter { return when (formatConfigProvider.objectStorageFormatConfiguration) { - is JsonFormatConfiguration -> JsonFormattingWriter(outputStream, recordDecorator) + is JsonFormatConfiguration -> JsonFormattingWriter(stream, outputStream) is AvroFormatConfiguration -> AvroFormattingWriter( stream, outputStream, formatConfigProvider.objectStorageFormatConfiguration as AvroFormatConfiguration, - recordDecorator ) is ParquetFormatConfiguration -> ParquetFormattingWriter( @@ -56,19 +55,18 @@ class ObjectStorageFormattingWriterFactory( outputStream, formatConfigProvider.objectStorageFormatConfiguration as ParquetFormatConfiguration, - recordDecorator ) - is CSVFormatConfiguration -> CSVFormattingWriter(stream, outputStream, recordDecorator) + is CSVFormatConfiguration -> CSVFormattingWriter(stream, outputStream) } } } class JsonFormattingWriter( + private val stream: DestinationStream, private val outputStream: OutputStream, - private val recordDecorator: DestinationRecordToAirbyteValueWithMeta ) : ObjectStorageFormattingWriter { override fun accept(record: DestinationRecord) { - outputStream.write(recordDecorator.decorate(record).toJson().serializeToString()) + outputStream.write(record.dataWithAirbyteMeta(stream).toJson().serializeToString()) outputStream.write("\n") } @@ -78,13 +76,12 @@ class JsonFormattingWriter( } class CSVFormattingWriter( - stream: DestinationStream, + private val stream: DestinationStream, outputStream: OutputStream, - private val recordDecorator: DestinationRecordToAirbyteValueWithMeta ) : ObjectStorageFormattingWriter { - private val printer = stream.schemaWithMeta.toCsvPrinterWithHeader(outputStream) + private val printer = stream.schema.withAirbyteMeta().toCsvPrinterWithHeader(outputStream) override fun accept(record: DestinationRecord) { - printer.printRecord(*recordDecorator.decorate(record).toCsvRecord()) + printer.printRecord(*record.dataWithAirbyteMeta(stream).toCsvRecord()) } override fun close() { printer.close() @@ -92,16 +89,15 @@ class CSVFormattingWriter( } class AvroFormattingWriter( - stream: DestinationStream, + private val stream: DestinationStream, outputStream: OutputStream, formatConfig: AvroFormatConfiguration, - private val recordDecorator: DestinationRecordToAirbyteValueWithMeta ) : ObjectStorageFormattingWriter { - private val avroSchema = stream.schemaWithMeta.toAvroSchema(stream.descriptor) + private val avroSchema = stream.schema.withAirbyteMeta().toAvroSchema(stream.descriptor) private val writer = outputStream.toAvroWriter(avroSchema, formatConfig.avroCompressionConfiguration) override fun accept(record: DestinationRecord) { - writer.write(recordDecorator.decorate(record).toAvroRecord(avroSchema)) + writer.write(record.dataWithAirbyteMeta(stream).toAvroRecord(avroSchema)) } override fun close() { @@ -110,16 +106,15 @@ class AvroFormattingWriter( } class ParquetFormattingWriter( - stream: DestinationStream, + private val stream: DestinationStream, outputStream: OutputStream, formatConfig: ParquetFormatConfiguration, - private val recordDecorator: DestinationRecordToAirbyteValueWithMeta ) : ObjectStorageFormattingWriter { - private val avroSchema = stream.schemaWithMeta.toAvroSchema(stream.descriptor) + private val avroSchema = stream.schema.withAirbyteMeta().toAvroSchema(stream.descriptor) private val writer = outputStream.toParquetWriter(avroSchema, formatConfig.parquetWriterConfiguration) override fun accept(record: DestinationRecord) { - writer.write(recordDecorator.decorate(record).toAvroRecord(avroSchema)) + writer.write(record.dataWithAirbyteMeta(stream).toAvroRecord(avroSchema)) } override fun close() { diff --git a/airbyte-cdk/bulk/toolkits/load-object-storage/src/testFixtures/kotlin/io/airbyte/cdk/load/ObjectStorageDataDumper.kt b/airbyte-cdk/bulk/toolkits/load-object-storage/src/testFixtures/kotlin/io/airbyte/cdk/load/ObjectStorageDataDumper.kt index 00210b252097..f683be9efc3e 100644 --- a/airbyte-cdk/bulk/toolkits/load-object-storage/src/testFixtures/kotlin/io/airbyte/cdk/load/ObjectStorageDataDumper.kt +++ b/airbyte-cdk/bulk/toolkits/load-object-storage/src/testFixtures/kotlin/io/airbyte/cdk/load/ObjectStorageDataDumper.kt @@ -15,6 +15,7 @@ import io.airbyte.cdk.load.data.avro.toAirbyteValue import io.airbyte.cdk.load.data.avro.toAvroSchema import io.airbyte.cdk.load.data.csv.toAirbyteValue import io.airbyte.cdk.load.data.json.toAirbyteValue +import io.airbyte.cdk.load.data.withAirbyteMeta import io.airbyte.cdk.load.file.GZIPProcessor import io.airbyte.cdk.load.file.NoopProcessor import io.airbyte.cdk.load.file.avro.toAvroReader @@ -76,7 +77,7 @@ class ObjectStorageDataDumper( .map { line -> line .deserializeToNode() - .toAirbyteValue(stream.schemaWithMeta) + .toAirbyteValue(stream.schema.withAirbyteMeta()) .toOutputRecord() } .toList() @@ -84,27 +85,33 @@ class ObjectStorageDataDumper( is CSVFormatConfiguration -> { CSVParser(inputStream.bufferedReader(), CSVFormat.DEFAULT.withHeader()).use { it.records.map { record -> - record.toAirbyteValue(stream.schemaWithMeta).toOutputRecord() + record.toAirbyteValue(stream.schema.withAirbyteMeta()).toOutputRecord() } } } is AvroFormatConfiguration -> { inputStream - .toAvroReader(stream.schemaWithMeta.toAvroSchema(stream.descriptor)) + .toAvroReader(stream.schema.withAirbyteMeta().toAvroSchema(stream.descriptor)) .use { reader -> reader .recordSequence() - .map { it.toAirbyteValue(stream.schemaWithMeta).toOutputRecord() } + .map { + it.toAirbyteValue(stream.schema.withAirbyteMeta()).toOutputRecord() + } .toList() } } is ParquetFormatConfiguration -> { inputStream - .toParquetReader(stream.schemaWithMeta.toAvroSchema(stream.descriptor)) + .toParquetReader( + stream.schema.withAirbyteMeta().toAvroSchema(stream.descriptor) + ) .use { reader -> reader .recordSequence() - .map { it.toAirbyteValue(stream.schemaWithMeta).toOutputRecord() } + .map { + it.toAirbyteValue(stream.schema.withAirbyteMeta()).toOutputRecord() + } .toList() } } From 5157ad3a00731adda8a1e17b3e2a187ebb500bf8 Mon Sep 17 00:00:00 2001 From: Natik Gadzhi Date: Wed, 6 Nov 2024 13:36:38 -0800 Subject: [PATCH 11/45] source-brex contribution from natikgadzhi (#48017) --- .../connectors/source-brex/README.md | 33 ++ .../source-brex/acceptance-test-config.yml | 17 + .../connectors/source-brex/icon.svg | 3 + .../connectors/source-brex/manifest.yaml | 419 ++++++++++++++++++ .../connectors/source-brex/metadata.yaml | 35 ++ docs/integrations/sources/brex.md | 30 ++ 6 files changed, 537 insertions(+) create mode 100644 airbyte-integrations/connectors/source-brex/README.md create mode 100644 airbyte-integrations/connectors/source-brex/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-brex/icon.svg create mode 100644 airbyte-integrations/connectors/source-brex/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-brex/metadata.yaml create mode 100644 docs/integrations/sources/brex.md diff --git a/airbyte-integrations/connectors/source-brex/README.md b/airbyte-integrations/connectors/source-brex/README.md new file mode 100644 index 000000000000..d6fa23bf4c87 --- /dev/null +++ b/airbyte-integrations/connectors/source-brex/README.md @@ -0,0 +1,33 @@ +# Brex +This directory contains the manifest-only connector for `source-brex`. + +Fetches data on users, expenses, transactions, vendors, and budgets from Brex API. + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-brex:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-brex build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-brex test +``` + diff --git a/airbyte-integrations/connectors/source-brex/acceptance-test-config.yml b/airbyte-integrations/connectors/source-brex/acceptance-test-config.yml new file mode 100644 index 000000000000..352887b11a2e --- /dev/null +++ b/airbyte-integrations/connectors/source-brex/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-brex:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-brex/icon.svg b/airbyte-integrations/connectors/source-brex/icon.svg new file mode 100644 index 000000000000..244b5e327edf --- /dev/null +++ b/airbyte-integrations/connectors/source-brex/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/airbyte-integrations/connectors/source-brex/manifest.yaml b/airbyte-integrations/connectors/source-brex/manifest.yaml new file mode 100644 index 000000000000..3a6c6869bfd1 --- /dev/null +++ b/airbyte-integrations/connectors/source-brex/manifest.yaml @@ -0,0 +1,419 @@ +version: 5.15.0 + +type: DeclarativeSource + +description: >- + Fetches data on users, expenses, transactions, vendors, and budgets from Brex + API. + +check: + type: CheckStream + stream_names: + - transactions + +definitions: + streams: + transactions: + type: DeclarativeStream + name: transactions + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /v2/transactions/card/primary + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - items + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: cursor + pagination_strategy: + type: CursorPagination + cursor_value: "{{ response.get('next_cursor') }}" + stop_condition: "{{ response.get('next_cursor') is none }}" + incremental_sync: + type: DatetimeBasedCursor + cursor_field: posted_at_date + lookback_window: P1D + cursor_datetime_formats: + - "%Y-%m-%d" + datetime_format: "%Y-%m-%dT%H:%M:%S" + start_datetime: + type: MinMaxDatetime + datetime: "{{ config[\"start_date\"] }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + start_time_option: + type: RequestOption + inject_into: request_parameter + field_name: posted_at_start + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/transactions" + users: + type: DeclarativeStream + name: users + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /v2/users + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - items + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: cursor + pagination_strategy: + type: CursorPagination + cursor_value: "{{ response.get('next_cursor') }}" + stop_condition: "{{ response.get('next_cursor') is none }}" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/users" + departments: + type: DeclarativeStream + name: departments + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /v2/departments + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - items + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: cursor + pagination_strategy: + type: CursorPagination + cursor_value: "{{ response.get('next_cursor') }}" + stop_condition: "{{ response.get('next_cursor') is none }}" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/departments" + vendors: + type: DeclarativeStream + name: vendors + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /v1/vendors + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - items + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: cursor + pagination_strategy: + type: CursorPagination + cursor_value: "{{ response.get('next_cursor') }}" + stop_condition: "{{ response.get('next_cursor') is none }}" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/vendors" + expenses: + type: DeclarativeStream + name: expenses + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /v1/expenses/card + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - items + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: cursor + pagination_strategy: + type: CursorPagination + cursor_value: "{{ response.get('next_cursor') }}" + stop_condition: "{{ response.get('next_cursor') is none }}" + incremental_sync: + type: DatetimeBasedCursor + cursor_field: purchased_at + lookback_window: P1M + cursor_datetime_formats: + - "%Y-%m-%dT%H:%M:%SZ" + - "%Y-%m-%dT%H:%M:%S.%fZ" + datetime_format: "%Y-%m-%dT%H:%M:%S" + start_datetime: + type: MinMaxDatetime + datetime: "{{ config[\"start_date\"] }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + start_time_option: + type: RequestOption + inject_into: request_parameter + field_name: purchased_at_start + end_time_option: + type: RequestOption + inject_into: request_parameter + field_name: purchased_at_end + end_datetime: + type: MinMaxDatetime + datetime: "{{ now_utc().strftime('%Y-%m-%dT%H:%M:%SZ') }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/expenses" + budgets: + type: DeclarativeStream + name: budgets + primary_key: + - budget_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /v2/budgets + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - items + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: cursor + pagination_strategy: + type: CursorPagination + cursor_value: "{{ response.get('next_cursor') }}" + stop_condition: "{{ response.get('next_cursor') is none }}" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/budgets" + base_requester: + type: HttpRequester + url_base: https://platform.brexapis.com + authenticator: + type: BearerAuthenticator + api_token: "{{ config[\"user_token\"] }}" + +streams: + - $ref: "#/definitions/streams/transactions" + - $ref: "#/definitions/streams/users" + - $ref: "#/definitions/streams/departments" + - $ref: "#/definitions/streams/vendors" + - $ref: "#/definitions/streams/expenses" + - $ref: "#/definitions/streams/budgets" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - user_token + - start_date + properties: + user_token: + type: string + description: >- + User token to authenticate API requests. Generate it from your Brex + dashboard under Developer > Settings. + name: user_token + title: User Token + airbyte_secret: true + order: 0 + start_date: + type: string + title: Start date + format: date-time + pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ + order: 1 + additionalProperties: true + +metadata: + autoImportSchema: + transactions: true + users: false + departments: false + vendors: false + expenses: false + budgets: false + testedStreams: + transactions: + streamHash: 9095688de0c91a5eafb2969f27859182d0eb1329 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + users: + streamHash: 22fb9114f7bec24f5b753c0eed981138105cd5e6 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + departments: + streamHash: 3a7de8adfde28e720a9f9254c63103d34bfc3966 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + vendors: + streamHash: 678154fbac1bda4601d2a9ce17e8e4283df96e22 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + expenses: + streamHash: 83d3aa00696a8b74df332824a39fbcf75bdebd7d + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + budgets: + streamHash: 9bfb438f0d1d68b61814339b4153e4904088baaa + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + assist: + docsUrl: https://developer.brex.com/openapi/transactions_api/ + +schemas: + transactions: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + type: + type: + - string + - "null" + description: + type: + - string + - "null" + amount: + type: + - object + - "null" + properties: + amount: + type: + - number + - "null" + currency: + type: + - string + - "null" + card_id: + type: + - string + - "null" + id: + type: string + initiated_at_date: + type: + - string + - "null" + merchant: + type: + - object + - "null" + properties: + country: + type: + - string + - "null" + mcc: + type: + - string + - "null" + raw_descriptor: + type: + - string + - "null" + posted_at_date: + type: string + required: + - id + - posted_at_date + users: + type: object + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: true + properties: {} + departments: + type: object + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: true + properties: {} + vendors: + type: object + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: true + properties: {} + expenses: + type: object + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: true + properties: {} + budgets: + type: object + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: true + properties: {} diff --git a/airbyte-integrations/connectors/source-brex/metadata.yaml b/airbyte-integrations/connectors/source-brex/metadata.yaml new file mode 100644 index 000000000000..cdafa47cecb0 --- /dev/null +++ b/airbyte-integrations/connectors/source-brex/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "platform.brexapis.com" + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-brex + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.17.0@sha256:9c6bfd080a247b7781ce5b25687e7c44e29d31315d0bf656584b38810521bbaa + connectorSubtype: api + connectorType: source + definitionId: 7d605ca7-f2b8-41c4-96a7-3d5637e58d6d + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-brex + githubIssueLabel: source-brex + icon: icon.svg + license: MIT + name: Brex + releaseDate: 2024-10-30 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/brex + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/brex.md b/docs/integrations/sources/brex.md new file mode 100644 index 000000000000..f1c70051d964 --- /dev/null +++ b/docs/integrations/sources/brex.md @@ -0,0 +1,30 @@ +# Brex +Fetches data on users, expenses, transactions, vendors, and budgets from Brex API. + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `user_token` | `string` | User Token. User token to authenticate API requests. Generate it from your Brex dashboard under Developer > Settings. | | +| `start_date` | `string` | Start date. | | + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| transactions | id | DefaultPaginator | ✅ | ✅ | +| users | id | DefaultPaginator | ✅ | ❌ | +| departments | id | DefaultPaginator | ✅ | ❌ | +| vendors | id | DefaultPaginator | ✅ | ❌ | +| expenses | id | DefaultPaginator | ✅ | ✅ | +| budgets | budget_id | DefaultPaginator | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-10-30 | | Initial release by [@natikgadzhi](https://github.com/natikgadzhi) via Connector Builder | + +
From 5bf61fd1956b6d80748c80f50e0987ffb4c93d6e Mon Sep 17 00:00:00 2001 From: Parthiv Makwana <75653580+parthiv11@users.noreply.github.com> Date: Thu, 7 Nov 2024 04:43:57 +0530 Subject: [PATCH 12/45] source-jobnimbus contribution from parthiv11 (#47707) Co-authored-by: Marcos Marx --- .../connectors/source-jobnimbus/README.md | 33 + .../acceptance-test-config.yml | 17 + .../connectors/source-jobnimbus/icon.svg | 1 + .../connectors/source-jobnimbus/manifest.yaml | 1274 +++++++++++++++++ .../connectors/source-jobnimbus/metadata.yaml | 35 + docs/integrations/sources/jobnimbus.md | 28 + 6 files changed, 1388 insertions(+) create mode 100644 airbyte-integrations/connectors/source-jobnimbus/README.md create mode 100644 airbyte-integrations/connectors/source-jobnimbus/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-jobnimbus/icon.svg create mode 100644 airbyte-integrations/connectors/source-jobnimbus/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-jobnimbus/metadata.yaml create mode 100644 docs/integrations/sources/jobnimbus.md diff --git a/airbyte-integrations/connectors/source-jobnimbus/README.md b/airbyte-integrations/connectors/source-jobnimbus/README.md new file mode 100644 index 000000000000..a80b59fd0a07 --- /dev/null +++ b/airbyte-integrations/connectors/source-jobnimbus/README.md @@ -0,0 +1,33 @@ +# JobNimbus +This directory contains the manifest-only connector for `source-jobnimbus`. + +The JobNimbus Airbyte connector enables seamless integration between JobNimbus, a popular CRM and project management tool, and your data pipeline. This connector allows you to automatically sync data from JobNimbus, including contacts records, task information, and more, into your preferred destination. It simplifies data extraction and helps you streamline workflows, enabling efficient analysis and reporting for enhanced business insights. + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-jobnimbus:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-jobnimbus build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-jobnimbus test +``` + diff --git a/airbyte-integrations/connectors/source-jobnimbus/acceptance-test-config.yml b/airbyte-integrations/connectors/source-jobnimbus/acceptance-test-config.yml new file mode 100644 index 000000000000..fcb55d97f254 --- /dev/null +++ b/airbyte-integrations/connectors/source-jobnimbus/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-jobnimbus:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-jobnimbus/icon.svg b/airbyte-integrations/connectors/source-jobnimbus/icon.svg new file mode 100644 index 000000000000..6b9f7bf24bcd --- /dev/null +++ b/airbyte-integrations/connectors/source-jobnimbus/icon.svg @@ -0,0 +1 @@ +/Logo Vector Files (PDF, EPS, SVG)/JN_Logo SVG/2. JN_Logo_Long-Blue diff --git a/airbyte-integrations/connectors/source-jobnimbus/manifest.yaml b/airbyte-integrations/connectors/source-jobnimbus/manifest.yaml new file mode 100644 index 000000000000..1a669af03dee --- /dev/null +++ b/airbyte-integrations/connectors/source-jobnimbus/manifest.yaml @@ -0,0 +1,1274 @@ +version: 5.15.0 + +type: DeclarativeSource + +description: >- + The JobNimbus Airbyte connector enables seamless integration between + JobNimbus, a popular CRM and project management tool, and your data pipeline. + This connector allows you to automatically sync data from JobNimbus, including + contacts records, task information, and more, into your preferred destination. + It simplifies data extraction and helps you streamline workflows, enabling + efficient analysis and reporting for enhanced business insights. + +check: + type: CheckStream + stream_names: + - jobs + +definitions: + streams: + jobs: + type: DeclarativeStream + name: jobs + primary_key: + - jnid + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /jobs + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: from + pagination_strategy: + type: OffsetIncrement + page_size: 1000 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/jobs" + contacts: + type: DeclarativeStream + name: contacts + primary_key: + - jnid + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /contacts + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: from + pagination_strategy: + type: OffsetIncrement + page_size: 1000 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/contacts" + tasks: + type: DeclarativeStream + name: tasks + primary_key: + - jnid + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /tasks + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: from + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: size + pagination_strategy: + type: OffsetIncrement + page_size: 100 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/tasks" + activities: + type: DeclarativeStream + name: activities + primary_key: + - jnid + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /activities + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - activity + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: from + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: size + pagination_strategy: + type: OffsetIncrement + page_size: 1000 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/activities" + files: + type: DeclarativeStream + name: files + primary_key: + - jnid + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /files + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - files + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: from + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: size + pagination_strategy: + type: OffsetIncrement + page_size: 1000 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/files" + base_requester: + type: HttpRequester + url_base: https://app.jobnimbus.com/api1 + authenticator: + type: BearerAuthenticator + api_token: "{{ config[\"api_key\"] }}" + +streams: + - $ref: "#/definitions/streams/jobs" + - $ref: "#/definitions/streams/contacts" + - $ref: "#/definitions/streams/tasks" + - $ref: "#/definitions/streams/activities" + - $ref: "#/definitions/streams/files" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - api_key + properties: + api_key: + type: string + description: >- + API key to use. Find it by logging into your JobNimbus account, + navigating to settings, and creating a new API key under the API + section. + name: api_key + order: 0 + title: API Key + airbyte_secret: true + additionalProperties: true + +metadata: + autoImportSchema: + jobs: true + contacts: true + tasks: true + activities: true + files: true + testedStreams: + jobs: + streamHash: bca8871c147fef7f6d0f77bfce826a62427599f3 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + contacts: + streamHash: 252325bb77028aaf0376b4576d2cf10e3ab87481 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + tasks: + streamHash: efb1562d11b177ff66e9d91bb3cf1ba1bcb74709 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + activities: + streamHash: d1ae5b85712261b52cae2a375adaf139c96978fd + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + files: + hasRecords: true + streamHash: 8a69c7b1902db69e4a5549ec3c52e587fd48d85d + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + assist: + docsUrl: https://documenter.getpostman.com/view/3919598/S11PpG4x + +schemas: + jobs: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + type: + type: + - string + - "null" + actual_time: + type: + - number + - "null" + address_line1: + type: + - string + - "null" + address_line2: + type: + - string + - "null" + all_day: + type: + - boolean + - "null" + all_day_end_date: + type: + - string + - "null" + all_day_start_date: + type: + - string + - "null" + approved_estimate_total: + type: + - number + - "null" + approved_invoice_due: + type: + - number + - "null" + approved_invoice_total: + type: + - number + - "null" + attachment_count: + type: + - number + - "null" + city: + type: + - string + - "null" + country_name: + type: + - string + - "null" + created_by: + type: + - string + - "null" + created_by_name: + type: + - string + - "null" + customer: + type: + - string + - "null" + date_created: + type: + - number + - "null" + date_end: + type: + - number + - "null" + date_start: + type: + - number + - "null" + date_status_change: + type: + - number + - "null" + date_updated: + type: + - number + - "null" + estimated_time: + type: + - number + - "null" + geo: + type: + - object + - "null" + properties: + lat: + type: + - number + - "null" + lon: + type: + - number + - "null" + is_active: + type: + - boolean + - "null" + is_archived: + type: + - boolean + - "null" + jnid: + type: string + last_budget_gross_margin: + type: + - number + - "null" + last_budget_gross_profit: + type: + - number + - "null" + last_budget_revenue: + type: + - number + - "null" + last_estimate: + type: + - number + - "null" + last_estimate_date_created: + type: + - number + - "null" + last_estimate_date_estimate: + type: + - number + - "null" + last_invoice: + type: + - number + - "null" + last_invoice_date_created: + type: + - number + - "null" + last_invoice_date_invoice: + type: + - number + - "null" + location: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + name: + type: + - string + - "null" + number: + type: + - string + - "null" + owners: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + parent_approved_estimate_total: + type: + - number + - "null" + parent_approved_invoice_due: + type: + - number + - "null" + parent_approved_invoice_total: + type: + - number + - "null" + parent_fax_number: + type: + - string + - "null" + parent_home_phone: + type: + - string + - "null" + parent_last_estimate: + type: + - number + - "null" + parent_last_invoice: + type: + - number + - "null" + parent_mobile_phone: + type: + - string + - "null" + parent_work_phone: + type: + - string + - "null" + primary: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + id: + type: + - string + - "null" + name: + type: + - string + - "null" + number: + type: + - string + - "null" + recid: + type: + - number + - "null" + record_type: + type: + - number + - "null" + record_type_name: + type: + - string + - "null" + related: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + id: + type: + - string + - "null" + name: + type: + - string + - "null" + number: + type: + - string + - "null" + sales_rep: + type: + - string + - "null" + sales_rep_name: + type: + - string + - "null" + state_text: + type: + - string + - "null" + status: + type: + - number + - "null" + status_name: + type: + - string + - "null" + tags: + type: + - array + - "null" + task_count: + type: + - number + - "null" + zip: + type: + - string + - "null" + required: + - jnid + contacts: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + type: + type: + - string + - "null" + actual_time: + type: + - number + - "null" + address_line1: + type: + - string + - "null" + address_line2: + type: + - string + - "null" + approved_estimate_total: + type: + - number + - "null" + approved_invoice_due: + type: + - number + - "null" + approved_invoice_total: + type: + - number + - "null" + attachment_count: + type: + - number + - "null" + city: + type: + - string + - "null" + company: + type: + - string + - "null" + country_name: + type: + - string + - "null" + created_by: + type: + - string + - "null" + created_by_name: + type: + - string + - "null" + customer: + type: + - string + - "null" + date_created: + type: + - number + - "null" + date_end: + type: + - number + - "null" + date_start: + type: + - number + - "null" + date_status_change: + type: + - number + - "null" + date_updated: + type: + - number + - "null" + disable_auto_text: + type: + - boolean + - "null" + display_name: + type: + - string + - "null" + email: + type: + - string + - "null" + estimated_time: + type: + - number + - "null" + fax_number: + type: + - string + - "null" + first_name: + type: + - string + - "null" + geo: + type: + - object + - "null" + properties: + lat: + type: + - number + - "null" + lon: + type: + - number + - "null" + home_phone: + type: + - string + - "null" + is_active: + type: + - boolean + - "null" + is_archived: + type: + - boolean + - "null" + is_sub_contractor: + type: + - boolean + - "null" + is_user: + type: + - boolean + - "null" + jnid: + type: string + last_budget_gross_margin: + type: + - number + - "null" + last_budget_gross_profit: + type: + - number + - "null" + last_budget_revenue: + type: + - number + - "null" + last_estimate: + type: + - number + - "null" + last_estimate_date_created: + type: + - number + - "null" + last_estimate_date_estimate: + type: + - number + - "null" + last_invoice: + type: + - number + - "null" + last_invoice_date_created: + type: + - number + - "null" + last_invoice_date_invoice: + type: + - number + - "null" + last_name: + type: + - string + - "null" + location: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + mobile_phone: + type: + - string + - "null" + number: + type: + - string + - "null" + owners: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + recid: + type: + - number + - "null" + record_type: + type: + - number + - "null" + record_type_name: + type: + - string + - "null" + sales_rep: + type: + - string + - "null" + sales_rep_name: + type: + - string + - "null" + state_text: + type: + - string + - "null" + status: + type: + - number + - "null" + status_name: + type: + - string + - "null" + tags: + type: + - array + - "null" + task_count: + type: + - number + - "null" + website: + type: + - string + - "null" + work_phone: + type: + - string + - "null" + zip: + type: + - string + - "null" + required: + - jnid + tasks: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + type: + type: + - string + - "null" + description: + type: + - string + - "null" + actual_time: + type: + - number + - "null" + all_day: + type: + - boolean + - "null" + all_day_end_date: + type: + - string + - "null" + all_day_start_date: + type: + - string + - "null" + created_by: + type: + - string + - "null" + created_by_name: + type: + - string + - "null" + customer: + type: + - string + - "null" + date_created: + type: + - number + - "null" + date_end: + type: + - number + - "null" + date_start: + type: + - number + - "null" + date_updated: + type: + - number + - "null" + estimated_time: + type: + - number + - "null" + hide_from_calendarview: + type: + - boolean + - "null" + hide_from_tasklist: + type: + - boolean + - "null" + is_active: + type: + - boolean + - "null" + is_archived: + type: + - boolean + - "null" + is_completed: + type: + - boolean + - "null" + jnid: + type: string + location: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + number: + type: + - string + - "null" + owners: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + priority: + type: + - number + - "null" + recid: + type: + - number + - "null" + record_type: + type: + - number + - "null" + record_type_name: + type: + - string + - "null" + related: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + id: + type: + - string + - "null" + name: + type: + - string + - "null" + number: + type: + - string + - "null" + tags: + type: + - array + - "null" + title: + type: + - string + - "null" + required: + - jnid + activities: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + type: + type: + - string + - "null" + created_by: + type: + - string + - "null" + created_by_name: + type: + - string + - "null" + customer: + type: + - string + - "null" + date_created: + type: + - number + - "null" + date_updated: + type: + - number + - "null" + is_active: + type: + - boolean + - "null" + is_archived: + type: + - boolean + - "null" + is_editable: + type: + - boolean + - "null" + is_status_change: + type: + - boolean + - "null" + jnid: + type: string + location: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + note: + type: + - string + - "null" + owners: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + primary: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + id: + type: + - string + - "null" + name: + type: + - string + - "null" + new_status: + type: + - number + - "null" + number: + type: + - string + - "null" + record_type: + type: + - number + - "null" + record_type_name: + type: + - string + - "null" + related: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + id: + type: + - string + - "null" + name: + type: + - string + - "null" + new_status: + type: + - number + - "null" + number: + type: + - string + - "null" + sales_rep: + type: + - string + - "null" + sales_rep_name: + type: + - string + - "null" + source: + type: + - string + - "null" + required: + - jnid + files: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + type: + type: + - string + - "null" + description: + type: + - string + - "null" + content_type: + type: + - string + - "null" + created_by: + type: + - string + - "null" + created_by_name: + type: + - string + - "null" + customer: + type: + - string + - "null" + date_created: + type: + - number + - "null" + date_file_created: + type: + - number + - "null" + date_updated: + type: + - number + - "null" + exif: + type: + - array + - "null" + filename: + type: + - string + - "null" + is_active: + type: + - boolean + - "null" + jnid: + type: string + md5: + type: + - string + - "null" + owners: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + primary: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + id: + type: + - string + - "null" + name: + type: + - string + - "null" + record_type: + type: + - number + - "null" + record_type_name: + type: + - string + - "null" + related: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + id: + type: + - string + - "null" + name: + type: + - string + - "null" + size: + type: + - number + - "null" + required: + - jnid diff --git a/airbyte-integrations/connectors/source-jobnimbus/metadata.yaml b/airbyte-integrations/connectors/source-jobnimbus/metadata.yaml new file mode 100644 index 000000000000..7d8a6e60dea5 --- /dev/null +++ b/airbyte-integrations/connectors/source-jobnimbus/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "app.jobnimbus.com" + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-jobnimbus + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.17.0@sha256:9c6bfd080a247b7781ce5b25687e7c44e29d31315d0bf656584b38810521bbaa + connectorSubtype: api + connectorType: source + definitionId: 0b98589b-ccee-40fd-9e9d-28d94087b4e1 + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-jobnimbus + githubIssueLabel: source-jobnimbus + icon: icon.svg + license: MIT + name: JobNimbus + releaseDate: 2024-10-29 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/jobnimbus + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/jobnimbus.md b/docs/integrations/sources/jobnimbus.md new file mode 100644 index 000000000000..a777f5696fe2 --- /dev/null +++ b/docs/integrations/sources/jobnimbus.md @@ -0,0 +1,28 @@ +# JobNimbus +The JobNimbus Airbyte connector enables seamless integration between JobNimbus, a popular CRM and project management tool, and your data pipeline. This connector allows you to automatically sync data from JobNimbus, including contacts records, task information, and more, into your preferred destination. It simplifies data extraction and helps you streamline workflows, enabling efficient analysis and reporting for enhanced business insights. + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `api_key` | `string` | API Key. API key to use. Find it by logging into your JobNimbus account, navigating to settings, and creating a new API key under the API section. | | + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| jobs | jnid | DefaultPaginator | ✅ | ❌ | +| contacts | jnid | DefaultPaginator | ✅ | ❌ | +| tasks | jnid | DefaultPaginator | ✅ | ❌ | +| activities | jnid | DefaultPaginator | ✅ | ❌ | +| files | jnid | DefaultPaginator | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-10-29 | | Initial release by [@parthiv11](https://github.com/parthiv11) via Connector Builder | + +
From 4fbbf58b4d209ebe133757f9d65bcc520ddd8352 Mon Sep 17 00:00:00 2001 From: Benoit Moriceau Date: Wed, 6 Nov 2024 15:18:58 -0800 Subject: [PATCH 13/45] feat: add application yaml (#48385) --- airbyte-cdk/bulk/core/base/build.gradle | 4 ++ .../base/src/main/resources/application.yaml | 4 ++ .../cdk/initialization/TestApplicationYaml.kt | 40 +++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 airbyte-cdk/bulk/core/base/src/main/resources/application.yaml create mode 100644 airbyte-cdk/bulk/core/base/src/test/kotlin/io/airbyte/cdk/initialization/TestApplicationYaml.kt diff --git a/airbyte-cdk/bulk/core/base/build.gradle b/airbyte-cdk/bulk/core/base/build.gradle index 5ecf5624885e..a6696aaacc45 100644 --- a/airbyte-cdk/bulk/core/base/build.gradle +++ b/airbyte-cdk/bulk/core/base/build.gradle @@ -50,3 +50,7 @@ dependencies { testFixturesApi 'io.micronaut.test:micronaut-test-junit5:4.5.0' testFixturesApi 'io.github.deblockt:json-diff:1.1.0' } + +test { + environment "PRESENT", "present-value" +} diff --git a/airbyte-cdk/bulk/core/base/src/main/resources/application.yaml b/airbyte-cdk/bulk/core/base/src/main/resources/application.yaml new file mode 100644 index 000000000000..4f45ac9d8a2e --- /dev/null +++ b/airbyte-cdk/bulk/core/base/src/main/resources/application.yaml @@ -0,0 +1,4 @@ +airbyte: + file-transfer: + enabled: ${USE_FILE_TRANSFER:false} + staging-path: ${AIRBYTE_STAGING_DIRECTORY:/staging/files} diff --git a/airbyte-cdk/bulk/core/base/src/test/kotlin/io/airbyte/cdk/initialization/TestApplicationYaml.kt b/airbyte-cdk/bulk/core/base/src/test/kotlin/io/airbyte/cdk/initialization/TestApplicationYaml.kt new file mode 100644 index 000000000000..40b926300377 --- /dev/null +++ b/airbyte-cdk/bulk/core/base/src/test/kotlin/io/airbyte/cdk/initialization/TestApplicationYaml.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Airbyte, Inc., all rights reserved. + */ + +package io.airbyte.cdk.initialization + +import io.micronaut.context.annotation.Bean +import io.micronaut.context.annotation.Factory +import io.micronaut.context.annotation.Value +import io.micronaut.test.extensions.junit5.annotation.MicronautTest +import jakarta.inject.Inject +import kotlin.test.assertEquals +import org.junit.jupiter.api.Test + +@MicronautTest +class TestApplicationYaml { + @Inject lateinit var defaultValueBean: DefaultValueBean + + @Test + fun testMainDefaultValue() { + assertEquals("/staging/files", defaultValueBean.stagingFolder) + assertEquals(false, defaultValueBean.fileTransferEnable) + } +} + +data class DefaultValueBean( + val stagingFolder: String, + val fileTransferEnable: Boolean, +) + +@Factory +class TestFactory { + @Bean + fun defaultValueBean( + @Value("\${airbyte.file-transfer.staging-path}") stagingFolder: String, + @Value("\${airbyte.file-transfer.enabled}") fileTransferEnable: Boolean, + ): DefaultValueBean { + return DefaultValueBean(stagingFolder, fileTransferEnable) + } +} From 43055d5cf7d2906148403490c8fd6e93e3eb95ce Mon Sep 17 00:00:00 2001 From: Aazam Thakur <59562284+aazam-gh@users.noreply.github.com> Date: Thu, 7 Nov 2024 05:14:40 +0530 Subject: [PATCH 14/45] source-taboola contribution from aazam-gh (#47640) Co-authored-by: Marcos Marx --- .../connectors/source-taboola/README.md | 39 + .../source-taboola/acceptance-test-config.yml | 17 + .../connectors/source-taboola/icon.svg | 20 + .../connectors/source-taboola/manifest.yaml | 1260 +++++++++++++++++ .../connectors/source-taboola/metadata.yaml | 35 + docs/integrations/sources/taboola.md | 38 + 6 files changed, 1409 insertions(+) create mode 100644 airbyte-integrations/connectors/source-taboola/README.md create mode 100644 airbyte-integrations/connectors/source-taboola/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-taboola/icon.svg create mode 100644 airbyte-integrations/connectors/source-taboola/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-taboola/metadata.yaml create mode 100644 docs/integrations/sources/taboola.md diff --git a/airbyte-integrations/connectors/source-taboola/README.md b/airbyte-integrations/connectors/source-taboola/README.md new file mode 100644 index 000000000000..e4d4de6d6a28 --- /dev/null +++ b/airbyte-integrations/connectors/source-taboola/README.md @@ -0,0 +1,39 @@ +# Taboola +This directory contains the manifest-only connector for `source-taboola`. + +This is the Taboola source that ingests data from the Taboola API. + +Taboola helps you reach customers that convert. Drive business results by reaching people genuinely, effectively at just the right moment https://www.taboola.com/ + +In order to use this source, you must first create an account. Once logged in you can contact Taboola support to provide you with a Client ID, Client Secret and Account ID. Once these credentials have been obtained, you can input them into the appropriate fields. + +You can learn more about the API here https://developers.taboola.com/backstage-api/reference + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-taboola:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-taboola build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-taboola test +``` + diff --git a/airbyte-integrations/connectors/source-taboola/acceptance-test-config.yml b/airbyte-integrations/connectors/source-taboola/acceptance-test-config.yml new file mode 100644 index 000000000000..982a88d91231 --- /dev/null +++ b/airbyte-integrations/connectors/source-taboola/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-taboola:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-taboola/icon.svg b/airbyte-integrations/connectors/source-taboola/icon.svg new file mode 100644 index 000000000000..ea523afea87b --- /dev/null +++ b/airbyte-integrations/connectors/source-taboola/icon.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + diff --git a/airbyte-integrations/connectors/source-taboola/manifest.yaml b/airbyte-integrations/connectors/source-taboola/manifest.yaml new file mode 100644 index 000000000000..7f689ccd34d9 --- /dev/null +++ b/airbyte-integrations/connectors/source-taboola/manifest.yaml @@ -0,0 +1,1260 @@ +version: 5.15.0 + +type: DeclarativeSource + +description: >- + This is the Taboola source that ingests data from the Taboola API. + + + Taboola helps you reach customers that convert. Drive business results by + reaching people genuinely, effectively at just the right moment + https://www.taboola.com/ + + + In order to use this source, you must first create an account. Once logged in + you can contact Taboola support to provide you with a Client ID, Client Secret + and Account ID. Once these credentials have been obtained, you can input them + into the appropriate fields. + + + You can learn more about the API here + https://developers.taboola.com/backstage-api/reference + +check: + type: CheckStream + stream_names: + - account + +definitions: + streams: + account: + type: DeclarativeStream + name: account + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: backstage/api/1.0/users/current/allowed-accounts + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + primary_key: + - id + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/account" + audiences: + type: DeclarativeStream + name: audiences + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: >- + backstage/api/1.0/{{ + config["account_id"]}}/combined_audiences/resources/audiences + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + primary_key: + - id + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/audiences" + campaigns: + type: DeclarativeStream + name: campaigns + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /backstage/api/1.0/{{ config["account_id"] }}/campaigns/ + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + primary_key: + - id + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/campaigns" + motion_ads: + type: DeclarativeStream + name: motion_ads + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: >- + /backstage/api/1.0/{{ config["account_id"] }}/campaigns/{{ + stream_partition.parent_id }}/performance-video/items/ + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + stream: + $ref: "#/definitions/streams/campaigns" + parent_key: id + partition_field: parent_id + primary_key: + - id + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/motion_ads" + audience_rules: + type: DeclarativeStream + name: audience_rules + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: >- + /backstage/api/1.0/{{ config["account_id"] + }}/universal_pixel/custom_audience_rule + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + primary_key: + - id + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/audience_rules" + campaign_items: + type: DeclarativeStream + name: campaign_items + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: >- + /backstage/api/1.0/{{ config["account_id"] }}/campaigns/{{ + stream_partition.parent_id }}/items/ + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + stream: + $ref: "#/definitions/streams/campaigns" + parent_key: id + partition_field: parent_id + primary_key: + - id + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/campaign_items" + conversion_rules: + type: DeclarativeStream + name: conversion_rules + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: >- + /backstage/api/1.0/{{ config["account_id"] + }}/universal_pixel/conversion_rule + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + primary_key: + - id + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/conversion_rules" + base_requester: + type: HttpRequester + url_base: https://backstage.taboola.com + authenticator: + type: OAuthAuthenticator + client_id: "{{ config[\"client_id\"] }}" + grant_type: client_credentials + client_secret: "{{ config[\"client_secret\"] }}" + access_token_name: access_token + refresh_request_body: {} + token_refresh_endpoint: https://backstage.taboola.com/backstage/oauth/token + +streams: + - $ref: "#/definitions/streams/account" + - $ref: "#/definitions/streams/campaigns" + - $ref: "#/definitions/streams/campaign_items" + - $ref: "#/definitions/streams/audience_rules" + - $ref: "#/definitions/streams/conversion_rules" + - $ref: "#/definitions/streams/motion_ads" + - $ref: "#/definitions/streams/audiences" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - client_id + - client_secret + - account_id + properties: + client_id: + type: string + order: 0 + title: Client ID + airbyte_secret: true + account_id: + type: string + description: The ID associated with your taboola account + order: 2 + title: Account ID + client_secret: + type: string + order: 1 + title: Client secret + airbyte_secret: true + additionalProperties: true + +metadata: + assist: {} + testedStreams: + account: + hasRecords: true + streamHash: c0e4963d6cadfe3fa143ae49734e5d9cc889684b + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + audiences: + hasRecords: true + streamHash: 082a5eaa73d4b0c8760b6ddb088d52d34a9c77db + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + campaigns: + hasRecords: true + streamHash: d966feac19f7d299089c2544174b780b40dbda8a + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + motion_ads: + hasRecords: true + streamHash: c2a2b9778f2a76a1e82d96247b87a05aaff86ca1 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + audience_rules: + hasRecords: true + streamHash: c5d52d8fd716911fef81d1e65dd291e4b6332464 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + campaign_items: + hasRecords: true + streamHash: 8baabb3aa0e3b9f19901b527bb844fff967d9b40 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + conversion_rules: + hasRecords: true + streamHash: b00c3d15be83f431de7e5ec6696e6593e1afb9da + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + autoImportSchema: + account: true + audiences: true + campaigns: true + motion_ads: true + audience_rules: true + campaign_items: true + conversion_rules: true + +schemas: + account: + type: object + $schema: http://json-schema.org/schema# + required: + - id + properties: + type: + type: + - string + - "null" + id: + type: number + name: + type: + - string + - "null" + is_fla: + type: + - boolean + - "null" + country: + type: + - string + - "null" + currency: + type: + - string + - "null" + language: + type: + - string + - "null" + is_active: + type: + - boolean + - "null" + account_id: + type: + - string + - "null" + partner_types: + type: + - array + - "null" + items: + type: + - string + - "null" + campaign_types: + type: + - array + - "null" + items: + type: + - string + - "null" + time_zone_name: + type: + - string + - "null" + default_platform: + type: + - string + - "null" + additionalProperties: true + audiences: + type: object + $schema: http://json-schema.org/schema# + required: + - id + properties: + description: + type: + - string + - "null" + id: + type: number + size: + type: + - number + - "null" + provider: + type: + - string + - "null" + data_type: + type: + - string + - "null" + is_archived: + type: + - boolean + - "null" + audience_name: + type: + - string + - "null" + exclude_from_campaigns: + type: + - boolean + - "null" + additionalProperties: true + campaigns: + type: object + $schema: http://json-schema.org/schema# + required: + - id + properties: + type: + type: + - string + - "null" + id: + type: string + name: + type: + - string + - "null" + spent: + type: + - number + - "null" + status: + type: + - string + - "null" + bid_type: + type: + - string + - "null" + comments: + type: + - string + - "null" + cpa_goal: + type: + - number + - "null" + end_date: + type: + - string + - "null" + daily_cap: + type: + - number + - "null" + is_active: + type: + - boolean + - "null" + start_date: + type: + - string + - "null" + bid_strategy: + type: + - string + - "null" + os_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + advertiser_id: + type: + - string + - "null" + branding_text: + type: + - string + - "null" + policy_review: + type: + - object + - "null" + properties: {} + pricing_model: + type: + - string + - "null" + safety_rating: + type: + - string + - "null" + tracking_code: + type: + - string + - "null" + approval_state: + type: + - string + - "null" + city_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + spending_limit: + type: + - number + - "null" + end_date_in_utc: + type: + - string + - "null" + conversion_rules: + type: + - object + - "null" + properties: + rules: + type: + - array + - "null" + activity_schedule: + type: + - object + - "null" + properties: + mode: + type: + - string + - "null" + rules: + type: + - array + - "null" + time_zone: + type: + - string + - "null" + browser_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + country_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + start_date_in_utc: + type: + - string + - "null" + campaign_item_type: + type: + - string + - "null" + platform_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + items: + type: + - string + - "null" + segments_targeting: + type: + - object + - "null" + properties: + AGE: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + href: + type: + - string + - "null" + value: + type: + - array + - "null" + GENDER: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + href: + type: + - string + - "null" + value: + type: + - array + - "null" + audiences_targeting: + type: + - object + - "null" + properties: + href: + type: + - string + - "null" + state: + type: + - string + - "null" + marketing_objective: + type: + - string + - "null" + publisher_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + contextual_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + performance_rule_ids: + type: + - array + - "null" + spending_limit_model: + type: + - string + - "null" + day_time_bid_modifier: + type: + - object + - "null" + properties: + values: + type: + - array + - "null" + dma_country_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + external_brand_safety: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + values: + type: + - array + - "null" + is_spend_guard_active: + type: + - string + - "null" + platform_bid_modifier: + type: + - object + - "null" + properties: + values: + type: + - array + - "null" + postal_code_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + href: + type: + - string + - "null" + sub_country_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + publisher_bid_modifier: + type: + - object + - "null" + properties: + values: + type: + - array + - "null" + daily_ad_delivery_model: + type: + - string + - "null" + traffic_allocation_mode: + type: + - string + - "null" + auto_publisher_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + region_country_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + connection_type_targeting: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + value: + type: + - array + - "null" + custom_audience_targeting: + type: + - object + - "null" + properties: + href: + type: + - string + - "null" + state: + type: + - string + - "null" + custom_contextual_targeting: + type: + - object + - "null" + properties: + href: + type: + - string + - "null" + state: + type: + - string + - "null" + lookalike_audience_targeting: + type: + - object + - "null" + properties: + href: + type: + - string + - "null" + state: + type: + - string + - "null" + predefined_targeting_options: + type: + - object + - "null" + properties: + predefined_supply_targeting: + type: + - string + - "null" + contextual_segments_targeting: + type: + - object + - "null" + properties: + href: + type: + - string + - "null" + state: + type: + - string + - "null" + marking_label_multi_targeting: + type: + - object + - "null" + properties: + href: + type: + - string + - "null" + state: + type: + - string + - "null" + publisher_platform_bid_modifier: + type: + - object + - "null" + properties: + values: + type: + - array + - "null" + publisher_bid_strategy_modifiers: + type: + - object + - "null" + properties: + values: + type: + - array + - "null" + audience_segments_multi_targeting: + type: + - object + - "null" + properties: + href: + type: + - string + - "null" + state: + type: + - string + - "null" + additionalProperties: true + motion_ads: + type: object + $schema: http://json-schema.org/schema# + required: + - id + properties: + description: + type: + - string + - "null" + id: + type: string + url: + type: + - string + - "null" + title: + type: + - string + - "null" + status: + type: + - string + - "null" + gif_url: + type: + - string + - "null" + is_active: + type: + - boolean + - "null" + video_url: + type: + - string + - "null" + campaign_id: + type: + - string + - "null" + custom_data: + type: + - object + - "null" + properties: + custom_id: + type: + - string + - "null" + creative_name: + type: + - string + - "null" + fallback_url: + type: + - string + - "null" + policy_review: + type: + - object + - "null" + properties: {} + approval_state: + type: + - string + - "null" + motion_ads_studio: + type: + - object + - "null" + properties: + vendor_video_id: + type: + - string + - "null" + vendor_template_type: + type: + - string + - "null" + media_upload_source: + type: + - string + - "null" + recommended_fbimage: + type: + - string + - "null" + additionalProperties: true + audience_rules: + type: object + $schema: http://json-schema.org/schema# + required: + - id + properties: + type: + type: + - string + - "null" + id: + type: number + status: + type: + - string + - "null" + category: + type: + - string + - "null" + condition: + type: + - object + - "null" + properties: + value: + type: + - string + - "null" + children: + type: + - array + - "null" + property: + type: + - string + - "null" + predicate: + type: + - string + - "null" + event_name: + type: + - string + - "null" + display_name: + type: + - string + - "null" + advertiser_id: + type: + - string + - "null" + audience_size: + type: + - number + - "null" + last_modified_at: + type: + - string + - "null" + last_modified_by: + type: + - string + - "null" + look_back_window: + type: + - number + - "null" + exclude_from_campaigns: + type: + - boolean + - "null" + additionalProperties: true + campaign_items: + type: object + $schema: http://json-schema.org/schema# + required: + - id + properties: + type: + type: + - string + - "null" + description: + type: + - string + - "null" + id: + type: string + url: + type: + - string + - "null" + title: + type: + - string + - "null" + status: + type: + - string + - "null" + is_active: + type: + - boolean + - "null" + campaign_id: + type: + - string + - "null" + custom_data: + type: + - object + - "null" + properties: + custom_id: + type: + - string + - "null" + creative_name: + type: + - string + - "null" + policy_review: + type: + - object + - "null" + properties: {} + thumbnail_url: + type: + - string + - "null" + approval_state: + type: + - string + - "null" + creative_focus: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + additionalProperties: true + conversion_rules: + type: object + $schema: http://json-schema.org/schema# + required: + - id + properties: + type: + type: + - string + - "null" + id: + type: number + status: + type: + - string + - "null" + effects: + type: + - array + - "null" + category: + type: + - string + - "null" + condition: + type: + - object + - "null" + properties: + value: + type: + - string + - "null" + children: + type: + - array + - "null" + property: + type: + - string + - "null" + predicate: + type: + - string + - "null" + event_name: + type: + - string + - "null" + display_name: + type: + - string + - "null" + advertiser_id: + type: + - string + - "null" + aggregation_type: + type: + - string + - "null" + last_modified_at: + type: + - string + - "null" + last_modified_by: + type: + - string + - "null" + look_back_window: + type: + - number + - "null" + exclude_from_campaigns: + type: + - boolean + - "null" + include_in_total_value: + type: + - boolean + - "null" + include_in_total_conversions: + type: + - boolean + - "null" + view_through_look_back_window: + type: + - number + - "null" + additionalProperties: true diff --git a/airbyte-integrations/connectors/source-taboola/metadata.yaml b/airbyte-integrations/connectors/source-taboola/metadata.yaml new file mode 100644 index 000000000000..087139f21a1a --- /dev/null +++ b/airbyte-integrations/connectors/source-taboola/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "backstage.taboola.com" + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-taboola + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.17.0@sha256:9c6bfd080a247b7781ce5b25687e7c44e29d31315d0bf656584b38810521bbaa + connectorSubtype: api + connectorType: source + definitionId: 40bb20de-e03c-4aa2-80bc-72234598380f + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-taboola + githubIssueLabel: source-taboola + icon: icon.svg + license: MIT + name: Taboola + releaseDate: 2024-10-28 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/taboola + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/taboola.md b/docs/integrations/sources/taboola.md new file mode 100644 index 000000000000..422460c4319d --- /dev/null +++ b/docs/integrations/sources/taboola.md @@ -0,0 +1,38 @@ +# Taboola +This is the Taboola source that ingests data from the Taboola API. + +Taboola helps you reach customers that convert. Drive business results by reaching people genuinely, effectively at just the right moment https://www.taboola.com/ + +In order to use this source, you must first create an account. Once logged in you can contact Taboola support to provide you with a Client ID, Client Secret and Account ID. Once these credentials have been obtained, you can input them into the appropriate fields. + +You can learn more about the API here https://developers.taboola.com/backstage-api/reference + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `client_id` | `string` | Client ID. | | +| `account_id` | `string` | Account ID. The ID associated with your taboola account | | +| `client_secret` | `string` | Client secret. | | + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| account | id | No pagination | ✅ | ❌ | +| campaigns | id | No pagination | ✅ | ❌ | +| campaign_items | id | No pagination | ✅ | ❌ | +| audience_rules | id | No pagination | ✅ | ❌ | +| conversion_rules | id | No pagination | ✅ | ❌ | +| motion_ads | id | No pagination | ✅ | ❌ | +| audiences | id | No pagination | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-10-28 | | Initial release by [@aazam-gh](https://github.com/aazam-gh) via Connector Builder | + +
From c25f3fc140ef4fdfb180ddd4800ba9407814efe7 Mon Sep 17 00:00:00 2001 From: Aazam Thakur <59562284+aazam-gh@users.noreply.github.com> Date: Thu, 7 Nov 2024 05:20:42 +0530 Subject: [PATCH 15/45] source-shippo contribution from aazam-gh (#47485) Co-authored-by: Marcos Marx --- .../connectors/source-shippo/README.md | 37 + .../source-shippo/acceptance-test-config.yml | 17 + .../connectors/source-shippo/icon.svg | 1 + .../connectors/source-shippo/manifest.yaml | 1748 +++++++++++++++++ .../connectors/source-shippo/metadata.yaml | 35 + docs/integrations/sources/shippo.md | 34 + 6 files changed, 1872 insertions(+) create mode 100644 airbyte-integrations/connectors/source-shippo/README.md create mode 100644 airbyte-integrations/connectors/source-shippo/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-shippo/icon.svg create mode 100644 airbyte-integrations/connectors/source-shippo/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-shippo/metadata.yaml create mode 100644 docs/integrations/sources/shippo.md diff --git a/airbyte-integrations/connectors/source-shippo/README.md b/airbyte-integrations/connectors/source-shippo/README.md new file mode 100644 index 000000000000..bd52fb172d2d --- /dev/null +++ b/airbyte-integrations/connectors/source-shippo/README.md @@ -0,0 +1,37 @@ +# Shippo +This directory contains the manifest-only connector for `source-shippo`. + +This is the Shippo source for ingesting data using the Shippo API. + +Shippo is your one-stop solution for shipping labels. Whether you use our app to ship or API to power your logistics workflow, Shippo gives you scalable shipping tools, the best rates, and world-class support https://goshippo.com/ + +In order to use this source, you must first create a Shippo account. Once logged in, head over to Settings -> Advanced -> API and click on generate new token. You can learn more about the API here https://docs.goshippo.com/shippoapi/public-api/#tag/Overview + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-shippo:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-shippo build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-shippo test +``` + diff --git a/airbyte-integrations/connectors/source-shippo/acceptance-test-config.yml b/airbyte-integrations/connectors/source-shippo/acceptance-test-config.yml new file mode 100644 index 000000000000..0cfe1d0e6190 --- /dev/null +++ b/airbyte-integrations/connectors/source-shippo/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-shippo:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-shippo/icon.svg b/airbyte-integrations/connectors/source-shippo/icon.svg new file mode 100644 index 000000000000..d011ec476ea5 --- /dev/null +++ b/airbyte-integrations/connectors/source-shippo/icon.svg @@ -0,0 +1 @@ + diff --git a/airbyte-integrations/connectors/source-shippo/manifest.yaml b/airbyte-integrations/connectors/source-shippo/manifest.yaml new file mode 100644 index 000000000000..8d6fca2ec52b --- /dev/null +++ b/airbyte-integrations/connectors/source-shippo/manifest.yaml @@ -0,0 +1,1748 @@ +version: 5.15.0 + +type: DeclarativeSource + +description: >- + This is the Shippo source for ingesting data using the Shippo API. + + + Shippo is your one-stop solution for shipping labels. Whether you use our app + to ship or API to power your logistics workflow, Shippo gives you scalable + shipping tools, the best rates, and world-class support https://goshippo.com/ + + + In order to use this source, you must first create a Shippo account. Once + logged in, head over to Settings -> Advanced -> API and click on generate new + token. You can learn more about the API here + https://docs.goshippo.com/shippoapi/public-api/#tag/Overview + +check: + type: CheckStream + stream_names: + - addresses + +definitions: + streams: + addresses: + type: DeclarativeStream + name: addresses + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /v2/addresses + http_method: GET + request_headers: + Authorization: ShippoToken {{ config["shippo_token"] }} + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: limit + pagination_strategy: + type: OffsetIncrement + page_size: 100 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/addresses" + parcels: + type: DeclarativeStream + name: parcels + primary_key: + - object_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /parcels + http_method: GET + request_headers: + Authorization: ShippoToken {{ config["shippo_token"] }} + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: results + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/parcels" + custom_items: + type: DeclarativeStream + name: custom_items + primary_key: + - object_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /customs/items + http_method: GET + request_headers: + Authorization: ShippoToken {{ config["shippo_token"] }} + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: results + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/custom_items" + accounts: + type: DeclarativeStream + name: accounts + primary_key: + - object_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /shippo-accounts + http_method: GET + request_headers: + Authorization: ShippoToken {{ config["shippo_token"] }} + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: results + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/accounts" + carrier_acounts: + type: DeclarativeStream + name: carrier_acounts + primary_key: + - object_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /carrier_accounts + http_method: GET + request_headers: + Authorization: ShippoToken {{ config["shippo_token"] }} + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: results + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/carrier_acounts" + shipments: + type: DeclarativeStream + name: shipments + primary_key: + - object_id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /shipments + http_method: GET + request_headers: + Authorization: ShippoToken {{ config["shippo_token"] }} + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - results + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: results + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + inject_on_first_request: true + incremental_sync: + type: DatetimeBasedCursor + cursor_field: object_updated + cursor_datetime_formats: + - "%Y-%m-%dT%H:%M:%S.%fZ" + datetime_format: "%Y-%m-%dT%H:%M:%S.%fZ" + start_datetime: + type: MinMaxDatetime + datetime: "{{ config[\"start_date\"] }}" + datetime_format: "%Y-%m-%dT%H:%M:%SZ" + start_time_option: + type: RequestOption + inject_into: request_parameter + field_name: object_created_gt + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/shipments" + base_requester: + type: HttpRequester + url_base: https://api.goshippo.com + +streams: + - $ref: "#/definitions/streams/addresses" + - $ref: "#/definitions/streams/parcels" + - $ref: "#/definitions/streams/custom_items" + - $ref: "#/definitions/streams/accounts" + - $ref: "#/definitions/streams/carrier_acounts" + - $ref: "#/definitions/streams/shipments" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - shippo_token + - start_date + properties: + shippo_token: + type: string + description: The bearer token used for making requests + title: Shippo Token + order: 0 + start_date: + type: string + title: Start date + format: date-time + pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ + order: 1 + additionalProperties: true + +metadata: + autoImportSchema: + addresses: true + parcels: true + custom_items: true + accounts: true + carrier_acounts: true + shipments: true + testedStreams: + addresses: + streamHash: c2a5943c1cc3c14e6f43ea454bdff5b2627cfc4b + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + parcels: + streamHash: 9d088b7ef340fbe88c91a131f51eb872a5348cd1 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + custom_items: + streamHash: 21c2e781324ebf7429927cedb992834eb107142a + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + accounts: + streamHash: ff3113723dffb561047ec7348ba6e637c7068376 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + carrier_acounts: + streamHash: 17b1fc52b7f21c14dc6587530013aea90327a886 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + shipments: + streamHash: 4e50c91f3910aeb19cb1bb3270663c88d7efb3bd + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + assist: {} + +schemas: + addresses: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + address: + type: + - object + - "null" + properties: + address_line_1: + type: + - string + - "null" + address_line_2: + type: + - string + - "null" + address_type: + type: + - string + - "null" + city_locality: + type: + - string + - "null" + country_code: + type: + - string + - "null" + email: + type: + - string + - "null" + name: + type: + - string + - "null" + organization: + type: + - string + - "null" + phone: + type: + - string + - "null" + postal_code: + type: + - string + - "null" + state_province: + type: + - string + - "null" + created_at: + type: + - string + - "null" + id: + type: string + updated_at: + type: + - string + - "null" + required: + - id + parcels: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + metadata: + type: + - string + - "null" + distance_unit: + type: + - string + - "null" + extra: + type: + - object + - "null" + properties: + COD: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + currency: + type: + - string + - "null" + payment_method: + type: + - string + - "null" + insurance: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + content: + type: + - string + - "null" + currency: + type: + - string + - "null" + provider: + type: + - string + - "null" + reference_1: + type: + - string + - "null" + reference_2: + type: + - string + - "null" + height: + type: + - string + - "null" + length: + type: + - string + - "null" + line_items: + type: + - array + - "null" + mass_unit: + type: + - string + - "null" + object_created: + type: + - string + - "null" + object_id: + type: string + object_owner: + type: + - string + - "null" + object_state: + type: + - string + - "null" + object_updated: + type: + - string + - "null" + test: + type: + - boolean + - "null" + weight: + type: + - string + - "null" + width: + type: + - string + - "null" + required: + - object_id + custom_items: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + description: + type: + - string + - "null" + metadata: + type: + - string + - "null" + eccn_ear99: + type: + - string + - "null" + hs_code: + type: + - string + - "null" + mass_unit: + type: + - string + - "null" + net_weight: + type: + - string + - "null" + object_created: + type: + - string + - "null" + object_id: + type: string + object_owner: + type: + - string + - "null" + object_state: + type: + - string + - "null" + object_updated: + type: + - string + - "null" + origin_country: + type: + - string + - "null" + quantity: + type: + - number + - "null" + sku_code: + type: + - string + - "null" + tariff_number: + type: + - string + - "null" + test: + type: + - boolean + - "null" + value_amount: + type: + - string + - "null" + value_currency: + type: + - string + - "null" + required: + - object_id + accounts: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + email: + type: + - string + - "null" + first_name: + type: + - string + - "null" + last_name: + type: + - string + - "null" + object_created: + type: + - string + - "null" + object_id: + type: string + object_updated: + type: + - string + - "null" + required: + - object_id + carrier_acounts: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + metadata: + type: + - string + - "null" + account_id: + type: + - string + - "null" + active: + type: + - boolean + - "null" + carrier: + type: + - string + - "null" + carrier_images: + type: + - object + - "null" + properties: + "75": + type: + - string + - "null" + "200": + type: + - string + - "null" + carrier_name: + type: + - string + - "null" + is_shippo_account: + type: + - boolean + - "null" + object_id: + type: string + object_info: + type: + - object + - "null" + properties: + authentication: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + object_owner: + type: + - string + - "null" + parameters: + type: + - object + - "null" + properties: + expresslink_password: + type: + - string + - "null" + is_commercial: + type: + - string + - "null" + test: + type: + - boolean + - "null" + required: + - object_id + shipments: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + metadata: + type: + - string + - "null" + address_from: + type: + - object + - "null" + properties: + city: + type: + - string + - "null" + company: + type: + - string + - "null" + country: + type: + - string + - "null" + email: + type: + - string + - "null" + is_complete: + type: + - boolean + - "null" + is_residential: + type: + - boolean + - "null" + name: + type: + - string + - "null" + object_id: + type: + - string + - "null" + phone: + type: + - string + - "null" + state: + type: + - string + - "null" + street1: + type: + - string + - "null" + street2: + type: + - string + - "null" + street3: + type: + - string + - "null" + street_no: + type: + - string + - "null" + test: + type: + - boolean + - "null" + validation_results: + type: + - object + - "null" + properties: + is_valid: + type: + - boolean + - "null" + messages: + type: + - array + - "null" + zip: + type: + - string + - "null" + address_return: + type: + - object + - "null" + properties: + city: + type: + - string + - "null" + company: + type: + - string + - "null" + country: + type: + - string + - "null" + email: + type: + - string + - "null" + is_complete: + type: + - boolean + - "null" + is_residential: + type: + - boolean + - "null" + name: + type: + - string + - "null" + object_id: + type: + - string + - "null" + phone: + type: + - string + - "null" + state: + type: + - string + - "null" + street1: + type: + - string + - "null" + street2: + type: + - string + - "null" + street3: + type: + - string + - "null" + street_no: + type: + - string + - "null" + test: + type: + - boolean + - "null" + validation_results: + type: + - object + - "null" + properties: + is_valid: + type: + - boolean + - "null" + messages: + type: + - array + - "null" + zip: + type: + - string + - "null" + address_to: + type: + - object + - "null" + properties: + city: + type: + - string + - "null" + company: + type: + - string + - "null" + country: + type: + - string + - "null" + email: + type: + - string + - "null" + is_complete: + type: + - boolean + - "null" + name: + type: + - string + - "null" + object_id: + type: + - string + - "null" + phone: + type: + - string + - "null" + state: + type: + - string + - "null" + street1: + type: + - string + - "null" + street2: + type: + - string + - "null" + street3: + type: + - string + - "null" + street_no: + type: + - string + - "null" + test: + type: + - boolean + - "null" + validation_results: + type: + - object + - "null" + properties: + is_valid: + type: + - boolean + - "null" + messages: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + code: + type: + - string + - "null" + source: + type: + - string + - "null" + text: + type: + - string + - "null" + zip: + type: + - string + - "null" + carrier_accounts: + type: + - array + - "null" + items: + type: + - string + - "null" + customs_declaration: + type: + - object + - "null" + properties: + metadata: + type: + - string + - "null" + address_importer: + type: + - string + - "null" + aes_itn: + type: + - string + - "null" + b13a_filing_option: + type: + - string + - "null" + b13a_number: + type: + - string + - "null" + certificate: + type: + - string + - "null" + certify: + type: + - boolean + - "null" + certify_signer: + type: + - string + - "null" + commercial_invoice: + type: + - boolean + - "null" + contents_explanation: + type: + - string + - "null" + contents_type: + type: + - string + - "null" + disclaimer: + type: + - string + - "null" + duties_payor: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + account: + type: + - string + - "null" + address: + type: + - object + - "null" + properties: + country: + type: + - string + - "null" + name: + type: + - string + - "null" + zip: + type: + - string + - "null" + object_created: + type: + - string + - "null" + object_updated: + type: + - string + - "null" + eel_pfc: + type: + - string + - "null" + exporter_identification: + type: + - object + - "null" + properties: + eori_number: + type: + - string + - "null" + tax_id: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + number: + type: + - string + - "null" + exporter_reference: + type: + - string + - "null" + importer_reference: + type: + - string + - "null" + incoterm: + type: + - string + - "null" + invoice: + type: + - string + - "null" + is_vat_collected: + type: + - boolean + - "null" + items: + type: + - array + - "null" + items: + type: + - string + - "null" + license: + type: + - string + - "null" + non_delivery_option: + type: + - string + - "null" + notes: + type: + - string + - "null" + object_created: + type: + - string + - "null" + object_id: + type: + - string + - "null" + object_owner: + type: + - string + - "null" + object_state: + type: + - string + - "null" + object_updated: + type: + - string + - "null" + test: + type: + - boolean + - "null" + extra: + type: + - object + - "null" + properties: + COD: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + currency: + type: + - string + - "null" + payment_method: + type: + - string + - "null" + accounts_receivable_customer_account: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + billing: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + account: + type: + - string + - "null" + country: + type: + - string + - "null" + participation_code: + type: + - string + - "null" + zip: + type: + - string + - "null" + bypass_address_validation: + type: + - boolean + - "null" + carbon_neutral: + type: + - boolean + - "null" + carrier_hub_id: + type: + - string + - "null" + carrier_hub_travel_time: + type: + - number + - "null" + cod_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + delivery_instructions: + type: + - string + - "null" + dept_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + dry_ice: + type: + - object + - "null" + properties: + contains_dry_ice: + type: + - boolean + - "null" + weight: + type: + - string + - "null" + fda_product_code: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + fulfillment_center: + type: + - string + - "null" + insurance: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + content: + type: + - string + - "null" + currency: + type: + - string + - "null" + provider: + type: + - string + - "null" + invoice_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + is_return: + type: + - boolean + - "null" + lasership_attrs: + type: + - array + - "null" + items: + type: + - string + - "null" + lasership_declared_value: + type: + - string + - "null" + manifest_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + model_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + part_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + po_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + preferred_delivery_timeframe: + type: + - string + - "null" + premium: + type: + - boolean + - "null" + production_code: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + purchase_request_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + qr_code_requested: + type: + - boolean + - "null" + reference_1: + type: + - string + - "null" + reference_2: + type: + - string + - "null" + request_retail_rates: + type: + - boolean + - "null" + return_service_type: + type: + - string + - "null" + rma_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + salesperson_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + saturday_delivery: + type: + - boolean + - "null" + serial_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + signature_confirmation: + type: + - string + - "null" + store_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + transaction_reference_number: + type: + - object + - "null" + properties: + prefix: + type: + - string + - "null" + ref_sort: + type: + - number + - "null" + value: + type: + - string + - "null" + messages: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + code: + type: + - string + - "null" + source: + type: + - string + - "null" + text: + type: + - string + - "null" + object_created: + type: + - string + - "null" + object_id: + type: string + object_owner: + type: + - string + - "null" + object_updated: + type: string + parcels: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + metadata: + type: + - string + - "null" + distance_unit: + type: + - string + - "null" + extra: + type: + - object + - "null" + properties: + COD: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + currency: + type: + - string + - "null" + payment_method: + type: + - string + - "null" + height: + type: + - string + - "null" + length: + type: + - string + - "null" + line_items: + type: + - array + - "null" + mass_unit: + type: + - string + - "null" + object_created: + type: + - string + - "null" + object_id: + type: + - string + - "null" + object_owner: + type: + - string + - "null" + object_state: + type: + - string + - "null" + object_updated: + type: + - string + - "null" + test: + type: + - boolean + - "null" + weight: + type: + - string + - "null" + width: + type: + - string + - "null" + rates: + type: + - array + - "null" + shipment_date: + type: + - string + - "null" + status: + type: + - string + - "null" + test: + type: + - boolean + - "null" + required: + - object_id + - object_updated diff --git a/airbyte-integrations/connectors/source-shippo/metadata.yaml b/airbyte-integrations/connectors/source-shippo/metadata.yaml new file mode 100644 index 000000000000..c516081efcb1 --- /dev/null +++ b/airbyte-integrations/connectors/source-shippo/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "api.goshippo.com" + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-shippo + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.16.0@sha256:6800f806944ee4fccf24ae01f6b8fbefb12d952c3b3da338f51f732b55de51f2 + connectorSubtype: api + connectorType: source + definitionId: e809021f-7258-42c0-8aa6-4bc563b27837 + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-shippo + githubIssueLabel: source-shippo + icon: icon.svg + license: MIT + name: Shippo + releaseDate: 2024-10-28 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/shippo + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/shippo.md b/docs/integrations/sources/shippo.md new file mode 100644 index 000000000000..d98ebe64d065 --- /dev/null +++ b/docs/integrations/sources/shippo.md @@ -0,0 +1,34 @@ +# Shippo +This is the Shippo source for ingesting data using the Shippo API. + +Shippo is your one-stop solution for shipping labels. Whether you use our app to ship or API to power your logistics workflow, Shippo gives you scalable shipping tools, the best rates, and world-class support https://goshippo.com/ + +In order to use this source, you must first create a Shippo account. Once logged in, head over to Settings -> Advanced -> API and click on generate new token. You can learn more about the API here https://docs.goshippo.com/shippoapi/public-api/#tag/Overview + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `shippo_token` | `string` | Shippo Token. The bearer token used for making requests | | +| `start_date` | `string` | Start date. | | + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| addresses | id | DefaultPaginator | ✅ | ❌ | +| parcels | object_id | DefaultPaginator | ✅ | ❌ | +| custom_items | object_id | DefaultPaginator | ✅ | ❌ | +| accounts | object_id | DefaultPaginator | ✅ | ❌ | +| carrier_acounts | object_id | DefaultPaginator | ✅ | ❌ | +| shipments | object_id | DefaultPaginator | ✅ | ✅ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-10-28 | | Initial release by [@aazam-gh](https://github.com/aazam-gh) via Connector Builder | + +
From 74746e466941af4ca75516ea38152e154936ccfa Mon Sep 17 00:00:00 2001 From: Biplab Bera <123734227+bishalbera@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:22:16 +0530 Subject: [PATCH 16/45] source-freshbooks contribution from bishalbera (#47422) Co-authored-by: Marcos Marx --- .../connectors/source-freshbooks/README.md | 33 + .../acceptance-test-config.yml | 17 + .../connectors/source-freshbooks/icon.svg | 1 + .../source-freshbooks/manifest.yaml | 2086 +++++++++++++++++ .../source-freshbooks/metadata.yaml | 35 + docs/integrations/sources/freshbooks.md | 42 + 6 files changed, 2214 insertions(+) create mode 100644 airbyte-integrations/connectors/source-freshbooks/README.md create mode 100644 airbyte-integrations/connectors/source-freshbooks/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-freshbooks/icon.svg create mode 100644 airbyte-integrations/connectors/source-freshbooks/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-freshbooks/metadata.yaml create mode 100644 docs/integrations/sources/freshbooks.md diff --git a/airbyte-integrations/connectors/source-freshbooks/README.md b/airbyte-integrations/connectors/source-freshbooks/README.md new file mode 100644 index 000000000000..08b01a383c6e --- /dev/null +++ b/airbyte-integrations/connectors/source-freshbooks/README.md @@ -0,0 +1,33 @@ +# FreshBooks +This directory contains the manifest-only connector for `source-freshbooks`. + +FreshBooks connector seamlessly syncs invoicing, expenses, and client data from FreshBooks into data warehouses or analytics platforms. + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-freshbooks:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-freshbooks build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-freshbooks test +``` + diff --git a/airbyte-integrations/connectors/source-freshbooks/acceptance-test-config.yml b/airbyte-integrations/connectors/source-freshbooks/acceptance-test-config.yml new file mode 100644 index 000000000000..e64c1189e300 --- /dev/null +++ b/airbyte-integrations/connectors/source-freshbooks/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-freshbooks:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-freshbooks/icon.svg b/airbyte-integrations/connectors/source-freshbooks/icon.svg new file mode 100644 index 000000000000..9b4ac79421e5 --- /dev/null +++ b/airbyte-integrations/connectors/source-freshbooks/icon.svg @@ -0,0 +1 @@ + diff --git a/airbyte-integrations/connectors/source-freshbooks/manifest.yaml b/airbyte-integrations/connectors/source-freshbooks/manifest.yaml new file mode 100644 index 000000000000..3acf8f940d8b --- /dev/null +++ b/airbyte-integrations/connectors/source-freshbooks/manifest.yaml @@ -0,0 +1,2086 @@ +version: 5.15.0 + +type: DeclarativeSource + +description: >- + FreshBooks connector seamlessly syncs invoicing, expenses, and client data + from FreshBooks into data warehouses or analytics platforms. + +check: + type: CheckStream + stream_names: + - user + +definitions: + streams: + user: + type: DeclarativeStream + name: user + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /auth/api/v1/users/me + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/user" + clients: + type: DeclarativeStream + name: clients + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /accounting/account/{{ config["account_id"] }}/users/clients + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - response + - result + - clients + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/clients" + invoices: + type: DeclarativeStream + name: invoices + primary_key: + - id + - invoiceid + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /accounting/account/{{ config['account_id'] }}/invoices/invoices + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - response + - result + - invoices + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/invoices" + expenses: + type: DeclarativeStream + name: expenses + primary_key: + - id + - expenseid + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /accounting/account/{{ config['account_id'] }}/expenses/expenses + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - response + - result + - expenses + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/expenses" + expense_summaries: + type: DeclarativeStream + name: expense_summaries + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /accounting/account/{{ config["account_id"] }}/expenses/summaries + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - response + - result + - summaries + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/expense_summaries" + expense_categories: + type: DeclarativeStream + name: expense_categories + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /accounting/account/{{ config["account_id"] }}/expenses/categories + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - response + - result + - categories + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/expense_categories" + invoice_details: + type: DeclarativeStream + name: invoice_details + primary_key: + - invoiceid + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: >- + /accounting/account/{{ config["account_id"] + }}/reports/accounting/invoice_details + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - response + - result + - invoice_details + - clients + - "*" + - invoices + - "*" + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/invoice_details" + expense_details: + type: DeclarativeStream + name: expense_details + primary_key: + - expenseid + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: >- + /accounting/account/{{ config['account_id'] + }}/reports/accounting/expense_details + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - response + - result + - expense_details + - data + - "*" + - expenses + - "*" + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/expense_details" + accounts: + type: DeclarativeStream + name: accounts + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: >- + /accounting/businesses/{{ config['business_uuid'] + }}/ledger_accounts/accounts + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: [] + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/accounts" + taxes: + type: DeclarativeStream + name: taxes + primary_key: + - taxid + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /accounting/account/{{ config["account_id"] }}/taxes/taxes + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - response + - result + - taxes + - "*" + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: page + pagination_strategy: + type: PageIncrement + page_size: 100 + start_from_page: 1 + inject_on_first_request: true + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/taxes" + base_requester: + type: HttpRequester + url_base: https://api.freshbooks.com + authenticator: + type: OAuthAuthenticator + client_id: "{{ config[\"client_id\"] }}" + grant_type: refresh_token + client_secret: "{{ config[\"client_secret\"] }}" + refresh_token: "{{ config[\"client_refresh_token\"] }}" + refresh_request_body: + redirect_uri: "{{ config[\"redirect_uri\"] }}" + refresh_token_updater: + refresh_token_name: refresh_token + access_token_config_path: + - oauth_access_token + token_expiry_date_config_path: + - oauth_token_expiry_date + refresh_token_config_path: + - client_refresh_token + token_refresh_endpoint: https://api.freshbooks.com/auth/oauth/token + +streams: + - $ref: "#/definitions/streams/user" + - $ref: "#/definitions/streams/clients" + - $ref: "#/definitions/streams/invoices" + - $ref: "#/definitions/streams/expenses" + - $ref: "#/definitions/streams/expense_summaries" + - $ref: "#/definitions/streams/expense_categories" + - $ref: "#/definitions/streams/invoice_details" + - $ref: "#/definitions/streams/expense_details" + - $ref: "#/definitions/streams/accounts" + - $ref: "#/definitions/streams/taxes" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - client_id + - client_secret + - redirect_uri + - account_id + - client_refresh_token + - business_uuid + properties: + client_id: + type: string + order: 0 + title: Client ID + airbyte_secret: true + client_secret: + type: string + order: 1 + title: Client secret + airbyte_secret: true + redirect_uri: + type: string + order: 2 + title: Redirect Uri + airbyte_secret: true + account_id: + type: string + order: 3 + title: Account Id + client_refresh_token: + type: string + order: 4 + title: Refresh token + airbyte_secret: true + oauth_access_token: + type: string + description: >- + The current access token. This field might be overridden by the + connector based on the token refresh endpoint response. + order: 5 + title: Access token + airbyte_secret: true + oauth_token_expiry_date: + type: string + description: >- + The date the current access token expires in. This field might be + overridden by the connector based on the token refresh endpoint + response. + order: 6 + title: Token expiry date + format: date-time + business_uuid: + type: string + order: 7 + title: Business uuid + additionalProperties: true + +metadata: + autoImportSchema: + user: true + clients: true + invoices: true + expenses: true + expense_summaries: true + expense_categories: true + invoice_details: true + expense_details: true + accounts: true + taxes: true + testedStreams: + user: + hasRecords: true + streamHash: 084154685fd57ba95fed5b9a5e68c697a9a98fcc + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + clients: + hasRecords: true + streamHash: 35e9f0487bda5713e6b688ebca713409e707eaf5 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + invoices: + hasRecords: true + streamHash: c583e59892652200fda6b7b0672278b0a859550d + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + expenses: + hasRecords: true + streamHash: 2b5c12602f5d15f916ccbcb17749332488e7ff45 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + expense_summaries: + hasRecords: true + streamHash: 958e28c6ad13fef3a7ca192cce4d007217744dfa + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + expense_categories: + hasRecords: true + streamHash: 175f129069e1b76a1aa4e4cc09c720b2432e58f0 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + invoice_details: + hasRecords: true + streamHash: b2ea5342d54bed63b89a45a85024677a999f5171 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + expense_details: + hasRecords: true + streamHash: 9b667c02b8aab2f4c21c39b4b7a8d7bc6377f78a + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + accounts: + hasRecords: true + streamHash: e393f445f6fa6f1161556aca19e5a9a4dd75bbf2 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + taxes: + streamHash: 12b2752c45f9d92bcc1e51161a53bf54fa9962d2 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + assist: + docsUrl: https://www.freshbooks.com/api/start + +schemas: + user: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + response: + type: + - object + - "null" + properties: + addresses: + type: + - array + - "null" + items: + type: + - "null" + - "null" + business_memberships: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + business: + type: + - object + - "null" + properties: + account_id: + type: + - string + - "null" + active: + type: + - boolean + - "null" + address: + type: + - object + - "null" + properties: + city: + type: + - string + - "null" + country: + type: + - string + - "null" + id: + type: + - number + - "null" + postal_code: + type: + - string + - "null" + province: + type: + - string + - "null" + street: + type: + - string + - "null" + street2: + type: + - string + - "null" + advanced_accounting_enabled: + type: + - boolean + - "null" + business_clients: + type: + - array + - "null" + business_uuid: + type: + - string + - "null" + date_format: + type: + - string + - "null" + first_day_of_week: + type: + - number + - "null" + id: + type: + - number + - "null" + industry: + type: + - string + - "null" + name: + type: + - string + - "null" + phone_number: + type: + - object + - "null" + properties: + id: + type: + - number + - "null" + phone_number: + type: + - string + - "null" + status: + type: + - string + - "null" + timezone: + type: + - string + - "null" + fasttrack_token: + type: + - string + - "null" + id: + type: + - number + - "null" + role: + type: + - string + - "null" + unacknowledged_change: + type: + - boolean + - "null" + business_statuses: + type: + - object + - "null" + properties: + YpZK5G: + type: + - string + - "null" + confirmed_at: + type: + - string + - "null" + created_at: + type: + - string + - "null" + email: + type: + - string + - "null" + first_name: + type: + - string + - "null" + groups: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + active: + type: + - boolean + - "null" + business_id: + type: + - number + - "null" + group_id: + type: + - number + - "null" + id: + type: + - number + - "null" + identity_id: + type: + - number + - "null" + identity_uuid: + type: + - string + - "null" + role: + type: + - string + - "null" + id: + type: + - number + - "null" + identity_id: + type: + - number + - "null" + identity_origin: + type: + - string + - "null" + identity_uuid: + type: + - string + - "null" + integrations: + type: + - object + - "null" + language: + type: + - string + - "null" + last_name: + type: + - string + - "null" + links: + type: + - object + - "null" + properties: + me: + type: + - string + - "null" + roles: + type: + - string + - "null" + permissions: + type: + - object + - "null" + properties: + YpZK5G: + type: + - object + - "null" + properties: + BankReconciliation.access: + type: + - boolean + - "null" + BetaHeliosAsyncExpenses.access: + type: + - boolean + - "null" + accountant_partner.limit: + type: + - number + - "null" + accounting_reports_v2_general_ledger.access: + type: + - boolean + - "null" + accounts_payable.access: + type: + - boolean + - "null" + accounts_payable_beta.access: + type: + - boolean + - "null" + accounts_payable_reports.access: + type: + - boolean + - "null" + acss_direct_debit.access: + type: + - boolean + - "null" + advanced_accounting.access: + type: + - boolean + - "null" + andromeda_manual_mileage_tracking.access: + type: + - boolean + - "null" + andromeda_mileage_tracker.dev.access: + type: + - boolean + - "null" + attachments.access: + type: + - boolean + - "null" + audit_log.access: + type: + - boolean + - "null" + audit_log_backend.access: + type: + - boolean + - "null" + audit_log_expense.access: + type: + - boolean + - "null" + auto_bank_import.access: + type: + - boolean + - "null" + bacs_direct_debit.access: + type: + - boolean + - "null" + bacs_direct_debit_mandate.access: + type: + - boolean + - "null" + bacs_fee_cap.limit: + type: + - number + - "null" + bank_import_failure_notifications.access: + type: + - boolean + - "null" + bank_rec_smart_match.access: + type: + - boolean + - "null" + beta_mobile_create_expense_subcategory.access: + type: + - boolean + - "null" + bill_payment_reconciliation.access: + type: + - boolean + - "null" + business_accountant.limit: + type: + - number + - "null" + cardapp_new_payment_methods.access: + type: + - boolean + - "null" + client.limit: + type: + - number + - "null" + core_data_from_es.access: + type: + - boolean + - "null" + credit_notes_payment_method.access: + type: + - boolean + - "null" + credit_reconciliation.access: + type: + - boolean + - "null" + customizable_dashboard.access: + type: + - boolean + - "null" + display_credits_plan_summary.access: + type: + - boolean + - "null" + documents_ocr.access: + type: + - boolean + - "null" + documents_ocr_bill.access: + type: + - boolean + - "null" + documents_ocr_bill_complete.access: + type: + - boolean + - "null" + documents_ocr_bill_plan.access: + type: + - boolean + - "null" + documents_ocr_complete_plan.access: + type: + - boolean + - "null" + documents_ocr_match_expenses.access: + type: + - boolean + - "null" + documents_ocr_plan.access: + type: + - boolean + - "null" + email_verification_with_code_page.access: + type: + - boolean + - "null" + emails_page.access: + type: + - boolean + - "null" + embedded_payroll.access: + type: + - boolean + - "null" + embedded_payroll_journal_entries.access: + type: + - boolean + - "null" + enhanced_project_client_typeahead.access: + type: + - boolean + - "null" + es_migrations.access: + type: + - boolean + - "null" + estimate_convert_to_project.access: + type: + - boolean + - "null" + fiscal_year.access: + type: + - boolean + - "null" + flexcoa_advanced_accounting.access: + type: + - boolean + - "null" + flexcoa_beta_rollout.access: + type: + - boolean + - "null" + flexible_chart_of_accounts.access: + type: + - boolean + - "null" + helios_bulk_actions_invoices.beta.access: + type: + - boolean + - "null" + helios_company_taxes.beta.access: + type: + - boolean + - "null" + helios_dashboard.access: + type: + - boolean + - "null" + helios_expense_rebilling.beta.access: + type: + - boolean + - "null" + helios_invoice_archive.beta.access: + type: + - boolean + - "null" + helios_late_fee_reminder.beta.access: + type: + - boolean + - "null" + helios_manual_mileage_tracking.access: + type: + - boolean + - "null" + helios_push_resource_to_use_execute.beta.access: + type: + - boolean + - "null" + helios_pushnotifications.beta.access: + type: + - boolean + - "null" + helios_rebill_time.access: + type: + - boolean + - "null" + helios_remote_search.beta.access: + type: + - boolean + - "null" + helios_stripe_virtual_terminal.beta.access: + type: + - boolean + - "null" + helios_sync_throttle.beta.access: + type: + - boolean + - "null" + helios_virtual_terminal.beta.access: + type: + - boolean + - "null" + helios_virtual_terminal_advertising.beta.access: + type: + - boolean + - "null" + helios_virtual_terminal_tutorial.beta.access: + type: + - boolean + - "null" + historical_payroll_minimum.access: + type: + - boolean + - "null" + improved_bank_transfer_education_for_clients.access: + type: + - boolean + - "null" + invoice_pdf_email.access: + type: + - boolean + - "null" + ios_beta_payment_schedules.access: + type: + - boolean + - "null" + ios_beta_zendesk_widget.access: + type: + - boolean + - "null" + latest_released_api_version.access: + type: + - boolean + - "null" + launchpad_cards_version.access: + type: + - boolean + - "null" + launchpad_cards_version_with_personalization.access: + type: + - boolean + - "null" + limit_add_businesses.access: + type: + - boolean + - "null" + manual_journal_entries_feature.access: + type: + - boolean + - "null" + metapane_payment_methods_improvement.access: + type: + - boolean + - "null" + mobile_receipt_rebilling.access: + type: + - boolean + - "null" + modern_friendbuy.access: + type: + - boolean + - "null" + mtd_enhancements.access: + type: + - boolean + - "null" + new_payments_page: + type: + - boolean + - "null" + new_setup_quiz.access: + type: + - boolean + - "null" + no_seat_employee.access: + type: + - boolean + - "null" + opus_items_services_page.access: + type: + - boolean + - "null" + opus_my_team_page.access: + type: + - boolean + - "null" + outstanding_invoices_summary.access: + type: + - boolean + - "null" + partial_payments.access: + type: + - boolean + - "null" + payment_links.access: + type: + - boolean + - "null" + payments_withdrawal_report.access: + type: + - boolean + - "null" + paypal_gateway.access: + type: + - boolean + - "null" + paypal_tiered_pricing.access: + type: + - boolean + - "null" + payroll_notifications.access: + type: + - boolean + - "null" + payroll_user_invite.access: + type: + - boolean + - "null" + plaid_integration.access: + type: + - boolean + - "null" + premium_contractor_role.access: + type: + - boolean + - "null" + project_line_items.access: + type: + - boolean + - "null" + project_profitability.access: + type: + - boolean + - "null" + project_service_estimates.access: + type: + - boolean + - "null" + project_widget_updates.access: + type: + - boolean + - "null" + proposals_candidate.access: + type: + - boolean + - "null" + recurring_ach.access: + type: + - boolean + - "null" + recurring_paypal.access: + type: + - boolean + - "null" + recurring_revenue_monarch_updates.access: + type: + - boolean + - "null" + refunds.access: + type: + - boolean + - "null" + retained_earnings.access: + type: + - boolean + - "null" + retainers.limit: + type: + - number + - "null" + retainers_feature.access: + type: + - boolean + - "null" + rich_proposals.access: + type: + - boolean + - "null" + rounded_time_tracking.access: + type: + - boolean + - "null" + sales_managed_receipt.access: + type: + - boolean + - "null" + saltedge_integration.access: + type: + - boolean + - "null" + save_emails.access: + type: + - boolean + - "null" + scopes_ui_phase1.access: + type: + - boolean + - "null" + sepa_direct_debit.access: + type: + - boolean + - "null" + sepa_direct_debit_mandate.access: + type: + - boolean + - "null" + sepa_fee_cap.limit: + type: + - number + - "null" + solvvy_support_chat.access: + type: + - boolean + - "null" + staff.limit: + type: + - number + - "null" + stripe_ach.access: + type: + - boolean + - "null" + stripe_advanced_payments.access: + type: + - boolean + - "null" + stripe_payments_intent.access: + type: + - boolean + - "null" + stripe_unified_account.access: + type: + - boolean + - "null" + team_members.access: + type: + - boolean + - "null" + team_utilization_report.access: + type: + - boolean + - "null" + ted_report_refactor.access: + type: + - boolean + - "null" + time_entry_billed_amount.access: + type: + - boolean + - "null" + time_zone_enhancement.access: + type: + - boolean + - "null" + wepay_kyc_redirect.access: + type: + - boolean + - "null" + yodlee_fastlink_upgrade.access: + type: + - boolean + - "null" + phone_numbers: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + title: + type: + - string + - "null" + profile: + type: + - object + - "null" + properties: + first_name: + type: + - string + - "null" + has_password: + type: + - boolean + - "null" + is_email_confirmed: + type: + - boolean + - "null" + last_name: + type: + - string + - "null" + professions: + type: + - array + - "null" + setup_complete: + type: + - boolean + - "null" + time_format: + type: + - number + - "null" + timezone: + type: + - string + - "null" + roles: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + accountid: + type: + - string + - "null" + created_at: + type: + - string + - "null" + id: + type: + - number + - "null" + links: + type: + - object + - "null" + properties: + destroy: + type: + - string + - "null" + role: + type: + - string + - "null" + systemid: + type: + - number + - "null" + userid: + type: + - number + - "null" + setup_complete: + type: + - boolean + - "null" + subscription_statuses: + type: + - object + - "null" + properties: + YpZK5G: + type: + - string + - "null" + timezone: + type: + - string + - "null" + clients: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + accounting_systemid: + type: + - string + - "null" + allow_email_include_pdf: + type: + - boolean + - "null" + allow_late_fees: + type: + - boolean + - "null" + allow_late_notifications: + type: + - boolean + - "null" + bus_phone: + type: + - string + - "null" + currency_code: + type: + - string + - "null" + email: + type: + - string + - "null" + exceeds_client_limit: + type: + - number + - "null" + fax: + type: + - string + - "null" + fname: + type: + - string + - "null" + id: + type: + - number + - "null" + language: + type: + - string + - "null" + level: + type: + - number + - "null" + lname: + type: + - string + - "null" + mob_phone: + type: + - string + - "null" + notified: + type: + - boolean + - "null" + num_logins: + type: + - number + - "null" + organization: + type: + - string + - "null" + p_city: + type: + - string + - "null" + p_code: + type: + - string + - "null" + p_country: + type: + - string + - "null" + p_province: + type: + - string + - "null" + p_street: + type: + - string + - "null" + p_street2: + type: + - string + - "null" + pref_email: + type: + - boolean + - "null" + pref_gmail: + type: + - boolean + - "null" + role: + type: + - string + - "null" + s_city: + type: + - string + - "null" + s_code: + type: + - string + - "null" + s_country: + type: + - string + - "null" + s_province: + type: + - string + - "null" + s_street: + type: + - string + - "null" + s_street2: + type: + - string + - "null" + signup_date: + type: + - string + - "null" + updated: + type: + - string + - "null" + userid: + type: + - number + - "null" + username: + type: + - string + - "null" + uuid: + type: + - string + - "null" + vis_state: + type: + - number + - "null" + invoices: + type: object + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: true + properties: {} + expenses: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + version: + type: + - string + - "null" + account_name: + type: + - string + - "null" + accounting_systemid: + type: + - string + - "null" + amount: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + bank_name: + type: + - string + - "null" + bill_matches: + type: + - array + - "null" + billable: + type: + - boolean + - "null" + categoryid: + type: + - number + - "null" + clientid: + type: + - number + - "null" + compounded_tax: + type: + - boolean + - "null" + date: + type: + - string + - "null" + expenseid: + type: number + ext_invoiceid: + type: + - number + - "null" + ext_systemid: + type: + - number + - "null" + from_bulk_import: + type: + - boolean + - "null" + has_receipt: + type: + - boolean + - "null" + id: + type: number + include_receipt: + type: + - boolean + - "null" + is_cogs: + type: + - boolean + - "null" + isduplicate: + type: + - boolean + - "null" + markup_percent: + type: + - string + - "null" + notes: + type: + - string + - "null" + potential_bill_payment: + type: + - boolean + - "null" + projectid: + type: + - number + - "null" + staffid: + type: + - number + - "null" + status: + type: + - number + - "null" + updated: + type: + - string + - "null" + vis_state: + type: + - number + - "null" + required: + - id + - expenseid + expense_summaries: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + amounts: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + count: + type: + - number + - "null" + counts: + type: + - number + - "null" + id: + type: + - string + - "null" + expense_categories: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + category: + type: + - string + - "null" + categoryid: + type: + - number + - "null" + created_at: + type: + - string + - "null" + id: + type: number + is_cogs: + type: + - boolean + - "null" + is_editable: + type: + - boolean + - "null" + parentid: + type: + - number + - "null" + transaction_posted: + type: + - boolean + - "null" + updated_at: + type: + - string + - "null" + vis_state: + type: + - number + - "null" + required: + - id + invoice_details: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + create_date: + type: + - string + - "null" + currency_code: + type: + - string + - "null" + discount_total: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + due_offset_days: + type: + - number + - "null" + invoice_number: + type: + - string + - "null" + invoiceid: + type: number + lines: + type: + - array + - "null" + outstanding: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + paid: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + subtotal: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + tax: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + tax_summaries: + type: + - array + - "null" + total: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + v3_status: + type: + - string + - "null" + required: + - invoiceid + expense_details: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + account_name: + type: + - string + - "null" + amount: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + authorid: + type: + - string + - "null" + categoryid: + type: + - string + - "null" + clientid: + type: + - string + - "null" + date: + type: + - string + - "null" + expenseid: + type: number + imported_from_csv: + type: + - boolean + - "null" + is_cogs: + type: + - boolean + - "null" + notes: + type: + - string + - "null" + taxAmount1: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + taxAmount2: + type: + - object + - "null" + properties: + amount: + type: + - string + - "null" + code: + type: + - string + - "null" + vendor: + type: + - string + - "null" + vendorid: + type: + - string + - "null" + required: + - expenseid + accounts: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + data: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + description: + type: + - string + - "null" + auto_created: + type: + - boolean + - "null" + name: + type: + - string + - "null" + number: + type: + - string + - "null" + parent_account: + type: + - string + - "null" + state: + type: + - string + - "null" + sub_accounts: + type: + - array + - "null" + items: + type: + - string + - "null" + sub_type: + type: + - string + - "null" + system_account_name: + type: + - string + - "null" + updated_at: + type: + - string + - "null" + uuid: + type: + - string + - "null" + taxes: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + accounting_systemid: + type: + - string + - "null" + amount: + type: + - string + - "null" + compound: + type: + - boolean + - "null" + id: + type: + - number + - "null" + name: + type: + - string + - "null" + taxid: + type: number + updated: + type: + - string + - "null" + required: + - taxid diff --git a/airbyte-integrations/connectors/source-freshbooks/metadata.yaml b/airbyte-integrations/connectors/source-freshbooks/metadata.yaml new file mode 100644 index 000000000000..f5ac14178c6d --- /dev/null +++ b/airbyte-integrations/connectors/source-freshbooks/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "api.freshbooks.com" + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-freshbooks + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.16.0@sha256:6800f806944ee4fccf24ae01f6b8fbefb12d952c3b3da338f51f732b55de51f2 + connectorSubtype: api + connectorType: source + definitionId: e1e86c88-fdd6-41d3-9516-3564021a1902 + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-freshbooks + githubIssueLabel: source-freshbooks + icon: icon.svg + license: MIT + name: FreshBooks + releaseDate: 2024-10-27 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/freshbooks + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/freshbooks.md b/docs/integrations/sources/freshbooks.md new file mode 100644 index 000000000000..6ff67f97ab87 --- /dev/null +++ b/docs/integrations/sources/freshbooks.md @@ -0,0 +1,42 @@ +# FreshBooks +FreshBooks connector seamlessly syncs invoicing, expenses, and client data from FreshBooks into data warehouses or analytics platforms. + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `client_id` | `string` | Client ID. | | +| `client_secret` | `string` | Client secret. | | +| `redirect_uri` | `string` | Redirect Uri. | | +| `account_id` | `string` | Account Id. | | +| `client_refresh_token` | `string` | Refresh token. | | +| `oauth_access_token` | `string` | Access token. The current access token. This field might be overridden by the connector based on the token refresh endpoint response. | | +| `oauth_token_expiry_date` | `string` | Token expiry date. The date the current access token expires in. This field might be overridden by the connector based on the token refresh endpoint response. | | +| `business_uuid` | `string` | Business uuid. | | + +Read [this](https://documenter.getpostman.com/view/3322108/S1ERwwza#intro) section carefully to get your Account Id and Business UUID. + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| user | | DefaultPaginator | ✅ | ❌ | +| clients | | DefaultPaginator | ✅ | ❌ | +| invoices | id.invoiceid | DefaultPaginator | ✅ | ❌ | +| expenses | id.expenseid | DefaultPaginator | ✅ | ❌ | +| expense_summaries | | DefaultPaginator | ✅ | ❌ | +| expense_categories | id | DefaultPaginator | ✅ | ❌ | +| invoice_details | invoiceid | DefaultPaginator | ✅ | ❌ | +| expense_details | expenseid | DefaultPaginator | ✅ | ❌ | +| accounts | | DefaultPaginator | ✅ | ❌ | +| taxes | taxid | DefaultPaginator | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-10-27 | | Initial release by [@bishalbera](https://github.com/bishalbera) via Connector Builder | + +
From db95f7a6afd900e86576b453be4f67aa3761f505 Mon Sep 17 00:00:00 2001 From: Biplab Bera <123734227+bishalbera@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:25:34 +0530 Subject: [PATCH 17/45] source-zoho-analytics-metadata-api contribution from bishalbera (#48397) Co-authored-by: Marcos Marx --- .../README.md | 33 + .../acceptance-test-config.yml | 17 + .../icon.svg | 323 +++++++++ .../manifest.yaml | 654 ++++++++++++++++++ .../metadata.yaml | 35 + .../sources/zoho-analytics-metadata-api.md | 35 + 6 files changed, 1097 insertions(+) create mode 100644 airbyte-integrations/connectors/source-zoho-analytics-metadata-api/README.md create mode 100644 airbyte-integrations/connectors/source-zoho-analytics-metadata-api/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-zoho-analytics-metadata-api/icon.svg create mode 100644 airbyte-integrations/connectors/source-zoho-analytics-metadata-api/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-zoho-analytics-metadata-api/metadata.yaml create mode 100644 docs/integrations/sources/zoho-analytics-metadata-api.md diff --git a/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/README.md b/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/README.md new file mode 100644 index 000000000000..bf7320e7e53e --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/README.md @@ -0,0 +1,33 @@ +# Zoho Analytics metadata api +This directory contains the manifest-only connector for `source-zoho-analytics-metadata-api`. + +Zoho Analytics Metadata api connector enables seamless data syncing from Zoho Analytics metadata into data warehouses or BI tools. This connector automates OAuth authentication and ensures reliable data transfer, empowering businesses to streamline analytics workflows and gain deeper insights efficiently. + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-zoho-analytics-metadata-api:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-zoho-analytics-metadata-api build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-zoho-analytics-metadata-api test +``` + diff --git a/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/acceptance-test-config.yml b/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/acceptance-test-config.yml new file mode 100644 index 000000000000..346c79194115 --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-zoho-analytics-metadata-api:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/icon.svg b/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/icon.svg new file mode 100644 index 000000000000..45638ae4a8ff --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/icon.svg @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + diff --git a/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/manifest.yaml b/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/manifest.yaml new file mode 100644 index 000000000000..9e25c0756a4a --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/manifest.yaml @@ -0,0 +1,654 @@ +version: 6.1.0 + +type: DeclarativeSource + +description: >- + Zoho Analytics Metadata api connector enables seamless data syncing from Zoho + Analytics metadata into data warehouses or BI tools. This connector automates + OAuth authentication and ensures reliable data transfer, empowering businesses + to streamline analytics workflows and gain deeper insights efficiently. + +check: + type: CheckStream + stream_names: + - users + +definitions: + streams: + users: + type: DeclarativeStream + name: users + primary_key: + - emailId + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /restapi/v2/users + http_method: GET + request_headers: + ZANALYTICS-ORGID: "{{ config['org_id'] }}" + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + - users + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/users" + workspaces: + type: DeclarativeStream + name: workspaces + primary_key: + - workspaceId + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /restapi/v2/workspaces + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + - ownedWorkspaces + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/workspaces" + organizations: + type: DeclarativeStream + name: organizations + primary_key: + - orgId + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /restapi/v2/orgs + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + - orgs + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/organizations" + views: + type: DeclarativeStream + name: views + primary_key: + - viewId + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /restapi/v2/workspaces/{{ stream_partition.workspace }}/views + http_method: GET + request_headers: + ZANALYTICS-ORGID: "{{ config[\"org_id\"] }}" + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + - views + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: workspaceId + partition_field: workspace + stream: + $ref: "#/definitions/streams/workspaces" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/views" + dashboards: + type: DeclarativeStream + name: dashboards + primary_key: + - viewId + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /restapi/v2/dashboards + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + - ownedViews + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/dashboards" + trash: + type: DeclarativeStream + name: trash + primary_key: + - viewId + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /restapi/v2/workspaces/{{ stream_partition.workspace }}/trash + http_method: GET + request_headers: + ZANALYTICS-ORGID: "{{ config[\"org_id\"] }}" + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + - views + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: workspaceId + partition_field: workspace + stream: + $ref: "#/definitions/streams/workspaces" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/trash" + workspace_users: + type: DeclarativeStream + name: workspace_users + primary_key: + - emailId + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /restapi/v2/workspaces/{{ stream_partition.workspace }}/users + http_method: GET + request_headers: + ZANALYTICS-ORGID: "{{ config[\"org_id\"] }}" + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + - users + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: workspaceId + partition_field: workspace + stream: + $ref: "#/definitions/streams/workspaces" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/workspace_users" + folders: + type: DeclarativeStream + name: folders + primary_key: + - folderId + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /restapi/v2/workspaces/{{ stream_partition.workspace }}/folders + http_method: GET + request_headers: + ZANALYTICS-ORGID: "{{ config[\"org_id\"] }}" + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + - folders + partition_router: + type: SubstreamPartitionRouter + parent_stream_configs: + - type: ParentStreamConfig + parent_key: workspaceId + partition_field: workspace + stream: + $ref: "#/definitions/streams/workspaces" + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/folders" + base_requester: + type: HttpRequester + url_base: https://analyticsapi.zoho.{{ config["data_center"] }} + authenticator: + type: OAuthAuthenticator + client_id: "{{ config[\"client_id\"] }}" + grant_type: refresh_token + client_secret: "{{ config[\"client_secret\"] }}" + refresh_token: "{{ config[\"refresh_token\"] }}" + expires_in_name: expires_in + access_token_name: access_token + refresh_request_body: {} + token_refresh_endpoint: https://accounts.zoho.{{ config["data_center"] }}/oauth/v2/token + +streams: + - $ref: "#/definitions/streams/users" + - $ref: "#/definitions/streams/workspaces" + - $ref: "#/definitions/streams/organizations" + - $ref: "#/definitions/streams/views" + - $ref: "#/definitions/streams/dashboards" + - $ref: "#/definitions/streams/trash" + - $ref: "#/definitions/streams/workspace_users" + - $ref: "#/definitions/streams/folders" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - client_id + - client_secret + - refresh_token + - data_center + - org_id + properties: + client_id: + type: string + name: client_id + order: 0 + title: OAuth Client ID + airbyte_secret: true + client_secret: + type: string + name: client_secret + order: 1 + title: OAuth Client Secret + airbyte_secret: true + refresh_token: + type: string + name: refresh_token + order: 2 + title: OAuth Refresh Token + airbyte_secret: true + data_center: + type: string + enum: + - com + - eu + - in + - com.au + - com.cn + - jp + order: 3 + title: Data Center + default: com + org_id: + type: number + order: 4 + title: Org Id + additionalProperties: true + +metadata: + autoImportSchema: + users: true + workspaces: true + organizations: true + views: true + dashboards: true + trash: true + workspace_users: true + folders: true + testedStreams: + users: + hasRecords: true + streamHash: 30b9674113495c708b88b03206126e121d54637b + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + workspaces: + hasRecords: true + streamHash: 412f2d3ff4d56ea7401a905def908c42a0ee450a + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + organizations: + hasRecords: true + streamHash: fe29c6780e8b266bbe44d9feb13a6f0d486b425c + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + views: + hasRecords: true + streamHash: 32af56a385b314e6e5c9238a605b9d0c2ad7f8b0 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + dashboards: + hasRecords: true + streamHash: e13cb711a5f8a91eb42ac3f163d5a9c39f164346 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + trash: + hasRecords: true + streamHash: 0d8d1ecf9c0edad58d67d0a255234ee38278eb6c + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + workspace_users: + hasRecords: true + streamHash: dc51d9f2fc5b699cf7dad3a46a5396d85cca9d5c + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + folders: + hasRecords: true + streamHash: 2c74239bed086e4cd23916c4f5f450df3215db18 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + assist: + docsUrl: https://www.zoho.com/analytics/api/v2/introduction.html + +schemas: + users: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + emailId: + type: string + role: + type: + - string + - "null" + status: + type: + - boolean + - "null" + required: + - emailId + workspaces: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + createdBy: + type: + - string + - "null" + createdTime: + type: + - string + - "null" + isDefault: + type: + - boolean + - "null" + orgId: + type: + - string + - "null" + workspaceDesc: + type: + - string + - "null" + workspaceId: + type: string + workspaceName: + type: + - string + - "null" + required: + - workspaceId + organizations: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + createdBy: + type: + - string + - "null" + createdByZuId: + type: + - string + - "null" + isDefault: + type: + - boolean + - "null" + numberOfWorkspaces: + type: + - number + - "null" + orgDesc: + type: + - string + - "null" + orgId: + type: string + orgName: + type: + - string + - "null" + planName: + type: + - string + - "null" + role: + type: + - string + - "null" + required: + - orgId + views: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + createdBy: + type: + - string + - "null" + createdTime: + type: + - string + - "null" + folderId: + type: + - string + - "null" + isFavorite: + type: + - boolean + - "null" + lastModifiedBy: + type: + - string + - "null" + lastModifiedTime: + type: + - string + - "null" + parentViewId: + type: + - string + - "null" + sharedBy: + type: + - string + - "null" + viewDesc: + type: + - string + - "null" + viewId: + type: string + viewName: + type: + - string + - "null" + viewType: + type: + - string + - "null" + required: + - viewId + dashboards: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + createdBy: + type: + - string + - "null" + createdTime: + type: + - string + - "null" + folderId: + type: + - string + - "null" + isFavorite: + type: + - boolean + - "null" + lastModifiedBy: + type: + - string + - "null" + lastModifiedTime: + type: + - string + - "null" + orgId: + type: + - string + - "null" + parentViewId: + type: + - string + - "null" + sharedBy: + type: + - string + - "null" + viewDesc: + type: + - string + - "null" + viewId: + type: string + viewName: + type: + - string + - "null" + viewType: + type: + - string + - "null" + workspaceId: + type: + - string + - "null" + required: + - viewId + trash: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + deletedBy: + type: + - string + - "null" + deletedTime: + type: + - string + - "null" + viewId: + type: string + viewName: + type: + - string + - "null" + viewType: + type: + - string + - "null" + required: + - viewId + workspace_users: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + emailId: + type: string + role: + type: + - string + - "null" + status: + type: + - boolean + - "null" + required: + - emailId + folders: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + folderDesc: + type: + - string + - "null" + folderId: + type: string + folderIndex: + type: + - number + - "null" + folderName: + type: + - string + - "null" + isDefault: + type: + - boolean + - "null" + parentFolderId: + type: + - string + - "null" + required: + - folderId diff --git a/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/metadata.yaml b/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/metadata.yaml new file mode 100644 index 000000000000..0f3c6a87ffcb --- /dev/null +++ b/airbyte-integrations/connectors/source-zoho-analytics-metadata-api/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "analyticsapi.zoho." + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-zoho-analytics-metadata-api + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:6.2.1@sha256:641f18edae53820aa91de6c828d1318ec5da22dc2a9f407cc8b6b75cc4e96569 + connectorSubtype: api + connectorType: source + definitionId: 63114ebf-1c0e-4e6e-bb93-28ae03332b14 + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-zoho-analytics-metadata-api + githubIssueLabel: source-zoho-analytics-metadata-api + icon: icon.svg + license: MIT + name: Zoho Analytics metadata api + releaseDate: 2024-11-07 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/zoho-analytics-metadata-api + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/zoho-analytics-metadata-api.md b/docs/integrations/sources/zoho-analytics-metadata-api.md new file mode 100644 index 000000000000..cc7f78228baf --- /dev/null +++ b/docs/integrations/sources/zoho-analytics-metadata-api.md @@ -0,0 +1,35 @@ +# Zoho Analytics Metadata API +Zoho Analytics Metadata api connector enables seamless data syncing from Zoho Analytics metadata into data warehouses or BI tools. This connector automates OAuth authentication and ensures reliable data transfer, empowering businesses to streamline analytics workflows and gain deeper insights efficiently. + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `org_id` | `number` | Org Id. | | +| `data_center` | `string` | Data Center. | `com` | +| `client_id` | `string` | OAuth Client ID. | | +| `client_secret` | `string` | OAuth Client Secret. | | +| `refresh_token` | `string` | OAuth Refresh Token. | | + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| users | emailId | No pagination | ✅ | ❌ | +| workspaces | workspaceId | No pagination | ✅ | ❌ | +| organizations | orgId | No pagination | ✅ | ❌ | +| views | viewId | No pagination | ✅ | ❌ | +| dashboards | viewId | No pagination | ✅ | ❌ | +| trash | viewId | No pagination | ✅ | ❌ | +| workspace_users | emailId | No pagination | ✅ | ❌ | +| folders | folderId | No pagination | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-11-07 | | Initial release by [@bishalbera](https://github.com/bishalbera) via Connector Builder | + +
From 29b212b56c7fdeff1ed72a3f90b1b6424cbf5979 Mon Sep 17 00:00:00 2001 From: Aazam Thakur <59562284+aazam-gh@users.noreply.github.com> Date: Thu, 7 Nov 2024 19:04:38 +0530 Subject: [PATCH 18/45] source-cloudbeds contribution from aazam-gh (#48044) Co-authored-by: Marcos Marx --- .../connectors/source-cloudbeds/README.md | 39 + .../acceptance-test-config.yml | 17 + .../connectors/source-cloudbeds/icon.svg | 16 + .../connectors/source-cloudbeds/manifest.yaml | 703 ++++++++++++++++++ .../connectors/source-cloudbeds/metadata.yaml | 35 + docs/integrations/sources/cloudbeds.md | 35 + 6 files changed, 845 insertions(+) create mode 100644 airbyte-integrations/connectors/source-cloudbeds/README.md create mode 100644 airbyte-integrations/connectors/source-cloudbeds/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-cloudbeds/icon.svg create mode 100644 airbyte-integrations/connectors/source-cloudbeds/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-cloudbeds/metadata.yaml create mode 100644 docs/integrations/sources/cloudbeds.md diff --git a/airbyte-integrations/connectors/source-cloudbeds/README.md b/airbyte-integrations/connectors/source-cloudbeds/README.md new file mode 100644 index 000000000000..95850dce43f8 --- /dev/null +++ b/airbyte-integrations/connectors/source-cloudbeds/README.md @@ -0,0 +1,39 @@ +# Cloudbeds +This directory contains the manifest-only connector for `source-cloudbeds`. + +This is Cloudbeds source that ingests data from the Cloudbeds API. + +Cloudbeds is an unified hospitality platform https://cloudbeds.com + +In order to use this source, you must first create a cloudbeds account. Once logged in, navigate to the API credentials page for your property by clicking Account > Apps & Marketplace in the upper right corner. Use the menu on the top to navigate to the API Credentials Page. Click the New Credentials button, fill in the details and click on Create. This will create an application, then click on the API Key and provide all the required scopes as needed. + +You can learn more about the API here https://hotels.cloudbeds.com/api/v1.2/docs/ + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-cloudbeds:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-cloudbeds build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-cloudbeds test +``` + diff --git a/airbyte-integrations/connectors/source-cloudbeds/acceptance-test-config.yml b/airbyte-integrations/connectors/source-cloudbeds/acceptance-test-config.yml new file mode 100644 index 000000000000..5a07e461b1e8 --- /dev/null +++ b/airbyte-integrations/connectors/source-cloudbeds/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-cloudbeds:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-cloudbeds/icon.svg b/airbyte-integrations/connectors/source-cloudbeds/icon.svg new file mode 100644 index 000000000000..f35ae0ef7cdb --- /dev/null +++ b/airbyte-integrations/connectors/source-cloudbeds/icon.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/airbyte-integrations/connectors/source-cloudbeds/manifest.yaml b/airbyte-integrations/connectors/source-cloudbeds/manifest.yaml new file mode 100644 index 000000000000..e92303143a2c --- /dev/null +++ b/airbyte-integrations/connectors/source-cloudbeds/manifest.yaml @@ -0,0 +1,703 @@ +version: 5.15.0 + +type: DeclarativeSource + +description: >- + This is Cloudbeds source that ingests data from the Cloudbeds API. + + + Cloudbeds is an unified hospitality platform https://cloudbeds.com + + + In order to use this source, you must first create a cloudbeds account. Once + logged in, navigate to the API credentials page for your property by clicking + Account > Apps & Marketplace in the upper right corner. Use the menu on the + top to navigate to the API Credentials Page. Click the New Credentials button, + fill in the details and click on Create. This will create an application, then + click on the API Key and provide all the required scopes as needed. + + + You can learn more about the API here + https://hotels.cloudbeds.com/api/v1.2/docs/ + +check: + type: CheckStream + stream_names: + - guests + +definitions: + streams: + guests: + type: DeclarativeStream + name: guests + primary_key: + - guestID + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: getGuestList + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: pageNumber + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: pageSize + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/guests" + hotels: + type: DeclarativeStream + name: hotels + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: getHotels + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: pageNumber + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: pageSize + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/hotels" + rooms: + type: DeclarativeStream + name: rooms + primary_key: + - propertyID + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: getRoomBlocks + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: pageNumber + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: pageSize + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/rooms" + reservations: + type: DeclarativeStream + name: reservations + primary_key: + - reservationID + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: getReservations + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: pageNumber + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: pageSize + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/reservations" + transactions: + type: DeclarativeStream + name: transactions + primary_key: + - transactionID + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: getTransactions + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: pageNumber + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: pageSize + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/transactions" + packages: + type: DeclarativeStream + name: packages + primary_key: + - itemID + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: getItems + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - data + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: pageNumber + page_size_option: + type: RequestOption + inject_into: request_parameter + field_name: pageSize + pagination_strategy: + type: PageIncrement + start_from_page: 1 + page_size: 100 + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/packages" + base_requester: + type: HttpRequester + url_base: https://api.cloudbeds.com/api/v1.2/ + authenticator: + type: BearerAuthenticator + api_token: "{{ config[\"api_key\"] }}" + +streams: + - $ref: "#/definitions/streams/guests" + - $ref: "#/definitions/streams/hotels" + - $ref: "#/definitions/streams/rooms" + - $ref: "#/definitions/streams/reservations" + - $ref: "#/definitions/streams/transactions" + - $ref: "#/definitions/streams/packages" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - api_key + properties: + api_key: + type: string + title: API Key + airbyte_secret: true + order: 0 + additionalProperties: true + +metadata: + autoImportSchema: + guests: true + hotels: true + rooms: true + reservations: true + transactions: true + packages: true + testedStreams: + guests: + streamHash: 8c3f802465858a6249b0a34501d4cc04c681e1b5 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + hotels: + streamHash: 6e6352cd896d02b30f29b21c2a21ef6d14c27dab + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + rooms: + streamHash: d1b2ac08b13a5b601839be16f3f343e4c84239c1 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + reservations: + streamHash: c318b9390d7f2ac48fc3bf923e6d350ff572000d + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + transactions: + streamHash: c8f35dae787999ae71131884c4fffa6dc51759fe + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + packages: + streamHash: 743c759bcb3abca3ad9c48675da1e955673d5642 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + assist: + docsUrl: https://hotels.cloudbeds.com/api/v1.2/docs/ + openapiSpecUrl: https://hotels.cloudbeds.com/api/v1.2/docs/cb-v1.2-openapi-3.0.1.yaml + +schemas: + guests: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + dateCreated: + type: + - string + - "null" + dateModified: + type: + - string + - "null" + guestEmail: + type: + - string + - "null" + guestID: + type: string + guestName: + type: + - string + - "null" + isAnonymized: + type: + - boolean + - "null" + isMainGuest: + type: + - boolean + - "null" + isMerged: + type: + - boolean + - "null" + newGuestID: + type: + - string + - "null" + propertyID: + type: + - string + - "null" + reservationID: + type: + - string + - "null" + required: + - guestID + hotels: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + organizationID: + type: + - string + - "null" + propertyCurrency: + type: + - object + - "null" + properties: + currencyCode: + type: + - string + - "null" + currencyPosition: + type: + - string + - "null" + currencySymbol: + type: + - string + - "null" + propertyDescription: + type: + - string + - "null" + propertyID: + type: + - string + - "null" + propertyImage: + type: + - string + - "null" + propertyName: + type: + - string + - "null" + propertyTimezone: + type: + - string + - "null" + rooms: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + propertyID: + type: string + roomBlocks: + type: + - array + - "null" + required: + - propertyID + reservations: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + adults: + type: + - string + - "null" + balance: + type: + - number + - "null" + children: + type: + - string + - "null" + dateCreated: + type: + - string + - "null" + dateModified: + type: + - string + - "null" + endDate: + type: + - string + - "null" + guestID: + type: + - string + - "null" + guestName: + type: + - string + - "null" + origin: + type: + - string + - "null" + profileID: + type: + - string + - "null" + propertyID: + type: + - string + - "null" + reservationID: + type: string + sourceID: + type: + - string + - "null" + sourceName: + type: + - string + - "null" + startDate: + type: + - string + - "null" + status: + type: + - string + - "null" + required: + - reservationID + transactions: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + description: + type: + - string + - "null" + amount: + type: + - number + - "null" + cardType: + type: + - string + - "null" + category: + type: + - string + - "null" + currency: + type: + - string + - "null" + guestCheckIn: + type: + - string + - "null" + guestCheckOut: + type: + - string + - "null" + guestID: + type: + - string + - "null" + guestName: + type: + - string + - "null" + isDeleted: + type: + - boolean + - "null" + itemCategoryName: + type: + - string + - "null" + notes: + type: + - string + - "null" + parentTransactionID: + type: + - string + - "null" + propertyID: + type: + - string + - "null" + propertyName: + type: + - string + - "null" + quantity: + type: + - string + - "null" + reservationID: + type: + - string + - "null" + roomName: + type: + - string + - "null" + roomTypeID: + type: + - string + - "null" + roomTypeName: + type: + - string + - "null" + subReservationID: + type: + - string + - "null" + transactionCategory: + type: + - string + - "null" + transactionCode: + type: + - string + - "null" + transactionDateTime: + type: + - string + - "null" + transactionDateTimeUTC: + type: + - string + - "null" + transactionID: + type: string + transactionModifiedDateTime: + type: + - string + - "null" + transactionModifiedDateTimeUTC: + type: + - string + - "null" + transactionType: + type: + - string + - "null" + userName: + type: + - string + - "null" + required: + - transactionID + packages: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + description: + type: + - string + - "null" + categoryName: + type: + - string + - "null" + fees: + type: + - array + - "null" + grandTotal: + type: + - number + - "null" + itemCode: + type: + - string + - "null" + itemID: + type: string + itemType: + type: + - string + - "null" + name: + type: + - string + - "null" + price: + type: + - number + - "null" + priceWithoutFeesAndTaxes: + type: + - number + - "null" + sku: + type: + - string + - "null" + stockInventory: + type: + - boolean + - "null" + taxes: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + taxName: + type: + - string + - "null" + taxValue: + type: + - number + - "null" + totalFees: + type: + - number + - "null" + totalTaxes: + type: + - number + - "null" + required: + - itemID diff --git a/airbyte-integrations/connectors/source-cloudbeds/metadata.yaml b/airbyte-integrations/connectors/source-cloudbeds/metadata.yaml new file mode 100644 index 000000000000..ca89d07d4a8a --- /dev/null +++ b/airbyte-integrations/connectors/source-cloudbeds/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "api.cloudbeds.com" + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-cloudbeds + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.17.0@sha256:9c6bfd080a247b7781ce5b25687e7c44e29d31315d0bf656584b38810521bbaa + connectorSubtype: api + connectorType: source + definitionId: 021d2bd7-40de-43b8-8a93-21e8b731eceb + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-cloudbeds + githubIssueLabel: source-cloudbeds + icon: icon.svg + license: MIT + name: Cloudbeds + releaseDate: 2024-10-31 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/cloudbeds + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/cloudbeds.md b/docs/integrations/sources/cloudbeds.md new file mode 100644 index 000000000000..738b3a53ceec --- /dev/null +++ b/docs/integrations/sources/cloudbeds.md @@ -0,0 +1,35 @@ +# Cloudbeds +This is Cloudbeds source that ingests data from the Cloudbeds API. + +Cloudbeds is an unified hospitality platform https://cloudbeds.com + +In order to use this source, you must first create a cloudbeds account. Once logged in, navigate to the API credentials page for your property by clicking Account > Apps & Marketplace in the upper right corner. Use the menu on the top to navigate to the API Credentials Page. Click the New Credentials button, fill in the details and click on Create. This will create an application, then click on the API Key and provide all the required scopes as needed. + +You can learn more about the API here https://hotels.cloudbeds.com/api/v1.2/docs/ + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `api_key` | `string` | API Key. | | + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| guests | guestID | DefaultPaginator | ✅ | ❌ | +| hotels | | DefaultPaginator | ✅ | ❌ | +| rooms | propertyID | DefaultPaginator | ✅ | ❌ | +| reservations | reservationID | DefaultPaginator | ✅ | ❌ | +| transactions | transactionID | DefaultPaginator | ✅ | ❌ | +| packages | itemID | DefaultPaginator | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-10-31 | | Initial release by [@aazam-gh](https://github.com/aazam-gh) via Connector Builder | + +
From 2e5e5d43e5af53f827195ecbb45258eefc1c3b46 Mon Sep 17 00:00:00 2001 From: Biplab Bera <123734227+bishalbera@users.noreply.github.com> Date: Thu, 7 Nov 2024 20:25:13 +0530 Subject: [PATCH 19/45] source-tremendous contribution from bishalbera (#47860) Co-authored-by: Marcos Marx --- .../connectors/source-tremendous/README.md | 33 + .../acceptance-test-config.yml | 17 + .../connectors/source-tremendous/icon.svg | 276 +++++ .../source-tremendous/manifest.yaml | 954 ++++++++++++++++++ .../source-tremendous/metadata.yaml | 35 + docs/integrations/sources/tremendous.md | 34 + 6 files changed, 1349 insertions(+) create mode 100644 airbyte-integrations/connectors/source-tremendous/README.md create mode 100644 airbyte-integrations/connectors/source-tremendous/acceptance-test-config.yml create mode 100644 airbyte-integrations/connectors/source-tremendous/icon.svg create mode 100644 airbyte-integrations/connectors/source-tremendous/manifest.yaml create mode 100644 airbyte-integrations/connectors/source-tremendous/metadata.yaml create mode 100644 docs/integrations/sources/tremendous.md diff --git a/airbyte-integrations/connectors/source-tremendous/README.md b/airbyte-integrations/connectors/source-tremendous/README.md new file mode 100644 index 000000000000..5185190f0fe9 --- /dev/null +++ b/airbyte-integrations/connectors/source-tremendous/README.md @@ -0,0 +1,33 @@ +# Tremendous +This directory contains the manifest-only connector for `source-tremendous`. + +Tremendous connector enables seamless integration with Tremendous API. This connector allows organizations to automate and sync reward, incentive, and payout data, tapping into 2000+ payout methods, including ACH, gift cards, PayPal, and prepaid cards, all from a single platform. + +## Usage +There are multiple ways to use this connector: +- You can use this connector as any other connector in Airbyte Marketplace. +- You can load this connector in `pyairbyte` using `get_source`! +- You can open this connector in Connector Builder, edit it, and publish to your workspaces. + +Please refer to the manifest-only connector documentation for more details. + +## Local Development +We recommend you use the Connector Builder to edit this connector. + +But, if you want to develop this connector locally, you can use the following steps. + +### Environment Setup +You will need `airbyte-ci` installed. You can find the documentation [here](airbyte-ci). + +### Build +This will create a dev image (`source-tremendous:dev`) that you can use to test the connector locally. +```bash +airbyte-ci connectors --name=source-tremendous build +``` + +### Test +This will run the acceptance tests for the connector. +```bash +airbyte-ci connectors --name=source-tremendous test +``` + diff --git a/airbyte-integrations/connectors/source-tremendous/acceptance-test-config.yml b/airbyte-integrations/connectors/source-tremendous/acceptance-test-config.yml new file mode 100644 index 000000000000..ed2d47e4740e --- /dev/null +++ b/airbyte-integrations/connectors/source-tremendous/acceptance-test-config.yml @@ -0,0 +1,17 @@ +# 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-tremendous:dev +acceptance_tests: + spec: + tests: + - spec_path: "manifest.yaml" + connection: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + discovery: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + basic_read: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + incremental: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" + full_refresh: + bypass_reason: "This is a builder contribution, and we do not have secrets at this time" diff --git a/airbyte-integrations/connectors/source-tremendous/icon.svg b/airbyte-integrations/connectors/source-tremendous/icon.svg new file mode 100644 index 000000000000..f1458bdd973e --- /dev/null +++ b/airbyte-integrations/connectors/source-tremendous/icon.svg @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/airbyte-integrations/connectors/source-tremendous/manifest.yaml b/airbyte-integrations/connectors/source-tremendous/manifest.yaml new file mode 100644 index 000000000000..b54510f646a0 --- /dev/null +++ b/airbyte-integrations/connectors/source-tremendous/manifest.yaml @@ -0,0 +1,954 @@ +version: 5.17.0 + +type: DeclarativeSource + +description: >- + Tremendous connector enables seamless integration with Tremendous API. This + connector allows organizations to automate and sync reward, incentive, and + payout data, tapping into 2000+ payout methods, including ACH, gift cards, + PayPal, and prepaid cards, all from a single platform. + +check: + type: CheckStream + stream_names: + - orders + +definitions: + streams: + orders: + type: DeclarativeStream + name: orders + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /api/v2/orders + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - orders + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: OffsetIncrement + page_size: 100 + inject_on_first_request: false + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/orders" + products: + type: DeclarativeStream + name: products + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /api/v2/products + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - products + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + pagination_strategy: + type: OffsetIncrement + inject_on_first_request: false + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/products" + funding_sources: + type: DeclarativeStream + name: funding_sources + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /api/v2/funding_sources + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - funding_sources + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + pagination_strategy: + type: OffsetIncrement + page_size: 10 + inject_on_first_request: false + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/funding_sources" + account_members: + type: DeclarativeStream + name: account_members + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /api/v2/members + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - members + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + pagination_strategy: + type: OffsetIncrement + page_size: 10 + inject_on_first_request: false + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/account_members" + campaigns: + type: DeclarativeStream + name: campaigns + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /api/v2/campaigns + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - campaigns + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + pagination_strategy: + type: OffsetIncrement + page_size: 10 + inject_on_first_request: false + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/campaigns" + organizations: + type: DeclarativeStream + name: organizations + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /api/v2/organizations + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - organizations + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + pagination_strategy: + type: OffsetIncrement + page_size: 10 + inject_on_first_request: false + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/organizations" + balance_transactions: + type: DeclarativeStream + name: balance_transactions + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /api/v2/balance_transactions + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - transactions + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + pagination_strategy: + type: OffsetIncrement + page_size: 10 + inject_on_first_request: false + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/balance_transactions" + rewards: + type: DeclarativeStream + name: rewards + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /api/v2/rewards + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - rewards + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + pagination_strategy: + type: OffsetIncrement + page_size: 10 + inject_on_first_request: false + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/rewards" + members: + type: DeclarativeStream + name: members + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /api/v2/members + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - members + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: OffsetIncrement + page_size: 100 + inject_on_first_request: false + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/members" + invoices: + type: DeclarativeStream + name: invoices + primary_key: + - id + retriever: + type: SimpleRetriever + requester: + $ref: "#/definitions/base_requester" + path: /api/v2/invoices + http_method: GET + record_selector: + type: RecordSelector + extractor: + type: DpathExtractor + field_path: + - invoices + paginator: + type: DefaultPaginator + page_token_option: + type: RequestOption + inject_into: request_parameter + field_name: offset + page_size_option: + type: RequestOption + field_name: limit + inject_into: request_parameter + pagination_strategy: + type: OffsetIncrement + page_size: 100 + inject_on_first_request: false + schema_loader: + type: InlineSchemaLoader + schema: + $ref: "#/schemas/invoices" + base_requester: + type: HttpRequester + url_base: https://{{ config["environment"] }}.tremendous.com + authenticator: + type: BearerAuthenticator + api_token: "{{ config[\"api_key\"] }}" + +streams: + - $ref: "#/definitions/streams/orders" + - $ref: "#/definitions/streams/products" + - $ref: "#/definitions/streams/funding_sources" + - $ref: "#/definitions/streams/account_members" + - $ref: "#/definitions/streams/campaigns" + - $ref: "#/definitions/streams/organizations" + - $ref: "#/definitions/streams/balance_transactions" + - $ref: "#/definitions/streams/rewards" + - $ref: "#/definitions/streams/members" + - $ref: "#/definitions/streams/invoices" + +spec: + type: Spec + connection_specification: + type: object + $schema: http://json-schema.org/draft-07/schema# + required: + - api_key + - environment + properties: + api_key: + type: string + description: >- + API key to use. You can generate an API key through the Tremendous + dashboard under Team Settings > Developers. Save the key once you’ve + generated it. + name: api_key + order: 0 + title: API Key + airbyte_secret: true + environment: + type: string + enum: + - api + - testflight + order: 1 + title: Environment + additionalProperties: true + +metadata: + autoImportSchema: + orders: true + products: true + funding_sources: true + account_members: true + campaigns: true + organizations: true + balance_transactions: true + rewards: true + members: true + invoices: true + testedStreams: + orders: + hasRecords: true + streamHash: dd9ac3e7308d44dfed6de40252f859c99e04c1c8 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + products: + hasRecords: true + streamHash: c6cb16800a3e12c9eebc9c9fbd449f959d7ebec1 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + funding_sources: + hasRecords: true + streamHash: 06dcd4a04c1ebd97832ba7c747bfbadb3f6cdb1e + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + account_members: + hasRecords: true + streamHash: 371cc88527067daa1f8b1752d31f516a2c676da7 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + campaigns: + hasRecords: true + streamHash: 15fed638e51342240d46f6efb8f3cb8e384db2aa + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + organizations: + hasRecords: true + streamHash: c1ab194ed5a9d57adf721f9d3d66ef97702cdbd3 + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + balance_transactions: + hasRecords: true + streamHash: dae4c687de6c466303e9dae084c6249dbb9e7cab + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + rewards: + hasRecords: true + streamHash: 30f243c223daae7ffb4d35b4acfc8e6774daa11b + hasResponse: true + primaryKeysAreUnique: true + primaryKeysArePresent: true + responsesAreSuccessful: true + members: + streamHash: efe28ca3fd698fd3e3b911c2691cf3552f887a01 + hasResponse: true + responsesAreSuccessful: true + hasRecords: true + primaryKeysArePresent: true + primaryKeysAreUnique: true + invoices: + streamHash: e889a596aaf7abae9f180dfe83617f44b71acfe3 + hasResponse: true + responsesAreSuccessful: true + hasRecords: false + primaryKeysArePresent: true + primaryKeysAreUnique: true + assist: + docsUrl: https://developers.tremendous.com/docs/introduction + +schemas: + orders: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + campaign_id: + type: + - string + - "null" + channel: + type: + - string + - "null" + created_at: + type: + - string + - "null" + id: + type: string + payment: + type: + - object + - "null" + properties: + discount: + type: + - number + - "null" + fees: + type: + - number + - "null" + subtotal: + type: + - number + - "null" + total: + type: + - number + - "null" + rewards: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + created_at: + type: + - string + - "null" + delivery: + type: + - object + - "null" + properties: + method: + type: + - string + - "null" + status: + type: + - string + - "null" + id: + type: + - string + - "null" + order_id: + type: + - string + - "null" + recipient: + type: + - object + - "null" + properties: + email: + type: + - string + - "null" + name: + type: + - string + - "null" + phone: + type: + - string + - "null" + value: + type: + - object + - "null" + properties: + currency_code: + type: + - string + - "null" + denomination: + type: + - number + - "null" + status: + type: + - string + - "null" + required: + - id + products: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + description: + type: + - string + - "null" + category: + type: + - string + - "null" + countries: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + abbr: + type: + - string + - "null" + currency_codes: + type: + - array + - "null" + items: + type: + - string + - "null" + disclosure: + type: + - string + - "null" + id: + type: + - string + - "null" + images: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + type: + type: + - string + - "null" + src: + type: + - string + - "null" + name: + type: + - string + - "null" + skus: + type: + - array + - "null" + items: + type: + - object + - "null" + properties: + max: + type: + - number + - "null" + min: + type: + - number + - "null" + funding_sources: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + id: + type: string + meta: + type: + - object + - "null" + properties: + available_cents: + type: + - number + - "null" + pending_cents: + type: + - number + - "null" + method: + type: + - string + - "null" + required: + - id + account_members: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + active: + type: + - boolean + - "null" + created_at: + type: + - string + - "null" + email: + type: + - string + - "null" + id: + type: string + name: + type: + - string + - "null" + role: + type: + - string + - "null" + status: + type: + - string + - "null" + required: + - id + campaigns: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + email_style: + type: + - object + - "null" + properties: + sender_name: + type: + - string + - "null" + subject_line: + type: + - string + - "null" + id: + type: string + name: + type: + - string + - "null" + products: + type: + - array + - "null" + items: + type: + - string + - "null" + webpage_style: + type: + - object + - "null" + properties: + background_color: + type: + - string + - "null" + headline: + type: + - string + - "null" + logo_background_color: + type: + - string + - "null" + logo_image_height_px: + type: + - number + - "null" + logo_image_url: + type: + - string + - "null" + message: + type: + - string + - "null" + required: + - id + organizations: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + id: + type: string + name: + type: + - string + - "null" + status: + type: + - string + - "null" + website: + type: + - string + - "null" + required: + - id + balance_transactions: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + description: + type: + - string + - "null" + action: + type: + - string + - "null" + amount: + type: + - number + - "null" + balance: + type: + - number + - "null" + created_at: + type: + - string + - "null" + order: + type: + - object + - "null" + properties: + id: + type: + - string + - "null" + payment: + type: + - object + - "null" + properties: + discount: + type: + - number + - "null" + fees: + type: + - number + - "null" + subtotal: + type: + - number + - "null" + total: + type: + - number + - "null" + rewards: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + created_at: + type: + - string + - "null" + delivery: + type: + - object + - "null" + properties: + method: + type: + - string + - "null" + status: + type: + - string + - "null" + id: + type: string + order_id: + type: + - string + - "null" + recipient: + type: + - object + - "null" + properties: + email: + type: + - string + - "null" + name: + type: + - string + - "null" + phone: + type: + - string + - "null" + value: + type: + - object + - "null" + properties: + currency_code: + type: + - string + - "null" + denomination: + type: + - number + - "null" + required: + - id + members: + type: object + $schema: http://json-schema.org/schema# + additionalProperties: true + properties: + active: + type: + - boolean + - "null" + created_at: + type: + - string + - "null" + email: + type: + - string + - "null" + id: + type: string + name: + type: + - string + - "null" + role: + type: + - string + - "null" + status: + type: + - string + - "null" + required: + - id + invoices: + type: object + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: true + properties: {} diff --git a/airbyte-integrations/connectors/source-tremendous/metadata.yaml b/airbyte-integrations/connectors/source-tremendous/metadata.yaml new file mode 100644 index 000000000000..a03202db418b --- /dev/null +++ b/airbyte-integrations/connectors/source-tremendous/metadata.yaml @@ -0,0 +1,35 @@ +metadataSpecVersion: "1.0" +data: + allowedHosts: + hosts: + - "https:" + registryOverrides: + oss: + enabled: true + cloud: + enabled: true + remoteRegistries: + pypi: + enabled: false + packageName: airbyte-source-tremendous + connectorBuildOptions: + baseImage: docker.io/airbyte/source-declarative-manifest:5.17.0@sha256:9c6bfd080a247b7781ce5b25687e7c44e29d31315d0bf656584b38810521bbaa + connectorSubtype: api + connectorType: source + definitionId: 6a91b109-0286-40a0-801b-ac86933a44d4 + dockerImageTag: 0.0.1 + dockerRepository: airbyte/source-tremendous + githubIssueLabel: source-tremendous + icon: icon.svg + license: MIT + name: Tremendous + releaseDate: 2024-10-29 + releaseStage: alpha + supportLevel: community + documentationUrl: https://docs.airbyte.com/integrations/sources/tremendous + tags: + - language:manifest-only + - cdk:low-code + ab_internal: + ql: 100 + sl: 100 diff --git a/docs/integrations/sources/tremendous.md b/docs/integrations/sources/tremendous.md new file mode 100644 index 000000000000..4adcba6991b6 --- /dev/null +++ b/docs/integrations/sources/tremendous.md @@ -0,0 +1,34 @@ +# Tremendous +Tremendous connector enables seamless integration with Tremendous API. This connector allows organizations to automate and sync reward, incentive, and payout data, tapping into 2000+ payout methods, including ACH, gift cards, PayPal, and prepaid cards, all from a single platform. + +## Configuration + +| Input | Type | Description | Default Value | +|-------|------|-------------|---------------| +| `api_key` | `string` | API Key. API key to use. You can generate an API key through the Tremendous dashboard under Team Settings > Developers. Save the key once you’ve generated it. | | +| `environment` | `string` | Environment. | | + +## Streams +| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental | +|-------------|-------------|------------|---------------------|----------------------| +| orders | id | DefaultPaginator | ✅ | ❌ | +| products | id | DefaultPaginator | ✅ | ❌ | +| funding_sources | id | DefaultPaginator | ✅ | ❌ | +| account_members | id | DefaultPaginator | ✅ | ❌ | +| campaigns | id | DefaultPaginator | ✅ | ❌ | +| exchange_rates | | DefaultPaginator | ✅ | ❌ | +| organizations | id | DefaultPaginator | ✅ | ❌ | +| balance_transactions | | DefaultPaginator | ✅ | ❌ | +| rewards | id | DefaultPaginator | ✅ | ❌ | +| invoices | id | DefaultPaginator | ✅ | ❌ | + +## Changelog + +
+ Expand to review + +| Version | Date | Pull Request | Subject | +|------------------|-------------------|--------------|----------------| +| 0.0.1 | 2024-10-29 | | Initial release by [@bishalbera](https://github.com/bishalbera) via Connector Builder | + +
From 5ca44e5d0d2d96bb0b0113a277b9667832947755 Mon Sep 17 00:00:00 2001 From: Artem Inzhyyants <36314070+artem1205@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:04:52 +0100 Subject: [PATCH 20/45] feat(airbyte-cdk): add clear method to HttpMocker (#48399) Signed-off-by: Artem Inzhyyants --- airbyte-cdk/python/airbyte_cdk/test/mock_http/mocker.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/airbyte-cdk/python/airbyte_cdk/test/mock_http/mocker.py b/airbyte-cdk/python/airbyte_cdk/test/mock_http/mocker.py index 5287c7451d2d..4ac690dc5275 100644 --- a/airbyte-cdk/python/airbyte_cdk/test/mock_http/mocker.py +++ b/airbyte-cdk/python/airbyte_cdk/test/mock_http/mocker.py @@ -133,3 +133,7 @@ def wrapper(*args, **kwargs): # type: ignore # this is a very generic wrapper return result return wrapper + + def clear_all_matchers(self) -> None: + """Clears all stored matchers by resetting the _matchers list to an empty state.""" + self._matchers = [] From 944e01085233fdd204001f5e671f1af2cfeb40f1 Mon Sep 17 00:00:00 2001 From: artem1205 Date: Thu, 7 Nov 2024 15:09:08 +0000 Subject: [PATCH 21/45] =?UTF-8?q?=F0=9F=A4=96=20minor=20bump=20Python=20CD?= =?UTF-8?q?K=20to=20version=206.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- airbyte-cdk/python/CHANGELOG.md | 3 +++ airbyte-cdk/python/pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/airbyte-cdk/python/CHANGELOG.md b/airbyte-cdk/python/CHANGELOG.md index 90d513a24afb..38a5ce366bca 100644 --- a/airbyte-cdk/python/CHANGELOG.md +++ b/airbyte-cdk/python/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 6.3.0 + add clear method to HttpMocker + ## 6.2.0 Support multiple input datetime formats as part of the concurrent cursor diff --git a/airbyte-cdk/python/pyproject.toml b/airbyte-cdk/python/pyproject.toml index cda13ba93c7f..76e87eabf5dd 100644 --- a/airbyte-cdk/python/pyproject.toml +++ b/airbyte-cdk/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "airbyte-cdk" -version = "6.2.0" +version = "6.3.0" description = "A framework for writing Airbyte Connectors." authors = ["Airbyte "] license = "MIT" From 87f514d56adeac134f61400a70095b2a73b0bf02 Mon Sep 17 00:00:00 2001 From: artem1205 Date: Thu, 7 Nov 2024 15:15:59 +0000 Subject: [PATCH 22/45] =?UTF-8?q?=F0=9F=A4=96=20Cut=20version=206.3.0=20of?= =?UTF-8?q?=20source-declarative-manifest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../connectors/source-declarative-manifest/metadata.yaml | 2 +- .../connectors/source-declarative-manifest/poetry.lock | 8 ++++---- .../connectors/source-declarative-manifest/pyproject.toml | 4 ++-- docs/integrations/sources/low-code.md | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/airbyte-integrations/connectors/source-declarative-manifest/metadata.yaml b/airbyte-integrations/connectors/source-declarative-manifest/metadata.yaml index 53039337afab..8af16dc7b9e8 100644 --- a/airbyte-integrations/connectors/source-declarative-manifest/metadata.yaml +++ b/airbyte-integrations/connectors/source-declarative-manifest/metadata.yaml @@ -8,7 +8,7 @@ data: connectorType: source definitionId: 64a2f99c-542f-4af8-9a6f-355f1217b436 # This version should not be updated manually - it is updated by the CDK release workflow. - dockerImageTag: 6.2.1 + dockerImageTag: 6.3.0 dockerRepository: airbyte/source-declarative-manifest # This page is hidden from the docs for now, since the connector is not in any Airbyte registries. documentationUrl: https://docs.airbyte.com/integrations/sources/low-code diff --git a/airbyte-integrations/connectors/source-declarative-manifest/poetry.lock b/airbyte-integrations/connectors/source-declarative-manifest/poetry.lock index 5877e15bc13c..3b2d09724e00 100644 --- a/airbyte-integrations/connectors/source-declarative-manifest/poetry.lock +++ b/airbyte-integrations/connectors/source-declarative-manifest/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "airbyte-cdk" -version = "6.2.0" +version = "6.3.0" description = "A framework for writing Airbyte Connectors." optional = false python-versions = "<4.0,>=3.10" files = [ - {file = "airbyte_cdk-6.2.0-py3-none-any.whl", hash = "sha256:731f908577800a0c3f7f930629955ad27429cda3aed3c2436a9230f0692732fd"}, - {file = "airbyte_cdk-6.2.0.tar.gz", hash = "sha256:019e360c7e343246285b467d2a4d5aa9cb725690f818bc7056eb9e69fbdbc895"}, + {file = "airbyte_cdk-6.3.0-py3-none-any.whl", hash = "sha256:e7c0bf0798aa6e8b0ba542a001551d21523e9f02609a4576bfffe0c9ce402606"}, + {file = "airbyte_cdk-6.3.0.tar.gz", hash = "sha256:5dc996f3c3cc65e3f10a120ce7f49790a6023478f70743caf2c840edc9b38eb3"}, ] [package.dependencies] @@ -1779,4 +1779,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10,<3.12" -content-hash = "eb2117f91ca29b32f5c630abeb0f86872aef6a883540a32bb87a3a84e16d49f8" +content-hash = "a9d70525034a9d720aa7700ca3bde75cd3d8ed925f35ae452017fe4e0bcc7069" diff --git a/airbyte-integrations/connectors/source-declarative-manifest/pyproject.toml b/airbyte-integrations/connectors/source-declarative-manifest/pyproject.toml index d44f0c01b34a..f1e48fabdc58 100644 --- a/airbyte-integrations/connectors/source-declarative-manifest/pyproject.toml +++ b/airbyte-integrations/connectors/source-declarative-manifest/pyproject.toml @@ -3,7 +3,7 @@ requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.poetry] -version = "6.2.1" +version = "6.3.0" name = "source-declarative-manifest" description = "Base source implementation for low-code sources." authors = ["Airbyte "] @@ -17,7 +17,7 @@ include = "source_declarative_manifest" [tool.poetry.dependencies] python = "^3.10,<3.12" -airbyte-cdk = "6.2.0" +airbyte-cdk = "6.3.0" [tool.poetry.scripts] source-declarative-manifest = "source_declarative_manifest.run:run" diff --git a/docs/integrations/sources/low-code.md b/docs/integrations/sources/low-code.md index 273b561b3f01..19d3cbad9097 100644 --- a/docs/integrations/sources/low-code.md +++ b/docs/integrations/sources/low-code.md @@ -9,6 +9,7 @@ The changelog below is automatically updated by the `bump_version` command as pa | Version | Date | Pull Request | Subject | | :------ | :--------- | :------------------------------------------------------- | :------------------------------------------------------------------- | +| 6.3.0 | 2024-11-07 | [36501](https://github.com/airbytehq/airbyte/pull/36501) | Bump CDK version to 6.3.0 | | 6.2.1 | 2024-11-05 | [48373](https://github.com/airbytehq/airbyte/pull/48373) | Add CDK v6 support to remote manifest mode | | 6.2.0 | 2024-11-05 | [36501](https://github.com/airbytehq/airbyte/pull/36501) | Bump CDK version to 6.2.0 | | 6.1.2 | 2024-11-05 | [48344](https://github.com/airbytehq/airbyte/pull/48344) | Fix discover failing on new CDK | From 2e3023a8cf204ecbd18804f681a79a86e82d6e9b Mon Sep 17 00:00:00 2001 From: Johnny Schmidt Date: Thu, 7 Nov 2024 07:45:33 -0800 Subject: [PATCH 23/45] Bulk Load CDK: Mapper Pipeline (#48371) --- .../load/data/AirbyteValueIdentityMapper.kt | 39 +++++- .../airbyte/cdk/load/data/MapperPipeline.kt | 39 ++++++ .../cdk/load/data/SchemalessTypesToJson.kt | 3 +- .../load/data/SchemalessTypesToJsonString.kt | 4 +- .../cdk/load/data/TimeStringToInteger.kt | 3 +- .../load/data/UnionTypeToDisjointRecord.kt | 4 +- .../data/AirbyteValueIdentityMapperTest.kt | 22 ++-- .../cdk/load/data/MapperPipelineTest.kt | 117 ++++++++++++++++++ .../data/SchemalessTypesToJsonStringTest.kt | 5 +- .../load/data/SchemalessTypesToJsonTest.kt | 5 +- .../cdk/load/data/TimeStringToIntegerTest.kt | 11 +- .../cdk/load/test/util/SchemaRecordBuilder.kt | 7 +- .../cdk/load/test/util/ValueTestBuilder.kt | 9 +- 13 files changed, 223 insertions(+), 45 deletions(-) create mode 100644 airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/MapperPipeline.kt create mode 100644 airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/MapperPipelineTest.kt diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapper.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapper.kt index f72d908425fd..b10f2c340775 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapper.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapper.kt @@ -8,20 +8,47 @@ import io.airbyte.cdk.load.message.DestinationRecord import io.airbyte.protocol.models.v0.AirbyteRecordMessageMetaChange.Change import io.airbyte.protocol.models.v0.AirbyteRecordMessageMetaChange.Reason -open class AirbyteValueIdentityMapper( - val meta: DestinationRecord.Meta, -) { +interface AirbyteValueMapper { + val collectedChanges: List + + fun map( + value: AirbyteValue, + schema: AirbyteType, + path: List = emptyList(), + ): AirbyteValue +} + +/** An optimized identity mapper that just passes through. */ +class AirbyteValueNoopMapper : AirbyteValueMapper { + override val collectedChanges: List = emptyList() + + override fun map( + value: AirbyteValue, + schema: AirbyteType, + path: List, + ): AirbyteValue = value +} + +open class AirbyteValueIdentityMapper : AirbyteValueMapper { + override val collectedChanges: List + get() = changes.toList().also { changes.clear() } + + private val changes: MutableList = mutableListOf() + private fun collectFailure( path: List, reason: Reason = Reason.DESTINATION_SERIALIZATION_ERROR ) { - meta.changes.add(DestinationRecord.Change(path.joinToString("."), Change.NULLED, reason)) + val joined = path.joinToString(".") + if (changes.none { it.field == joined }) { + changes.add(DestinationRecord.Change(path.joinToString("."), Change.NULLED, reason)) + } } - fun map( + override fun map( value: AirbyteValue, schema: AirbyteType, - path: List = emptyList() + path: List, ): AirbyteValue = try { when (schema) { diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/MapperPipeline.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/MapperPipeline.kt new file mode 100644 index 000000000000..3b3b194d588c --- /dev/null +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/MapperPipeline.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Airbyte, Inc., all rights reserved. + */ + +package io.airbyte.cdk.load.data + +import io.airbyte.cdk.load.command.DestinationStream +import io.airbyte.cdk.load.message.DestinationRecord.Change + +class MapperPipeline( + inputSchema: AirbyteType, + schemaValueMapperPairs: List>, +) { + private val schemasWithMappers: List> + + val finalSchema: AirbyteType + + init { + val (schemaMappers, valueMappers) = schemaValueMapperPairs.unzip() + val schemas = + schemaMappers.runningFold(inputSchema) { schema, mapper -> mapper.map(schema) } + schemasWithMappers = schemas.zip(valueMappers) + finalSchema = schemas.last() + } + + fun map(data: AirbyteValue): Pair> { + val results = + schemasWithMappers.runningFold(data) { value, (schema, mapper) -> + mapper.map(value, schema) + } + val changesFlattened = + schemasWithMappers.flatMap { it.second.collectedChanges }.toSet().toList() + return results.last() to changesFlattened + } +} + +interface MapperPipelineFactory { + fun create(stream: DestinationStream): MapperPipeline +} diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJson.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJson.kt index 5ac72691d6d3..2b8dcc7d5169 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJson.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJson.kt @@ -5,7 +5,6 @@ package io.airbyte.cdk.load.data import io.airbyte.cdk.load.data.json.toJson -import io.airbyte.cdk.load.message.DestinationRecord import io.airbyte.cdk.load.util.serializeToString class SchemalessTypesToJson : AirbyteSchemaIdentityMapper { @@ -15,7 +14,7 @@ class SchemalessTypesToJson : AirbyteSchemaIdentityMapper { override fun mapArrayWithoutSchema(schema: ArrayTypeWithoutSchema): AirbyteType = StringType } -class SchemalessValuesToJson(meta: DestinationRecord.Meta) : AirbyteValueIdentityMapper(meta) { +class SchemalessValuesToJson : AirbyteValueIdentityMapper() { override fun mapObjectWithoutSchema( value: ObjectValue, schema: ObjectTypeWithoutSchema, diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonString.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonString.kt index 7946bdc22b9b..e4eaee2663c4 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonString.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonString.kt @@ -5,7 +5,6 @@ package io.airbyte.cdk.load.data import io.airbyte.cdk.load.data.json.toJson -import io.airbyte.cdk.load.message.DestinationRecord import io.airbyte.cdk.load.util.serializeToString class SchemalessTypesToJsonString : AirbyteSchemaIdentityMapper { @@ -15,8 +14,7 @@ class SchemalessTypesToJsonString : AirbyteSchemaIdentityMapper { override fun mapArrayWithoutSchema(schema: ArrayTypeWithoutSchema): AirbyteType = StringType } -class SchemalessValuesToJsonString(meta: DestinationRecord.Meta) : - AirbyteValueIdentityMapper(meta) { +class SchemalessValuesToJsonString : AirbyteValueIdentityMapper() { override fun mapObjectWithoutSchema( value: ObjectValue, schema: ObjectTypeWithoutSchema, diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/TimeStringToInteger.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/TimeStringToInteger.kt index 0bfe95911288..5cee5e5aa8fd 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/TimeStringToInteger.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/TimeStringToInteger.kt @@ -4,7 +4,6 @@ package io.airbyte.cdk.load.data -import io.airbyte.cdk.load.message.DestinationRecord import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime @@ -30,7 +29,7 @@ class TimeStringTypeToIntegerType : AirbyteSchemaIdentityMapper { * NOTE: To keep parity with the old avro/parquet code, we will always first try to parse the value * as with timezone, then fall back to without. But in theory we should be more strict. */ -class TimeStringToInteger(meta: DestinationRecord.Meta) : AirbyteValueIdentityMapper(meta) { +class TimeStringToInteger : AirbyteValueIdentityMapper() { companion object { private val DATE_TIME_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern( diff --git a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecord.kt b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecord.kt index c6af8baad53f..0e9f7a4c9075 100644 --- a/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecord.kt +++ b/airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/data/UnionTypeToDisjointRecord.kt @@ -4,8 +4,6 @@ package io.airbyte.cdk.load.data -import io.airbyte.cdk.load.message.DestinationRecord - class UnionTypeToDisjointRecord : AirbyteSchemaIdentityMapper { override fun mapUnion(schema: UnionType): AirbyteType { if (schema.options.size < 2) { @@ -46,7 +44,7 @@ class UnionTypeToDisjointRecord : AirbyteSchemaIdentityMapper { } } -class UnionValueToDisjointRecord(meta: DestinationRecord.Meta) : AirbyteValueIdentityMapper(meta) { +class UnionValueToDisjointRecord : AirbyteValueIdentityMapper() { override fun mapUnion( value: AirbyteValue, schema: UnionType, diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapperTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapperTest.kt index f162f8bfc630..0c9a57cb739d 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapperTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/AirbyteValueIdentityMapperTest.kt @@ -4,7 +4,6 @@ package io.airbyte.cdk.load.data -import io.airbyte.cdk.load.message.DestinationRecord import io.airbyte.cdk.load.test.util.Root import io.airbyte.cdk.load.test.util.SchemaRecordBuilder import io.airbyte.cdk.load.test.util.ValueTestBuilder @@ -39,10 +38,10 @@ class AirbyteValueIdentityMapperTest { .endRecord() .build() - val meta = DestinationRecord.Meta() - val values = AirbyteValueIdentityMapper(meta).map(inputValues, inputSchema) + val mapper = AirbyteValueIdentityMapper() + val values = mapper.map(inputValues, inputSchema) Assertions.assertEquals(expectedValues, values) - Assertions.assertTrue(meta.changes.isEmpty()) + Assertions.assertTrue(mapper.collectedChanges.isEmpty()) } @Test @@ -56,16 +55,15 @@ class AirbyteValueIdentityMapperTest { nameOverride = "bad" ) .build() - val meta = DestinationRecord.Meta() - val values = AirbyteValueIdentityMapper(meta).map(inputValues, inputSchema) as ObjectValue - Assertions.assertTrue(meta.changes.isNotEmpty()) + val mapper = AirbyteValueIdentityMapper() + val values = mapper.map(inputValues, inputSchema) as ObjectValue + val changes = mapper.collectedChanges + Assertions.assertTrue(changes.isNotEmpty()) Assertions.assertTrue(values.values["bad"] is NullValue) - Assertions.assertTrue(meta.changes[0].field == "bad") + Assertions.assertTrue(changes[0].field == "bad") + Assertions.assertTrue(changes[0].change == AirbyteRecordMessageMetaChange.Change.NULLED) Assertions.assertTrue( - meta.changes[0].change == AirbyteRecordMessageMetaChange.Change.NULLED - ) - Assertions.assertTrue( - meta.changes[0].reason == + changes[0].reason == AirbyteRecordMessageMetaChange.Reason.DESTINATION_SERIALIZATION_ERROR ) } diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/MapperPipelineTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/MapperPipelineTest.kt new file mode 100644 index 000000000000..937d80db21c7 --- /dev/null +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/MapperPipelineTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024 Airbyte, Inc., all rights reserved. + */ + +package io.airbyte.cdk.load.data + +import io.airbyte.cdk.load.test.util.Root +import io.airbyte.cdk.load.test.util.SchemaRecordBuilder +import io.airbyte.cdk.load.test.util.ValueTestBuilder +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class MapperPipelineTest { + class TurnSchemalessObjectTypesIntoIntegers : AirbyteSchemaIdentityMapper { + override fun mapObjectWithoutSchema(schema: ObjectTypeWithoutSchema): AirbyteType = + IntegerType + } + + class TurnSchemalessObjectsIntoIntegers : AirbyteValueIdentityMapper() { + override fun mapObjectWithoutSchema( + value: ObjectValue, + schema: ObjectTypeWithoutSchema, + path: List + ): AirbyteValue { + if (value.values.size == 1) { + throw IllegalStateException("Arbitrarily reject 1") + } + return IntegerValue(value.values.size.toLong()) + } + } + + class TurnIntegerTypesIntoStrings : AirbyteSchemaIdentityMapper { + override fun mapInteger(schema: IntegerType): AirbyteType = StringType + } + + class TurnIntegersIntoStrings : AirbyteValueIdentityMapper() { + override fun mapInteger(value: IntegerValue, path: List): AirbyteValue { + if (value.value == 2L) { + throw IllegalStateException("Arbitrarily reject 2") + } + return StringValue(value.value.toString()) + } + } + + private fun makePipeline(schema: AirbyteType) = + MapperPipeline( + schema, + listOf( + TurnIntegerTypesIntoStrings() to TurnIntegersIntoStrings(), + TurnSchemalessObjectTypesIntoIntegers() to TurnSchemalessObjectsIntoIntegers(), + ) + ) + + @Test + fun testSuccessfulPipeline() { + val (inputSchema, expectedSchema) = + SchemaRecordBuilder() + .with(ObjectTypeWithoutSchema, IntegerType) + .with(IntegerType, StringType) + .withRecord() + .with(IntegerType, StringType) + .with(BooleanType, BooleanType) // expect unchanged + .endRecord() + .build() + + val pipeline = makePipeline(inputSchema) + Assertions.assertEquals( + expectedSchema, + pipeline.finalSchema, + "final schema matches expected transformed schema" + ) + } + + @Test + fun testRecordMapping() { + val (inputValue, inputSchema, expectedOutput) = + ValueTestBuilder() + .with( + ObjectValue(linkedMapOf("a" to IntegerValue(1), "b" to IntegerValue(2))), + ObjectTypeWithoutSchema, + IntegerValue(2) + ) + .with(IntegerValue(1), IntegerType, StringValue("1")) + .withRecord() + .with(IntegerValue(3), IntegerType, StringValue("3")) + .with(BooleanValue(true), BooleanType, BooleanValue(true)) // expect unchanged + .endRecord() + .build() + val pipeline = makePipeline(inputSchema) + val (result, changes) = pipeline.map(inputValue) + + Assertions.assertEquals(0, changes.size, "no changes were captured") + Assertions.assertEquals(expectedOutput, result, "data was transformed as expected") + } + + @Test + fun testFailedMapping() { + val (inputValue, inputSchema, _) = + ValueTestBuilder() + .with( + ObjectValue(linkedMapOf("a" to IntegerValue(1))), + ObjectTypeWithoutSchema, + NullValue, + nullable = true + ) // fail: reject size==1 + .with(IntegerValue(1), IntegerType, StringValue("1")) + .withRecord() + .with(IntegerValue(2), IntegerType, NullValue, nullable = true) // fail: reject 2 + .with(BooleanValue(true), BooleanType, BooleanValue(true)) // expect unchanged + .endRecord() + .build() + val pipeline = makePipeline(inputSchema) + val (_, changes) = pipeline.map(inputValue) + + Assertions.assertEquals(2, changes.size, "two failures were captured") + } +} diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonStringTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonStringTest.kt index 27d5d9cf9042..eeac7504c8dc 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonStringTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonStringTest.kt @@ -5,7 +5,6 @@ package io.airbyte.cdk.load.data import io.airbyte.cdk.load.data.json.toAirbyteValue -import io.airbyte.cdk.load.message.DestinationRecord import io.airbyte.cdk.load.test.util.Root import io.airbyte.cdk.load.test.util.SchemaRecordBuilder import io.airbyte.cdk.load.test.util.ValueTestBuilder @@ -85,7 +84,7 @@ class SchemalessTypesToJsonStringTest { ArrayType(FieldType(StringType, nullable = false)) ) .build() - val mapper = SchemalessValuesToJsonString(DestinationRecord.Meta()) + val mapper = SchemalessValuesToJsonString() val output = mapper.map(inputValues, inputSchema) Assertions.assertEquals(expectedOutput, output) } @@ -120,7 +119,7 @@ class SchemalessTypesToJsonStringTest { ArrayType(FieldType(StringType, nullable = false)) ) .build() - val mapper = SchemalessValuesToJsonString(DestinationRecord.Meta()) + val mapper = SchemalessValuesToJsonString() val output = mapper.map(inputValues, inputSchema) Assertions.assertEquals(expectedOutput, output) } diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonTest.kt index 7dab6e3d4b1a..53266563a27a 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/SchemalessTypesToJsonTest.kt @@ -5,7 +5,6 @@ package io.airbyte.cdk.load.data import io.airbyte.cdk.load.data.json.toAirbyteValue -import io.airbyte.cdk.load.message.DestinationRecord import io.airbyte.cdk.load.test.util.Root import io.airbyte.cdk.load.test.util.SchemaRecordBuilder import io.airbyte.cdk.load.test.util.ValueTestBuilder @@ -85,7 +84,7 @@ class SchemalessTypesToJsonTest { ArrayType(FieldType(StringType, nullable = false)) ) .build() - val mapper = SchemalessValuesToJson(DestinationRecord.Meta()) + val mapper = SchemalessValuesToJson() val output = mapper.map(inputValues, inputSchema) Assertions.assertEquals(expectedOutput, output) } @@ -120,7 +119,7 @@ class SchemalessTypesToJsonTest { ArrayType(FieldType(StringType, nullable = false)) ) .build() - val mapper = SchemalessValuesToJson(DestinationRecord.Meta()) + val mapper = SchemalessValuesToJson() val output = mapper.map(inputValues, inputSchema) Assertions.assertEquals(expectedOutput, output) } diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/TimeStringToIntegerTest.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/TimeStringToIntegerTest.kt index 6cfc18a622fd..f17896fce9ef 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/TimeStringToIntegerTest.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/data/TimeStringToIntegerTest.kt @@ -4,7 +4,6 @@ package io.airbyte.cdk.load.data -import io.airbyte.cdk.load.message.DestinationRecord import io.airbyte.cdk.load.test.util.Root import io.airbyte.cdk.load.test.util.SchemaRecordBuilder import org.junit.jupiter.api.Assertions @@ -14,7 +13,7 @@ class TimeStringToIntegerTest { @Test fun testMapDate() { - val mapper = TimeStringToInteger(DestinationRecord.Meta()) + val mapper = TimeStringToInteger() listOf( "2021-1-1" to 18628, "2021-01-01" to 18628, @@ -65,7 +64,7 @@ class TimeStringToIntegerTest { @Test fun testMapTimestampWithTimezone() { - val mapper = TimeStringToInteger(DestinationRecord.Meta()) + val mapper = TimeStringToInteger() timestampPairs.forEach { Assertions.assertEquals( IntegerValue(it.second), @@ -77,7 +76,7 @@ class TimeStringToIntegerTest { @Test fun testMapTimestampWithoutTimezone() { - val mapper = TimeStringToInteger(DestinationRecord.Meta()) + val mapper = TimeStringToInteger() timestampPairs.forEach { Assertions.assertEquals( IntegerValue(it.second), @@ -100,7 +99,7 @@ class TimeStringToIntegerTest { @Test fun testTimeWithTimezone() { - val mapper = TimeStringToInteger(DestinationRecord.Meta()) + val mapper = TimeStringToInteger() timePairs.forEach { Assertions.assertEquals( IntegerValue(it.second), @@ -112,7 +111,7 @@ class TimeStringToIntegerTest { @Test fun testTimeWithoutTimezone() { - val mapper = TimeStringToInteger(DestinationRecord.Meta()) + val mapper = TimeStringToInteger() timePairs.forEach { Assertions.assertEquals( IntegerValue(it.second), diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/test/util/SchemaRecordBuilder.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/test/util/SchemaRecordBuilder.kt index b90cd930016e..16d1777e4be1 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/test/util/SchemaRecordBuilder.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/test/util/SchemaRecordBuilder.kt @@ -34,14 +34,15 @@ class SchemaRecordBuilder( fun with( given: AirbyteType, expected: AirbyteType = given, - nameOverride: String? = null + nameOverride: String? = null, ): SchemaRecordBuilder { return with(FieldType(given, false), FieldType(expected, false), nameOverride) } fun withRecord( nullable: Boolean = false, - nameOverride: String? = null + nameOverride: String? = null, + expectedInstead: ObjectType? = null ): SchemaRecordBuilder> { val name = nameOverride ?: UUID.randomUUID().toString() val inputRecord = ObjectType(properties = LinkedHashMap()) @@ -50,7 +51,7 @@ class SchemaRecordBuilder( expectedSchema.properties[name] = FieldType(outputRecord, nullable = nullable) return SchemaRecordBuilder( inputSchema = inputRecord, - expectedSchema = outputRecord, + expectedSchema = expectedInstead ?: outputRecord, parent = this ) } diff --git a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/test/util/ValueTestBuilder.kt b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/test/util/ValueTestBuilder.kt index 922cde30f918..3f8e0e10cd65 100644 --- a/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/test/util/ValueTestBuilder.kt +++ b/airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/test/util/ValueTestBuilder.kt @@ -6,6 +6,7 @@ package io.airbyte.cdk.load.test.util import io.airbyte.cdk.load.data.AirbyteType import io.airbyte.cdk.load.data.AirbyteValue +import io.airbyte.cdk.load.data.FieldType import io.airbyte.cdk.load.data.ObjectType import io.airbyte.cdk.load.data.ObjectValue import java.util.UUID @@ -21,12 +22,16 @@ data class ValueTestBuilder( inputValue: AirbyteValue, inputSchema: AirbyteType, expectedValue: AirbyteValue = inputValue, - nameOverride: String? = null + nameOverride: String? = null, + nullable: Boolean = false, ): ValueTestBuilder { val name = nameOverride ?: UUID.randomUUID().toString() inputValues.values[name] = inputValue expectedValues.values[name] = expectedValue - (schemaRecordBuilder as SchemaRecordBuilder<*>).with(inputSchema, nameOverride = name) + (schemaRecordBuilder as SchemaRecordBuilder<*>).with( + FieldType(inputSchema, nullable), + nameOverride = name + ) return this } From c6c1f6a2c305789c520bc07e482178ef9865948d Mon Sep 17 00:00:00 2001 From: Alexandre Cuoci Date: Thu, 7 Nov 2024 11:46:40 -0500 Subject: [PATCH 24/45] 1.2 release notes (#48127) --- .../assets/1.2-file-transfers.png | Bin 0 -> 135758 bytes docs/release_notes/assets/1.2-rbac-runner.png | Bin 0 -> 389449 bytes docs/release_notes/assets/1.2-xml-builder.png | Bin 0 -> 866477 bytes docs/release_notes/v-1.2.md | 30 ++++++++++++++++++ docusaurus/sidebars.js | 1 + 5 files changed, 31 insertions(+) create mode 100644 docs/release_notes/assets/1.2-file-transfers.png create mode 100644 docs/release_notes/assets/1.2-rbac-runner.png create mode 100644 docs/release_notes/assets/1.2-xml-builder.png create mode 100644 docs/release_notes/v-1.2.md diff --git a/docs/release_notes/assets/1.2-file-transfers.png b/docs/release_notes/assets/1.2-file-transfers.png new file mode 100644 index 0000000000000000000000000000000000000000..7dd6fa63b25b7a34ca08e63231158214ebb7d457 GIT binary patch literal 135758 zcmeFZbySqy*FQWo(gvWkC?TmJ-6CB|N_R7K4Ba6pDFTv;bk`6wv`BY%cXz|@Lht8! z?yvHD*ZbeQ*1I0onwe{^Ip;dH&)%QC_vi5bjjR|p1`!4b1j3dOe=QFJA@6`dh=u5= zz#Rf9ZzT{2OUP7M_>F|HFvS~NDtimtMkW!gZ2^TXOdrQh!~fivikLW{Nq^MlfuMdZRyTKBJqruW#T zT9cP07d#AW$RtbVR3$HmA0dXylPl171WEbDA{L2wKYxs<#}-7oU#!>oiCrIqb>yxu z`#sYS3?7(4`pDmOJpK8V!>R8Cza5KNTEOli9RKuq;Wh2<)y0PjJQ0sV0x}gcfdouP z5emH~+%@#QpZ>UA{>#lU^u zExzM@OwJ_k&!0ry6&9@U<2|8~t#Imt-I;blP90xR9Z3G2f)G;+aszZKKns$watBd^ z0%27UMDnR)Cg`WB61qcvg_@kp5aldeCd((Q{lNXy$F$h#rOtA2hLqpxqf;z` z3rh5dBzL=QkQ+x^M^Qkd$0ECDF83L)Eh(z&_9f9Ae<}&~VvUN!rJXc+k5zcf0DU z2SgxZPwzRrAA0RnK^PSj_v6uzrXPnt_bPx z5pJO>$u&y%H*b|*ytPrjBQK$RH|s%`V&;Y-SH7*%VqBYXG3sdKhD7Mo{nq|gsaEw? znT7TRraj|e>ZUJqGVfCjdPl=b}tFd zw+7%Tqj@5I#eEn9bLRNw;9l;$!`RT?&Q%NUF#hD4XpexYG?@YD(2|L-sJ*BP*z6AzUaXMj^G%p@T0vHKE*rOrz6Cf2 zya-^$Dt`ziIwF2Vj6(eW;}B6I`x@I2+YxKmOD$8kiHc@F%Q=0@m#U_=4GUbtf%SR@ ztV7S$9=fLoq(4rV@9#_przdyjcFuJMcO1p?J6ejzSHvbJt#VbeYn(LBt%?97}UP^H7Jc4ta;}#9uxZbUuhTi(iMnR!;|i-gFtK zcg#%8(G1W`n>2JCnCjFrshz5EN7nUgM(+(;yIW3@iNg?x7^oH~E2i0O-Avhx+8pzQ zQItANgMV+L#Y#3`c2rh9aqD9r5k7aeiDG}|aF=5j@3Q3bda7xvuW0w9NzSwAkm`^+ zL*I6=O4a(iVU+yNeC^4J$)}bMmV8?TThF$Xw#sA2I%t>lmYi{Y0&AN|!cxh|`6hYy z`Ho$$78hrt7t8u{Tt6H>tdGspCv%N=wyWlFbvk+OChx|6YJc+O%+@`}o%}TJeD@N1 z%BMeC%%Tc=$9$Q1L3I^)FTjhRp`5SsDG_qObZYAF9c9A3Y8> z2||uYXu*%b{g#`W{+jeXwWyegim1THNBFF)OBxJ(j*YLBa{$1nP+7VMxv+-{udg%=?hwo?`1{hd&xFzGqmGPNV@Vs!93zd|W?9z?+Oe-}I zX`aBtSgvWo%yidDdN-liOfvbYbTK1tYkcf*Yyr0Cf_fTzpZ51e6n>Ow6!LUxX};$& zc3(`?*6m@dA?X72SacU)RDNHMROj^74rf*#<02z)(#HxS~3V8M{|@PogysMGKRi=?5VOA(WA*+GO*aRoX>g zh@q?@zM(e3K7say!$5(#2kE;==y!0|cpr9qQ2Xn4OQvJZnpBMvc;Urva1s5Q3XAgR zd^eccUN$B^6F#wug0hr~Q|=w@{)!>J&EhicA;VnQ+g0t#jJ86vw}a=07l$ewC*aI+ zlhPuK`3#u`nQ{&^tNa3JcK*n#3Hids)giyUWe@!;<&sVv^-*ZSxX3t<mPe zkIYiCtYod};qcy`-q?W=56%N?yC{|ZuTMh7pyX_PYR=NjBYpFq=9`+)WFI802t4B- zb)~x;m|tq`JeCdPaj&MTO52nkiQ6~!?MmWr=60HcAD^ZY@-mauw7^HKTQ}w1bIjMj zPHTD!3@i;PdyZb(&f|yC%WUe8g=rsFyR50zm6Z&04Kq&$RrGqWUM+2^-?wy_Seo9d ziE>{aob-VuZ+BM9RcN0H&I>gOHwF%Rj$9RGH0I5}GZr*nPaP3ECfg^gg+v}NNU7{9 zjP(Z_Q+JzHCBnjvkPkJS`LCRZF?U0{tEoKAu2c{EmKN)4FRsisiHC;|CHKr`4(B+| zte~|H&f^p0gV#HnU9j$LDS;Kwk==;X-HD7Ja^3uLm#@x>mvT0T)2(!;w{RD*u!1V@ z-;(a6k|! zzeYs1ad^r{MX=#J2_qR9(9JbE2pNG0gaTY4055(7;{U!DMW6*C{ko3`0tK3a zkpC$o3w+-Eg#)jfI=?@WA_G8Zz_)wA3zCBPU!{?EQjq@Z8nF=g4J4!>EFl4WD(Kr9 z8d}cBTXN)vl~Yi?#{CnqN+CsrmaTVv)ITwGkt&smsRSQvp4 zjCRhJ_PP*8OS>n(AM#(%d2MK?Z)<98Z)#;par0bVJu3%$K1#})hW`8ScRvjwrvKBD zrQJWn0tU!@^9%C}rsvH6{cNBr@6BEAH>MCn3)R=AU|`OGHu%{&IeCAT|Ld3kY4Kk* z-~CU`7cA^A|6TQ8KmA!%$-A6NfB*PTMPBBcq5o?te$Vr-yTC;A zWAHNn_oDG*n2bxO1M5g)`dUsA_ynZv<`3Z=@J9Rl6Szir7`}Ci#SQ`qf+SuGDMAo7 zCQ%YTHIX;(l8ZTIeE-Bq5Uj@$i~-ftxl4&U@-8+r$Y&DajF7l#t6_^tE!>iGZcCt6 zb$NEQda}A2_L2CMmwEeWlxEpkprmA!y<3a?d~3RcH-k^} z!{ydT+tLbp#R$8}e*CK%@F+nWL}WO*aZV`YTaspZjag|7L zzXSpEI9)k{7QBH@)WCV}^~+w5RlkLIx2@mRou{~q(jidfyq$ZD-F|{_i<}7*NGnDb zZPB{^v@v3+KIJU}b8(EU=UcgR4pG&DxTn9IGt^w9tHUIG&O&Z#y8LSf5xE=I4k{ z#msCoJh~(DBJ7qidkLB%V_;x_!>3n|L9PV_ zN;!(3gVS>u?*F-Ge@$%Vy?4e{gG&Wk#27m-JJhwm*at$G$kjbjJ3A=1C%D}Q3oznv zb{c3NWa-GQ$!b-*D5*{QZ{OR$ZB@AL_Y6VkRhGu_3Lqp(T$@|R4%*+ga(qBcwXc4{$vbwa}E`9FojNeg*uhh7B>okE7 zn30w9I|#A&`bIIBffeB~T`2RZ%=FRO2_|C89`ox9;d%A=Hi^SSzhlx>rYGWoH)ATr z+wZnHJp_qZp7&>$GaWKCo&Cro<#@a2^G?80nCA99FNV^XR z*ibDh1`oz;NoOjTJZ5)fV@%h1!2V)hgv*vC=(m{8Ivxbx_$~~+hN0On5PtK|~3N*%5?^ z$SXkqBu(^|A=A>~;q0_=hl_L}@osXfzrl0HA!FPLp0fCS3+zBYp_xI1!{HcE`aCV6 z@Az5^YH$eZ)YkGE=!#qJ;+8dIZ5H9+I%{WVm(2b;RDM z`!UGfb^A^nzZ?n|Y}-x9MYI9}vP+ zfOSlz=xxhlg@V2VuXcDqa!ZeNgz+fq#__gThJ$XIzL`7_LQ;8osLyR0)dC&BUBnHV zOGLM65;#6Z-Tu+gTkBhT#=Y##O_x7S;a~Zl*`1dD8e1wyrQnN#c^%`^+z$uw_#nv7USg0cN>qP zQ&p)Xt{WbNa<;pUiwo%7Zyv9Ci% zE+IP4YTr5eLuxd#5%48yQ^(~&zrm}cH3}Pili+gj!uxB@s69h%`(Dz$kyK{1 zabvnV*!I-2;Xo$MhBIoS!*?ta$ZPAa9V`T`cfNnwnqp>Pe%$vnSpyo$u-{Z!^gnx7 z3)y9fV>3^JUr!D1H4zG+wVMy+GRg6|SDWO(?d4h#dHIH!1>}k{CJg$aN6}UyC=JJo z!)6>~xB^uwV^Hv0`}tU^_Ro2rz~x**pP98E<+dt%akv~rvCIXCZ8`^FacOlC+iLv3 z=~5CJ83{ES30`f^;_>wLv&*gu*>u@N@Uhi&O&a{_=R=8OoqJ6vkhlKJG8*1Hd=A71 z>(lB?wHJ!V?6d2sVbY&Mm}^+g_z<)nh!t6_yi#YlOZb z#IaWKR87HCHlM z7v||E^gljwOo_9Pe*gH=eCYWWv}$ME%CmfY?IM)Nl1V8?IU<^Vf6*7WCL+0$b@6Dl zJjbHu^d}VME4}&pkW3UbSgI`7rEBLwO@^g2fIt@83&_eAZQJLnx4>Hk=8G#s8TV$Q zf2;?RUwuNs{2T;8j`O_!qj4)b;Q%tL<@lGTI@O!R`;2cf;g49CT41uLahBqO zWWEeI!5*BSb#m0UYA;=_;ija^tV?Ccg1>dCP9S_{P-RMX+w(Pfq*iM3RI|z+V+{=m zDY*O@QrMC40x>r`F_0WR0POl(ZD+&C1V^(4XEP!}nX`rZ3YF)EZPT{9B2I1R+n$;R zmjvpiF?Ts|ioc3&v>6~wwM5J>e2xek*JP?$FnIKGMV#llN0_;-P-+{x53`?E`x#ct zbW~V>P6|6J`x?UZkjJuOqjK4?o;%TbIg#1$MCrKQs#YdME#Jz8tT;!*VepZS7(RBO zbh6u73jAn{#&)s{SIXnc%r(J0f0Nn${OFUlYKSYlrj214TIfEbo1D{>A^qt|>h^|m z`J`hKiOs^4%~24;*~NbHLZNVXxmA_JILkv$N>wivqJsNp8jr1NFGU(`O&*oyT4Ic^ zQ4r88FSMtyT75F@zp{xRQOQ#Y`&dn98Xl7i9|xDe*Rb0Jjhf})yluv=RV~jKw5&1B z<=v_C%e4Gi)%l`%gNEY+r$QI!B{ymjdg`AJ@k})kfe5wI<=l{D0IrUU`cqJs^t*QHGw-z3y$G(8(BR{NK|GG!$1&zPF$gFEcThw@eF@#&XZ zho?V82Vk#@lB!IbK8D%CoZDrPpbJ!y!jjgaQ4HU?y1@CP-&41Ub9(&u|$1(P;pxJwQWO27m z%^;Ute?Dt8zQ*Es z?c?)`Sj(I<-h_Lja8jnOA{fMc7q9&f5CeZeQP;i;#th`kLWZbKR38)3)?Tm-es=ru z(OAUadJKFJ^n~f&rhHa^Js+%nHgKD2(s8W8QDSgMzhh;W{Y^IDKEe05H2aDT@5Ayu z=w-Yz&J*4GtexjS$~h=o!dDr%u1;2T-o$9Tb_&}rC(5noCV2eRwOx$P7i3+5P7eN({qhgoVxjyl+C1b}j+YL&?VIHShJ;O@ z+2ES+Q()SUt6__K?OS)$Ue)X_Z5EFjZ3b_oKVEShPof13Yy>;_%jkKr4NS`s20_qL zg&%0-C@;n{mCpP2T86PHTjF6JVg!EC9UpTr8)GUlPIC4uW2B`GAJ)43F{MICm)u(r zqhZrz`gZtay+8rH{%Z3qTVZ?3b*!p#6B^$VNbd2Zo9{Ruez>&kmO`f-J*UFX0{|tz% zdZVt5{m|NX#o4~9&pwUs@<4bK?tvL7J$AMZm|tRt3msurR^5hkmXvFBm`OpkQ+P8O zj0HZ{wOzcw`fW1Y!DaKXm;Gt6mHY`oO$O1-szaj3SeautKMe`(6HzQW+fT49H_`k` z>ORu5K3(_DGE!>r`0IRTEiV;iW=26%(XZf%iX^aZhHs`{#`%`}6ms$LsK^;sgK;wK z1zfFa+qPkFFdOVs~-Y`LMGYMZufOFL=VGiu+iSM&BL+YefNLrfd2t}w( zv!;XDgSK^7zP>Ks*8wN=)t_;m6S^C-$U}(ZDi3(X`0-Gq{?#8*IFaiGx77+^A`O7za7CfJ37A?efNDGe(q(>eV^xh&gxAd+`{0zYEF5 z4T6)@+FQ;(XTB$;_-__|~s3tTf4 z<~7!woxR9X%5kwq$1ZSufe_>9JhZo>L+Bvwd=CX+GW12oP}fPl{E`nPv8c6SCN4F@ ztkCvwr+Y}d*v{Q9+R$&z zWp=Ed$UY_>W>cOAyWS##yB$xG+s+^uqix^WLBufnBeudz(4ypJoa9@UUdW(L?f2~E zqzGmw(jjIHv$j%&#c12B`x^LPBGfRKJJl^%?g~(|nh5EJEb(m&(-*iU$bT)UUaHq2 z?_Ya1vRu;bf%Tc(bHcEG55kIcHjQV&W4Dx`bDwqQRpCX2ejrM_!_rcGGwS-!50A*p zWT&x6ZPg2}-qUD>sChOXh9R%(9anD-U8Af^mk(xM>P^SabXuDGz%!Ksk87TD20Ti6 zY^Dk4SWaT48@XD|m<}CV_(@ETIW%b_wzv0s^JF`IhmXzIW+V+zT34~S0tfN~mPR^( zGj7H>@ZNk@?{|f{QZf8*$1@tGa3SO1^+~ZQUZypYSq|Ot_xA-b3WC2(>z+sXumr9T zWU7uKl^-#0+zfkiw;uia&is}wv zI8h;Emv&ndEwm|46VBf5v9)Bf$?h55b@Zz*_db^_#_AuHz@hViZ2!(p+fWjW8Zg)1WQUJxQtp$sG^BHPrUJRB+8) z)~=9n9MNTUy}E&i^;#9?HpL)jQZ2P03lzxg-jql4XcsdX*vL}L9ER>ffEvLU%)gc8rvF+Z>>eUlV$N;J^x92!` zx%IBt78vOfwvl}>VU31uYvNu@V}v({{p*n@p(E{<>JuDbL%=6rRhwBYDu5k5uAC2T z$*gdic8@sVHBh;OCSE?}l0@P>Fx7;g+H$tThm8hFH!n!&S;l}><2+|sI{KB?B%qY=JbA*-J)RqV*k8_YdN3XzKIFToo6{geIaYt zYLtpwvM3ak8--K#&9`9X0nX8ePpsAK+b=-K-x~5qc4{W=7cf=-1x#TV#jXq9Xw)O} z+t6`#qtZchA9{3Lf;V642@eDfv3IUc^aD0()CJVj-H#{Vl6frH+GxtNS)4D8+c9|6SWLfa#bj_7E`-nx6swwsTzo!=EVGTAeUVK7G} z+NzeA(5VmwCLC!s>q>;PQ#v@!#FI?N{ZK5LE_$(Fj{MVQrxzws+fe3PnHm;fm5Tb8 zfZGR{gE?dRy8dv*M)^b>K6{SJ{d{o_a~Q?axDa0#KIwPi4A<@IcR1V?utbh-(NF6I zt|MK|p$Qg*SLZUYcTOhL`u9vyGD~>-nRkV`wm}JT7*bN3Ety?C+0SlsN8TmELUd ztX&P<@0auZB%7!v<%`t`d3>3(akjAjh9 zx9Z?Qp#%OE`w5%#b{P&_=>8d_s#gpSGoKhIfV0|GOBUy3vc{-gP_;N|^Hd6;aD7n< zM@Ig}<^Enz+?NiC);c2?c6Mdp`ye*{_0=Xsx;6b+a{%KHxj){p;e|vg^ge)M+tb>r2cL}; z2)*MCeD>ikvznH_y08mK^gQULH{5o2EZi@e#(Bxus}Vc6?1yCdBS@$ye#D!}{y9=& zso`g6kF7%Xg?z*s}FDneufe=xo%Y^j_1BE0A!7LrZc`=96G)AJ;gtkXNSIE z%BXvL3fN@!@x?ByFG_Q=9aRTS|3QX*UCka+gY3X?nt8r2J$hp&kjS96YXbKVurc`kjsvt#Q<->54W1Ll_Kr;af0H8n3e-6xC}!dV)>E8W;M zVLY908?zdanfFtV23&1l{D@&T(W$01haGT9Nc566q4TopBX3;eJk(r_8KNGQqy67hK za5nRy(VvoIna`M+yU#Ic00)y1E0C`VUj*r6y)ikMd@eaR`M53m)OO%)OpD4j6e4Lq zLB{7uK-#37+`Nt!i0bzra!3g7d6ij`i(zGevz$7aes5uv7 z$_&IgfaRBh^vExx4;A9_K|kz~5fX(Hob~K5l0Kiavzl=ifWtpAcc^8~rULY~>So`1 zO%~4~@=oS6Xl?P%-c_G09@&BeTNrcaV1%fL)TQMOadD+xQ;k`O4Cz-6Xh`X}FyxX&wBNkZNp2z^7yrS5eB+)K zS&!UTf8eh5u4D;&v!n)x*V=fWp;9x%KJepYbM>&LA9vO2$*xr!kmj4z8_&)Kf@duD zCgeP8W`%RparP*X`_yn|q_Z$|oa;UM@?ghRM$0~?PpP7OOa(P(@7)Q$jBA}2Ij#2f z26(Ak^iDD2XenUF`H;S1l@qvxCh$QF3KtGJevT06)%)D*0s_o%kQ{D?Cg1g$dHUt! zz4wngD38!2=m3?PQL0r{AbZwm@dG<-Wa#7B#_PmV`Xa|w2Zd}X5M6ZPW!z?b(j0fr zBQcamZ=HcxU3)fB|5!LIihwbgf1GKgr^95u>1{bLQ|&cB-@DvoeA3yYg&MT>%H>cx z4g{??f`2*!eM~8{6Jup4>1C5dt=-xR3pPsmgegaO1GTXAfvmVA7K3#!WXNb_SFiI+ zbRe<~T@B#r#!>q^iNnV5Y`ecNjo#qwz|&xB!rw9FBjCEnR7nAR>1-tZgDnT|$=uS^ zV*;m8=>oaH7=B?{bt|aF=+kT^BkH!A0lzkdj{S|&ct}Y)_IHPPBO90>4R^WYi)4QH z(2j%`VrsEWCE$)L=|9ps#L&96L36{V;Ar*^yL-`erc!Xk^E7AiYL~JJx&o~`JMb#a z2kiF|0iT+g+a*i3s2jQC$0;ZEe_|yd>TMva19+ZLj&JN&Jrb~fGbEZsH|$;XosG@; z@c`-+ay|FZ6vyTPAu9X6^ipy;mpZ88L=SI;)}LYhD=EbFp}eT3kQlW3L>!B+j0g66 zMLdiMQ=VClwv5m2thAKyY}#kLUnPXbu2ZW`bKUui6|$o(NW6WMnGJy8(~F9JbLiF| z7UeDKPG#;^k@iwa+Qu<#52~9FtEFDXB_(yRAf-CqYnO|bOjl+6!+HVZy5K-QICbJS zNkw&1%2s60+Zh_c?ALMzk_*vrxOkz;RKaIZ0et##Ta2T3`P79*lKTqSc51IJ&)%5r5V|l72?6fMEXATy_V>&`y36JaO13CODZ%a9S*A@guGn+{K zQMvm~jIW&u&eia{icP4s$ zTzK{m_x#t_zGsCx_4mdxx=BlbP-b7DuI4dRqXvkhw~xR2dRqmS$0Ar3 zyfl@cu4arWK!z;tLEq5cHgak7ie8h(X3Mcsp%ZXrJWUa9jrtqoe=eATIs529*0)81gAZsGv$&(F6l6S1F{ zk=XtcV{nx+A{;%?$b!2v_algTuEFWHE;3-4aIzV9HDt2h1DZwO`Ie1hb=%Nwsdl;r zFu%zHJdRn4x{z!F_nz8ZPXwjLwd>Je+C3^HbP4g(9QuiQ0Qoe^e@E&a3({6-2FGJo6ewZ-#Q!yK`%mg zB%lx5Ms>oxJ7N5{m_0BpjOV=13gMJtJ~ zQtPjHDnu+3|B(~_ubYy*>p0(OtoKn+P>L-z7jkqPxvBV#*qwIrz8Ulf*xj-eaSwJF zV;YCWlReYvq*c&gCob3if$R;n=2Hzc0=?XTg42w99k~DxzfMI zQgG~dnLaHPd7czt9?Ej`=>Kjp)2|ymbOMjP1tH${5 zCVQ3z&z2{NTL5Mc#Xlc}AT{ z*efQLdr@7a_3r(QTj*4MOux$<(S?s8xaZJjY2vHjA~TEHb>$iXAZ9U#Hef=ZP|N-i z7`TCq-!*@i*;Jw^LpoD_D-WZC-a__ktNll?);|5OVEs$5?mYuCE{5&OEy4IXPX#>7 z023_=08()WC+J`L(A$>s4G@A-%-2xOR?eejGgFLGD>D<<($eC3ra2lsd$a5+&~weT zYG6s5G&2JnFbSC=060ZZ{_Jmlcb$TFm4ms|jhW6|UZon*JO3@rP(YX?UvwtI1PK`4 zkyyx=7&-J706Y+Y!V#UO*Axs;JN}ZoI$UpnG1m&fNrS;`H`N-qv`bqmtDcnnmzEQC z)rVVyzr{$YqdfRtn1uRdXA)nxN4SG~wTpuVM!{FoTd-F-^2?6?DW?zu#v;OWY^N`5 z<_m`AyuUSy`)6S97D>~qXl?%!0=D}BT-cRJ!q>eyF$ zmlwdLZxyy#{!vcuLBOMQslm=1?q>|dQ-k@M3P1wu37I)gWoZUI8{yyg@8%%o{a zmKaTwr^a@iO_lUpUGl#HHstgEfh^DQPl-EBxeb3+CrNoDhot^X&!m-irMtbHrVG}JbxcfTF-))iIg}UgI$X=Q z7~dH1mr@BY_Ts;9HCC$Xcn$?~?0uup`_kXp?Z(3mo~$a{Y$;v5eD#m+Oa&$lFg<(c+X{KgFh|BT$>=t&$oao@>6mon~1l?9d*MBQWwQY3~nlmyI*dWLMJ96In=DeZ9D;r}#tvD>V8 zliugHH(|U%;SofC)dqR*(G^-wa-YtJnNoAKu$~IV@2N?vGyN7|C7Nlvwt`w&sC*(% zd3(ge+p06oNO}CD$-e*{-CabAO&xFj+s4$PpG1=XHbg7)fXe^>Rl#S>hoLdHo&b7+@_Y5CcNQ~l zmqt&B60>0^S*b`aQNmX3_KdnNXgI~zhdM(n8hwiIra?C*Vf-GmdiM|4N7Z6=#1VY1lbn!;+@c?sd(ut2=Mf=i=vA<9^ zqs7ZR(74*IRTgq)I@~5k?rzNCyZ{^{4|vAi@D8%slp*1GaffsMNN^y^*BV6JjnS4e38RrqdL|IZ)K*Z# zEdc;8y@NrQ~qU#z9bVs+?WU~`*+{fD56eOdH~=76kKnnANW9gtq)>rTUR>+^>ips9hfjdmzRiTWA`iHX13V@mucKz^ur{Fv za4Ykk5}(Wfyh=jwZf2Atd7f5v_KjWeI5RDFyPhOJIy>Y9IE=xrvYN3Bb@AMWpQRJo z$G67wH8EKqO7#0R1DSVi&%>NYZx+b2RBEAI#$x*yjZ5-hU+wzhm`JfgNqwy}A?u@)C!0?D5@p71AHe2$D0f}2dlCd_%DMg}XNV83Q1LF>mc&xg- zzB;bL;$mi@ZTW|v6vrbSG29w;&CYmy6{%XXEEPiVv2%tpPbP7Sb`dn9nc=dwth=7a z(?Ez+4ln@$084X{Kv?amJ#c79=#u5hiWsc=m>KFTTZh>+t4*4@F3(PgqIYAEi8 zcDnS%TNk)PrEWF&*6f;RMl2ga3*bPWF2-aT34X4%nCuz)sD|dCL;N3SV`-}e)AxM) zVrZ|Der?X(ZT+7y05D`ZM9`?Pe%}$tI_)Q0)HOw8SJmzYr5P=B2T-GDM@6`5 zM}i7GuZb6$?vRhzYS@9VE0EMC?2b!pn@Q!`FHS>J+j5Y|6HHN~AM&2ij#F`&r8)mY zqmHnwB6ounm>f?k$W@E#t*3@Exi(_^es5|Yp*{j+(6MqW(<1>b94>?MOovU5Eh?9( z*ZqvCT@k5aQ{^pdecXgxZ%l+F5aH9F$xN;rQ`1|MDMF;DSAjII#DY6ydy==!XBq(ymr(L`4p!`jtpK>S1T#u)O^I;x3eb&H#6M4xfrhiMsDv&mvOr0~`aqM3N)ENxEw?IgucdPBdz z#5N8_jhtN_dMno=!|@TDIWic3|C>)Na5TyGn2#_dK){kt#!H33*l7w6eLF?DREoy-CBJ|0jF!IsAgFWJO;aL|Hs}fR0eZR2H z0}d`y*vYSbV(%+nYTExQTPfsfo}aWUi$ovre&e_gw?E)~dkbukbsU=Zj<^V>(t>J! zwNm`(_)^0u(ETcW0s_fY(L;l9Q*ik?*?2It>+EEHd*SGX+$fWGmHTuJpNRU7e+H1a z*94c1&))&~gy);(Q7=v2sZ2rU6;7w`TLnSa`XpciHRn^4-@51R6gcglnR+?3QaQCf z!Idiw?6XXwIp%wJG-`~dT4Rxh%?MDtW%`M1D>V!9pl7kgl@c(Xt=elHRr9I~vmgi2 zTCMa6o;GOu_y))H1-0DOw#@jVV)^iAMS*J;tx}X&*%Fy@`AvdsO%t{U{iZ+?+Xyd& zaRcrcKj1#0sBG+%@M|fP-1e}!wkOm#br00YT~799!p9;v0M2c(ri8=nzc{zAu3l<} zXd}n?{`{~11>m}Q{}HPTJdfT5ybX6%i4c1Jl9r5fMieG|cNCurz8s9Z_%mvGm-WlK^cO?{JhtL}B5Cs$*!9(< zMSBg$)P*z|`Ej;sC#;7|*jF!+TF#k2dvRuEJn3Z~0(sfGBa7^Ef@`il8&aFedPn)x z*+l7_YaN*|$3x(-Npaw*#fyJO)dX9SS)$25ubhQC2$q{rFVHJ?cR(jZ3g4|7VOwreQrdE;pM=3P5UZ)N+#ee|DsLj=g&$c z(>slPT^?URK^GzOFlP3G#CYn}NV^VyQCnQy$ zp4RmjimMZ@eP=Ava#K&+`d9j}~I+n5HQoU*@r`wkppJ)&rxC*e5VYfyN;kP0#(75-@H0zGdH#rR&y< zI)0kBNKoKp5HYX0?0RcHEJ+thY0{%(Q&{E%nlV0DW9B|#-i=VtC!wreT4VNT&cX2N z>RezEbbqR9^@yh{MyvYB+G;b$kprNsXr}2S!I5w|)oWP0T?SSGx@#2mi*7!*{g2%S z6?b+^wamg}O$r~mf@=O_wKTk?7tw6wlmq;K*+;{QQNz)}U+PsSJygBVHEPw?4+)+W zxIu)E#h_l*;JDh4cDzn;-;&7fsCUU(o2IJV(O!)-O*un~X_FqHSHCV~;=ONK#%3|{ zj74W=wtmgptNg#H%DI@`A6WC^m^eK@XnseSp)U|cmG41G!`_iyvGa#t9uXeJ4r}q$ zL2F8^UQNIJ0k;_8t1r0(Na?wmL!MPxmZ=IA^jVrZHLLr*H`GA)oO3cyH~aElMMPdw zl#1N0eBNEHMuepN;ruU_eab%l>0Ru3Xc;Z8+wP1~*WiztjE}Wrt?hbzWYNe*&D!TJ z%tf$WJfCZt-{&joEL#~+hSXkO+0QrvtoQb^VO|3{_n!L6#VJ>2$EF~cL4qjm6hGOF zWRJ{EoSc3ApC4j~^F3c1gWZREqvvIqYcGm6+4I(c(@%!#_Enn=4id0c*#yqEykHkn zo5oZY;1rRgYK1y@Ky>LNe~26sCr1-1WZc5er{l$>iG$0W=WvT93_Q=ii2rXVvF~a4 zYHdNkVh3fn&+;S4UDacb^8ZSt)iy{y+s66s*$#8QoL5nwT~PsCHc97&jSW#RLK`M# z@(t;jCnPgz@{L^_NL(hzH9Phly)kWQ#m zI(YA8`aCm2>IC@^`)Ux@;g>J#R1x64RsrvYQNOfjEDXMDH~|%qSN3TP#UEkp&Oe;M)0#r;qb-kT zkD#{t+xM^*Nh*D!rOIG3fcf!BOQsY=8^LEcAF3E6OXiFMs94h!(d?teAZCj)Rgj3V2vKvY+( z!4cyk(aQ;<(r`p~5Sdu*hI?L;5!EGuYsR8uxQx3Bv`eIz;){E@h6F{k_itvb8Rveq z?>f#XoG~4#^CeL=nu4z#zRc>`b?+1az^`N#P+Epp_ADV4FZ*eJ?g>KpFQ9K` z(Yq>zY^lYqtEFC%?PhGs_nMBkmIZZ=oe!%gF#LY_!^OAXf!Q>)V1s?4DLgbqY8=5Z z3dg4DY?0oj_TKHwse97+@QO=D$hNuDSrSu*aIzg*u3B}pI!Mx;Dn+^t&DdQ}Z^#h5qE&}1#Jz)HvR9A=o+HHtU#l2|DQ7<9ifslcV->v?ke&L- z9@X(miV;Oeb96Mvg9pf#&>{_p)J%eTDA!Vf)FRpqHHOCXq6C0v1u{q=6&LZnz791^ z7e*UjZI@gWAhM+xB}LnL&Fuh{%ye3Cl5(TZG{+F-KDaH?$ODZzTeL0*r+p)5=nznXcV^#)M8i^_IS*c?smgjTsk9{ z56TC~%VRSj*(h&c>%1?%gz9LXWqvupt<{BZAQQ>Ncl-WD!*o&8S);gMN_FRdA?_)$ z@65DZ4yUQ=x0xQCv68F(G1I#w(8+DuHBUY#+8Ak!lYF9`#CD>v*5K`T{p0N<2 zklXRAsPJDOi)_NaU8lYTz=l&c8%IaJ8qU=lN2qoIB=wZ`E_44`9RA>XXTS%8iioV= zfkIZ(r2fq4pFE6j!R>H`V9m+M$-ukkGk!mFOSF#R8F8cij%sv`f^vQi9k74P-Xb#N z!(42U!_fxjb$gBYD?N0(RTh`0J8ve{i8jOG6Oc)v!04Wprkm&is;S{Hkv4hG<}Fjz z;s(TwnN^932=%zEnB7RU%FM@J$?lIadRwhUkFhb8f2K64vVIuFbHSCgJg+yNR)9!J zUS6?+LgJ;mh3R;=hmO4NW!Kze8m7tIw6s?|(3F3)Us)BeS>ncL8(_weCs4vCZ`tBW z2@WyBpAZg)w{s`mkzuG}i%HlX`kJ5be`4y+ikZ_to7VR0Ux_mm5gg}mcnj;mRJ4NG zq3Ew`fXfR=oi~Jt95;zzT55~!F2!jfIxgLmVc*}K&QTUH?Oq`WNeLY9N+0)+`uH%T zb*XtnlLsZskdi~-mjI(KOKiL+y@*UV$D;8<@(J9kSVO&Y43gsR@7+=c{qIiSa|}IN z{?D!*BSi;yofv^ubN*j1ZGp>+yXz>z+5fUOGM~JQ|KO z#(wc;uUlfF8xhiOKgs)ZQ;IRxgE^8Y5wm<8lo%S}MWYj5on#J9dM`{y19oMPwmCo@ zYL0amYh}#vXzJ~B-qR_?J33s_-jK92sv?5^HNZXe`n@v<)|al z8~`~CKag%T4t@VuDjJBP1PmY)q9lg4s<>L#!;`e(>UxmPJdq91J7^*to+2 zqOGHyWFTr0Ql~PFOpghU`H(08FowdjaYM*#Qt&xR2aR=DAMv;ybVh(PwwWN3%+N_n z!!;vqD0>7f!zx1 zZ1;&Oa$V9LF82MwFS;EylA)zV5k`js$N$7Dc;KKi$B|_!dq=V#-Mv-uO>nk5InW89 z;{Q(gvM-r;@&U?S-PmoLLd4d~2w+@K@pzRehBNenX`}8*55Cp;mBEYvQ}1-!t_+9r zHS*`jwzUV*sg@Rh*u|7hXYXh-pafRiAiXT-bm*>^twtYdY9~p9gsvl zv3&P!f@j(|he7War7jLn-EMVUiMktV0eUsFBTH?Tl?mf$D@qFYVFaon2bBsb;N4MC zH8eO)&1F0wId!-ir9>r!NpE=u!xtU=WM2h^R|D8yDxeKe1!|g{NRG z*B-2$m@M8_JyZ29c1r_S-3A~Vd{E_uRh-+7!7}>R4XKZa+c!sV!?5_T6q6mVsur9r z8culLALcf+3H`U|librTJ-45&oAPtNem9R-)e`0~0iZ+N&AqypMOMoX*02t{j><;p zG>?ri@aUs!ZSeIdIVISdfl8PUCIM^xuyNPV<+~hK2v-vgvN3<_o9d%J*5f{JgpmOi z#(Fc-%0RrZX>)4(WpcV=y_4IUgyIt<4&IEm_y)MMOp~~UO*urWXB$j7Z7y(g@SUg< zz^QQPnaQyE&x7oa;H~LriV?gh(i&KN8$5B$t9!Yhf@(oVVG}|6Rv}!DeF;E)8z$MN zj%jC?Z|K<(N)02!W!+*ywey}V0pOY8lDrZ_cAZK1f>jGP&?e4B6oR*31^0uv0Ft)z zlr{t)`V0m`ZUG`AJ)oGtXbj@1F?6WiDh>>=EtAY@tg+sxO4iwJ!mxr_pXI-*0K@C| zJ)veVb9#ZWyK!>&mk1FPz8JjRadb0>jCST@h1{yxfnvpfiSUF!)Z^m+Yz=>2bokqY z4(~|Lcl>EF#V~3xy5EuzWR*)M;AJOhjUQ`wz7GeV$8)hgU#c`3{#7}fM_^cR%pk_r zJj!BHExN;(C*L1GZbrw2nbSH-WZ_EdFneRWU(Zo31$n<~rkbPSG`?w-;*a-aUz2t4 z*|+I(|Hq^}a(p-EkW`m+U%UqA9Y%Z&+i+w5@gWi}M@b;yRa<|O%8*vMmBaVRY^|@> zyliKtb~Tzr5MsSsVKMpD$x#?>@`dX}e>%#O8pg&tw=-CERZ-iV^Cd>D=@iU9#*ty) z)(3n}Y&}UdBMi z9KItN2uUc}t-+VKSqw`val=l2Pj;}!!{32<5SnT|#`_ojlTD}vcDGVVGoMT5Tz>FX zyq(UJ$`A=^y{5(bs^e9D1jU@xc(e|9q2UTKo0$yE+z#dpUISbK!-nJ(uK!UAPW^J%UlUWv z<>bD&gYUH0`cEsb0A1pa-(6yV-)9Iv+XBV@Wj+~xk~3k5vQ!FRFOY?)u>tKR8WO+$ z?ssbg8Wb%3-_d%bOWrc9s_^v=-#+7R#Axg5jBd zI&OmIR^*!s!IZWfSSKtOoEJH4mSLw^q5ui|cD8xCqn53HJNxdxuZ(o$=`O5*`JcJwLqN{4JdP|guW}0D{_XJ7F6uP_MP# zPvikL!hns~1|yzc1DLkhFfoj6*q9j+dtuDMut9~4U5{V={T$oLDDH|Wq z{+E^TFjE}BJqU>Q?)@&2j&LVt?*LxD_U0a=X1zouThf+d0^Dq9w|vH=#2luYx7%v} zKT8MXP{lmd*U|FupuaDQaqfh*9UzljT}7#JJ|VnAr#(76)IWibXSc&HiUanE4G-b(pDJ2JmTc|Qv@$6WpC-23#z_)7vWxY6|VE#yo$Vh-Dw8^3wM5qM1PsI`0!umE2H|){*Fv^G4BF!A^+yQOf{rA`h z|BPk+(o?<-|1&n)rS*SZ1#sTj{~238-Yd7a^Jgras5Ru@^BVj!mRYHPgrvDTV5t|a z)YieP;osIvg50=*_6ItWo0P@r`{w|?F-L{`kKz-+1c<-y+Ku(w)-vQvz^fQ9|HvGld?un%>wY#TC? zX$;zgxeP`YF96yz5kUXx)#Wx+fh$(4v3KiB{5QIi$&a;i*j=lG{bORvjnzFJO?FK; zTnbX$C#us%86C{_+X`}d)>((aA2-9%XC3yA0A_;6okEUWnU3a&nN5}T9iP`{eLiBb zZSbeplilP39UK=;tsa$aAByP(_gfbYMk4#rLglg^bGyHY;MCJz3UU9O$X?riNlVv1 zn{t2ly;gnODRu9>`=F$92!MYO+iN?EEGXG!a&o%w^eFAdi%vMyO-^`rKR3~7?U{9V z7O2A72M`yMm~+4VM(~%il*;=YZc(cg)M||qO_#16hs3?PXn0j=sZ8V5AwE%vMKz2n zi%S^n5D&0k`{LOKZ;)o{oia>Q_=?sqmjY8(hd%lCutV*mV>Ih1vvjdG_&-ufIL^ew(&*3k8+9VO-aa$EUCAM2zKAvt-Vyi&gA*}22g!?G7U(s=^I=y zT}v>s2uEWLW%s9*+xM$pd^@y}t1^kAPvNz$(>rTQO_gDXZX2o8>D3-@lvT_++-ALh zlaV|YV{L%``F4F?lv+a{s|ir2%LJs%tZObHCM69QWG!*b(yKLpAEUgn#>b}1e_XJK zT2%C>x}l{Yw}wo~ErXVKhJ(pW{5R(@W*$|p7h+Jc)d}bTUG;6wI~qt|s$1H9}? z?Vo#{#IdcPEeMTXD*J)xZ7Q9w9BQv>37Q1m82SM-0$}Pm79;he*)#NjNdc?iY;8ToQY18q*?hsrEh|i$uCs-tvR};ssV^;%EQWs`MVtb-qli(Q55o3floa+V5f9)g?JC>&u(XQ@@#& z$Z}l;C8%Gi#XO0)9T{A)b<{h0!3Qusk3ZWNV>Tq4d~?4}T7EZb=LWP2q=nn-=>NG8 z^~;|7N&kWDzF=pRKFagP1Bl6<>Q)yG)?>q$UAeOB9Sn^(+N_`sVihA#KC`qhC7L@m z&~`@K%RB5DSDe8rzyb(_QS7?xoN|AgaJ5NRT~jgc&$RYj@7`b$V|6W5uho&s29T%; zhANUA{Zr6iFbjZY+Gm5L6uYio6#&sZ(*pA2H{TTQ(ANwpH}iJa+eV}3Q-%*rlzg?^ zlU}psD@7<~7v|Xt*-uW^JAE=9PPbA=(a437E5@NM<$JLxR~qH*l&4NA0RfcvrLU3^ zzMs29LGONp_(^E|mtI1?l)1CKQ3L2Y6a1FIUPH$E!q12{#b=*wVJOb z@vHb|6O2hXR>q*b4rC+%R`eNfiqyO-_duiXRxXsOUSebd z0n}{zE23!QCqVtn(bV+~36qx`P}k_0*mVtdQ{_p3K710#&>K?{e%FvZe$RM^iWqoL zJ%Y@nzUN625I2n1FxuCkRr^{?6>LHaNaTe=MCu1s0QA(6N<7t0|KN+W=|G5$QJRgh z=|IL{%E%jeq~uE{cJ~_{eUqP3@KW^nWYnr z6IUg5AfdddUprhkFzzY1?Z_+Rb8Ik!;=QTvVA@?5H-(L!IrEs5cw5EdKM z=S(?OB{S7aQF zQr!E5MWEwmKM;MV(owhj!YR4(67=?Ts($dVkXE7?nde%!u#{eF4b5T^py2xe+I;x& z{Lbk8_mF(nLhC;s^-3?+I|cSPk*~TiYfxjfX{|ykeSDg(4r8uq_g3jkR-tZ?2y`{p zJ=yb%b$&|v^b6fd^HEUD=(0JBLEm{RLpA4wc}C|*Ny90j^7(E8AqZBY(CdY9 zHn-K+ip6(6VoNLRlclC#YRV5sq*@NKPqWJs}@HtuZnAp@P8h3VFz9u!NP zk-2O~)l5z{&10TU-ANdxx$RV(#R;g)Sj-meSIU>X;}2Adr4~E_t#%$SjB#5ahIt2NVx|WspPIRKJo5J!97dLaX4BZrqX;_PRy|%2NhFfe= zcm6TiWmiLO4iE-ynJAe$sfH20+a6v{{K$eFr$GXKGWd1I^tqKx@2|9f~L=4+L7wI zToQl+3T=&$s;7l3BWj1gjoOvTpW7IGJZWviYI=LVoi7xMGFW!PYR`YBdG>zMo6d94 zD_UtQX;V(meQxuzv=+)VTyR;)3y2b0aSW%B0eWZZV<$W1N5(#?BF`nMSec0JH#K~A zW=Qd>=8Iv2pX;>hN+f%8_|ZygmgZU+*!ijQ=~W#7MF7OjuL+bhzU??M5_@Q_e7dN6 zHvHh^EPP7Ty_ygxFbOma?Xa6W#9qJG`M8wZuR)-zwH#HOxoS0;)=9g2M7U=Yvu#)p z`C)rAOE2mq`BTPmwyZ53?tSB0d)RY{-hbO=-A;E%xW~18?fPhMW7lwdUBF#9_SWgy z9BZt@$*0>S$imgI4`MHQPWQT+gD2{AzAoIAv2{H#$ziR`u7H()T@1nUey-v3sf)bn zG|c51CTw=HZj!#~gFjaR-L6{y{yHJ@*f6u=z5B+->&_8We+k&2$@ST0nTQsnt)1X4 zpn1qFV7b_iKmY8cLdBi>%O!GzW0g7RM}yQgFVh^(XKB?4|~Z(l9;ZtDQ-hzvJ!U_vo7KmiXoLv z_z;^;7Y0!h`zCofDCl~mi6qvV4!FUe4(KgilSZnwryEWdinB0XZ<{>GR*5yxi4Qgw zRtyi4tPZ_at(CI@kDtaa78ZGu*3e&+)Z+NNb~lyytlg60PvK}roC7MDk*oUz^OMz? zlQH*smpO_fsXu3p58q)uAj9Uh`Yui<4JpQ zk-a?>q;_$U(|xJ}s*|i=7vIPYgX~R6-znKtvKEQ*U96y8UX?f9#2c%zY`Up7PGq-_ z1e23q^Si7gF)0+W?ms*&rooG(Rmggzqg2`;jpHJO)ESOD8c*g6{a1kxvL4hl z>`!r#Ajo+4F&_Sa8SXV!Yu(fi#Pf0(4o=%R?7@8cLVMr4j8H}BF~>)+|w-5xkMr!Z`Er%~;M&d0O zM{^ukZFWwc=c_=OoPtf~m!R%JTh>Emaic{c?cncC;CH%cnGDCxSdM!Ql+K2wq2OYK zf>~w}ZVjvF0oH)Bj6J+Er*1vDGciV)q9ux%%-N_8Zr37NAFpQMFbr9=FfJ$uw0-ko z_7=s)Dw1Ii3Yt34>=S2S0%CT*2CgNb$qsX6@bo}Prmp3pm*THnemi#%B5%?7+t0ZM z-z~nYn0U?u6>O!DtjWBJEUvvP-F}YEYN&;l)-ajY@O89Rc@bEd`{o>FWFS-|I6nQs zF`4m5SL~1&H%oLW+@$Cda64G++!w~bjsszC%8t8@A~q+dP_jIqJ&a$|V& znsHFJ)MC~;pTNcFE^zS_7WkZcPK(t022-$}D|j zJZqC$?(zVolHHX~D@OS-iE|Sn`!SNdYld)@LSQuJc;d809ENA+yd8gfDbSq&$<0C!oQZ~VJu1v7>VAA*9> z6oRw~t){*uPA!l!!l}Votzg4QUN!4b}YEbkf{KxBYCk z!s11;uh9I)B_c=T(~74}@%d-o^o|1F-4}aSjy%7*IOc-_nT`ZHy%^udOunUl8}l`3 zfG0rcv28s2b+mQ#kv(t5c_1tM(VTxBZy3iOohm#R2$zAn$#11T52Y^KCC38GB* z=D->P9PuRsgy#=MXC5mg?4P#9y>{wTj;QlKYb}kGhk}f*jQGikg68N;j7=v?vT7j? z3gfI;$^7o>h%a8=M?9-50%CNJ$8nWxl?;>Z1i5G)g;pBdz)se)kHTZ##s{d6HMmf- zIUj%VqOK<`=v(ACi(XMlSx@1YkW75r)&=egg>FLE4UIM1Vrx<-58%;iRNk68uMQcG z4ApC2#2dFqfCp~Hdx!1%6HkUk=#JHS=ew+wv_OigR3Xrtk7$gr@W2>vgE9XLd&IF) zpK_ZH@w7DEc~IfQy;;MQ2?Ew0JJOPe$vBCC8*7N@YA3*ba@AFZg+*vvjo5{zjFe;R zHo@lc96?N6Z*=)Ea8}(Fkx|8j++|Fh2`oI6H1ky6Xo*RVs?ic2iy7D7_vv~f%Z^e1 z>eDU@L_|4Ys=1gHBBoq~T0}zXVykG9ph_YiOh_81TDBz4tzt5gX(-8M|9wY;#-5Fu z=PKxt?&PzbhVQ5ChKH>>e)axzsi7R1nWTtpjtkS|a_Gb4+q04*mcud6qBr999h$BO zWPt*OS%@>psQ0&9Tmq+_;ZxQSe&xZa zO2fw>1fAj=GNubwlGQ?%-a_&ch`A{uq5kW*fCd3MW9cPBbcwP$s)rlXL?*)MTSx3r zr;wa7s@#JKeD<%hqsnJ&tuF%E>6K;J2OeUmw!+CPsrrN4NJ}FG8%L`vpTja6O3y4S zwwU}&?+sz_Vf&uMV4iYrx%H$5@dfC=icVi3AQo3%G7k?wrmOs-Q~n zNt6b7OQR#+tp5X#k+i0A@$^;DTf{rOY-OAV1RUZjhEn+wc5|7eTL>OOLac(HVN1TS zt4x!zYf~juUr-S!plx{gPFqRc%`Z5hY>He`2vK@`l&TP}@l$#5zC&_ZPns+JJNNUO z9NXvgI*fH2irDs3X`qpLCx7l;>vj=15})>RRqVL|v1^%f%sXF7Opx^VQm@2W8_xxDu7G0&nzd4jFTkrsagJ#36YV=t#>b#o4f~joiRXAsK%Z@mI`Y## zXF`K{O;fLa_)LmT+xrN=vRRxKUsqquZQqUPxXjQd0Hs%M&g_SY`(z^J_0RH+Kzr_2 zXK+kKFAYi|0I|F?WY1PnXg9oxZQY2%m&o(vwXyqkIm&*7CFYg73&#>HIWo z4H60}3m4>-HxJElOT+T%m=i{jTL_Q>PzkXYgA6tMKj17r@R7G1rVV?0NlhYRX8NJ* z`)fuC(YALlLb|P4Wj^4+TG~Cm$&I|IKh`%4mHOb~s0#c<6|+o};4u@>-#64Y_j(xR zTHX8l2?mzY#|AI-N3B9m-C?ps1H3sBd8rop<@`DGvW{WYopxqV1yIvwP|MF7PbSKB z8=i|?vaw!}*c@jO1du%v2_x@GOGRdSQ%s|sixT(_-*U{y@}?Cb{5I*-Kbq_MxFyY9 z`}px%n*{*|{mT}%ZLC%0r!vG|4%Nj<@h>BzuI3E)SXgir!x@WTd+^LTt&@u??&e+R zN6HV0Qgo1P%F;q#>KgBlpUtFnkHN{cK4AN&WNG2CpU>ZHM`5?C2;(08mdnwJKAU5~XF2L-ZxH9}~NOi|~;Dd{9G zt&Ul1aw!?msSwylKN>u6@YmH3!kqPpO4@&MEH7Y?rSk1#;HL^2E_*snDyC8`GL0mg z($}!l$t1p2@{pEkd3CM3($&q#D*wkAsh=Y<{Ph6f;(xrixmLCSN|rpZ)I<8T~2#34)BmA!$4}>W8Sz z;06MEPNAx%F2B?4h~rO|4rYqX(bKj%rf92BiX+Y%s+Nk^JOc2f0(E*@JKl&>?ZZQK z9(Hug}S_LWRn zPLbo&wCAXy7{8o@5oiKN&ohbmN>Vxakky-C6Z#O|B$;k~diJHYFkO!$){#BYK{KxE z6T24m$HCuLwxl&gnd(XOvGCl@zo1z+P3qG7dbrU_pyQ*m<{?ax;`1C0$Bo^B(}PgM z>?RY}kzofJ*P55buaQuFU~|Rwt&RS-yjL%0Ug~ydah5Nm&S!Y3@mY;-yt_VeLKiy~xC5){x4aD_P1E#7r&_UuZ8$*-|Uy7JD3H@J^4(58tRoSTg=N@qqWNr{FA}=KP{rM6A>6>@5d#?FeeqBApnMK|h1{ z(L61rjOB&jysAn@M=)q)HnKOk_WSweLqyU$4+OvK^r9iEJjx(Z1wdgxur%e9Txm5D}=RM~+pnl6())^Y{Um)YLG9;qH=5K}AF zKW`QfC;-#QIa?K&z@hT%CR&QxsrbYu-Qm)@m(8QT1n{D2zK6X7vzkBl+Bje8dL%?A zm}xPwiej6e+08l^GOPwQ(U_x5AlHmiXXvS}t?$b$ubW;_%dgrX2*xc&pPgV_`>Nrm zwO#HpDWw?Q8qbyo?J?iG4L-NJuSdTq4c~F-NU*TFh1hq!Xz%|8LL}xoYs%PTn9Wu` zm(~i=G5_Aq0kZ~S?{t#6o0}VDlhnej7t~L4gLcVGFFsQN7I#jS* z0jFX4*yg~>;Ig@GkNk$rtVw182lb+P+DG~eZ$Pkx@FE7uiLduTpg(_KDN)KvI_$j9 z(~xSH ztRw|fi>q0>*c4kw1Xpi07!GQiw6?K~MWIM5`j`nO`v%!r8=6@U_#$fV7qOCE*> zrAxk$)Tp0Pz?$R$9ex6Y^S=r;ky)u}Dd8-mh1MYl#_)s}e*=z<>(8p6wfEKx_4HiZ zcWVvBZM+&DJ-mu3y7q^se|pisU5LfAz{86MFhjNdV|sa{t|%N8y{T*9n54+s)@9yk zDY%<$D_I9?s$LhD^9r9lAVYog&2t1+9V<_Ux(~gXcJX*uy5#;kl(OI$Qq>*#m4%f*Rp^sQpM#lbB`QKxC7M=lxCVus}%k zp<2kvbFG=s1R)!rT#aMXi^W?EEq(UnebhqcRZh8OVG(`3XKVwpO5Ipn+C(Az#oHYc zAmgCx_siE&NPJZXIFFmPzlHt8Al{r>(9MPRlpTK^40F458;4%McKX zX&U^2oN`Ai)?P}0St<+(co%-jx1(u*S z(F@`W!)3r5%km6Ra-dpa-K+^~&9n>qv~}4^aHwua$gf4w_r$37-PqGJDLfjs4>RHxn&s7d@9R$i zH}wVO-A&!1V6p%7VMHJ$&v~SMmHy&aVmHW|Q=x|IjXZgmcDd}d}`iVHUGZRM-E+m;Z2Zqh;h&BL{w(&3%LcW=_eP9raX^V z%86Z&2=SB}Asz8UFDUice~R*$l8u<$EFSApiI zFP8g|dMVPAE7VZ()eJ0=1H=8WMqRpn?0;Pm5_{pv6NI3yAIJTVjG8FTW*TSR!+*U6jNt211qo~w|t zW$$%HdF}HD02emvO-#9&6MddKF}>f_0b7^P^Ylvd#^px@F_52BYM0o%>7SJODH^|v zd&Jn1QV;&+QyA=);|h>VUV~x}>yfc&amXA|HMM6l^FxN5QIqdtHxYc};^|1d^4@^k z>{KlZVlVRgmn-YY;UUl!Yb4h-SyBw|x13j{%t#|0Y+}l99-xw*OxwlCzusEUcaReh zGCx}qo`3tH7OCG1mDDO?i8fkC`^Hgb*C z(?>Qk1E{3lP;?&1wfewq8v^+*tSYTMez)H&@^$D9yYa|C{*j)?*^t+$q|@}3@o-K{ zUOGBY?FJPQS*As5jlcnU^NUnh#`;G@SWB!>#Rc4abI+J9e>>(H>x`P6(061YWmgQJ zH@Pyzvh40)WQ$gBg0+NiHr)zn7kEB4a<1uX1U*btjRa^~wZYG`1Q}nR^!YS(txCmc zIJ+3hRv7lPn7Pg5DH;Cqms?QKEAFMC*(Wzuzn`Av?hD)>qETNwL|Q~wvsp(}H1+tJ zbufLfZ8%m@U%d&XAUp8F%a+h(c{uk|8kMxVC&G}uDZ7ihHO3S*xvP45&Fdv7-HnC6 ztHWSlSi#t9VJ%=~GaRc8k(Rl*DYrnG)Y;ASC>eM0QL(d^--lxK`ha8Yo?~>LG*>I$ z1|Z1ZSp*C>?VDb4mO;J>Ey~;QeFdCX3CLKHD=#$n9|*NRO_bfW+MYn2O@41a8$nQg z?|EpFpm)PU5jY%eprVXVoBB&?=kz3jM>iiSoi@+(2`YXf9kiNK_z_)udNZ~MdEX}U zN1p1vhD&p*B1(;75u;TDKF0jgO-j}zZU{)%(?hDZ{vZ2ml zas;u|kU6ag`FfA$H0ZcxbhT|dJS`26sI#Usd76%3IFRiNL-Q=gP(FT2`ei}8sPn9} z%zG*Hpp#*Qy{jDy1#M2>J;ap^NawQaOEJ(&BT-UZRTjD>i0EVxjyRF_l?>*I z)|K?Hd@b{RBxBSPPZmGZEnZEd^Bju4#9Lct9~Lko%#4#ni9wcj zv?>PNym0pu#=F2<{bKs0SHVMMCKD7}sp)}+8-Vp!;jE;&AiYu3ge2?BgRi)3hI(bP z=sY;WI=DWmKq{Y1Y}Ovo@xa^iy=R*&JrSv6s_7(-Hm{?1gf7ZMT4JJw9%m%HGV7yh zAo-2ee$={KqRfNpb4jC}rHtcvPS_HIFZLO3h{DGv^t7`x9#a$*H-x^h z*|qn8YTM}gCUj{s=$>VykdDlOA8sshEv1&4hf;2x0CkV&;MDgeL07%S6y}GxJ8dhS zYftq@Lxr4B_?I{-LBi&ruxO12xX+laF30}X0`Rrvf&@?h+<>kdVB7y_TQ^`&j9B{H zqL?~$vdFAivdut`k~kYBAZ4!%2ggM(0vo?{taKKQT2sVWZ(uCN1UZrF`(ergN50Qa zw9_XzrQUB?eI=Iyy`CmH@r(KM#Ej&n5=nObGEEzS=cJ;1``mE_rC?lr0(55_(dT$y zgoV0=7>Rc*$~Qet=|sFA89tt}p}W?y3_l)8uI*~K_M&6<26=k(#9{r)$DJ=?7p!Uu ze4>3lG%$I0O@&7MRcoq&OS&P8;y8+4zSRe=2${y3c4^w9A zyMuGhW?1bM)tDmt%*q*5S-*txTj_QLdrxW>SY#95{H&F>BEldlusAl;V=)|1 zm^u8+_DyjxQ^lsAjrnjwQQ73z5C1hH4*K01U6hk0px&wWqCi1S5|hw28*YEtLSTGs zB?vm&i4tQP8jDt&ka5|Asm^C2${ImOpKMShC(TL57g`JrGyUtV3SO9};d<{N#(~a= zNsfi*RY~!dI8#H$u@yztqVHB-UMQA4CZQ?0Xkv9x7Rre@7z(~qEi&&MQDig82%b;^ zi0z7?(~C*C0!W^bW72MOMAo<l;sHIj5R*}G9wBq4pFj4v zg?}dQX&IeDurdAfCAE7v>AE4&3skT*E z5$$|)!6E5l>9R=~Gpo3$W|TG8igp33H0A{affC;z^#_*^0gd`g6T)rE;r=6d!LC9z zj9s=`#co{eNbd>Y?z`r*;{G8yL)yjo#vB4Q$tGStEE%!2hy4 z1>CeVWIDh=aWmga6bbBdjmW52&d&g?uR=G(rjiZB#tTs;F>i2_ilzDfYb3Bkdyyav zhRsSHkxfj@tf*!xSaH0TdE*5YymnnvY2NKJW5#hAa}1=5h)I|%UrfYF;arZY5A?C< zXm@B2D2Y_`j925RC9s*)aA4)o85ni-EW_9%bhELrJ72t%X<(&TWU4e=AGnU3qhZzc zyeP>wmu`s5sPr7FT+axF{d{*To2nSec_~6ja2;-2EY8KaUB1d2v?j|@WB~Drh6ZOi zmc!PUdL%t<#^lwc3wjwLFu@E9i9}WkR#D)T8!UJ5W--Fm?14t*R!PQsLHrutnB9bi zn$>05X-*-09IDz6{5h6}{TM^$<_{H3!CfEswge)!VzaeSrV~Q{gej5GWD@{RhEb$P zZ6_2ROo#8V#WmFD)u+_8)K_4$-cYi>a@NlS6PQPLn~y~jo`|+ipP3i(D|MrBI<$p- zOaNlZCJM)J#y8TI;|W*y%e&j3cb-JrQ_}D9NUOVJFnpom+piwN%a&}sSVJQxdPxST z<@FbmFJ#bD_axsI(xt!szIR`MPxOWK(QcgzdyEC(KqNcFyyb>dm0pWl)6#~FKZE3N zKQ4#9sGLfBYu`C-MxlN~xRvnuT|(zlJLG`%TSTBcSLMr8-f%()Jq zgwF>IJz)y?D;Zc3OcStK8BC^Bt%avrjO0BA+CCX>jDcmBxSTeTyhgZ;=Z0-6ODh4P z$2qB#KUWO-13fI(JWJ*%Se8F`@LYS<#$y;;Ry}AlA9q4U`rEv&ByN#e7QYYN`6=_zPgM*~%^fp(=&Dv7<&n z>rwas{#nrOzMc}j(@q4E@_l)<4+xDRPgRYV-(8%xuFtR6oU zl;pKPLRyFbToW_np@+>ZPr74yr4ZjLRpA+cF*EIe)nA--hR=uZe@;W<36FTFoTq>{ zgkNUVNt@!95=b8{$Y>Cv-gt$P6_wF3V>>Fz5u`Aldm0)g+V>eZt}|jug(mrFMGwI+ z?T8->^C5qJB=Q`P#;lakV(^qSAEDQ`9vh_;v-`G(2Xw5yf zE|K-aW$~4eWP)1(&=elX7kr?lbT{AgobPvuFw$Lq1vD|n>GofXu8=#T|L;>Mhf`xU z^Cn2h{?Ka|2qz+ee4C!pN0K^Rw{v>+7TC9;q6e?K(u(C=y!gnhs^ewyr3 zZvft^;D5c<4|wSG8i-oNQlP~b7?)aJyqak|Wj5B~Uy~{m(ClxOG!>lT(aU}-YRER3 z%>}E1g2$D7O)j_!y?^g4YyN-ktm4JPK?!#S(D@>@Nz=81BJg*}k}aEMM#FM;CT9f- zK=4#xwOmY3;PDlRJm4?~ymi48cC&8s@=0X>1ApvVAQ~p+bV|G4=>^i8Z9(K#v{8~ z#+yBI3PRBKo;}vocgNS|9st>dIRmW;ggQ0j z7$C{XS5PaG>nRENcyqoiZ3nQ{b4wbp6k#@dxcLHJt%HF+`Rlo15##Q+m;81!n|^ul z@dDiZSkF~%{T>W>4^qb1u*hBkV{N5cOV6YKkF~dqs_I?az6B*DRZ1GArMp2uK)PW` z3W#)fODnacyPHLKH!MQByPGA8Zg?hp@Be-8=NaSu_Kr1%e32M)Uh|sgb)LuXI0jwU z0|@tlO9Gt+p{Nfrpys2QK+px~t^vKur!QUE<%4lGoc{)~Qz$dR!zfvWw;~^14m6A6S^m z<1_vp+$TN#OnvC_L9a<~a~Bp3vI7U_Eq>{7FdFTX>MZwi&=F#Ls;m`ByTy zc8fTs=<`WbEa`fC^A%FXr}TXua`9Q*K9$r=Csj-YO6XLqGn~(v=qF>=m(Q@+_4fEgjMP)t$XSEXiJ<^ZFfWeSSRmL_k zdW*T%yRXmV*qOAs+0&!smZ^&WbsHzQ0tvQ@Ho#3fuvRJbb9k5kbqoT7|8E@(KSN|? zN^iiQO4j_QT61#GgrqcSHx;Iipk$gc+aB^XK}%I4||* zZ4Gls(^uO1t+?QDB)uba0Z2HvmdINj_O{cKc5z=hk;93dPgAFU8|Vrs#Tt^~NyW2Y zXMG->kyu8UpB?ld%-<+}?X9Xa-ipLq%Z)8*4^0Zq9gaQ6P+jd=o4W2D>$%lLo}(h* zhXHls(-?3lkIbm-?N@cRRdwn|h9K?{B$TrD!9cC!4Pevv)Y{_8(_Gz(O<^$p4*W3f z#Gidv=1cojs?@j$a}lxB>Z>JkXCYdg#Ta6hvRC?v^R~tXvMCYSZ~`C|jHj}btGE=&u*kEG?#+BV;vsG|8rX|6T^r4)SM zljmpXWf?}Lv=0Bvp$a9RNB>_E8))BvKeNY*gItmRX;Wu;dPf0oUd^)hP^vK*;W91mHpl)u@(30Iakp^bFB)o zWw|x0Ov8%w{M$C6P!X+6bIaO_`R~a|3gn=$^A*RoIomELaVS5mPUrUHtzMl0+P?Vgx%nV4qbn^ zJ5>zqL1DFdxZiFFvPKsOhmi8|U)^7dByX(D#hlZ0wLOLo62Lf{9n_9PN80)eWC3Y` z80Ea-Ad|&g22*@9(W_&q{v;rn&kWLWNH-jtgl+n(kE9t-Yg9|%>f?DEPQ=^^-`O@0 z-j+5PT%WL1V;AdJlTTZT?_d!t_VOD2^sJk!&6}6IJf?UhsSAiR&y^lzi1w2I==vpl z8vTu|jk~i<>p5Nr+>K-0`r2+ZinkyAMt6+Dv6Zu z<>^RUk@29gW}Ep+>VjocU{T>Q8shci-Kov6u))NT0`xBEsJpD~L1Ajq6Ne*yVa;wi zaQloe85?z_axln*)jC1Hb2F9K_>IY$2RX#+5@xVHaZCO-=VEcg(IM;($kNL(BmN_6 zi*};2C);@ouei#)Flnf$J;Wt=&x}Oom<^s8Z(i6UP#CDXHF`z6r|Fr0SMOlQO1@^K zZKW=5)#B9b#+x8jyTXj#T;I&E-TJ22^YnU0|G=nGKiPXS!@ET3agcfWC`z8n=9b>R zGs&XVU~~0+h9%nSkayfVprmS8%lWq(Ew<+<(rx6Yn1|}UJYxQxGv3qIO=FSVvifbQ zE9nFhW-m&ai}(u5%NgUrR@PHQsAKjvUOXc9^11H?X}=cYGT!#i#8jU1k{g&K|HTAc zdwYuRn$8?Y-<8n~t?X42p5LSIrOMFdu~7dWx?Kgu{ls?3s@mbdDzVHWmDP$2dG$?2 z?6jEHK-%2YsQhYp)Htcoc^9@wKBvFD8MM~j>dPYhdS@l)ux-7UJ5~z_lelJZ77Qg6 zxiLA?ytm$D9L^sYk%VPba0lpoh zahDq*v^|as6T8vUc9DBxKnekkae zbJTdWEI^@SXG(J^T5_;g9KE~{-2+<@R(@}{S3N(^=5!iB=SO>h&I9S z=T|l>hqJh_0;Ez3v_kwLMOM2#Vo@L$l&|pq0u~ubuyJ-+Rf=gBANaR#*zKg-j`+QPohoo4FFe+;)HAv0o&5 znUlmNG`%VPq}@w>(xpZ__i~ga-4Wujb5|dIrY*@Wr-;Zo7+pJ@40TwJqT4emf9yYs zAZ7UsU6X+4k{-3*j!l*}r9|b4ZuCkew@Q;^J|`d_>?NR1=d0RDzCqU$tf5wtVc1pe z3KlW7Er*8Xgu07mZwtho%l8wL6@&ZZWZjn=cybOt*li_R-APNbM`RZ2eI*oTJRNh! zjQD)cd)qjf?sI3B<*&KGtvj*BwfMJhQs`3f!idHzHTx9so7| zL(mHPvb#Z|JD`PC;U4natGv(Ci`QX)Z#?mw$eahS@X@S9!b&Ju6@P;nb$%v2QRD?d zT9|;TbOPq43AAb@-Rr_hz+8qQO*LNM*#lXjdq#%U#Aom`$<;f~;FWm_&(O+i|1z|Z zufl>gx9VTqf>PrTynqT@&MO$BVdTX`?c^HpG}ub;th}K8Mo*l>!lr))b8v8P{hso& zFTvv~(?sQFJt0Q8GePu79P6)osSjYm*3r$km-7%M->?%y&mo_J1sh>Eu-h^0@9Tjk zVE0CNjnl74H|nJSQ5?OjnZeJAp7;9WjeWB3hzLbL;DHS?4UJHfZ=%h|vAua=#^Qk$ ze@sYI_ny}Dl44KzGhdhx%F2T)}HA@>N@Z_tdp z$iZfPW}|yin`a63fsz+E?noU|Je5Os>~U-90Ai|VUM8fd7%w%a%<6qNLE}A=)`cM- znQ2^tSlTC8dL8DeYxQAki#`7{ksGox*zzQDQlxLNFVra{4B-S9RhF`uhW^ng0x#*K zz_y4hO?CMxzM#z77eFL%6>_h6SS>LVI2_vQi-n>4mY>a)fxlA6(Xt!+;r@ofy;ejn zt+v#Pi@o%xf(p(wuksYQEuG$q4=z%rJzy;nPDHFhb?cXZj_-;@Yp0CYUb-Yrd%@G% zTmQF3w+S{hJn1mfn&~p)!*!H>bdbJA1!9B`AGxIvc^#?sl$?0qxPCZzR zJRuPzZqoeemMnqH&kx!cB<0xe1DCW`>o>4iN3d>dl}QEuS}!Ak8=dN=y$WO3kv}pM zwy%eKXAtbtEZ5AWLKFm#^|y5&7(R*os3pa2Z0EJgx7uA9y)cGhzkUGQl%w?O$1XD= z#h0fpD`)2wAlyq4IT{z6QbQUN-^#{LH>O=7Rl96PS08ivOF^`4Qol0(sz+*YI}O^8 zjb8zhpG!;h$bCE$I^Q+@2=ket=H|g7E;}>q`jlI`aHO=dimp4mN5%`sFiNF%Gxk8! zt@kvT9lf2hZmJu_+KWxPzDo}B`JuYfN?SOgKx}y<5}(pse4KamY%z=PYrEN2$Saby zmto0y@&|=8`ubn3r)liV%T{090xx=Ytc{oP-k-?@09@tz+ebKTrmTsmm}$&;wzV}+ z+l#}xf~qHLqmOa{0-y=ZJ0Ovl`Z=SQ%i393ut_M~{I1k)f~ zaUZYsSXO4mT@LBxJS0}D*;T?FgTvdx;$Y&Slr;Ov&e}T7TjP|uSoVj9)TJs4$?v_> zFgH<#c3d96iO2sC93R%di!s8=JAceLyo`7jzy4zcU_|)53i|68|04{H`okAtg^o7A z2^{#?>kxn}3Ed6OnLj-6^?ub-MeS+npg-BE{JI4;M7nuQh%-t}N6U+0PTL?xmeTrl z+zsAV^#ed;S1PW<`*>#%q&WVVdFecV;u)6~zsEyx#;>7QJ130xbT41e`Ql}7-qi>dlaylOh-o$u!1eapmwP=K z(q;?+&TF?Pv}@Y%u-B9{MD84#Ut4krYS0*XZWa?No{ww9&J9#_t-`G49o6;CiG)Q? zv5ba%W^`569=$22UQ1PL)-Aeck_z23^CGzw!r$x$=nE;}aZa$SGsqJ&=O6$=vJ{61 zZle@FUtzHY!7?|h6JiZMU`(jmXtO7gQA_o=sA%tj2k3*_pAcTB*`HA4;xRS)I6ZMJ z0c-ho+lJ%wCvWfU0IeXvXEDrQHy(YYWERRUy?z#tq^F~oT5|U{UnjTPZ}HQbS}(Jf zu&1`ZiufZ+qsvhaG8TVV^PtbT-i3r(=Y$S{r9q;XEmUv5^N83eHG&aYq|=7o{-u%o zdiOgsbdGO{KBTT`W>8IiVy2!X+R9aBDP7Cu$d_aumZp;Rpv*PwH)%A#51Lu_4r!ZR z;8WSDrNhOg&p+Iz8MRdP)(jg6Scom;jpbio|ui76P+qD=qiM6+Z!lGA(ht?h*r~##uj3X5j2i+XCrj98cEU#2FPK5~KYLyon~Cz|+7xoue>F4r zKQy1ei#w)>0Zj8$1!j+;co2CP^K5Y){H$`EO z8B?f6uTo}}_3Pmqjv63FIoB&foFFc|CVRV&yM9|s9!K}l3hf)yZh5oK|TMYsOpYwdbI5~Qazl!wr`V;W8 zU86M@XGns+OBqi%jn)8K9b0L~K}TyM?N46v*bO<48@$mZ&*U;AbZPh@6hTPqOg@G# z5>3l{94ztsnP8(&o50YexPUHQ`ozfT2)TJ@1aEU0q5qp7cto6(ZMrV7G=t+TFlq7; z=)U2Z1=+X||5jkwS2G_Ek_Ex$TY7)?dDKu7M(Tdq;#w;4{Xir^1Gt-O=(GCJbG)?% z5P?CVXwuV22H!V|)=15!H<7jTx{3+DtF2XG)@rncfrZ#?kqdr#u;P>BsUVVl^Ws{W z%rnT_qRWbsCW&yyHpt;d5g)chT#B|{Z_as0YN32Bz zY&z)sWOG>zzTgFcmr!AOdz!%L(=6L?xyEnqkHBiUdJd+tLxl!s zev+wMnCe^jOX5{GLqhPmsqn{5i3LQk=LUSg{KObbywvj+CKswwF>G8Kpzx&BG17O{ z8U>DBK|ml+Hj7~YTa!Pq6vTIj02RTpt=r4El39i6U)DO~nLl(ziA~I_E6QIIAnl{T zew2?RpqFKu<`@xOf$;!;n@r0$N_Q#E2>(1n|C3r)BWI)i^o(gZi2KiVN5k+`9+d)n z{%?gF4Q8OkTG=)(uIO$|=VL@IU8M1&Y+ue6||3rsoU+H(KB*tQHP&-WzQ3f2*1Qnzn z((CzXEH?+vA4iP!I_<|?_*rmes6EQ}L^=+uFppya#HvIr3l?cS&?obF)>F~ZR2E4SRlPeT~`fdCe4csUI z(BO~veD(qMoTyDO_oA2BXj^AU_z4|HQF_|c367&PK?O)?c1SC%TX-SPwpioxSD;c) zW$mkky9hr1Dv_7VmWDsy@Dd=U(FFq?a*6&A8}uP%tCG(mvtqai3BQOO+J&)Gqh{^e z)7!7=j&iwvlilXQ-yqS)L^OIeABis!`OJaz^wq%jY9t3Adr;Q<`h=K^y6Z5w?KY?O z9C>`Y{vwc8BGsB5Ua#eiF(S_L??bxelx8=u#fmJkcCp$~p0X z-afg`TZd0kM9jtbdQl?q9E;a5G!%D4bP2s-eAS zbMk)R7DwPRT5->0QpfWzpYcg*37rh>Gz$rJ5PrQ*KMJmxFN&T`YsKhYgY65K{a1^) zaTk@)R(N%WCyhN~@^)!O_wkp0o4<=b+*6<45ihQO|0^l^4d>b7?9VqJA2NMdSuD`C zuPtLbyN8UI6=|KDKMjGUcX$a0=~i^A?K7f|`!!Yr`%k@YmkoEF_j2oub7Hy67dZD) z#Cm}_T45WU-S+iduai!ay`R|?GHO|W>|^HVyt37ZE~Mfr)EySN3x`~WAR9nsl(7VB zGtAKCCIKdT#7<~$zq`Cy4kZPi1bj-;gMe%uVD4#Zr~rrurY zil9V8_3$O7L6!Dq?pPU~@P_6S)FUyG9r9Lwthtw&UBh!Tky94-vBPs^79}PD{Rlaa zY3U-91i*e}=1`5|=N}M(6@Df0w8C-mN|D&zev0rj4!i(*1>IRJrkEd$gM@Vb3_&{A z!gqP>E8njuDuB1hG*o`#{&i1M38?zCX2tk!AylE5YgZdJ{u(h8J*0r*XL??FMTZN# zqC^wa4#yr1Apv|`DF8QBDDEe^N*>Nt9>ss0%v(C|*K`cyZ zWm{u5&L^=wrL#p@DAJ5^Ex~P7S)zZ)>FgCSvpMkiO`OHl>4O_bX~nQVFt4Rm>p#M2 zHGJISxxCu}hN9Q`X2m2h1w%#2iZ_~~RAue53Xb?Fth(Bte?}+Ey%5b*9Ulnz8Jqm< z54q^`{N1^a&RvJ37V#8R*GMJIm0R)&AZCNP_MF`t90-Qo)+uHfz3H&N4w9bjJHrhs zsETD^N~m6r=xaZ?+#KlLJ6yK81t{8Kb5)C`Bw-ABjVWqZS8uAjiiroC-cRp#YwoRY z+KzLs{Y|n;F|7EQWfly)B&b!-!N%RpFa_={B@FLSoCKmpI?wHriPZBJIkK#PR zMbaA${1I^4Js!oM=~^iofe=A-4h|%jI#K>(RIzVv+@! zAY}A(Lu*luuVlW=h*_wPUlh0ak7UzIO=_2wV;X!eeA#M>kqV~s0Zff+#jg18Ms=) zG*_u@4$yKLYa-V1T>F!?8KYmR+4~m!YR_%opd-UkoHP`-mfo0_MtE9^q>f68zwi&} zo$y5&7dR|u1T{q8MIIul0Mnt5ziK<6Xo#V@F2&SVD=P1H^F^BGKOvr~BVQY9bG}N$ z#O_3hM=X1F{}Dgg03X$OHs*1Kn(hsHC@(I$vp~f}Kbu;_;;~RcJEd;vnUtyVB9cv} zFx`JGb9B3KGDjz%B*&W+Op!<#)+M&LRX1)TWHB z2F7~mT9cnibztPR2~nH|Xo7?*qBk=9X>_m6{0mws&ARK-VJc#so4?+CET;FnM+@{{ zEdH<&WalR$&F7x{$u~F(oq?TR^F-3Xj~(+d*ONg5iEO&ak7MA^YI=a^BSpO{y`7F1 zeS!wAE8VNEj}e|#dq&TN`8o#w3UsFXq5DY3TQTDc(>R5@2YsQJ{bH9E2*4+t%AO3e zLP~!#l<-Ar@swA8rMx21cTVkD(Nk&_f=xo?o9xcWerGRKb5MAR#rR*@=V8@if5B0H zjDI>%>Wo;PW}jg;l{K8$_CF}!O~iulfej4=F?qfxd!g(&^Hz>OzdKdapI7ESmwbRD znfQ$~4hhttS#kg%GB-KZ%gJSk+(NdjCv_N(L$`p|oxjS&ARwl|JBOZQe8gcNQj+8T1>Vex_ zuCbHkXYt+s3n-|MN38}?alZ>Nc#*!kH!Cc8ok^{MYtR}=l`5!f_1@s5*pzTH+G^~h zD~X^ETC>>M^NQhL9+T*I2=N(x%G?#^9Ykp4l4t6C@B1pG=l|rkGYoPWlUVJ{_g?K8 zy@PgsVf^4HJgu`q?)%2mPs$xBo=TBGmnxkKO|atQx|qM^hF)h5W7-@(-5VC-*6v88 zN-JN@*R6E-;e(!?tA1%bNzT7i`lQ|WG&@4=sC3dQH?T@9>1dGBbUx!Y%k}pXKqzlY ztZLq`8Rk+=uY!VJ^Bkf^LGHWoRaGPt5Ebt#knoVAB1}s0UD+k8NE^N($dX=H;bDNF zzmVR@2yUCiAZDF)+XH1BV+qpV{-0WZC%5dwDDw%xa-bF%Hd>J7NY(D)o8?q)+x8Ohh6@o7F|b1=jz=iSI##mU9j>=y>OtIw zZbVREOBb2plW?^u1r93dcE83o?w0NMg6Pb0$xdQ8ua#EUl2G_?1D&~FxdED{N@aRC z5CHN5HJO{XCY^Di6E5&dTwXNvwz>WDGg>=gl->-O_IAP_+dttpn<lNZFJ^4g-!>AcsO{4dHy*%FTqc6u_cPun}A*z$4qvHJpNEg zWZz6*=3^7Ev#jWP{W{|Qi3m3OWANjBu)P(z>l@A94{^S9)_V(co{kbjJ@3KB(MLIl zo`UFr`GAGYjD-FwBUFJQt36sfeohJP`LWHf0}SE}rU9DyUwnzRFqa+?cEmM0o$^&Y zLMT?4w6Z zag|5(_tRaEj&Fhvu~aFYEWa zf`+@Q^4re8yaGYauD~it2%v{|FRlP!k(q1dX{fDVI`#sAL_!quAQc>Y5StpMexVbx z+4JdxRJJ9F>&}Hl0Hhp%>2_Zg)oK| z|3wG7@(Th2vNtq}00A?;RYksP5&F>AgMeBs`cWxIBcZ;WlzI;T)cnGyixka22{HryWJTD|uY_zpk8hl|onwHm;%eBP(D8LF-Ra(JLiqY=jRAJ}Bo+uxTR9Yj`M zeaF&(KLe67JV${Y0vrQ2p#$i|9-ZU3llj*>`AHo{C*P5VAFT>}Adju*VtB4>!0vNx z?>ODVvRYxUmH8e`aGSt1ym)JUfo?4TC0h>N&(eMndDvm(o`VPH@@p!~^f&f6-8;(u zX7cW?2ht~!1Xy#5#3(aObnpxwjd14QgBk0KV%m{>)W1A8i$pfGtmAv7AWX68!|t(e(D7cl-Y=B?HK)Iruph-Wo1Lv= zJMwrRB2+_IDevlFjV_f0A*z~={gt=Urx)O;@@Jx%z7)y0fKh>zkOVKOz(qgCSw+EX z1MKuKcI{I%*UZlaXP2^>c*oZ(J?++Bcdz8OLq3b3ZnLxzid1U%i^)ecy@?o?3SidG zluNLvpozm_UZH#=*;a?=D!t2sPj&bk1=yuJ0G^V?)bQS!*$9TzX$EYos9{v-j2Lkgvbcyum?F1}M zXzX8{6#<*+wIbvF>R_KfBK&y_drXHJYJ6uvs3+=FM>KyNhOuEvm#z z>XHM(e^x00^97H#FACG4(I8y#Ww1|$b;UbN44PC`K&fhQzT?Voj}Ie>nd=%4I3_A| zNI(ShNii#I#yjRs=CpXp95L$H_lORlqK(p4+Ys1FUf63ekR8bE{wEKX~WG ze8-hhl-n+o498^n?XecU-;t!;hY!eBkDF$_8MUgrGbZcpS`5WYsNiGKP2-5M{m+DiXh~ngXQZ+231j^F6s0TeJ(R$3@LLK3O#wW7b?Qm&B&=#^SI4aow9Q$tDuK zIt=3^_rCfy?$=zTX=B0VFry%T>L8x70ZdH1*SOw@tc8$1vmQiThy08`& zRvQ4hWA23V&T#52z1Jt1l9r1kb6Q;s3E+#W%xcAt;RkJ#PBZDm7~FuxnZh2a zHWwg?YFy&xUg_M;4ODOf)UfoXV>!~gHKEoAxw0Z0R%Nj~MSy~TD&Uv@6rl3@%Q*M| zA~gQ&cEkSq9GKfe&Zo7drpnrCo-85w0hM*twEFmo;Qg7!x@NhR<4x^PNLJzAguXHg$*97TK%+iUvd#jC z{76MhwCuKH2YF@5#iEqEtl<|Sag4Zr!|posK48Ww9Up0RXMuA41jW((ebR0U{GI*S zay7nSz6VWDY!x;gO)OgIN69}OxLD5$?GAqY0d2jlQ8((v_&u_>!v@tc-)DX3fK2Ln z)X4bkNZf|0@L>D&tybM%oIh!ar+AWvpD4VW6T}HgU<#M5^@QP#n;qbCIqu#U0V!ie z%Gvxy@(X!{g71l2zvYy~Od7{8=|$ANiVI+gvIcV1WVBc5dygcEOUf>m>Pl`7Ps^qt zWz7&kx%c^Byj+_?krX(4GB-X|E(%1!`QCma?_4buAJd6Et*m;4WrEhasl}!j6eU+i znq}s3Dqf_Rj-x(;9uYqhIl}7w{^goKyo;f9w{%$W0QEY>+dS7du1ckmT#@ZG;l-?x z=lK)_;+(WIHF=t>XG+)doBMPD;H)x;IA<&urWbEOi~nr1x{8kqb0Rh#`fl!O}{b zstn$fz*F6T8Ht@HT39772P`SO2I zIVGFchR}p***9!o6<^={kdS819UzfG+ z>p1~D2bT||RBNvSRiW?I=1>aON`Q!v5bV1|OEyK=_S_Y|n?mwM$T(VxQra6vP3^;i zTS<}92$6?pPF+AGUI$@YX1WG0!{$$qzb#^2Cpz0?~RHEqT>YPj|&N zD3xD3>G(pQ>7eCoTws4fu}y_|pU-Z8ql>*axfZUBEyTHm%LaHn7FjKPSJ9a^S>Rrh zOH(Clc_%JRt&CfSHJHG6KKWOZ_`2Gl%L>pF@M!8EG^Ds)0`f8S5*KsB?O2583aa_3 z-XMNrO%LM@v$N;hx|^a`{lYL86_JCxNRc(bFo~Pp3Ss1>-%vC#q|fe+Xkw=vXl^)S zD4FD0`RyaMIL$+uLN2|1Z=VPve;DVTDRd}ERE@OI{dyT*U+DuhVYZF{i$5K;BfT!X zgOC|L-Hd7rvZ>~4XUT^t;@hByzSYa+1w~lhKv8G7BBBv?#b$mSY43+jm1R(_6$CNP%L=P_--qHMOEbSE_wO-W(ve@ zX8d!x;Z84Jc~QwcNe>w49zS$jqcx?k=LhPNGN$n|<5sI=Ru`nEu%Zig;7aXX4D)YQ z2m~2NSwPta2GAY_?PDaI$tvVJ<|!~{_qs3B7eAWjJCJIBtHW|HUkC`4Q7Hg8oQ`!+=ki5iYaW|w#8R{_g{RZnfaCKb%Ke}WWa zb;qGl{_GWms2@G_rzMX$+TRO~Fy?M)b6?tPBd=VfGfv$OoOwe#yzlbjOSbmRam66X zx!;)j>w#ZZrO6sjB&35S%0b>TB7E>44wy%AOIJFKa4P*`&hocWf^MAxmDjF}+~|V) zi24U}4xo8O@FWU!TQvQfip%XUB!08X>ukza$bH}gFv5F*!bn=)2&DKud`~gF_m@yD zts9l`KC>x4=&^b2)MX!Xft*7oI!IQL^+s5ZzqkZGL4TSw2$ba&o*h1EaQ2AB`5#k4 zX1P|b_CW02FQdylW*T(C;=BOp8Uq{FZckHTFsx4)h!!CJIK%o1U69c*15>!d@`laj z)45HAg^b7dDrP*A%AuY&!F=M(v#&jOE&gM&UhA`2eUh2Yifw8G4cWO9I>)yf?f=FFX~P3$NCs7H{bm>erf(bX zfH*YDbDiFkQM2`Pqw!$Dpbu^wrLrd@^YC~C$P%@z?>iR>&q~ox+SBCaZ(rASZ2$2B zP+u?)rDD{UB`i;x5WWiwq>syMKs;+tLXlOy!D!mDdnVkxs_S(kdX&de>#&9cuRP*Z zEKrB!RZLe5W@8)GeA1^}k>~CgCRi}L_aH#6^+16JOpQWYFvIXPlz19o&Jd6AbcH;VLXG(}du1OM!Gd;RdURtZw zZQsWJF8$P-q&a%n&h8`v2ZiAlFe6U7POd?WYmqI?Tj}Pu$4E9{sCiM3Ry1gp2>+d;|L>=KK=s*R!Tp7Rlvfs$VcD)r5&!w5 zg4qF;XQvQg-jzDB!5_$FptE8N5mXjJz(6WbgXE>Y9!h@ny~yNhzcNkbzSn>e6i4L$ z{ue*^5gDmFpR85drftK}tJ7@Ik$1Y)zl^NptVd?+jxC=O?^yP~PNjSQb1FUSdE)o} z`2dJrz3KDd8@fZ@*5-Sxb9qd?NrLsS=k0dl%<+2=ma4wXv933iu~IxJXp6j-%?)9} z*2L zhXo{^M(eOV;JAH(h>HRDf6g@3NntJt+A>>`mpUhKJ0XBy)F4G$tvZqv!VV`F8Cuj( z7&;CN#qL--mmdQCf}W>;kyB&%x78m&6^6LG6*|SJ5dKh~OlbxE7q#g7Ca+!J9pOB% z)l!0ZDG?n_rv3w&(|`Vh@0;9b1U9x~uL!nmkLBpa|Mv=WevtX+-2Z=H;eW35KVMDJ zVnQA={@tvS=z>i0%M@a?e=t|je}XgrKR4)sdWpG`m=30d~>Nxa#(6jAqw)ha)MC+b-n zgBSmWN`iXm0Mo92n);YI|I^f$EuDqt4{Hp@`xIjB*EbdEdW3?m!2d64Sj-tq_=|W& z_zsR!v+^JSft~qrG0P>M_%+(M(>a}u-4@9*!|-{&g$&GXs;37lauq1#mBEdt!?GW6ko4p7Kjx}@{hJ&*$p9QSfZUTD=Q-nG z876_E@*F|l4=b@;cXNbgdqcrnTSavpK?g+u9rx!?^n#^ol}`Pm(lzeYad7$+K-HAk z!1Oeo{@#@hNVUg<&T#4ecj-2I|IgB;);v6h>Xz!c8k7>bJbs*Ny;rD!+stKEJsC<& z0&d|&OtrI|8c73Tj?;-VMN;HmqD%Z)VE#L}mm$GY%|UF4#5GN_WyZ3%Npq{OlD;VD zn-U&x<4xb=L%hmR5-H@>{68e&8`YvLw<(}nU7zmjktGAjRdud9M{1bElXjlV=K;mM z<$PKha6OSJfuwf@U$R&KanYtFtpgTL8h`afV9}FObbr#|*Fr-APS(fP(+D48B>0m- zJ28%U(AlWWpu9RrfXeE0AnSQE#-i_&KDRgA0zwJ4EaN|1sI622-I4<)z>IKCD<9OU4v2}f#GA>QOk-AKkyy-s&uVSXO-$#2=e=Kcegq{>A3lLYHr0#Jnny$)C1I0|p~^&=7&}+RnAg?^IUCOEN~{0y zcj16}Y&E6$i3#1oS3|znjptvYHOIF8V$kXM_j|RM0sdi;E81A;-)2pf)1Y&eKzfr( z<@bf}DYVnBF&HiaorDjx2;HdnC}Vx&W0Z@~1iw(&P97XXZDRIu4-*|1OApZmi+T3M zK@QE29o@6m2qCE>KkdIC)-$LFV_Bh1mwEOYDKKn1L!v~G5)9Fz5H*=~PYn>N9)LQh1d>0>`i-`yde>v6#z@Xv1b2k3|YeN-gX|L0NR!@Es? zyvv9)t2&*UDJUiQtsTfu0F8O%f&K8@2%pikxPz&slJ)_upmAu{e!-Mx8QB+Rzu|G? z^aD!aHX;jpKJ$c6d>bk+JTK;E_5ryxw-n|qX1JdSm+)amrH7Wosk~ZhqqYP!+uEeZ zu@r+Erd3J65l;LF$4n;?5i>v-I)usXlQF`9pd}?B2`m&K2FvS6TO2x`K?QkM?#R&# z0sKk{NQc%uL`Uh|Ju+XQ(7QNdCDFacZ3KT#BsNc{9mN%KJTq(0iwze!in0cp1ng2$ zFq#wioE|BypB# zm;QWeKki&6KDCN46bzuB0!uA+-Q;g5&#&QKTX|)JB;N_jJ+BG!T55r4wKEcvV5nEH zqR#_hqXe`HxCNU<@3rt+e^M3c`v4N=eXz)b{kXO%`h)TrexrJfNe8VN?K5G(1JAyP zFHCA+7X1WP&`7jd{Fr0Ib~Ihm1a!`#t$mUClTC+u_THWXsCTG;pos z`hXMx_OQD1`*cN-dl|2bIi7iJ9n6~ZX=Bf%BhlX%ua`exsR#wz^MT%C1jSfN)MCpMpyEVn(nUdRJW6^AU%V0)O)K{D5^E?|%& zV)5n3!$nhi%4ro-)#o%p^bVKxFe74=<&E0lKW5HerndkeS%|C^(Vi<+x~j6erXA;2 z3`Af6K29ZuO}xJr+8lbupk-SBlqo>BShel(!KCQDPtxn$vmrZrn3~8rKu!W$_V@o{ z_|5{7Zf*{aY8dqwGr-EZ|1ZPW1|?6Ap0`;cD=ewwr@Pmz8_q1O8fnzHTp%CFEKOZZ zb{!GEbq^64pPp~bzMjr_SSwU*($Bqpw0R=;JdWHLbCIBqUux=vsGR1_{Lewx)J}^P z@wumQSIQ$#D4f}D9bjZ&YCfZ@Aic9xe?0ato^J2gLSi_6?Y{`DBE6-qakGrBG>?=t z_sz(iRI{tAe=s>p`Q+5bJiurA3v>AB!OEjI^EqJKkc%a_x;V*QGq9_k@0>pwDWw;4 zb24Z%m^*=$C;#BZ+{{NQw|~1})0Qt8mhgMN!K#jvOyIAr=YHcs?{@$?s^3YIQjJ1! z3Vf(0^pvs9)!q2`Ho{prc1kKoZWqH8fpx07pDl>{bh`y;!aXclj7-R<6snr!`}@1) zK0gc9V9a4|`1VOM-K>5ovM`fpG5sbzF2&-2JFv7G-2Q|IuQ;#3)UMV3(p!m%V2O>h zeW*s=HhClyc`)-&7JAr9wWEJEJ^jXekO@0~*C#5@BpAyiUoYh}?o+?(cSxB=Mvel1 zp-)oTZvsJa@<@f2i+MyAr~rNF4%PyByaLfQmWnkfd1qc2m)GU*`>wLurR`Nt>jf^}_fA8rV`iRN zdIfd51h-ymo#2W7ToG26CzLIuA)tOI)kB;gi^UxwZk$+%(956AMFD(J6LzjEspH)@ zo87jHWLzX0k3hf8bCSm_G~CL3o=-OMjMIE!o7|Id20o~RK&W)fDp%JsndnTyzA4;A zYd^$c-S%BL^2ARunO0&c5U(lkt*P&%*x)!P zOhxFyd{<}VY>7tLNge%CH>yuz_2F!SC4c_NK?AuZ;W@zDvFGBoZxzwPp4)uVn9p4j zVaH{kaTsYa7&qMtjST&kGVb!^fRDSP3!l~|&uesUV1JJNZJZKZPbUFteWv$+{0kHT>|Yf&AP7?Sw=={rV*(KYFzYT&9sLzq#aX({ZT1JSv`-(d333mliGE6v{- zr626l*wf$VTS%Dk@wM4yXRC;p_78_Tz*g+wCw3vhPdN-fL>7E{#6@{%b{j|CIs`_2 zGttFqk$uFZ;zA?BuR7ZFeI!OMg8L_?Yf2=0@y1>r>=#suoL_R}x`;oRIA_d_6)PKc zg?!V8SLf#KG2vmlcMvlhAY@C*D?h&ha~95(ELt2*%P_zu(K&WLIHPDEO?F=`w4aE5 zWE8r&5Rwjm%QkkH@;7Ir;sAog(Q^BkKM$Csng5wZ*eWI<`%e%N4TE*!l7x0nb8hPP zMl;b)+mr?*53mA=-+dG6ecuS_m|4t!;urygYa)vWpD{ANuK97Y{nsuPL2W{h$)@#Bab&0qzS#Hj+h1yO;16$J^+(fJdnRxSJ1cP& zd#treCX}rKpcppp`!3rUe$DUXk?gQRu?%wFb%HXVN7X=#y`82bW^S)%gDGyO(A--H z1BKsi&B>hW|;9Gg&wcth_e?qwwtu3 z#Tm)l4qE0dL&(pb&4|5l-ODYRZ{d&(IsOn3%jt=^i}qK@em&w9x)_m1=-1*Ly_u$@ zYxCoEyg<`vHm}N>8iCe>d5%r+p~QT{Q}{;0#M5y|w)C0I?!BjI&eG?48R7FOS?Xz3 z(}g6rgZ)|&6?dw3&urUQq)vfukW)R@`x~H3d7}MR7EpS7A*J<9;>kAon~vktr9xMZ zaEptM+JoT7K#{3Iu>+XGOV4LIPhMI1UfcfliB#r4c8Y0xZRM%d@{D8i`3-9P_c6V4 zksq%o^H!7&q;5S9S;p^vLkedl+(R9&@!ht=P`EDsy8=D9q+erPh=b@IqlA4vLWYjsS&;u9_BkFZG*$B9jZHSKI*w zB+2M3E=%%d{M>B11uTNTjwAeEnratIo<$ah zvNNC3+dkfpNN*eJ5*gHnC75+UKh^r`SPdAsx&lzaUPerdF-K~8^R3n7ZXQXmY0fxW zbX9i{{x{AJBQ?$;L`-x%GmGc@QmN&?D@#~@a(T=Mou-z*O+6dVHV+|~=*{&3oQqVZ zA|OhrL5>o!G5?#%X4?06MV!mUv#?C{te@`G3I6FBzCah9s7o+%d6Qv&D*JWicKTFO zZq}1gSS7y5`TYHMTvBg6^v%pnjFtHzV7r)whE_&x@#+eSgt3h62WqD>x}-b&$&E4^|EpaPsc^ z`?vbGfOKdQw>pria>+Z#bVM=c@cH}6!bta{)O;C}jBTbC(423RdFK~=2vb$qyz;-R z3mwiOA(g{idvYB;?^RYhIHT*yQz~+5wro3IivWUI*`m;+X|(^5moB>E50nYC|^~Ee=N%BXx0dEWDik0&=Gwc9=+Zaj7i+(Q}1bdIkMaCQ;%5$J`vfwBPt7D!1tx+(&$5u)sAYe zA?G{`N{7C^hw6s1sh2~$XhOT~vD6C=YAiK8Pyp&vj|scaZLO6)0MUdzDdc#Dx=5^< z`JIvBN=v_8L2IJk?>u(A;rF5M{qbI_G5r1oY_1i?XbRLjS<%V1TJ9Qru)QKd?|y6N zsd|Iki-~NsH?C^Mjx2^BB;?n&_+H=^)sa1Xm-mjkPnnvW6}L}WE3PlUcr@p}Aq$8s z;lJBH%!%Lg$!sefaf)U&!TuV5DWo7UO*VygMOP4PcucT$+{6ueSz*;Zqe1K~A;PQ? zKPhd@OK2bml&VHB-d!bHMfH4Lr6Ka*1OdH!A&ypNvW_2YV~mD85XIxQjq@Ex85X26 zwF%0_WGOPr765ZT82smHm?+B99?h(4qy>y@7WV1y2`wW48mMg%(e{?0Vs!YQS{#e(^0`4Yqk zQ}g%MZQCBbBZ<1O6~uhhy?a6~ebrWt$+Iy8#dq<*07>&sJ3oq@)LS|6FfOBS2oLGoA8q z{9>*@iU*gbUv&oG^3PD~o-9#r@tg#<23l;c^gE7#B;s&VniRdq#!ma&A%ANW?%JtMa-G2q`f8=^Omp?9P^@NgkEXcu0l-= z7v=^9mk=8$!nnjqPB3t=KE_h*@LwCYLe9!(iG!j}oD0EBHr;4P;XIK|&c`e@L!m2*{urR0Zb^Y$-DaEe(a^bnIHHYRa zcGdI0@u)X^*Om4F0&p}WSrLY4`4TDK?4sUFVc|u)X~fmrX9}LLzP)-C<;~6@fsVd! zHj#4}?-z`{n#nJ_eD`S$msxqLA=)lrb4y?I;E)=oz;F*8UTB6`VCran^xDX4^`p8Y zj@L#Yuc6HT3E>)oRmCeQJX* zA5Ob3Z8{wZI*F`!85ATUlq?Q?Xh(^Nn&)^~S^Y7AE-isl;`XBeX;uEB*X4T>jL-fi z*-Xh+4VgnJ{39ahx3=lcqY>Aq1idxw62un0Gy|-!UAAs6TfZvoEGq`S;;+@Lfu?a1 ze?7TaM4q7{1|1&}n6oo z;}baO-#Qur9Uf!MN0-uA(k%(7hfs7?yoP5bEI;f7Gbuz@j)?T+nd0~Uu`IIbkzJHH zDN{Qf*u-w)Kt$&tsfAui##VeuwfrHOb<)*nmpqfNNg~H(x)go>!_5EV3}~Dt-X&hQ zxnbiyc3d1bRA!V5Wl&LapN&dVCk02>N57~8Rc3z_b5k@09;CD<9jRV_TM#$foHSgt zitK*qrK#B%e;|0!Ie*hnXWG{3|>(Pg;WKL0o@V-V`<*)va(d_17;MLKWGcIcBF zN+!rZWs)YrO@hvm#5uD(7sJ52W9Y$BW4rm$#hVZVi=($}!_Kf3O)x>RYNf@LxY3TH ze~{~-{K6TPphr;gxHAprjPUMLK%`pALzxugv#|cZWm$pYDlzG<)Dl*eD)pA?& z$0heSUUA!Vsy=oX?3K76a&il6Eo46e;@+^p6#dGRIrCbLMu+yRyEN`?evXm&jjIu| zjlAR==-YsswE2FgURhr2%KFNST71@&m}-ix7D$8X1Gs&N$#HY2R;Yu|NCZVgtD>tt zkm2HC{mYvcR=h*;I>m!>P-h;iiy&-X%4t{6BC+%4y$b)5r;n={n;R_bavw?#KYpW? zITY3zr1v(pk<=*LmG6kuvgq`BJDbY7*uXKa#h&p}YLD=kBMr z&%LlqsmaLeH!yY>dX2IucbXXi^VJlXR7d}o6@;)gl8hCqbawd7YBdpd@-18!{X%xD znX5umx4TpjeRwoUtiC5z>4w9YAx}7Kle+}RMPWG7+R}$Jl$Sfj<0}amy6U-dgfq)7 zZ~&Dp!I+k$TX_pTVGU^QoVXm_VMR2LqyUqE>xGOy;>SS=#I*OlIF>m&I!cdp%`n2N zCCwu7M^ZVm@iMiqRnI80cjto9)A@Np=)sm_8v4;RTA4*uC>x&rt@+u1PRa?qa2{ts zt%}mq&qe--kl@Nc*ZFk#QcL@Vo*?Lrc#BgJzF|H0kwq3H@N32x!9#hC>b~l};B_f9 z)1IMt<+n?eJC#%C?tLhc?Ys-B#p-bjw58A-rA)I-T$2mBojn3zzN1!e44R_7h+ugs z#9&Q`=YH1VkNppd6$ZJ>xICnH!^irwC`O?c#xm2TN}tzkTG2L(K%0y9r4h7=7OVBJ z++-Wo=WHLD`Y^VZfu4hP#a=Ys7|Fgt$=8_jmT6%v_XfN&%eT~?I*iK!cG+zPUh2(t z4FF&aPl?ZsKcuS6y@E0Ff5ffcmaj|6#aZ|6?v`C))tdRwagN zxyzuOx4=o^c-HN*r+?RXxwT0$^JD{y=foc znIkd=P1%R*+Jg*jOu1f*e>!G+xNLgBSfc_Y^MGen;_Jz)-EylGz$HNbA{D#dO+;Xu ze~`p#AO`Kc*#Z41l-EQH#h)?3Izw+f|D2w(w{vaf8xmNPpw#}lL8S?OO+urihk3u# z9)cPju2g6&0z0#0scDCV7#N0a_Mf+1m@a_%ng@8jk4JQ<3Y23#CC^y-eCp&I)e+L- z*c4VK9qKkkxV>8}DYb#G7x+xo$L5cBxa7vZ0!{e%J!nZ5F6RFvu$9)%@9X_Bx8$#7 zEY|9F#isYY1e{xJ!r3(Qx_%L+5apdxCEPKC&uW&7dc;Q_;+94~L3QBH-GciNKF}#z zmW-P7$$J&;aPeMj=_5}xz*Ix4mYXM}VIeCQA5P@eYKpC>d+Dw^DMd)X>BWx(&M8dt zmOC-%%90Jxw?~%Bi8jKPAu1rbP|X_12Ek5V9xb|O+-Jgji;9$hxPDnaHf(vobiA8 zdEthu2VXW|z)H>vT9u7t;<2~f*}|s!JxD(ZG}xA;n_XzdW08fEPrIz3M9nO`;m(4}VuBxAQZz1{rMjt4KA+ zvMAvqyGeQ`6>!EJAcE32_7fMVT3Ti#4lJL(zgXsr{`Ibjfoj9CS}pp-RfYV)ZRz7% zmJ`3Keq+E96TeFXNl44SDE{KOIgH;<8m$$xX2=}|)?-CTbI%MD`0+D{?*ztH`s^1P zt%m=*7^-yo=Sx;tRm`Q&AX~L77heLY+70|&HP_by1*kv>49weM=cA|{BoqFZBS%_q56JPX&PZoS^TP4qZO)Z8!lv%WT?);#v3@>Y8=_LM*lK@i~V}&Re)4{BXO$e7Mmo6k9pm0$TdRDzDBS&1GP&s>r|; z5~$h|VJT+SpO!9(*`Ol+((l?P-M9t}(2^F9K3Jn2t@_LmZ0sgQW`+-B15U!P5RiyD zN^0uyYsSmE=o|_zosSfyIE2q3+iWSay2Qkz$NTKn&BP>vVv)h3x;DW)A7XMw{AM_?C& zwbSer_D)KK){T1MHMr&I{nn{xO}(9CJ0$*P$bO@Ukn#huiP}qf@^$0ywx7q{gc~C- zJFuzbIwj}F;%{4FCjLey4Pd!Z2BRT!${UkMOEMr@zM~y||+gD=G zm0tS#+6#QU{d{@w+|i$*nx##dQe@dfCrf9PFMB7O@Qwsn^_a5T%OZjB5h%Gtz^42< zHBsfydz%_>g=|Vo%$fa82mBwbQ!5I0m0Bt|em%o%r-o+fKwOe#kGfoj ze_v73x4}MO!H{a~`LjrYkN0yVI@Zt&BRZA3pz1hibgms;tcorbl=i!^?|42eqVOE4 za1r);+tLem0n6FXbX1Xld9)fS9i0b%FQMVwgqqeRHL$`@bVcDz?pxJ|IYAp=7=w~_ zDZbl0rddYY8t^#O@kzkB>HSbgn|sjGD43vFtt@TTdfyLIB+d`hh6T*Q97Lem?Ra-Z z*X7AlElR>OKLzHoVyHA3u1xDZvZ-6X+3kzzpEUFkqsgRBoBHXx&tqsjRq$(uX}h!$ z6d%WuOjKr!ULv^+5U-%_ncdN`y8-y41|~2{A-&p%`9H3WIz}*g5Z0;j^0%4^8K#%y z6C;9UTpH@ZC!zU={grvmV-~vT_{?!skiV7-+UfQ>#<8);pKGJZ-4sGIm_QGT%bjS` z{6pv^adZq2+=0nHXqTDoFKouSVsfy4`FY*%>xNlR^Tbaj=f7pNI=L2jY^i|+dy=O~ z&q#|`V}Giv0=Cdjy{?(G$Hm^|#ubb;*0xp0MmURyhdgxKPXhhR(O;oHFeYuu6Cy<6A)jN?wYAShBbPt?nxC)Rw@Wd3Hqyc2W+T?rfn%0+%F zzPn{1-wG}71$XIE7ZH!zhoS?>eM2|+`UG#L5pEnG_Ion`B)u|(PrEL$+r5j=Qd6;i z-+^9+p7z`PQL$E+S(3CuPhroqDH?};U6LP|5PVHcp@kl6iY9cfhe;HVy>W4~&cw>) z$l2laxt{~RFAgYOiP&H{L_wi0U!---`e86gJoK2L%nIx(lyQIXXX-3}+vJW%9Nm1{ zLNM`a6>E@9Gf#Y3WpFDF1{d}b6dcwCVxEV&^S6Mwr2qS=w*Y}3kO!sETc*1rN=UlEvZzJi=LTv+ixh?I*)2N5Z>*U)P_6# zEzkEn+1@%C2(8T#^E=N0a%Xk3ACh? z?e9J?6EEE~M*Py3$cOC>EJyZ;e6eO}WWuaSexEoTWVzyvDHw>{Jf@IxRCRWs18={J zk_{!eS+3XpTP@dK1l&+IcVfj%f3x+AKk^^xwv0w0$1R7*z( zPBcr>0VNL~_ICkagId!{6acmrccn^P8FbBD=L-vUm?C;r19>l&7xwsdeIz$qn%ii7 zM)-TB9|wy(f)qMdGssF@Tb+7~PP>Xbbt?y;44u;VB(QDX+J9i+v8Qux9 z=+Hsr3_}6ld(AJ_=Br=faTOAE73ZB5h*c>luSuh?w?wy~v$1F5MQ~V>za-k4T!Y!$ zF)tBa2%f!E*L;KByk)^L`cA%Q|)~k`C^DV16a9Hp?sH!N6uR>~Ic1Y5OpD(;9 zrtHz2uvEH-c|CI}ERIVvCHVRLhhB0IX&!l<4kkDgm%-T1Ct0;>)3Jdr<`@VzGaR8?!yX0C%Nydk9Zd73r2$XAkF`j%Yt(~iB)V7tA-Jmrw~ zM@sE1?jzh6^D<;+X5K>~W=UCvBsm2al-h5A0T=e951I~@ke-^_WNHI5aD#LI!+r_1 zP?{FMP$FsOYgvw89Mx;tu{)-ZfV-RX>xEYNbOQ>+Gu~s7a`fA+ebmL6h!PGlIEd6U zZMrXzVv3b=zvrs`F3y&mDXz7BP}v;|aC+J-SG{Z%xbF{buTKt#>mHl=kuOv4wHlm0 z^>~sFgE554d8pLaCE2@ht zdMtR7^jf-xov6&rkMOG9PJ23LFZsIE#JKHS9uHoaw|K!wOCWs5yWbKz!!~jBD(Qp; zij!PETF2M29!se&jpvCeq8f;;KaiJfL!UzsKd{8R_NmH-hzhh&?0X+Yp?1n_lW0k$ z9d*Icfh0IWMB=ELY|@FbJ1e1xzUjMp^=^{3DCwcLdi)t<9vhxqlN`cOht@K$5c3-s#?6YmmJZUNYrOwP_hJ@0Gza0!hR zCzwB_P7hht3&I6h@n>~OX3Z|>B?ZX~mQE!Ga{#@l@ZBz2AY3jo7cU~ikl2y)Hq)H~ z`A(jWHtqysn?kw0RA|rOmL+&xLyYT04!}RrYvtR09Uvnq!Fyvd;UEX zMr>t7dN@84-N!C19}c*7U}SOKw%8Rk9r3E*)of+_?rAK9NR9ycF4;!4ihYC%Mgsoz z)nh@_{jSC(zl#j!@w04Kx!&1+lZ5Gbvgz@q&*SkbAl?pAnyAJ)@@cIAyNUUFN zK99F0JOhwyUp|QjDx4Fx|9b*~(V{&aw*ML})@gp2A~1JMQz;@^|L>{(--CJezsI5b zzsK?4EAzj9{`6YE_nI3o%%cav2)Y3cYX89{wkyy7UC96Q=TGl`3qV_9^BbJN0%sn? z{wMssvHJg7vwwF5`x6Ed0jdurSp8byk^7&ad@{c6CU}em3ihQy^JZS=6GkCf>o;xx z=VtoPHxvy1ugCn{o$R4E-~NtgF`_=_{cx@3Fq8OUBR77zFVJUmPRqD)pST~;yoA@A zbLDow)*5`cJ&f}@v>otkOBjdr0iY$b>6ws>s)jNAg!+a=8b14d$xu0OFo?pNmP~M; zC5+fwvnH;{+A4vU-21P_oY5r4MuOpddZV=5a^aZp+eRQpRv)fN>Smr_Zb zRS<-Jqa?W|*mKdix z&%8oEDRP^sJB~a7DxwEooKnOARr4hq?rwWBf#s?=SNAXD2OUR;V}L?tnfcn4W-GV& z{3`F5=H zL$32KS>>ot7%v>Z3cNSbO=HmU%V8>G(erD8Tq_=>%D-b27s5RkK|3u}){CM`PoNYY z6`J?lGbhDc5_QFT4-FVAvR$!pctCD-T~=9&^JpoH6HBC+LPpTd!xYz1R*;D#PI#c#Y@QU6*wZXzg zbob#=^rPPythE{4z~2hHRBtSr6#D8h_-2tf{JI1+(;}_3ovGsbA8Zw2;VJ>@2@g1 zlLmxJq)s=(;17D->oe+JUo~b)?E}_PkK$N$SVQo=7zg9M=(KFA3$6b2qfE;TaE!CH z-hjAUt7kNq$X5X2uGUz9mOoC~-S`Ec^-e_s(js?Zp<_I20@Nmy#nEC)U3Nl5qS+o# ztGUe^0*YH78Zy_wKcioV>(-mIXr9?#ANNui3hvX>iq^gzj4O#iMi;3_3Lj45iHcP# z6_8M76_X|1t9k%L)541zEQD*pbGJaLL%eN^hRJeSRJT&Pxgd4|$j5Z~-CTLQ`=3~I z$CA&W%!uTMz$Kqoqs*M1s-siY_OM}Npy~jPp&DxF&eU?-zIrUW2q=(|tnm>85*f{3 z#bNcBg}369k}dZx_0#whu!&qWI+-=5byn@GUu%!)xf+!vMqxbbWNWI6AkckEHuy^n z7TRsN{|l<{h=9lw`f(kM7(9MEMYL|sB?Ci_8-rKdO^(x(+ATwwd+YMsNke2S2YUrsvA4^+kv-(_AiH`lomf7&ts! zefx>rw zGZK1l;GF*%PjfONE!;S??0_`;Zg*X8uStBt(`ZH%%CT$&!^o-FUJzrfPH8{Gl^wvq zB+!(t+eMj4JsoPuEYv76_|qNzkEc+8`(KAwnE`=3+Bq*qluN@jj_r}P(rV6?s8W9* z0YhsO*nuWP9V~z{(BtrwGp8gY*MXi68xXTn7+syJkRm?8^X7GKy<@FhBZMjPdk2l= zl?FGuPL3Ce%R_1Rx_Oe9V*KV-T5U%Y$+;z~6}KV`ozJ z+@du-w5^Bx4vh%ji_neuzDSF9I}Vte?l{SSv(n9%=kPde_z&rNq@{$(3Va9(M-R)# z3>k)iS9~5nZ2lRPFsYflDX`KVt`P<~c9wf^i6)b2WJ^dI4dz@^*Y~uSYRxiP>(++5 zvvY>XvR+4HOt!9+%&`wVubMPX_e$hHsFUT=vKeR4O0Th27?$%sySBP2Pj2vEGK4fo z@E3H}&z*FJXz_}0I*!cECfeWZT6FWi*0qBA+`;sS9)6;yGE2gz4dV&YD1C)7xn!!8n=}DbIrGHq1Y*s zCXZ`Dk0U8g6W^_te#tfuar6v{KJxd@WGm;RJFK_f9t2%nFYhaix*a@>dG|MEtGrJ0 zxI2#L+Q;M*3=xFqrb9@0c?>~YNnfii<*U@*>{aP^*TjEQ(7My_p)l;^HoY@nzPlPa zpM_MyAV5wZKSYP-=I7@1*oV;#T)k_Cem+~ISJOw|noMl;n_MPG`m5R?Zm0Mrq2(g^ zhwK-RYm2+X4M#;56-%I;vP%J5^}Ozj)XJvCntk)c*8BZ#i{mVE_7yWdOkJYMn5q?y zm1Ox3d9qvHiL?tU!#bgEFZO&?H)GY7CswCRyH6~tk;KWH*zeEff}T6}?gI6`Y=-`p ziP%&JmFE*YAKBe2Jmow$`DS=R_k;0ZhNEeIj+;Szmc$c|9Prwx6Wb`aZd6XL_{&zF zd_CnEugmR3x2rW%9&`_u>XamP!nmUi=h-DPfi#O|bCMp>8Tz5h7{?+O%t6A1!5R!6 zFQ0$?tLk4*Cb(3tM~7~fR<(O0pcp-oU>Ctizpo?W7bVFD3DwZhu%q@?_+VtEm+hpJ zu{Beq9m9mavUK51v|cGu_W5b$Id?xnGYZB|e3WNcuY7V2FUy-VV%xfRT$rmc0ZeItm;dg4#)eKgS;ZQx{Eaq`ywSTs)_A9rLAXe2sB1LBfZv-R0ns9oMCL z3FRFhO~X!GQnt>(B|{AH!#{&H6ItCL33RjYu+NLTfoXJ04fo*-%tQu$%o-=LuSmgw zTH3z4xpWg*P2{C~+KzASXaLl1JZ?@V+&P-+n=5_s`cz&v!s8m-QMjxL$lM>B~8yIACrd8$HGeTzRk76pK{iP)h z8exU4^3BrpY&k-90}MffXWNy`J(>jJa8Hw+A10%99w`0ZxBE%*pKW*ExES`xMfpCL zSjfs`Xh=UUw(@tGndF>*)wti%Ts>U3nN(zX3hdIO54Y(tA_R*@lj0!&AwSi)#-7kmLbkElH8NIBbMBjFF-}O1 z=O+?ZQS*#x|4--zB@CEJ_H0c@4f`Z!zvMs)^e*i89W>l8#zS)#yA~E=$U^goFsBFO z%OXU)8s;AeoxX|rEc>)_S?i@ebP)3P?=@e@TG`n&YzJPr6$-sbci9(cyC?x-7A|r| z*ErXe+b_DjF#OY_aY>4|KE!jpS}4thzb<5g$&6(tbc6X#u*Zs1j@l+AI-R)8t(%-$ zM0Rzu5h}-2sMnCnI?13iKlrw1nyPO zhX0u}!RSIA>_)xXQ+M3bd2Z#dd0DWF)1$fy^YP&qtbL=fUo`rfVGgb>n)=blw}O2O`%XQq*5!e?R?? zM>fU%ABUFbC>Yx{j>Br6x)~ss#8>|NQt?vM7NG_7mW--{0vO150aVo>`gNCwV zT0*_ST1ywJO`zIv&@F!+$5VqhIX}g9Q$121XTKn$MsMP7Odva@NBZYIVhK@Jzx@`J z;ezb&8hx|SAf$P-GrWvqxK?mvGw4D*VhkyR*-WD@N2sk)$3=ugQZ}T4-erMgIPG%z zb}THZP!^gPqqD6xt#va*w9Fy=-fY_ubd=rmIlO02blxfosskKth-% z$h2y@!I?yVC9~T+T-|hKd}aJy*6X`it>+N#5O}@ovPO(A0SU>TUtLGK;6C^V3s4gG zpj1P?$W9y6*euf?&IvR@pI$~3?CGFO>w|Ccu6VA5Lz;XxBF&AWb94UP_n{oDG-O*r zD_3fR1VBC-m`fUyXrmOn-uHGPg1V>3#3gp?uSof5>BtRVYnC+$!Z(T>suC7KA3gBG zm}qYDuIcq*M9KL#d^6LenbpSymW1`^IiuN;1zmT(D6DYd+hWQaJ`eehqPadwUO@2N zd<%TicpTVjqV7tcnEf{q(8Mzz?6}Q9(Nz)wQ6O6HF>{3-k@BIvo4*N@>D@m@<`&ZLIC%!+qVhId%+|#M`9QzY z?r`IzLbE{r{g_jJXcGzNMrjK8A?RO64FhT7ov2i@b{;@Ed_XM$nA9XNiPV0o?W_?` z`RWcLSH+UpZ=HP|dj(XtJK6tiyr}?(m}i*2gvqD%js!-N&bAMHR|?6ZB((8PhP@TZ z?CVl5;NJVw_55Ygo4vRN2Ux0mOJ7^G-*tmTK`o_N3a9#pd3P{0ug#(Bww~y{pw9#tED;79;$9=_kI zgeZec>|d%R)0k&v3HId6l7B6O*5tzg27+M$!F)Eqz%)1>m6J zw+H_P-c8bO%gerCM)eFpq}G{&GqHjk)R7LyvSh3E15T)Zpka4_lq6!Id8waIpUD%S zh#p_YZ*7#3_bcjGYiE=Z3bohtSVo^`DA8=9oV}1O^{I`bL+aCs`Q&5O0Mh% z#DyIFc(vy}!Ma(&r*RV7CevN}p>YCJzZ{H4Y~HVP${l&BvW)aUCo~mVK6KRk2hv-I z4KFP9~(x{Fcie1p+U|4x2AR_nVO^QfZ8-unvQ zPloiNP`N+Hy*2Sg@=7@SU!PE%LA-~d^hbL_qzj^ZvXsSPXQe&HaY^n`dIVh#m=Irk zs~OLy@v|+{Id*^Gh!?49ijZW^yOuW>s%SP6xO;@@dY~(a zXhvrWVc#x3D$Wt$;FfO(%vnsGRg*13erp~UOp@*K(9`X5e*Om>%c%J;Bs5XxHtdyp(hbnRlpG_t5Sx_3UQ<-rV7U;Griy1yQ4d zL}k}Eaj)0>In|-fr!pm)Gbp-aGjS+wEiwFvbY!pRl@D4xGQ2`-6=CdRAP&%y3GsrB zq7g`PW=2Ju1Pt6O8Dc(h#l2!^BE?P=6o__W&Az*H-w{9-8To20wNXc6dEN+nIY$E_OEeXn94zNy8k^wXgf zd}CEF8|yU%7?7HQa-563s)b_Vjua%H>rA&sfM|{-Sgu$@g(;j9p8y+MpJ&35UGZ{R zifBY0E%CZ_i9Tg3I1%4%nXM2pD;vh>tdsCbJilqyu&tAJo0jJVudKB>?lr6fblu7t zCqDz$1CJ0wmPXR2x!?d@maFx72D(mvcx_E}n^ZjxG`QqkP9{OAtZhXvG0$QTTh|+= zw$dGU)js}dRkgCQUyp8-d29#q>Hyk3w9W6q?g2hHv*7+zjFznQz^`UuDlG1r#@Xmu z)0HCWuZ_f8Y7V1yg`A#O$P@L0Vi#U2%{-jT2VpZs)#%p;9+*3Z|A~)&JjF+Qcsjp# z9f)k7m(_e4Z{3IoN!tS1OaS@)F-LpP7!pP#>@c9F@^Hln!4DzF_|!=y1(+pI5f;ws z$6U9~MkVrimFJk`m{j3DQE9umJm%iOl|30O&>`_)#%1>^SO{j}6^AK^5%fal z#HkwpD0rYU1B7mW2J}+4J&l~>Ti~A3Ro3ZxjagNCX|N+lvRV?8Ep#VR-(*1B_5aoXycV>YO(JfA3j-C`BFx_j|#IxwdwGcHDL z$;t21T?jB()@UXt91u?gTf}fKkn&%FB22oMs%$a*8TS# z@-OBum&^TRqMjD7HzIg5-bk@m5e+|txdm%IjN%czxnE0Q6t?bY6yeH4*_O;{+#sKv zokaMB^=#`$vXNmtw*BT8`MPfj49BkE;7lg3Yno*F0y)ymA8Rj7&~e*{*jvy$130{UPz z%zDAo-@|I|4erfN5rdT47+}w11-90ai1T_wy+{$rkGV;cBys2-I^!s17NiN*R|!55 z>gSVNvBvBWyTt3LR`>tp>?uLn@=LzTAz_(53;9D788L&NeqM{IIDPrjxlA{$;#I5RvRHtnVEmQGsZgv z<%3RCO2H&H)N5@LOX-TDj}*TKxB!ePoJuoI%uieNJ(9ihmfR|_Ym+J@({t!2Ku#^< z&N{4vjay+^cUGYuMV(mGbWrj>aitfi)9>H+kj!`DtwyRSiM(bA^zv_3vdWGkn0I5P z(fEKZ3LKSvb_8zbM@FHbt4L`+n_Gp_%9K3D8LABxk0wv z{!ii2;W=Ms;CK;h7sD1nWZQwI6p{C$yJwcjLMaonUt@|~K~nzgVph?({X=2p2@{YI zc0Z#6iJpY6W3wcC$PcJ7;;x#}=rVWyy!ZrNZBhIDdt(=Ej$6dw>lGgEDz?O}P6E4h z?;CczN!EZZC&N7f+McexwTQ0lu%O>T_QH;)m>5Mt1q)<|OWaS6I`VD>X7hNRH zeLU_>|FgY~As(sYGk&m6UlLVgFoz6oKrypxqi}m1aD_GT(fut0Ev|`ttI>0;V4HBgXb& z+R-Ww8GsTRkW{H(BaH+`?Ng`DYRA4q3*BnnPa?Bi33_>i3b7WjdY&ndE8$MsODOT$ zBelA}ry@Kf{*zEaR;uJRK)__9^PUJxW&Id3{Mot<8}l$z#_F~98ip~34JyHqo)v^v z^5qbB$!ay03vn71FO<-t!pqchH)z-rnip`S=`-*PtG7FakMWfm0K!@jixSs@DkcR` z%7W`HEoD0Y3ht=0B9J30?$O)tG17-hoGMxiMVfLTHUaRIR$)dg)nBt`4IdMeK>YI; zpFzelW^*pP?z#KQIs%uKi0ocLCc$VTg^~%+J|lOnmXe2K|7_z8rL?ddz^kKQp|9f3w=AP z#10wY+UA3Jac$DvFm<5q2{`yiaFRP4=`rPOlGP#iT6VL6;e@oB&@QpQ4GY0{ZU5jO zf>jOLYe6*zF&TlLQP4K!N|df)Q`Ss%1YIRvjCJ_KX5Sz8z#f-$57HFZ-$Z%P2K(qE z4j82);dU4a@3j0#O|rLL=RulmzznxQ95M&s>c|Vq{T&8pQP}Za?7x{G0mtXx-5~>< z)mt1cX*58Wmt=8jywZ*`pX@mJ;-6IGV=N$I`i|io%PqqW#Y$kKs z(wQH>gSkf(%3f|3S&i$O4ZmlUB>Y@aMuZt?S=O9aWnEjzJ#V4BJBL#S-d{MR1iuA9 z)@tg%0dWn<0f|j#>m(qXXj+V14~T-p`mZ1pgZ4_zt&#g;I%%O+YiLG`ToN~PZ}PhH zERn4{)j{p3%VNc^s`=`APlF~Tg=CO&s4!?(=e(j7BOb^d@g$dGzw3{p(Ua!THRExJ zy8CIlN2~#AIH6qO&(oCS7+u!LU?_mufIbW3{ey7l}@E9%)Qe9f@1cI7=uZPLxfz*a^hzfxpm| zSZ_OD!&~mP$bkyfP=YlSd<45wA_bR}I^J?yuZvjf_BMQkA)|d!H0OD=1gQhjc*St- zCa>`~?6Fap&$hM_(V-^0Q`iNo=*2NDxgA$`N)KYKnw6Mk%TKm9!+DKer$|jRQ>SAo zbChgXj#4&SC;N>=bhDTdv*7k7vTqjnbt_+}6#|=%{i0A>D>ce$wXBkV_`ZVtZSPrCQZ)Hg0o{L}&+#|rSa)0boek3l;Rwc|0Y|o3Vav}E1R6*)&<7{PBEd??*PGyu= zWM6_^kI8iCf=Z}Fq~TLbq{7>zZAerE!iCFw6g6zqXGcMyj&T=Ka5IkMz9+25R&36@;VI@VNS38cJMXEvF@T_4a9cxy76_q;1{ zcLj)@aoMUYTFoK8m^8>ljCcz_YfudBWG~F|)pTnR_>da^*d-h;#iz5OT5^Q%ju-Io~U&e6o@K^>=*cR z>vpAhU-8z$XnHy6cAbhwNTu#8CF*p_3wBSGxtAKTe4(w{U6vevrT4XWB{EIH6Xdwn zl(A+(OgG+=)#igy=hee;P=(TibNta*cOa#l->l~7+qKIQM!BGNf6e5}8rP1{ljp}V zbc5-7R^ZG123>{b6=0a$Q-c240dxD|>^SuG5tEcEZexTf@QmY5w)dO8+80I&ZCEGC zwJcM3S!&s072>mfCG8lA3Ltwr!~V{Q8^Ws10Tt4LH^yTmZ;VDF5?g#-hc9~BHXLRS ziF(h-KLJDTzL;-Uh?lVDtFNA|q;FKDA&*@#U@&{R^kvEQdY>l|%}*Q%h8jJ173zQ7 z@w8fCva{sf!9qUL)Vu+%sAI9Y{^B$=A4VMT|DF5za4^qg;-) z6ZF9wmjK=e)nU7GEs=0q@GF}QK3XFa_617TjaA(ND1Ynns?7D$BC6Ij+6alOA4~F4 zUM57NJn?NPpvH~-AcG(F}#9}fCo}-ssS+s&Mq#%h$ zbOXKYcsGzu74aNBTp7U~M%gzPOt8&W`QM3df?aBhw%|vX<+4YM`DWp2Y$09#nD%J+V5ALI!qa94qLuWOJ2|fW+XEHet33{FxLD};D-FFFmzV?m z&59}VhiJs^+VB-gL}St4uVQ02?bF!_N@(>#6+ii*Jun}MAyz$nQzY<)x%uSUWb#p* zNqW!V$ix$3t@d}48ZRy+uFnv^IY@?cIu_k@gwuriP7Nj;0^<+Azf9r#%78j^Fc|^N z3|xN&%y*)ylQT)k=*Z%p|`pk$>i?B|rem`mLnnu7Y zBTAwcq+yTb9MvOFZ_*oX^{#DrY}G9LO1;V^|tyPu6+f%W)Nsh)oZgDCbt& z24|Q1dmTt6W#QXt2xUqoOHUo}CV@X<9SP(>$1s})?bP19>j>xMF&xb!{rUu>#6QhN z2A;C$)0$b4b*D|HtG*beeAf&ForYDay68ZGMMkPI*cbwq1M*~9p|olPT=>?GTN8Bx z@QI%tq0d!GdB24hDNG}Xgug;%RLU|&`kDi(5tq+Z&hE{Z&+TwGW>VVT1#;Iia2 zjK^k?Mq`?7*j6@yDrXy-$w>D5BrQT`P465)j!{JwvIUijf*{6Mr;)Q~1fN^%dImQXZ;q8tNJ7g5=v-J4c>7qUn5)Sqlao_vk26H_LquP(GI;b;} zKa-IVup1u}7Dfj91ioP^HE!qJY@h=kDQ3wMCTl~` zk{U`3tQM!NB!-ZqAdEMc2Ro@-XDmXU;eJcwE1veq2kh{H4{jfB&tYE9rluzXPhLh- zs%kP0YHMy54UACmm;4Vva)$ao$6!a@fi21`BK=F@cZwZlw`usMk#kYw^P#{lg2ot|WDzt6}t5S{! z4A;dbyTNFgt7>nBwzycg^-F9v-w0_$Jm=FOcgvqRtu0+XAB9CZI3gcGl~gV!O4p0{ z6L{)_Qt|=}YuPDNJ?)4fKgxiCfCkPr&3y`AWUfbL3GIF)aK)=qioSz-!E^fvG=R!m zh09xGc(%)+J)Vc-Lq$N(-`)O^;Dy`SCFV^DA1-qi-Z7L=i-86x!K$Z{m-(_%YJD`H za@w&ZOv|5@!*qnM=ByH}1tVJ1{r{i+e?kYH_TZ%=y4tP=*w1oxIFw=ba{XwKMUbU7c9(pUlLhIGr(A&bq^C zd*l8`etxUp?KigN!!R44s*L2E$soP`ks_O8C+U)#mKSj+5m6yVo<$Z5x=J+;xuGwm zRI}uBBS{RY!(jkZ_xFRi4o$Ig-*&u6Cq_0)R$Mr2g8Q#UjaS>|Q-i>&gIRLfSo;6_IU(PNWH*t(FL?>duG#b68 z1STVwqf>Q6vK@r{4_yFC5j(>GKt!=Jvl$Yd4t(OJjxZs0UdpcVTYf#k;||JEWqfuq zCE7}@RP+Tn_aR)(Cin8)*}LAXE;mRlrtIy?>OwRv|3fbSc(M?a8Hh^1= zF;vm6s}Yl2{2N*etm|qU%E1#%YJH@=d+A+|3I%j*qI%nyPyG_dYeqR+gN7$EdKZJg zC>}rUU|+Eno!HC|ZN`zY>v~f|36dqG_-_UK=e2og{X@9Wq2wJOOq%&s9BqZk-zwJ4 z#1HRd0Jzyj_6PFgRo2x!LzAUO5%Cms`VK#z|MTzvQZynM`Z~3_#;->x<5$O2cJEgJ2j=^KPRlkX{&p1ddu0~j7bV5)t#B-n zqfqmYB9e_My~eyxJs!X>xbn$$F7Wc$m&UfSxw;-~SxV|D}Szv@pT# z&K`}28hHkjh~962++d`|@5s|3My%)wY^R|3BlUfH^boD>2If|cb=uNY8AA~@Ki+S! zUj$m-kw@ATs)6BCx8&zI46UF)Tlnu6JlDTz&d>m1?JeKH>!xN1> z(Kd6ieeW|GZZP}Yzg4P;65t_!hSy4i<33Sv7e#ODKpS_xfs#&?4#$_K5b-yZ{ov1@ z7`R!k?k=i8L$*|XE2Tok6!G5xcI_ZWTo);ToDIB0U5&6>s_Y8vV((!e{)-1vJ0*+= zoo85>Valb$SY94?B8(FQdtROpoKwdj@vN7i201RxSt$Yt-X7S!a?kb4nH_2K!=+$&7iS}?Wh1%f|k z%GKBpHHN0Qo}Yv~60A!7Es2uAC~`?-(OqzL;Rz-Uq|{hW@evYj4@nU-v1!PEE7;xZ zde_(oo@4%C=NPA52rZ}eNaTgc+of+ScV{8N?OAvPcJP%SCxJYzs;O}Hp1fBTE>PU( zJc{3OeD87G?MLg0t^Doqgd2H&q>qB7>U&KTDfv0x_7NiYJ-gB%dhtL4Y*eMoeqK>h z@b&J0t!YOQVI%23m2F_eFcmryQ8%#eFle*iYpnLsA9j7SRqLgx5=M`onE94i^4kjU z<>5S`pXPLk0dtx}Xq1j~IZvy-V&fO@0_A@Jn$D`%N=)}mm;^}-! z@-150;Qxa=X}9z>ZhsegvgNDhKjb+Ata@*Du|nwFJO&N}_PyudS*)8vKk5GyQAtPmAkQG(pj<(?U%ZP z&!T_x`Il2{sqE|L@X&XSD0~22TK6&D{l>m69j0KH?9hX{bQ^fS#Q%NdL<1`sp)egv z(e3G(xeGb(kuf)zXa5P~)0e_QWsPSISI_88rd&li92)Xstz?fo#7Qo>-NMPQx z$+zl!6tjK<>;Ro$x4qT2 z{bFZlWHno!hB!T5{)4!o)Q2Z9_!xN7(KCWX+u=Baww)be<508h6j;Gj`LANvm$hB) zHGe)l;^GT9{1WPvG$GL^>Vy4E*k@p=VdINfv<_&e%g8O0{lQSE@qJT_>{Q zu31`>M@gEBjF@2x)z9oRlf~WoQRkSt$JKCGMQ@|JA}+_UzV$69 zF(nvk0AEmn!2Gm;VB!D?WH|~_7dW2bE(KY}uy`%jIu_MjNdEkx;!8gMS@b=WDau%_ zb9$bB;m3DV8T3X%|F&AQZ`{EH$d(B0wm*yy!%6EiPlED$F%B>IOl0U^8VH%#+;~vc!Cnj=! zL$`hw0mRN75!{mNLz(2M!eQRHy-UzUPV@>uQHE-lac1<=|F33@`UTk zd2`HYDW^EvE5P}5PF1OznSDn-H(LJ!foSp3-;VqXA!52ij~CgPL0z}Y7dTj&#sVZ} zngAuc8{(>7mfp9;@B5R0nO*OgFp9-2fci=&-&nHA6ctIOWpeN+3_wk%z3@@GTp%l(`z5089VL zhR54dQ5A|`&nw_NZHkzKLqu0>B|FJLt_oH#5&?ps7^D8I4QD8(uTJy9gm1J^J#^hw znf$L(2eau^lFYMLvXVU<#g*smpe(ukA18Fm6{_)emeGXrMnwc{IWeEMo$}7AdYvYq z2)|_Z+a4n&TmVTXowWhE{iYLH9`aCuIZ{{)c!Nh zH_HYXRPwUXOza$_J&i;WYdc5Cxj2OhP#_p27m|N#_Xkw>PwQ}$SxG%VZ+G}gh1(r` zx5Hxd7P&YC5x}B4QIbE0M1BAG&5p>WK!woNI3mT7*H`l8iwKaVZ&y60xX zApc@U@_MA9C%5emJ z#NM$OetMlLJDv_zNqL%3vWvFiG)!==Lc*jsd1$+ho-Y`xh6q!a&5k?@(HWb`byTU` zD(kTc^)|8l(H&F$!5%#?=yOLgGNHa$4B;!qRTAa;d7MrKZRDj~mGUat97TD_ZMwRg zU|>per#ut4dbe`eQiT@Ysria0Km|AKLZx8pW&aZ4C>tq#sa( z^AhnK{O6Fs8pS4qbOI{i`uFU1@_F=lN0du*f1vUCeRqtaG*X`0JNlI$>WUd0q@zH} zhKOx3FiX19vWNB9*ng=rBIx|cXtC`Hg4E@1lXMMHY(3S7wp=&8(cEyuPM{+4#aB$A zUZv`gWohfwH|N^9MprT)WSuFO?Zcyrgf{E^#H?MXQ(@z)VBoR$b0nnTP>a731NhGa zX|ITfv2Wd<)Cnl4<(25flx*mp*g_R@&SDXNGmzvr*>_HB)8^C16;_Zl@)Dbm;^69? zBck|=wkGzakbsj9UY!Uc8!}c;g+c zjdV(Z)4^7;yS(2o@_jYv-2Qai&9D@`u27S=37Tshe&6)oT4%M-)bb5hZ$Y7X%)d-)EX6HfH2&rbFvK(+xVew;8?zHL2E;Z^ zD9L^w zV-P=20`;pHwc3P8Zsdbidk>oJDXx-iH0c@4$C~|(56I?^ww`C)E&iWXhL9>zP!hC2;00vAZA}{P4Qq+qIvPRi8E}n8XK!-dGQpfPT>05u^1A-Kq47Vm$eQ@(7S*JgZFHo-cCDy=r%fwMq4Eu{@%rF z_ho{LZsEe0&d-|?D!SzI#lKCXxd%_xp@(cj&21NK}$y3^58JpM(3!pi0reB?eR?T58(}5S!ZmEHBo7LJFsIwmK#lTqygKB8$g^to`wnL>M4vMPK zg!+03)2hU6-DjtGrSm%%!WviNtzL=;Kk;mhdMP*ScgH;itpfqXP)dO7{+ib3gM&xj zJXUp}3|sEKMH9>Q%x(Nd(FWwEasF)RhTN>CNxzNZL`OE)U3Lpde)??2Dff9zE_sp4+q%5471* zUYi@@&Yo)>Om`(e32>D1?bJBFhC_ZwjSpoe={@q+Hg~K&SXZ zKwVO;8A{AA5q;J$+zDKjk+Z-c>Y{}29QpWtSgd3QF3*6=H}>`uHF z3TwxJqj!9M9;EY(D*Pr7ycAA}bf6uwPbg6Dar4CI z(=UJDSM2SwNgVWL)mea5QGWs8Yq|vSXyWcv~BL9*9DJ&T9DCk1c;Rr0F;yEfq z`J+-mpb2bLYFOIO&tOr{o-^pr;dty+xj4`3fW>jR?8NcfX$;d=xy+clm+K^7Pwnjh zH{LP5wB2k2dM}I>Vk0)@M1g{#iQ~1{{@KdRMuu6fy@cIqt`OeFAk}P(kFOT?`^!dz zrswj?eqoP3L!z1RwDXi}PL4^?hqFptS63mG*EZWzWM0`M1wgqR>v7x+t(`c69Ja25 z{*`YD*0}Yk`YVhuH@E624Z8X&g4~HSeH;lm&y@}*CHQHS5g{ik$O+sb3=>o`%B2*qd7TRK+Z?dXU8!u`wIb`B^q#M zjApw}?B2S_rE5-9BTfGDrUmgW3dZ8?ch5cGAcfs|e9v&0vLaOTdg^%92C3*OfkwH9`#%ydUpakr|ZUH?s2RI4V4y7>!<)Kx-W&;Ad zMxw91;mcR@Syv9*j1%bFNxr7x0V__)HPGBwn}zlKDq^)|Znk5ZrH6b3%h+64ujr~q za`WMRE$M3C@*=JNGT>EW2&WHzBnh@?T)YIvckR?yN?Vt!yY-&Kw(Q%FCkd*bI zO`mHN%5GZCYr!h(PLMuq_D_ETN3|d8gq9!Vx(D!Zj*%x%1!%H9e-$XmdM(ev{E&*U ziSc#x(-^z|yZ&E&6r)Xc3$#u9UN?ep`S$g^(kuBSzcMJA=#3yR+pMpTkN*j5e7qmV zywNSuY$6QtBI*EXJRn7HPP3a^iqLH@9o40KqDhDHkascAWAX_M7Yw49AM>-l8YT6P zr3mEyy>-fl^_r8LqnsjGaGDr$$Y>fQb>ZxaeZB3t$aGr$ystC=d+lE%hT|dfS_51x3VqJemhHALwm71^7lA_| z=6Cw$p=z{z@r-K#VH!H+dgFVZo#5sLonE$0157RQeMTiO@H&CG+kY(u>$rfk7vg$j zwAOdc9EZGoxiNKyRKrHg(Y2g~VJGD!z()=P&aVT#T!|l4P$+h}!e!W~aWx?isuJ|| zt%Oc+b@80C*?1mJG!NVrsd1*hp_N27=kjmM^3dyXiU{`yCa7D`L1vko+Zj;N1oYuo z7TN83GS$u^$cu1K{Tnj|$gUr}6I%N`(#vd;nQVSaN%WPRu&4(7*wkmu{2LiUmFk-) ztL}KJ9y;W%xREq5?q4}Qu1&w0($2^B2i*Zjg}_W^N&31dbvFtOjw}@VP4p&iu>x2Q zZ)qSc@e3;X5y^`i00L80f}1q12&^J$PPbXN;IaLGFwbHJ#ns(B>UO{dZSh#KOCB36&bd2WDX zJA=U1(}GnlkD%K%G^yU}teo%SONSyyg1tRfD8pC}MgACD`AH&Oe<1urWE1Z)ftW@u z)W!u~xUl2g^YR(mAZTRjsHSSP1H#4&^rmkT9EF;8V`XvhUvwUKJqBHrEm-_JF#J$Y z1GP4QEG^b>m&u|b)^Gvxp=|84yPE8aKmz?e!Fz<1ka!3gqu#QrIq~D|kz52JFaNL3 z$FS4jJgY1cpg5P?~BkZLkJ??X@{gY(a~nJVQDZ+&QHH7Pu8Yx6L*3RYG; zyglS(NnVoj;Cp;n#_Hn3%8NbXWdJERfhJ=PTYl~cX*ibaiV+CFcXvM`_QP;YGK%TtT@Q1Yq;*9zL7avY zw&<$m=*%^Dnw{(-vF-?L{pR386VGOEUgWvX%~e>3DQp*x-TB(6ffTN9ijVrZCJtKO|_uaS>+O4!Z*O@HHhSm1%o)CV%uJ25uCg4qYkp#$R&{k+tM zvtYGP+dfJG3AOnav>nO5AMTSnOC5m%2YtTEQ7@3{i{ayqf902BaS2UYvoTn+<*4$# z>}gdifLl#!XFZT>shf7}_*}nMe1?74^LF9wcpA7K;M}BzTbPsZb`5IaI zm#A{K)J#=7O)452+N&j&C{DdvW$if~&d|Y}G+eQIkJV2=Uw}dHn-Kee_$?R&w=vE6 z=1d0d-7mYeT{7NhyI?5Aw|w9IMB(jrpVjPlBz`7oc~8*DtDq^clCkKnjUw@&2Q1{nj=>KLERBWPC)B)~oxogI1!F6qnQ3&q|5@5hmP zJA=K1qZVfY|8|z^ouSr-kfnuM|DEu(?B4iQBIkK>V>rW(x=i%eno`#5xG{TRW@l{%;9-)ld zSO| zzMmXTf(930M5@?l71XzaE_OOWp>xNH?^1Phb0P&u7J&(ag5dzNg4wtY^8ksXz-{QR zF!7yoiey1YI<-xkuR=!Eui@pd1h>~N|WA>Ht zayVK+PIGEBkVtQoj7Nt%FZ}}Y%xvCWZ8|nNGuZDVI)g={q_h0uzhMXCF(vl83&3!A zivhoFJ*I-v6k=9WYV}s3i+3#j1bb`xu|@lrU%`A^5a%%8Q}y=Ta&5Xs)NW{G1CZ<0 z^R<5G5^L;r^o!BpQH%mO%836ZWt(M6Z2fJN`>eTfnl~Yeu}J^ zO({l{>@|Boku3f9yABX$FQZH4}%qdj2*rw(uz` z+#fJ-?hK6K8O#BI4A@VC<~lp2Wzse)$4ye=0Sa7Mp7?2cyx+QG_!%rukvKmcFNPqr zhbf%(21ICtkYf-uF)QSQ(!KJ-DXrXwa+E0Z^j8#6n$f6n3xvPDhef~`YEpeor<5~` zv0H%s2x`6LG##VJ;6NT|F$mUx9sV(Q=dE?bujI?5TP^y}O?!}chuC57S86o$Ixvkn z9Az1q)o@Ub?eP&at-@sz-{C+oa^nREe--e6{M!#$%&>~%b5csIJz~hTZLfHZ{(BI$YS0&hMWtYU zC{Phb{gWJQHbaiyf_W-=7oEQn6TsUs+sBJxE`cgZcPPEdd7(6*`m~<~eVyvrsz zwNH*sG5061@K413wT9BsAglBAWNR!=MNzk=bgQ^0sB$wL=z(OS?7 zOkf3#zKvmGS4{&utkn91uZaCyd3c~MsPAf$W4ET~PfZf2CfO|zwH*gTL+lV#U<{y& zHDKo(5AsHt-W?n4A>m7t%!&a)Ezm_3tGvcc_0hisNMeM)_uELs{wp{GA`EwD@7r>1 z!@tY50$-ZkD$BPrbD#NY-Ir0@Zofh*WbUzCtb`9K@bs!> z?>hM`8^6J#3;nFPx4bTd(pKVvQqplNF`vtfeHH-c^eh!R4@3~WgSq)%5C_SzP9%uE zPA6vIE4iY@KsKd;AH2MovaGXqlh4ZOitoXJx;qE)xb!2>7v7>{vGU&yeww;%ok+{9 zjHLOyIQ*+b$@Ab(p92)OI$P3ROTniSjDx?j*mtu3`2C-*4EhTZd+NKUfPK%8p7-w6 z{Z}*9Z7vnT_;aQofEKJ6cqlU|+~xQ0qx24tOpN+xEog;}&wg5WORBE9Q5uu#-i!SV zB>sbefye!rl?1Q@=!TFjMLzheHhH)753zr`8JbOAlTiY&Y6Uh8VO+1Mze=@tZ(qFo zvs3Bi$ctaVWuOJ*gr*7quL{=P+hDR;AD25|t&(q-4%;1JI(0QI|B6j+-@2=!n7`w` zgTf*tcZo^geLRL)Q~BPHPoe(#ID54yTPXnF6NVG5;*AWvmyV-GU(Bj-JmqPTPkl#;7)IVgWWatwp296AXOW(?%7w&=N0RUA0 zKV%;o*XLsegdO107hL!IaW&HaY5NiGeq*P5M|L{|dh&avH|vjpiIk>3`jxB)`h*@e z_v+s(jt0bknDq1~?hBDys$+lQT)0;PQoR1G{BZ17Q`D`%sP2CMZ{q!V9mxELiUYbu zg^V`0WGT!%czN%_+5WTe^x>avleY{1$_S9{iV82BG;UVXr@;}XHRqn6i}@G z2iha}_#Z9rl!PkD=5Uww!`Uu|ceTJ_SbM|MY|*KnweIP3eOq9-q6 ziO?5m+|LeNgH6>0J37LQJ~aUS58;&u`eJAuC8n)IkSPq;ZP7F8k}D$U_0aaKe*i6mYK$)r+_z;$?yj zI(eSPH-2EZ-45=gt(F_f8z=r%cXuHY);y*J@K+9Ln_px?gZ^o&dWNH)G=67t1tv-I z7jDish%B3)o^((Uxo)U@s&Qai3|CfCtT_2LO(s|^&41RrP6NokCW!LpC)j< z3=($AC982zBX&IT9P%A$DKc2VWIgCw(3wy6`1Cb`T8WZIe4V`1P%fO6NTTafC?)ez ze-Ba1S3Z)n^Np|4KX@Lpp0A$QGU?mNIx1Bk(~)^{C6AxK@1S5;$W^AwR>&XZ6HOI{ zTMAKLQe(=in76UFUB&|1hRAdpYD z3Tg69=C-5u)Rj=}iGg%V%Ni%+M0Sqs}U8*R_atU%KmrH~QuUzw(?eXF6dn=B*Sk{y-Ip zST7-Ds|Mqx)%y-cD4??Dt~$6uOExqdyvDfQJNNqRcru}*3nxKj)N~~qD-E{_WKV;( zh=Z8$`Lc(o--b$J5SOWE5bUDH+D(z_{n7vC!#}eCM38?b3v$u`?eWA{=hlH6d38f4 zPS>66uBV!rqOqsZm{~x*Biu~lC^=xcJvCyA6ewB?gPbe`{2i99XQm1?L%tInQoWnF?oj9XY$A85@%Y>CnxosCMTSd6!lGx2H~Qv4x>I`}q3Ou#%eQCQ z+7{Hh(J9)(pi%>u zfp4y{O5s2=c$1w#m5higyM)jxHQwNB;Cz?bZO1FnRyro>p$kAefb-vDzjwn9*h9ok z+<*wZ-UNx4Y*=P~K+r&{$a`7nx)O-(jy4XXZZPH%&>Q(3^OTfyPvj?jWJFCoCtVLvQz!3i(h#u4#y|I;@wcN(bup+gn*mPn)ewKoCzj4hZV_+o z74mrVh8XuS+f<+Y>6k1sxI3s5m%poQ0zKux)ehhnI-P{XW*H>#K*H*KblQyH!eZr0 zS|?dV>?^IE9vi9?-^)oe?8b5sy!(a7(K<$|d1z(mFAPcyvx%J5%Cj&uvLdfu+(iyh*6oz|l|j-W(Piw{P>keKu4{^uul85{dc*+=#xIZCX$ z&YKP&ACD2$9mUYKvvpgPiLUlZWh=gx_C_Vp!Mjw*ENaC!b*HvvDcYIUQR; ze(lm3zEx}ER*RiWw2pqgl2FJxSWa)OH(oScl`xW(xp_$xZiBnS40AK%s!jpG;lxL; zW+&v5deWC^khj=uw@E2;UDl5A8sXVnRZUhUD$1}Y%QiIfs6aMpS09C*=M|(D!U)QMlgIP!qYlRUrQUV-xFouf90{tBW)GR-xTq{<*`q z%Jt7MW3cDj+tZGz;&FzxU4a1!Vw!bx2BRh|KHNa>ruq@Ra~(5JmHtELSzBw@jKt0M ze6x#G#?b`QcTYBK0+^--jAYu7kEUxJ%ZKF4R;%|D4eI@+%!|ui;c@e!P6Sb$tpna_X$~OdGz{ zR=--9`vMQW6si046Ry=3yxwl_qw{q-?)=~)xUlf7br-aa_*JouEB_V5u7hf^ZX;zm zPQ6e%*B>lC*QV?7-diz7PN~4$0m~%~=b`dlSDWZ--+b$JS>#V~e5x`5pE$foTw$!H zixVh|0xmCzMyu6_W5IYsj4~!VI}jr88$X;%o%q6z%(B;`RoXBdgPvtqi7#YM1h(PY z$7m*ydwntlhYgpOh;{0|dHxtpm*q%*F$pT{p=2>J*yM&>b*i|4H{SJ?y*WXj%I>m? zuT+@m{bsifV5Fi9XP~ueW5$l&sD7*wTuh6%Q5q9(#_YDA99^(GkutMCyQ;pm<)UdY z989i5d6K5rL+{v9K!p5;jR%1aQBN}AuKIrZsqn39(v_G_&#a%ZyLtiALsWFLZ)?Jz zcoVl_f!;&c3&9u>3A$Pf!j=^RMj5X26J0Xwcx+?B#r*T|; z+lKDyHB$HTjlw}9l3~ib#V~D zJqkvXCiFdzWNnRR>OnZ#e64)6=Z9($)|%vRD^q8IdaL0gh2Po_ z7v#F7Y%)};?dZR7?9WxxDlpVKgpeS?asB?S;fZPTC-AR$e1~xbn9h~mbg}uAEc^>? z^qJ2yozX@n-b=osxVY+;+N+H8?a0YQyk4kyX-ED6=Q~Ugl!N6qIO*BWA0=wX3r33wO zH}nZ7Q-E^YqUJyY(tAILS0xIZa}FNge2?G95U5@_IU^Mquy@6jzx*_jS<`fdW=~{k zJ{frau+cxQyxY2^v!{exaTfWiLU?I&<7r1t)d9iliwOYnLTF(+>;9&qZqj}wBT%mu zxHSl`G|rsnO@pb{7^ZR8wu~R18exB(7~8=&hohP~2+Dc30fMSJJ1`nWnsAg{`E-z$ z4lHAW9iK5ds(yvdFe<;8*{sHH*kw}3WiC2M#Sdf`nGD%fU5Drw%-IvW)`xqaW@bVS z9So`0u6-A4@N5lmrToIsbw&gXkLioET?Rv<7a$Tl)(>FG9mGn`GAniYw)NTFb}*_LQD8edOn%O;j~(vQ~C;D_X*=D=S5T_eiOy^poYc-WYbB|@~@VG3(qw;tziZbe#??xrRWi=S?sor-Hktx{s%OI3QMKg5Opj{3J z_>-s-^|y*n;@hgO^Z_&PKPIi5iY{`JvK+mE#S8y7mc+Uvu$`R7p}+;W9mZlmcbs3= z?Rk#Cb|cupJw;T;e)AgqoaecSfEK^g&Phe>Yk1BMKlZZ3q9Kfuqq;x2WA6x+Bd+6k&Jj8-lCu8^j*nt z0SuSX&eFa=_miW2pJY#vYV0eBLK%7K#k+_+Ld2$vV~cxkwcMpZNI(T^wThnv76_cd zEuF^(e~g!+B8~qfooqgEeMJ-~)Cp;kA_u!+Be6e|J^EVltwpsWpY%71^l8^wd*mQ+WlN?N*dR6(KlcoTG zZ0T_Dd{sT5?sQ*BhV$xdjRHZSW_8sh#c{!}5VdtqX!OMM0>%o7^K8fjwH2%m7#>dm zix@C*(Sz?1@q%x%jOGYmWo#SBPJ^${1t-{{KxmJbr>-yy>q}>I81qN1@a7b(N*>Y1;(2cy#^@TNmh~L9h zrep`6;!UVt0$y|zddh*EZ5uGiD$D9i`eKqr24hKfdHdow$R4;B=b%)UeaVIZC`Au+y zdUPt567N|>Sv@~?gIODCUK0TwQBuw2Y^hDGAp-UK-d^j>YK!8mA5?%*SA@Z5%3enr zdX8T9ofp$9vwOZmiz1r}z@6vYWWZ<~CMAKZ=A#;CM{8F}$lG0ELzl*&b9~I}3ej|f z!L+Znw3_j9=(BSU$4V^vlT97H1^l6YE&&Jp1RxiCS^tah!b}qWsy2eAgurBf4gtF5 zgW{^XNTZ)mo#{eqohM_G4ojKatcjzHlIGwXo2^j^Ft?!Wg*GcGqAZ3g z!XbIEGe<6l9QHgQ=XnKp(=kGpo(Iben0@yXVFgm0Er{F1WB0U`R6^0&*|VxP1M8`K zI(r&QVa*${>4ouHlbO{7;Eel1El-#=lfhuoeds{;vkCd)=fSgW0uA@GhyAWIt~*9c zIyGWu+-E}?1_O(I-Z_@QHSCA1h_Eq6uQ+JC8NX)saoTd{r>pTMaG^Qx_9+|IHW7WQ z2I3NHg!t~=j%I6&Q5w~aJ&vP9ho2K=Ce_ONxivkELGP$V@zUm9P=-t`;x>Lvm>qAP zou8dYY9U2<40#73LvEDSoBF~lt{b;KzJTq-h0@2otiYPb&4=N-LUiXc?8Wh6BR?k$ig8E+Yn$}P+LE3~g|0~d&O-iQ ze7$v4lzrPSEZq#DbfcisT>~SafC?f8odN>VJ#wr+6w8sW@@)PJs4$!Gn*?8Of3yNt{UWt}=w#7R0 z$9g=t{sO95cto*Mjqm)^OvXPWLJ>wvXOkn$PHc$eM-%-Ds?fV_%!FHqJGHDYRd>TN zxvVPW0ZIqqM1Nec=|Bc045#59(2ctYeKjL0Bf& zD~HRlY1`J@EG3Ih;4Ib@fWmvwE11acjl5cTUYlIa=}qZqabg79Qd|r`1*drsE|ojo z@3OS;7|3D|u4TQ3+k-So*Cir+R|l!ZIfTB~quPU-pQfGs%L{m6kE4FkWwT58$ z?WnQLn6|e?TzeB4Ewzx`UDshimR1XUKQZrN7a8;U8q+(Xn!f56;XCnuj+N^(^QNWR z2Gcexy`*J~wB;0yVe(!F_wx6p=?2lxj@@FBpXQ`csR_9re5k)PDerQZBpj4G!e!m z^KIBy8nm=3fmZppS*(bLBb{~XO+;An0=`}b(}CK9oQkNhHw zx92}^;%*qjIp$V9<`^v)o-uqo4A%Af&64ooF)O%y_1>cYj3h|Aj>MbYS7UCctWkBD zxCu1X&F)#Vbl|5LQvN?Khs33cQ*#Q~nJD=oXZ-H7e?2^Y@S-!ULWxf86>3VwdiUX|67+r*v-`f%)SI zWihlzc+{8(51gL?TcyU6ERxi@K_*M@*@sxkg~i+GzM#19-`^)AH-PbUcuR{wfk*ol;p3Ay_z zGa}aZyBA}M$oeunOYUsqs@Itry=sJ}KXRjX_ymWz-5=*l_%zsWAp%1sY}eNRy*sL` zf|Z=JCS;bqr0Fy794Y!kICL6kd??bGmIAVH<3w>nD0XuDU6EAhc7N~5KWBp(2ZSa( z?7UL6_{cz-1qFH>b2dZE^Xyu%3T4P;*ymVZ=du$rh9L?q3G3E4qB_j^EbRqO-_fUc z`TG@iEf|T}_E**H^9pAxwwY3;L5l5TKZ@LZ$>nPTQiN@shko*2L<D6naKQq%(|1?-#;DOFWR~^%&C(6$@j~)59HG%moQJpj z9W*XiRmob|L!7Zqf7;Ve>%9XKNwbTGRRClElJZ|;*Syow=rW$vyVUZf$++{w^YlW$ zI0?+N89o+LIL@$2o!}bdtUpo_Zm|S_!&1EwU34)5_q^Gk7#WcOoNO>2^LaD>O?80Y zNB|fP0}Vy7i42jbCUad_0h59&?Mh7;vtF?YEhjNYEcTHVTkK(xU-d0Vw(lK6sUE^> zdDFB#P~_^Sgtf4gIaqi;%UyAtJ4^qE&NQv-yICBelb9D$};^aJbR>nTNj zp+e1P#s|+gIPch)H|?(0EqB0xcoOJ2u7TN+g*L^1jw4CgA$ISMN+d}+Ai5N;U5kTU zK;Y?hiVAJF&SybSRc;f=33obQK0&TURkFu6z5BF$o<|u8X7C{}ccpy)qWPOLyF}xO z6%a9&p9_h`{@)foYOE5!-^W*p{2Yul`y8n7(Q9DjMM`ovWj^?_!IZrlmRdlI%ZwO_ zpda|B)(xmmW?ugo2=x$NsxdxLq$SFYrWS!qTCJV=5#B+qobT{nacGJ`?t#Ckw=S5P z?jc7l*HU(Pcc((FB`u*ITcYicHU5mB;7Ht+bdp^UGP@~X)v)vDFGrp~W-)*5pGy_n zA=pZUv;@rcuRjkn5!ZYUcKxRCv-baTJzC8<=?itf+LiahejRJ z-;We_)u)d?_TpaiW(05<8M=KO}GGm3rj9cUbb?W6P^9X-|E`ih}C?gbuH`6a2eengyx6E za661p?aAYAB@|jY*p1G-%EgT4Ac3XR`u~ix`#-z?J8u3b|A{I1f~C467Bs8~pHPZ! zABoa!gI}2j>(LH`+%ewIBLLs-xFjUAngO+(-67k22mOSNx)2Cq8CS?(b&#}?p9gWqrvFx1$-FgXO8IY2qq)&G;~!CVEO}Z^u=(Z$@Ur0# z#Pl0ip

-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 literal 0 HcmV?d00001 diff --git a/docs/release_notes/assets/1.2-rbac-runner.png b/docs/release_notes/assets/1.2-rbac-runner.png new file mode 100644 index 0000000000000000000000000000000000000000..1df7c01bf5988d71dbaf76f46eb1bdc6281a481c GIT binary patch literal 389449 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

v1 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�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