diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..bba5f71 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,61 @@ +version: 2 +jobs: + build: + docker: + - image: 218546966473.dkr.ecr.us-east-1.amazonaws.com/circle-ci:stitch-tap-tester-uv + steps: + - checkout + - run: + name: 'Setup virtual env' + command: | + uv venv --python 3.9 /usr/local/share/virtualenvs/tap-chargebee + source /usr/local/share/virtualenvs/tap-chargebee/bin/activate + uv pip install -U 'pip<19.2' 'setuptools<51.0.0' + uv pip install .[dev] + - run: + name: 'JSON Validator' + command: | + source /usr/local/share/virtualenvs/tap-tester/bin/activate + stitch-validate-json tap_chargebee/schemas/*/*.json + - run: + name: 'Pylint' + command: | + source /usr/local/share/virtualenvs/tap-chargebee/bin/activate + uv pip install pylint==2.14.1 + pylint tap_chargebee --disable C,W,R,no-member + - run: + name: 'Unit Tests' + command: | + source /usr/local/share/virtualenvs/tap-chargebee/bin/activate + uv pip install pytest coverage + coverage run -m pytest tests/unittests + coverage html + - store_test_results: + path: test_output/report.xml + - store_artifacts: + path: htmlcov + - run: + name: 'Integration Tests' + command: | + source /usr/local/share/virtualenvs/tap-tester/bin/activate + uv pip install --upgrade awscli + aws s3 cp s3://com-stitchdata-dev-deployment-assets/environments/tap-tester/tap_tester_sandbox dev_env.sh + source dev_env.sh + run-test --tap=tap-chargebee tests +workflows: + version: 2 + commit: + jobs: + - build: + context: circleci-user + build_daily: + triggers: + - schedule: + cron: "0 0 * * *" + filters: + branches: + only: + - master + jobs: + - build: + context: circleci-user diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..ef49bc0 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +# Description of change +(write a short description or paste a link to JIRA) + +# Manual QA steps + - + +# Risks + - + +# Rollback steps + - revert this branch + +#### AI generated code +https://internal.qlik.dev/general/ways-of-working/code-reviews/#guidelines-for-ai-generated-code +- [ ] this PR has been written with the help of GitHub Copilot or another generative AI tool diff --git a/CHANGELOG.md b/CHANGELOG.md index fd88079..ed3e93b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,101 @@ # Changelog +## 1.4.0 + +- Added discounts field in subscriptions schema #[107](https://github.com/singer-io/tap-chargebee/pull/107) +- Fixed All fields & Pagination integration tests #[109](https://github.com/singer-io/tap-chargebee/pull/109) + +## 1.3.5 + +- Add missing Business Entity Details from Chargebee customers details #[100](https://github.com/singer-io/tap-chargebee/pull/100) +- Prevent truncating invoice lineItem taxRates #[102](https://github.com/singer-io/tap-chargebee/pull/102) + +## 1.3.4 + +- Custom field support for Item Model entities #[92](https://github.com/singer-io/tap-chargebee/pull/92) +- Updated integration test #[93](https://github.com/singer-io/tap-chargebee/pull/93) + +## 1.3.3 + +- Implement request timeout #[78](https://github.com/singer-io/tap-chargebee/pull/78) +- Add missing tap-tester tests #[83](https://github.com/singer-io/tap-chargebee/pull/83) +- Add custom exception handling #[85](https://github.com/singer-io/tap-chargebee/pull/85) +- Add missing fields to schema #[87](https://github.com/singer-io/tap-chargebee/pull/87) +- Revert back bookmark logic #[88](https://github.com/singer-io/tap-chargebee/pull/88) + +##1.3.2 + +- Revert back bookmarking logic [#86](https://github.com/singer-io/tap-chargebee/pull/86) + +## 1.3.1 + +- Added support for Chargebee Quotes [#75](https://github.com/singer-io/tap-chargebee/pull/75) + +## 1.3.0 + +- Added comments stream [#52](https://github.com/singer-io/tap-chargebee/pull/52) +- Added include_deleted configuration [#58](https://github.com/singer-io/tap-chargebee/pull/58) +- Added undocumented fields [#69](https://github.com/singer-io/tap-chargebee/pull/69) + +## 1.2.2 + +- Update the schema glob so that we include all schemas in the package distribution [#73](https://github.com/singer-io/tap-chargebee/pull/73) + +## 1.2.1 + +- Add a `MANIFEST.in` file to include schema files in the `tap-chargebee` package [#72](https://github.com/singer-io/tap-chargebee/pull/72) + +## 1.2.0 + +- Remove all minimum/maximum and minLength/maxLength [#45][#45] +- Fix JSONDecodeError in Invoices and Transactions streams [#51][#51] +- Add Tiersprice attribute [#53][#53] +- Updated integration test to cover product catalog v1 and v2 [#63][#63] +- Add additional fields from API [#64][#64] +- Upgraded event stream schema [#57][#57] +- Updated Bookmark handling, date without tz will updated in UTC tz format [#54][#54] + +[#45]: https://github.com/singer-io/tap-chargebee/pull/45 +[#51]: https://github.com/singer-io/tap-chargebee/pull/51 +[#53]: https://github.com/singer-io/tap-chargebee/pull/53 +[#63]: https://github.com/singer-io/tap-chargebee/pull/63 +[#64]: https://github.com/singer-io/tap-chargebee/pull/64 +[#57]: https://github.com/singer-io/tap-chargebee/pull/57 +[#54]: https://github.com/singer-io/tap-chargebee/pull/54 + +## 1.1.2 + +- Fix domain name comparison bug [#67](https://github.com/singer-io/tap-chargebee/pull/67) + +## 1.1.1 + +- Add an error message when we get an unexpected response from the Configurations API [#62](https://github.com/singer-io/tap-chargebee/pull/62) + +## 1.1.0 + +- Adds support for Item Model, Multi-decimal (for Plan Model), and Account hierarchy (for Plan Model) [#56](https://github.com/singer-io/tap-chargebee/pull/56) +- Organized the folder structure: + a. common(common schemas to both plan model and item model) + b. item_model + c. plan_model +- Introduces two new streams: ITEM_MODEL_AVAILABLE_STREAMS, PLAN_MODEL_AVAILABLE_STREAMS + +## 1.0.3 + +- Fix invalid JSON from #44 + +## 1.0.2 + +- Remove `maxLength` from `payment_sources` schema to address certain integrations having IDs of greater length than specified, and make the schema more flexible as the API evolves [#44](https://github.com/singer-io/tap-chargebee/pull/44) + +## 1.0.0 + +- No change from 0.0.12 + +## 0.0.12 + +- Add `custom_fields` to plans, addons, customers, and subscriptions [#9](https://github.com/singer-io/tap-chargebee/pull/9) + ## 0.0.3 - * Add `credit_notes` stream [#2](https://github.com/singer-io/tap-chargebee/pull/2) + +- Add `credit_notes` stream [#2](https://github.com/singer-io/tap-chargebee/pull/2) diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..fb297c4 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include tap_chargebee/schemas/*/*.json diff --git a/README.md b/README.md index 8e1209a..d85ee61 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,12 @@ This tap: - [Addons](https://apidocs.chargebee.com/docs/api/addons) - [Coupons](https://apidocs.chargebee.com/docs/api/coupons) - [Credit Notes](https://apidocs.chargebee.com/docs/api/credit_notes) + - [Comments](https://apidocs.chargebee.com/docs/api/comments) - [Customers](https://apidocs.chargebee.com/docs/api/customers) - [Events](https://apidocs.chargebee.com/docs/api/events) + - [Gifts](https://apidocs.chargebee.com/docs/api/gifts) - [Invoices](https://apidocs.chargebee.com/docs/api/invoices) + - [Orders](https://apidocs.chargebee.com/docs/api/orders) - [Payment Sources](https://apidocs.chargebee.com/docs/api/payment_sources) - [Plans](https://apidocs.chargebee.com/docs/api/plans) - [Subscriptions](https://apidocs.chargebee.com/docs/api/subscriptions) @@ -36,19 +39,25 @@ This tap: ```json { - "start_date": "2010-01-01", + "start_date": "2010-01-01T00:00:00Z", "api_key": "", - "site": "" + "site": "", + "include_deleted": "True|False", + "request_timeout": 300 } ``` - The `start_date` specifies the date at which the tap will begin pulling data + The `start_date` specifies the date in ISO(YYYY-mm-ddTHH:MM:SSZ) format at which the tap will begin pulling data (for those resources that support this). The `api_key` is the API key for your Chargebee site. The `site` parameter represents the name of your specific Chargebee site (e.g. `https://{site}.chargebee.com/api/v2/subscriptions`) + The `include_deleted` is an optional flag to ask if you want deleted records of all streams or not. Default: true + + The `request_timeout` is an optional paramater to set timeout for requests. Default: 300 seconds + 4. Run the Tap in Discovery Mode ```bash diff --git a/setup.py b/setup.py index b96446c..0481c04 100644 --- a/setup.py +++ b/setup.py @@ -3,13 +3,15 @@ from setuptools import setup, find_packages setup(name='tap-chargebee', - version='0.0.5', + version='1.4.0', description='Singer.io tap for extracting data from the Chargebee API', author='dwallace@envoy.com', classifiers=['Programming Language :: Python :: 3 :: Only'], py_modules=['tap_chargebee'], install_requires=[ - 'tap-framework==0.1.0' + 'singer-python==6.0.0', + 'backoff==2.2.1', + 'requests==2.31.0' ], entry_points=''' [console_scripts] @@ -18,6 +20,10 @@ packages=find_packages(), package_data={ 'tap_chargebee': [ - 'schemas/*.json' + 'schemas/common/*.json', + 'schemas/item_model/*.json', + 'schemas/plan_model/*.json' ] - }) + }, + include_package_data=True) + diff --git a/tap_chargebee/__init__.py b/tap_chargebee/__init__.py index e3d7ac2..f9acf12 100644 --- a/tap_chargebee/__init__.py +++ b/tap_chargebee/__init__.py @@ -1,32 +1,122 @@ import singer -import tap_framework -import tap_chargebee.client -import tap_chargebee.streams +import sys +import json +from singer import metadata, Catalog +from tap_chargebee.client import ChargebeeClient +import tap_chargebee.streams as streams LOGGER = singer.get_logger() -class ChargebeeRunner(tap_framework.Runner): - pass +def stream_is_selected(mdata): + """ + Check if the stream is selected + """ + return mdata.get((), {}).get("selected", False) + + +def get_available_streams(config: dict, cb_client: ChargebeeClient): + """ + Prepare the available streams based on the product catalog version + """ + site_name = config.get("site") + LOGGER.info("Site Name %s", site_name) + configuration_url = f"https://{site_name}.chargebee.com/api/v2/configurations" + + try: + # Make a request to the configurations API + response = cb_client.make_request(url=configuration_url, method="GET") + site_configurations = response["configurations"] + except Exception as e: + LOGGER.error("Failed to fetch configurations: %s", e) + raise e + + # Fetch the product catalog version + product_catalog_version = next( + iter( + config["product_catalog_version"] + for config in site_configurations + if config["domain"].lower() == site_name.lower() + ), + None, + ) + + if product_catalog_version == "v2": + available_streams = streams.ITEM_MODEL_AVAILABLE_STREAMS + config["item_model"] = True + LOGGER.info("Found product catalog version v2") + elif product_catalog_version == "v1": + available_streams = streams.PLAN_MODEL_AVAILABLE_STREAMS + config["item_model"] = False + LOGGER.info("Found product catalog version v1") + else: + LOGGER.error( + "Incorrect Product Catalog version {}".format(product_catalog_version) + ) + raise RuntimeError("Incorrect Product Catalog version") + + return available_streams + + +def do_discover(config: dict, state: dict, available_streams: list): + """ + Generate the catalog + """ + LOGGER.info("Starting discovery.") + catalog = [] + + # Generate catalog for each stream based on the product catalog version + for available_stream in available_streams: + stream = available_stream(config, state, None, None) + catalog += stream.generate_catalog() + + json.dump({"streams": catalog}, sys.stdout, indent=4) + LOGGER.info("Finished discover mode") + + +def do_sync(config: dict, catalog: Catalog, state: dict, client: ChargebeeClient): + """ + Sync data from Chargebee. + """ + + last_stream = singer.get_currently_syncing(state) + LOGGER.info("last/currently syncing stream: %s", last_stream) + + # Resume sync from the last stream + for catalog_entry in catalog.get_selected_streams(state): + mdata = metadata.to_map(catalog_entry.metadata) + replication_key = metadata.get(mdata, (), "replication-key") + key_properties = metadata.get(mdata, (), "table-key-properties") + stream_name = catalog_entry.tap_stream_id + + singer.set_currently_syncing(state, stream_name) + singer.write_state(state) + singer.write_schema( + stream_name, catalog_entry.schema.to_dict(), key_properties, replication_key + ) + + LOGGER.info("%s: Starting sync", stream_name) + instance = streams.STREAMS[stream_name](config, state, catalog_entry, client) + counter_value = instance.sync() + singer.set_currently_syncing(state, None) + singer.write_state(state) + LOGGER.info("%s: Completed sync (%s rows)", stream_name, counter_value) @singer.utils.handle_top_exception(LOGGER) def main(): args = singer.utils.parse_args( - required_config_keys=['api_key', 'start_date', 'site']) + required_config_keys=["api_key", "start_date", "site"] + ) - client = tap_chargebee.client.ChargebeeClient(args.config) - - runner = ChargebeeRunner( - args, client, tap_chargebee.streams.AVAILABLE_STREAMS - ) + client = ChargebeeClient(args.config) + available_streams = get_available_streams(args.config, client) if args.discover: - runner.do_discover() - else: - # import pdb; pdb.set_trace() - runner.do_sync() + do_discover(args.config, args.state, available_streams) + elif args.catalog: + do_sync(args.config, args.catalog, args.state, client) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tap_chargebee/client.py b/tap_chargebee/client.py index 3edde8d..8982746 100644 --- a/tap_chargebee/client.py +++ b/tap_chargebee/client.py @@ -1,69 +1,240 @@ -import time +import backoff import requests -import singer - -from tap_framework.client import BaseClient - - -LOGGER = singer.get_logger() - - -class ChargebeeClient(BaseClient): - - def __init__(self, config, api_result_limit=100, include_deleted=True): - super().__init__(config) - - self.api_result_limit = api_result_limit - self.include_deleted = include_deleted - self.user_agent = self.config.get('user_agent') +from singer import utils, get_logger +from requests.exceptions import Timeout, ConnectionError + +LOGGER = get_logger() +LIMIT = 100 +REQUEST_TIMEOUT = 300 + +# Define custom exceptions +class ChargebeeError(Exception): + pass + +class Server4xxError(ChargebeeError): + pass + +class Server5xxError(ChargebeeError): + pass + +class ChargebeeBadRequestError(Server4xxError): + pass + +class ChargebeeAuthenticationError(Server4xxError): + pass + +class ChargebeeForbiddenError(Server4xxError): + pass + +class ChargebeeNotFoundError(Server4xxError): + pass + +class ChargebeeMethodNotAllowedError(Server4xxError): + pass + +class ChargebeeNotProcessedError(Server4xxError): + pass + +class ChargebeeRateLimitError(Server4xxError): + pass + +class ChargebeeInternalServiceError(Server5xxError): + pass + +class ChargebeeServiceUnavailableError(Server5xxError): + pass + + +STATUS_CODE_EXCEPTION_MAPPING = { + 400: { + "raise_exception": ChargebeeBadRequestError, + "message": "The request URI does not match the APIs in the system.", + }, + 401: { + "raise_exception": ChargebeeAuthenticationError, + "message": "The user is not authenticated to use the API.", + }, + 403: { + "raise_exception": ChargebeeForbiddenError, + "message": "The requested operation is not permitted for the user.", + }, + 404: { + "raise_exception": ChargebeeNotFoundError, + "message": "The requested resource was not found.", + }, + 405: { + "raise_exception": ChargebeeMethodNotAllowedError, + "message": "The HTTP action is not allowed for the requested REST API.", + }, + 409: { + "raise_exception": ChargebeeNotProcessedError, + "message": "The request could not be processed because of conflict in the request.", + }, + 429: { + "raise_exception": ChargebeeRateLimitError, + "message": "You are requesting to many requests.", + }, + 500: { + "raise_exception": ChargebeeInternalServiceError, + "message": "The request could not be processed due to internal server error.", + }, + 503: { + "raise_exception": ChargebeeServiceUnavailableError, + "message": "The request could not be processed due to temporary internal server error.", + }, +} + + +def get_exception_for_status_code(status_code): + """Map the input status_code with the corresponding Exception Class \ + using 'STATUS_CODE_EXCEPTION_MAPPING' dictionary.""" + + exception = STATUS_CODE_EXCEPTION_MAPPING.get(status_code, {}).get( + "raise_exception" + ) + # If exception is not mapped for any code then use Server4xxError and Server5xxError respectively + if not exception: + if status_code > 400 and status_code < 500: + exception = Server4xxError + elif 500 <= status_code < 600: + exception = Server5xxError + else: + exception = ChargebeeError + return exception + + +def raise_for_error(response): + """Raises error class with appropriate msg for the response""" + try: + json_response = response.json() + except requests.exceptions.JSONDecodeError: + LOGGER.warning("Response is not in JSON format") + json_response = {} + + if response.status_code == 200: + return json_response + + status_code = response.status_code + msg = json_response.get( + "message", + STATUS_CODE_EXCEPTION_MAPPING.get(status_code, {}).get( + "message", "Unknown Error" + ), + ) + message = f"HTTP-error-code: {status_code}, Error: {msg}" + exc = get_exception_for_status_code(status_code) + raise exc(message) from None + + +class ChargebeeClient: + + def __init__(self, config: dict): + self.config = config + self.request_timeout = self.get_request_timeout() + self.include_deleted = self.get_include_deleted() def get_headers(self): + """ + Returns headers for the request + """ headers = {} - - if self.config.get('user_agent'): - headers['User-Agent'] = self.config.get('user_agent') - + if self.config.get("user_agent"): + headers["User-Agent"] = self.config.get("user_agent") return headers - def get_params(self, params): - - if params is None: - params = {} - - params['limit'] = self.api_result_limit - params['include_deleted'] = self.include_deleted - - return params - - def make_request(self, url, method, params=None, base_backoff=15, - body=None): - - if params is None: - params = {} - - LOGGER.info("Making {} request to {}".format(method, url)) - - response = requests.request( - method, - url, - auth=(self.config.get("api_key"), ''), - headers=self.get_headers(), - params=self.get_params(params), - json=body) - - # Handle Rate Limiting (429) - if response.status_code == 429: - if base_backoff > 120: - raise RuntimeError('Backed off too many times, exiting!') - - LOGGER.warn('Got a 429, sleeping for {} seconds and trying again' - .format(base_backoff)) - - time.sleep(base_backoff) - - return self.make_request(url, method, base_backoff * 2, body) - - if response.status_code != 200: - raise RuntimeError(response.text) - - return response.json() + def get_include_deleted(self): + """ + Returns whether to include deleted records based on config. + """ + include_deleted = self.config.get("include_deleted") + return include_deleted not in ["false", "False", False] + + def get_request_timeout(self): + """ + Set request timeout to config param `request_timeout` value. + """ + config_request_timeout = self.config.get("request_timeout") + if config_request_timeout and float(config_request_timeout): + request_timeout = float(config_request_timeout) + else: + # If value is 0,"0","" or not passed then set default to 300 seconds. + request_timeout = REQUEST_TIMEOUT + + return request_timeout + + @backoff.on_exception( + backoff.expo, + (Server4xxError, Server5xxError, Timeout, ConnectionError), + max_tries=5, + factor=3, + ) + @utils.ratelimit(100, 60) + def make_request( + self, url: str, method: str, params: dict = None, body: dict = None + ): + """ + Make a request to the Chargebee API + Args: + url (str): The URL to make the request to. + method (str): The HTTP method to use. + params (dict, optional): Additional parameters for the request. Defaults to None. + body (dict, optional): The body of the request. Defaults to None. + + Returns: + dict: The JSON response from the API. + """ + LOGGER.info(f"Making {method} request to {url}") + + try: + response = requests.request( + method, + url, + auth=(self.config.get("api_key"), ""), + headers=self.get_headers(), + params=params, + json=body, + timeout=self.request_timeout, + ) + + return raise_for_error(response) + except requests.exceptions.RequestException as e: + LOGGER.error("Request failed: %s", str(e)) + raise + + def get_offset_based_pages( + self, + url: str, + method: str, + sort_by: str, + params: dict = None, + body: dict = None, + ): + """ + Get all pages by using offset-based pagination. + Args: + url (str): The URL to make the request to. + method (str): The HTTP method to use. + sort_by (str, optional): The field to sort by. + params (dict, optional): Additional parameters for the request. Defaults to None. + body (dict, optional): The body of the request. Defaults to None. + + Yields: + list: A list of items from the current page. + """ + params = params or {} + params.update({"limit": LIMIT, "include_deleted": self.include_deleted}) + if sort_by: + params["sort_by[asc]"] = sort_by + + # Loop through the pages until next_offset is None + while True: + response = self.make_request(url, method, params, body) + yield response.get("list", []) + + next_offset = response.get("next_offset") + if not next_offset: + LOGGER.info("Final offset reached. Ending Pagination.") + break + + LOGGER.info("Advancing by one offset. %s", next_offset) + params["offset"] = next_offset diff --git a/tap_chargebee/schemas/addons.json b/tap_chargebee/schemas/addons.json deleted file mode 100644 index a087436..0000000 --- a/tap_chargebee/schemas/addons.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"], - "maxLength": 100 - }, - "name": { - "type": ["null", "string"], - "maxLength": 50 - }, - "invoice_name": { - "type": ["null", "string"], - "maxLength": 100 - }, - "description": { - "type": ["null", "string"], - "maxLength": 500 - }, - "pricing_model": { - "type": ["null", "string"] - }, - "charge_type": { - "type": ["null", "string"] - }, - "price": { - "type": ["null", "integer"], - "minimum": 0 - }, - "currency_code": { - "type": ["null", "string"], - "maxLength": 3 - }, - "period": { - "type": ["null", "integer"], - "minimum": 1 - }, - "period_unit": { - "type": ["null", "string"] - }, - "unit": { - "type": ["null", "string"], - "maxLength": 30 - }, - "status": { - "type": ["null", "string"] - }, - "archived_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "enabled_in_portal": { - "type": ["null", "boolean"] - }, - "tax_code": { - "type": ["null", "string"], - "maxLength": 50 - }, - "sku": { - "type": ["null", "string"], - "maxLength": 100 - }, - "accounting_code": { - "type": ["null", "string"], - "maxLength": 100 - }, - "accouting_category1": { - "type": ["null", "string"], - "maxLength": 100 - }, - "accouting_category2": { - "type": ["null", "string"], - "maxLength": 100 - }, - "is_shippable": { - "type": ["null", "boolean"] - }, - "shipping_frequency_period": { - "type": ["null", "integer"], - "minimum": 1 - }, - "shipping_frequency_period_unit": { - "type": ["null", "string"] - }, - "resource_version": { - "type": ["null", "integer"] - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "invoice_notes": { - "type": ["null", "string"], - "maxLength": 1000 - }, - "taxable": { - "type": ["null", "boolean"] - }, - "tax_profile_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "object": { - "type": ["null", "string"] - }, - "type": { - "type": ["null", "string"] - }, - "meta_data": { - "type": ["null", "string"] - }, - "tiers": { - "type": ["null", "array"], - "items": { - "type": ["null","object"], - "properties": { - "starting_unit": { - "type": ["null", "integer"], - "minimum": 1 - }, - "ending_unit": { - "type": ["null", "integer"] - }, - "price ": { - "type": ["null", "integer"], - "minimum": 0 - } - } - } - } - } -} diff --git a/tap_chargebee/schemas/cards.json b/tap_chargebee/schemas/cards.json deleted file mode 100644 index b80f811..0000000 --- a/tap_chargebee/schemas/cards.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "first_name": { - "type": ["null", "string"], - "maxLength": 50 - }, - "last_name": { - "type": ["null", "string"], - "maxLength": 50 - }, - "iin": { - "type": ["null", "string"], - "minLength": 6, - "maxLength": 6 - }, - "last4": { - "type": ["null", "string"], - "minLength": 4, - "maxLength": 4 - }, - "brand": { - "type": ["null", "string"] - }, - "funding_type": { - "type": ["null", "string"] - }, - "expiry_month": { - "type": ["null", "integer"], - "minimum": 1, - "maximum": 12 - }, - "expiry_year": { - "type": ["null", "integer"] - }, - "billing_addr1": { - "type": ["null", "string"], - "maxLength": 150 - }, - "billing_addr2": { - "type": ["null", "string"], - "maxLength": 150 - }, - "billing_city": { - "type": ["null", "string"], - "maxLength": 50 - }, - "billing_state_code": { - "type": ["null", "string"], - "maxLength": 50 - }, - "billing_state": { - "type": ["null", "string"], - "maxLength": 50 - }, - "billing_country": { - "type": ["null", "string"], - "maxLength": 50 - }, - "billing_zip": { - "type": ["null", "string"], - "maxLength": 20 - }, - "masked_number": { - "type": ["null", "string"], - "maxLength": 19 - } - } -} diff --git a/tap_chargebee/schemas/common/cards.json b/tap_chargebee/schemas/common/cards.json new file mode 100644 index 0000000..0efcced --- /dev/null +++ b/tap_chargebee/schemas/common/cards.json @@ -0,0 +1,98 @@ +{ + "type": ["null", "object"], + "additionalProperties": false, + "properties": { + "payment_source_id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "gateway": { + "type": ["null", "string"] + }, + "gateway_account_id": { + "type": ["null", "string"] + }, + "ref_tx_id": { + "type": ["null", "string"] + }, + "card_type": { + "type": ["null", "string"] + }, + "issuing_country": { + "type": ["null", "string"] + }, + "resource_version": { + "type": ["null", "integer"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "ip_address": { + "type": ["null", "string"] + }, + "powered_by": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "iin": { + "type": ["null", "string"] + }, + "last4": { + "type": ["null", "string"] + }, + "brand": { + "type": ["null", "string"] + }, + "funding_type": { + "type": ["null", "string"] + }, + "expiry_month": { + "type": ["null", "integer"] + }, + "expiry_year": { + "type": ["null", "integer"] + }, + "billing_addr1": { + "type": ["null", "string"] + }, + "billing_addr2": { + "type": ["null", "string"] + }, + "billing_city": { + "type": ["null", "string"] + }, + "billing_state_code": { + "type": ["null", "string"] + }, + "billing_state": { + "type": ["null", "string"] + }, + "billing_country": { + "type": ["null", "string"] + }, + "billing_zip": { + "type": ["null", "string"] + }, + "masked_number": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } +} diff --git a/tap_chargebee/schemas/common/comments.json b/tap_chargebee/schemas/common/comments.json new file mode 100644 index 0000000..caade93 --- /dev/null +++ b/tap_chargebee/schemas/common/comments.json @@ -0,0 +1,30 @@ +{ + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "notes": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + }, + "type": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "added_by": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + } + } +} diff --git a/tap_chargebee/schemas/invoices.json b/tap_chargebee/schemas/common/credit_notes.json similarity index 69% rename from tap_chargebee/schemas/invoices.json rename to tap_chargebee/schemas/common/credit_notes.json index bea8092..5dc6725 100644 --- a/tap_chargebee/schemas/invoices.json +++ b/tap_chargebee/schemas/common/credit_notes.json @@ -5,34 +5,40 @@ "id": { "type": ["null", "string"] }, - "po_number": { + "customer_id": { "type": ["null", "string"] }, - "customer_id": { + "generated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "channel":{ "type": ["null", "string"] }, - "recurring": { - "type": ["null", "boolean"] + "subscription_id": { + "type": ["null", "string"] }, - "status": { + "reference_invoice_id": { "type": ["null", "string"] }, - "vat_number": { + "type": { "type": ["null", "string"] }, - "price_type": { + "reason_code": { "type": ["null", "string"] }, - "date": { - "type": ["null", "string"], - "format": "date-time" + "status": { + "type": ["null", "string"] }, - "due_date": { + "vat_number": { + "type": ["null", "string"] + }, + "date": { "type": ["null", "string"], "format": "date-time" }, - "net_term_days": { - "type": ["null", "integer"] + "price_type": { + "type": ["null", "string"] }, "currency_code": { "type": ["null", "string"] @@ -40,39 +46,26 @@ "total": { "type": ["null", "integer"] }, - "amount_paid": { - "type": ["null", "integer"] - }, - "amount_adjusted": { + "amount_allocated": { "type": ["null", "integer"] }, - "write_off_amount": { + "amount_refunded": { "type": ["null", "integer"] }, - "credits_applied": { + "amount_available": { "type": ["null", "integer"] }, - "amount_due": { - "type": ["null", "integer"] - }, - "paid_at": { + "refunded_at": { "type": ["null", "string"], "format": "date-time" }, - "dunning_status": { - "type": ["null", "string"] - }, - "next_retry_at": { + "voided_at": { "type": ["null", "string"], "format": "date-time" }, "resource_version": { "type": ["null", "integer"] }, - "voided_at": { - "type": ["null", "string"], - "format": "date-time" - }, "updated_at": { "type": ["null", "string"], "format": "date-time" @@ -80,33 +73,47 @@ "sub_total": { "type": ["null", "integer"] }, - "tax": { + "sub_total_in_local_currency": { "type": ["null", "integer"] }, - "first_invoice": { - "type": ["null", "boolean"] - }, - "has_advance_charges": { - "type": ["null", "boolean"] + "total_in_local_currency": { + "type": ["null", "integer"] }, - "expected_payment_date": { - "type": ["null", "string"], - "format": "date-time" + "local_currency_code": { + "type": ["null", "string"] }, - "amount_to_collect": { + "round_off_amount": { "type": ["null", "integer"] }, - "round_off_amount": { + "fractional_correction": { "type": ["null", "integer"] }, "deleted": { "type": ["null", "boolean"] }, - "is_gifted": { - "type": ["null", "boolean"] + "create_reason_code": { + "type": ["null", "string"] }, - "term_finalized": { - "type": ["null", "boolean"] + "vat_number_prefix": { + "type": ["null", "string"] + }, + "tax_category": { + "type": ["null", "string"] + }, + "local_currency_exchange_rate": { + "type": ["null", "string"] + }, + "business_entity_id": { + "type": ["null", "string"] + }, + "base_currency_code": { + "type": ["null", "string"] + }, + "exchange_rate": { + "type": ["null", "number"] + }, + "object": { + "type": ["null", "string"] }, "line_items": { "type": ["null", "array"], @@ -133,6 +140,12 @@ "quantity": { "type": ["null", "integer"] }, + "amount": { + "type": ["null", "integer"] + }, + "pricing_model": { + "type": ["null", "string"] + }, "is_taxed": { "type": ["null", "boolean"] }, @@ -140,10 +153,16 @@ "type": ["null", "integer"] }, "tax_rate": { - "type": ["null", "integer"] + "type": ["null", "number"] }, - "amount": { - "type": ["null", "integer"] + "unit_amount_in_decimal": { + "type": ["null", "string"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + }, + "amount_in_decimal": { + "type": ["null", "string"] }, "discount_amount": { "type": ["null", "integer"] @@ -154,6 +173,9 @@ "description": { "type": ["null", "string"] }, + "entity_description": { + "type": ["null", "string"] + }, "entity_type": { "type": ["null", "string"] }, @@ -163,7 +185,7 @@ "entity_id": { "type": ["null", "string"] }, - "pricing_model": { + "customer_id": { "type": ["null", "string"] }, "object": { @@ -209,8 +231,49 @@ "coupon_id": { "type": ["null", "string"] }, + "entity_id": { + "type": ["null", "string"] + }, "discount_amount": { "type": ["null", "integer"] + }, + "object": { + "type": ["null", "string"] + } + } + } + }, + "line_item_tiers": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "starting_unit": { + "type": ["null", "integer"] + }, + "ending_unit": { + "type": ["null", "integer"] + }, + "quantity_used": { + "type": ["null", "integer"] + }, + "unit_amount": { + "type": ["null", "integer"] + }, + "starting_unit_in_decimal": { + "type": ["null", "string"] + }, + "ending_unit_in_decimal": { + "type": ["null", "string"] + }, + "quantity_used_in_decimal": { + "type": ["null", "string"] + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] } } } @@ -244,6 +307,15 @@ "type": ["null", "string"] }, "tax_rate": { + "type": ["null", "number"] + }, + "is_partial_tax_applied": { + "type": ["null", "boolean"] + }, + "is_non_compliance_tax": { + "type": ["null", "boolean"] + }, + "taxable_amount": { "type": ["null", "integer"] }, "tax_amount": { @@ -257,11 +329,17 @@ }, "tax_juris_code": { "type": ["null", "string"] + }, + "tax_amount_in_local_currency": { + "type": ["null", "integer"] + }, + "local_currency_code": { + "type": ["null", "string"] } } } }, - "linked_payments": { + "linked_refunds": { "type": ["null", "array"], "items": { "type": ["null", "object"], @@ -285,127 +363,87 @@ }, "txn_amount": { "type": ["null", "integer"] + }, + "refund_reason_code": { + "type": ["null", "string"] } } } }, - "applied_credits": { + "allocations": { "type": ["null", "array"], "items": { "type": ["null", "object"], "properties": { - "cn_id": { + "invoice_id": { "type": ["null", "string"] }, - "applied_amount": { + "allocated_amount": { "type": ["null", "integer"] }, - "applied_at": { + "allocated_at": { "type": ["null", "string"], "format": "date-time" }, - "cn_reason_code": { - "type": ["null", "string"] - }, - "cn_date": { + "invoice_date": { "type": ["null", "string"], "format": "date-time" }, - "cn_status": { + "invoice_status": { "type": ["null", "string"] } } } }, - "adjustment_credit_notes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "cn_id": { - "type": ["null", "string"] - }, - "cn_reason_code": { - "type": ["null", "string"] - }, - "cn_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "cn_total": { - "type": ["null", "integer"] - }, - "cn_status": { - "type": ["null", "string"] - } - } - } - }, - "issued_credit_notes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "cn_id": { - "type": ["null", "string"] - }, - "cn_reason_code": { - "type": ["null", "string"] - }, - "cn_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "cn_total": { - "type": ["null", "integer"] - }, - "cn_status": { - "type": ["null", "string"] - } + "einvoice": { + "type": ["null", "object"], + "properties":{ + "id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "message": { + "type": ["null", "string"] + }, + "reference_number": { + "type": ["null", "string"] } } }, - "linked_orders": { + "linked_tax_withheld_refunds": { "type": ["null", "array"], "items": { "type": ["null", "object"], - "properties": { + "properties":{ "id": { "type": ["null", "string"] }, - "status": { - "type": ["null", "string"] - }, - "reference_id": { - "type": ["null", "string"] - }, - "fulfillment_status": { - "type": ["null", "string"] + "amount": { + "type": ["null", "integer"] }, - "batch_id": { + "date": { "type": ["null", "string"] }, - "created_at": { + "invoice_date": { "type": ["null", "string"], "format": "date-time" + }, + "reference_number": { + "type": ["null", "string"] } } } }, - "notes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "entity_type": { - "type": ["null", "string"] - }, - "note": { - "type": ["null", "string"] - }, - "entity_id": { - "type": ["null", "string"] - } + "tax_origin": { + "type": ["null", "object"], + "properties":{ + "country": { + "type": ["null", "string"] + }, + "registration_number": { + "type": ["null", "string"] } } }, @@ -451,7 +489,13 @@ "zip": { "type": ["null", "string"] }, - "validation_status,": { + "validation_status": { + "type": ["null", "string"] + }, + "index": { + "type": ["null", "integer"] + }, + "object": { "type": ["null", "string"] } } @@ -506,23 +550,63 @@ } } }, - "exchange_rate": { - "type": ["null", "number"] - }, - "base_currency_code": { - "type": ["null", "string"] - }, - "new_sales_amount": { - "type": ["null", "integer"] - }, - "object": { - "type": ["null", "string"] - }, - "subscription_id": { - "type": ["null", "string"] - }, - "total": { - "type": ["null", "integer"] + "site_details_at_creation": { + "type": ["null", "object"], + "properties":{ + "timezone": { + "type": ["null", "string"] + }, + "organization_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + } } } } diff --git a/tap_chargebee/schemas/customers.json b/tap_chargebee/schemas/common/customers.json similarity index 56% rename from tap_chargebee/schemas/customers.json rename to tap_chargebee/schemas/common/customers.json index 82463e2..bb741a3 100644 --- a/tap_chargebee/schemas/customers.json +++ b/tap_chargebee/schemas/common/customers.json @@ -26,6 +26,9 @@ "auto_collection": { "type": ["null", "string"] }, + "offline_payment_method": { + "type": ["null", "string"] + }, "net_term_days": { "type": ["null", "integer"] }, @@ -40,12 +43,27 @@ "locale": { "type": ["null", "string"] }, + "einvoicing_method": { + "type": ["null", "string"] + }, "consolidated_invoicing": { "type": ["null", "boolean"] }, "billing_date": { + "type": ["null", "integer"] + }, + "billing_month": { + "type": ["null", "integer"] + }, + "is_einvoice_enabled": { "type": ["null", "boolean"] }, + "entity_identifier_scheme": { + "type": ["null", "string"] + }, + "entity_identifier_standard": { + "type": ["null", "string"] + }, "billing_date_mode": { "type": ["null", "string"] }, @@ -77,7 +95,10 @@ "type": ["null", "boolean"] }, "cf_company_id": { - "type": ["null","integer"] + "type": ["null", "integer", "string"] + }, + "cf_people_id": { + "type": ["null", "string"] }, "allow_direct_debit": { "type": ["null", "boolean"] @@ -94,10 +115,94 @@ "preferred_currency_code": { "type": ["null", "string"] }, + "taxability": { + "type": ["null", "string"] + }, + "tax_providers_fields": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "provider_name": { + "type": ["null", "string"] + }, + "field_id": { + "type": ["null", "string"] + }, + "field_value": { + "type": ["null", "string"] + } + } + } + }, + "vat_number_validated_time": { + "type": ["null", "string"], + "format": "date-time" + }, + "vat_number_status": { + "type": ["null", "string"] + }, + "is_location_valid": { + "type": ["null", "boolean"] + }, + "created_from_ip": { + "type": ["null", "string"] + }, + "entity_code": { + "type": ["null", "string"] + }, + "exempt_number": { + "type": ["null", "string"] + }, "resource_version": { "type": ["null", "integer"] }, - "taxability": { + "auto_close_invoices": { + "type": ["null", "boolean"] + }, + "fraud_flag": { + "type": ["null", "string"] + }, + "backup_payment_source_id": { + "type": ["null", "string"] + }, + "registered_for_gst": { + "type": ["null", "boolean"] + }, + "customer_type": { + "type": ["null", "string"] + }, + "meta_data": { + "type": ["null", "string"] + }, + "exemption_details": { + "type": ["null", "string"] + }, + "business_customer_without_vat_number": { + "type": ["null", "boolean"] + }, + "business_entity_id": { + "type": ["null", "string"] + }, + "client_profile_id": { + "type": ["null", "string"] + }, + "use_default_hierarchy_settings": { + "type": ["null", "boolean"] + }, + "vat_number_prefix": { + "type": ["null", "string"] + }, + "channel": { + "type": ["null", "string"] + }, + "active_id": { + "type": ["null", "string"] + }, + "mrr": { + "type": ["null", "integer"] + }, + "custom_fields": { "type": ["null", "string"] }, "billing_address": { @@ -142,7 +247,10 @@ "zip": { "type": ["null", "string"] }, - "validation_status,": { + "validation_status": { + "type": ["null", "string"] + }, + "object": { "type": ["null", "string"] } } @@ -228,7 +336,7 @@ "gateway": { "type": ["null", "string"] }, - "gateway_account_id ": { + "gateway_account_id": { "type": ["null", "string"] }, "status": { @@ -237,9 +345,6 @@ "reference_id": { "type": ["null", "string"] }, - "gateway_account_id": { - "type": ["null", "string"] - }, "object": { "type": ["null", "string"] } @@ -256,7 +361,7 @@ "excess_payments": { "type": ["null", "integer"] }, - "refundable_credits ": { + "refundable_credits": { "type": ["null", "integer"] }, "unbilled_charges": { @@ -264,6 +369,92 @@ }, "currency_code": { "type": ["null", "string"] + }, + "balance_currency_code": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + }, + "relationship": { + "type": ["null","object"], + "properties": { + "parent_id": { + "type": ["null", "string"] + }, + "payment_owner_id": { + "type": ["null", "string"] + }, + "invoice_owner_id": { + "type": ["null", "string"] + }, + "root_id": { + "type": ["null", "string"] + } + } + }, + "parent_account_access": { + "type": ["null","object"], + "properties": { + "portal_edit_child_subscriptions": { + "type": ["null", "string"] + }, + "portal_download_child_invoices": { + "type": ["null", "string"] + }, + "send_subscription_emails": { + "type": ["null", "boolean"] + }, + "send_invoice_emails": { + "type": ["null", "boolean"] + }, + "send_payment_emails": { + "type": ["null", "boolean"] + } + } + }, + "child_account_access": { + "type": ["null","object"], + "properties": { + "portal_edit_subscriptions": { + "type": ["null", "string"] + }, + "portal_download_invoices": { + "type": ["null", "string"] + }, + "send_subscription_emails": { + "type": ["null", "boolean"] + }, + "portal_download_invoices": { + "type": ["null", "string"] + }, + "send_invoice_emails": { + "type": ["null", "boolean"] + }, + "send_payment_emails": { + "type": ["null", "boolean"] + } + } + }, + "entity_identifiers": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "value": { + "type": ["null", "string"] + }, + "scheme": { + "type": ["null", "string"] + }, + "standard": { + "type": ["null", "string"] } } } diff --git a/tap_chargebee/schemas/common/gifts.json b/tap_chargebee/schemas/common/gifts.json new file mode 100644 index 0000000..6c5877e --- /dev/null +++ b/tap_chargebee/schemas/common/gifts.json @@ -0,0 +1,85 @@ +{ + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "scheduled_at": { + + "type": ["null", "string"], + "format": "date-time" + }, + "auto_claim": { + "type": ["null", "boolean"] + }, + "no_expiry": { + "type": ["null", "boolean"] + }, + "claim_expiry_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "resource_version": { + "type": ["null", "integer"] + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "gifter": { + "type": ["null", "object"], + "properties": { + "customer_id": { + "type": ["null", "string"] + }, + "invoice_id": { + "type": ["null", "string"] + }, + "signature": { + "type": ["null", "string"] + }, + "note": { + "type": ["null", "string"] + } + } + }, + "gift_receiver": { + "type": ["null", "object"], + "properties": { + "customer_id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + } + } + }, + "gift_timelines": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "status": { + "type": ["null", "string"] + }, + "occurred_at": { + "type": ["null", "string"], + "format": "date-time" + } + } + } + } + } +} \ No newline at end of file diff --git a/tap_chargebee/schemas/common/invoices.json b/tap_chargebee/schemas/common/invoices.json new file mode 100644 index 0000000..25dbf8c --- /dev/null +++ b/tap_chargebee/schemas/common/invoices.json @@ -0,0 +1,790 @@ +{ + "type": ["null", "object"], + "additionalProperties": false, + "properties": { + "id": { + "type": ["null", "string"] + }, + "po_number": { + "type": ["null", "string"] + }, + "channel":{ + "type": ["null", "string"] + }, + "business_entity_id":{ + "type": ["null", "string"] + }, + "generated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "customer_id": { + "type": ["null", "string"] + }, + "recurring": { + "type": ["null", "boolean"] + }, + "status": { + "type": ["null", "string"] + }, + "vat_number": { + "type": ["null", "string"] + }, + "price_type": { + "type": ["null", "string"] + }, + "date": { + "type": ["null", "string"], + "format": "date-time" + }, + "due_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "net_term_days": { + "type": ["null", "integer"] + }, + "currency_code": { + "type": ["null", "string"] + }, + "total": { + "type": ["null", "integer"] + }, + "amount_paid": { + "type": ["null", "integer"] + }, + "amount_adjusted": { + "type": ["null", "integer"] + }, + "write_off_amount": { + "type": ["null", "integer"] + }, + "credits_applied": { + "type": ["null", "integer"] + }, + "amount_due": { + "type": ["null", "integer"] + }, + "paid_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "dunning_status": { + "type": ["null", "string"] + }, + "next_retry_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "resource_version": { + "type": ["null", "integer"] + }, + "voided_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "sub_total": { + "type": ["null", "integer"] + }, + "sub_total_in_local_currency": { + "type": ["null", "integer"] + }, + "total_in_local_currency": { + "type": ["null", "integer"] + }, + "local_currency_code": { + "type": ["null", "string"] + }, + "tax": { + "type": ["null", "integer"] + }, + "local_currency_exchange_rate": { + "type": ["null", "string"] + }, + "first_invoice": { + "type": ["null", "boolean"] + }, + "has_advance_charges": { + "type": ["null", "boolean"] + }, + "expected_payment_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "amount_to_collect": { + "type": ["null", "integer"] + }, + "round_off_amount": { + "type": ["null", "integer"] + }, + "payment_owner": { + "type": ["null", "string"] + }, + "void_reason_code": { + "type": ["null", "string"] + }, + "deleted": { + "type": ["null", "boolean"] + }, + "vat_number_prefix": { + "type": ["null", "string"] + }, + "is_gifted": { + "type": ["null", "boolean"] + }, + "term_finalized": { + "type": ["null", "boolean"] + }, + "line_items": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "date_from": { + "type": ["null", "string"], + "format": "date-time" + }, + "date_to": { + "type": ["null", "string"], + "format": "date-time" + }, + "unit_amount": { + "type": ["null", "integer"] + }, + "quantity": { + "type": ["null", "integer"] + }, + "is_taxed": { + "type": ["null", "boolean"] + }, + "tax_amount": { + "type": ["null", "integer"] + }, + "tax_rate": { + "type": ["null", "number"] + }, + "amount": { + "type": ["null", "integer"] + }, + "discount_amount": { + "type": ["null", "integer"] + }, + "item_level_discount_amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "entity_description": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "tax_exempt_reason": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "pricing_model": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + }, + "amount_in_decimal": { + "type": ["null", "string"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] + } + } + } + }, + "discounts": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + }, + "line_item_discounts": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "discount_type": { + "type": ["null", "string"] + }, + "coupon_id": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "discount_amount": { + "type": ["null", "integer"] + }, + "object": { + "type": ["null", "string"] + } + } + } + }, + "taxes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "name": { + "type": ["null", "string"] + }, + "amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + } + } + } + }, + "line_item_taxes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "tax_name": { + "type": ["null", "string"] + }, + "tax_rate": { + "type": ["null", "integer", "number"] + }, + "is_partial_tax_applied": { + "type": ["null", "boolean"] + }, + "is_non_compliance_tax": { + "type": ["null", "boolean"] + }, + "taxable_amount": { + "type": ["null", "integer"] + }, + "tax_amount": { + "type": ["null", "integer"] + }, + "tax_juris_type": { + "type": ["null", "string"] + }, + "tax_juris_name": { + "type": ["null", "string"] + }, + "tax_juris_code": { + "type": ["null", "string"] + }, + "tax_amount_in_local_currency": { + "type": ["null", "integer"] + }, + "local_currency_code": { + "type": ["null", "string"] + } + } + } + }, + "line_item_tiers": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "starting_unit": { + "type": ["null", "integer"] + }, + "ending_unit": { + "type": ["null", "integer"] + }, + "quantity_used": { + "type": ["null", "integer"] + }, + "unit_amount": { + "type": ["null", "integer"] + }, + "starting_unit_in_decimal": { + "type": ["null", "string"] + }, + "ending_unit_in_decimal": { + "type": ["null", "string"] + }, + "quantity_used_in_decimal": { + "type": ["null", "string"] + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] + } + } + } + }, + "linked_payments": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "txn_id": { + "type": ["null", "string"] + }, + "applied_amount": { + "type": ["null", "integer"] + }, + "applied_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "txn_status": { + "type": ["null", "string"] + }, + "txn_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "txn_amount": { + "type": ["null", "integer"] + } + } + } + }, + "dunning_attempts": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "attempt": { + "type": ["null", "integer"] + }, + "transaction_id": { + "type": ["null", "string"] + }, + "dunning_type": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "txn_status": { + "type": ["null", "string"] + }, + "txn_amount": { + "type": ["null", "integer"] + } + } + } + }, + "applied_credits": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "cn_id": { + "type": ["null", "string"] + }, + "applied_amount": { + "type": ["null", "integer"] + }, + "applied_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "cn_reason_code": { + "type": ["null", "string"] + }, + "cn_create_reason_code": { + "type": ["null", "string"] + }, + "cn_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "cn_status": { + "type": ["null", "string"] + } + } + } + }, + "adjustment_credit_notes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "cn_id": { + "type": ["null", "string"] + }, + "cn_reason_code": { + "type": ["null", "string"] + }, + "cn_create_reason_code": { + "type": ["null", "string"] + }, + "cn_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "cn_total": { + "type": ["null", "integer"] + }, + "cn_status": { + "type": ["null", "string"] + } + } + } + }, + "issued_credit_notes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "cn_id": { + "type": ["null", "string"] + }, + "cn_reason_code": { + "type": ["null", "string"] + }, + "cn_create_reason_code": { + "type": ["null", "string"] + }, + "cn_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "cn_total": { + "type": ["null", "integer"] + }, + "cn_status": { + "type": ["null", "string"] + } + } + } + }, + "linked_orders": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "document_number": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "order_type": { + "type": ["null", "string"] + }, + "reference_id": { + "type": ["null", "string"] + }, + "fulfillment_status": { + "type": ["null", "string"] + }, + "batch_id": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + } + } + } + }, + "notes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "entity_type": { + "type": ["null", "string"] + }, + "note": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + } + } + } + }, + "shipping_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + }, + "billing_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + }, + "exchange_rate": { + "type": ["null", "number"] + }, + "base_currency_code": { + "type": ["null", "string"] + }, + "new_sales_amount": { + "type": ["null", "integer"] + }, + "object": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "einvoice": { + "type": ["null", "object"], + "properties":{ + "id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "message": { + "type": ["null", "string"] + } + } + }, + "statement_descriptor": { + "type": ["null", "object"], + "properties":{ + "id": { + "type": ["null", "string"] + }, + "descriptor": { + "type": ["null", "string"] + } + } + }, + "linked_taxes_withheld": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties":{ + "id": { + "type": ["null", "string"] + }, + "amount": { + "type": ["null", "string"] + }, + "description": { + "type": ["null", "string"] + }, + "date": { + "type": ["null", "string"], + "format": "date-time" + }, + "reference_number": { + "type": ["null", "string"] + } + } + } + }, + "tax_origin": { + "type": ["null", "object"], + "properties":{ + "country": { + "type": ["null", "string"] + }, + "registration_number": { + "type": ["null", "string"] + } + } + }, + "site_details_at_creation": { + "type": ["null", "object"], + "properties":{ + "timezone": { + "type": ["null", "string"] + }, + "organization_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + } + } + } +} diff --git a/tap_chargebee/schemas/common/orders.json b/tap_chargebee/schemas/common/orders.json new file mode 100644 index 0000000..ba539a7 --- /dev/null +++ b/tap_chargebee/schemas/common/orders.json @@ -0,0 +1,434 @@ +{ + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "document_number": { + "type": ["null", "string"] + }, + "invoice_id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "cancellation_reason": { + "type": ["null", "string"] + }, + "payment_status": { + "type": ["null", "string"] + }, + "order_type": { + "type": ["null", "string"] + }, + "price_type": { + "type": ["null", "string"] + }, + "reference_id": { + "type": ["null", "string"] + }, + "fulfillment_status": { + "type": ["null", "string"] + }, + "order_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "shipping_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "note": { + "type": ["null", "string"] + }, + "tracking_id": { + "type": ["null", "string"] + }, + "tracking_url": { + "type": ["null", "string"] + }, + "batch_id": { + "type": ["null", "string"] + }, + "created_by": { + "type": ["null", "string"] + }, + "shipment_carrier": { + "type": ["null", "string"] + }, + "invoice_round_off_amount": { + "type": ["null", "integer"] + }, + "tax": { + "type": ["null", "integer"] + }, + "amount_paid": { + "type": ["null", "integer"] + }, + "amount_adjusted": { + "type": ["null", "integer"] + }, + "refundable_credits_issued": { + "type": ["null", "integer"] + }, + "refundable_credits": { + "type": ["null", "integer"] + }, + "rounding_adjustement": { + "type": ["null", "integer"] + }, + "paid_on": { + "type": ["null", "string"], + "format": "date-time" + }, + "shipping_cut_off_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "status_update_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "delivered_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "shipped_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "resource_version": { + "type": ["null", "integer"] + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "cancelled_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "resent_status": { + "type": ["null", "string"] + }, + "is_resent": { + "type": ["null", "boolean"] + }, + "original_order_id": { + "type": ["null", "string"] + }, + "discount": { + "type": ["null", "integer"] + }, + "sub_total": { + "type": ["null", "integer"] + }, + "total": { + "type": ["null", "integer"] + }, + "deleted": { + "type": ["null", "boolean"] + }, + "currency_code": { + "type": ["null", "string"] + }, + "is_gifted": { + "type": ["null", "boolean"] + }, + "gift_note": { + "type": ["null", "string"] + }, + "gift_id": { + "type": ["null", "string"] + }, + "resend_reason": { + "type": ["null", "string"] + }, + "business_entity_id": { + "type": ["null", "string"] + }, + "order_line_items": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "invoice_id": { + "type": ["null", "string"] + }, + "invoice_line_item_id": { + "type": ["null", "string"] + }, + "unit_price": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "amount": { + "type": ["null", "integer"] + }, + "fulfillment_quantity": { + "type": ["null", "integer"] + }, + "fulfillment_amount": { + "type": ["null", "integer"] + }, + "tax_amount": { + "type": ["null", "integer"] + }, + "amount_paid": { + "type": ["null", "integer"] + }, + "amount_adjusted": { + "type": ["null", "integer"] + }, + "refundable_credits_issued": { + "type": ["null", "integer"] + }, + "refundable_credits": { + "type": ["null", "integer"] + }, + "is_shippable": { + "type": ["null", "boolean"] + }, + "sku": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "item_level_discount_amount": { + "type": ["null", "integer"] + }, + "discount_amount": { + "type": ["null", "integer"] + }, + "entity_id": { + "type": ["null", "string"] + } + } + } + }, + "shipping_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status,": { + "type": ["null", "string"] + } + } + }, + "billing_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + }, + "line_item_taxes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "tax_name": { + "type": ["null", "string"] + }, + "tax_rate": { + "type": ["null", "integer", "number"] + }, + "is_partial_tax_applied": { + "type": ["null", "boolean"] + }, + "is_non_compliance_tax": { + "type": ["null", "boolean"] + }, + "taxable_amount": { + "type": ["null", "integer"] + }, + "tax_amount": { + "type": ["null", "integer"] + }, + "tax_juris_type": { + "type": ["null", "string"] + }, + "tax_juris_name": { + "type": ["null", "string"] + }, + "tax_juris_code": { + "type": ["null", "string"] + }, + "tax_amount_in_local_currency": { + "type": ["null", "integer"] + }, + "local_currency_code": { + "type": ["null", "string"] + } + } + } + }, + "line_item_discounts": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "discount_type": { + "type": ["null", "string"] + }, + "coupon_id": { + "type": ["null", "string"] + }, + "discount_amount": { + "type": ["null", "integer"] + } + } + } + }, + "linked_credit_notes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "amount": { + "type": ["null", "integer"] + }, + "type": { + "type": ["null", "string"] + }, + "id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "amount_adjusted": { + "type": ["null", "integer"] + }, + "amount_refunded": { + "type": ["null", "integer"] + } + } + } + }, + "resent_orders": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "order_id": { + "type": ["null", "string"] + }, + "reason": { + "type": ["null", "string"] + }, + "amount": { + "type": ["null", "integer"] + } + } + } + } + } +} \ No newline at end of file diff --git a/tap_chargebee/schemas/common/payment_sources.json b/tap_chargebee/schemas/common/payment_sources.json new file mode 100644 index 0000000..1c3124a --- /dev/null +++ b/tap_chargebee/schemas/common/payment_sources.json @@ -0,0 +1,189 @@ +{ + "type": ["null", "object"], + "additionalProperties": false, + "properties": { + "id": { + "type": ["null", "string"] + }, + "resource_version": { + "type": ["null", "integer"] + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "customer_id": { + "type": ["null", "string"] + }, + "type": { + "type": ["null", "string"] + }, + "reference_id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "gateway": { + "type": ["null", "string"] + }, + "gateway_account_id": { + "type": ["null", "string"] + }, + "ip_address": { + "type": ["null", "string"] + }, + "issuing_country": { + "type": ["null", "string"] + }, + "business_entity_id": { + "type": ["null", "string"] + }, + "deleted": { + "type": ["null", "boolean"] + }, + "object": { + "type": ["null", "string"] + }, + "card": { + "$ref": "cards.json#/" + }, + "bank_account": { + "type": ["null", "object"], + "properties": { + "last4": { + "type": ["null", "string"] + }, + "name_on_account": { + "type": ["null", "string"] + }, + "bank_name": { + "type": ["null", "string"] + }, + "mandate_id": { + "type": ["null", "string"] + }, + "account_type": { + "type": ["null", "string"] + }, + "echeck_type": { + "type": ["null", "string"] + }, + "account_holder_type": { + "type": ["null", "string"] + } + } + }, + "boleto": { + "type": ["null", "object"], + "properties": { + "last4": { + "type": ["null", "string"] + }, + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + } + } + }, + "billing_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + }, + "amazon_payment": { + "type": ["null", "object"], + "properties": { + "email": { + "type": ["null", "string"] + }, + "agreement_id": { + "type": ["null", "string"] + } + } + }, + "paypal": { + "type": ["null", "object"], + "properties": { + "email": { + "type": ["null", "string"] + }, + "agremeent_id": { + "type": ["null", "string"] + } + } + }, + "upi": { + "type": ["null", "object"], + "properties": { + "vpa": { + "type": ["null", "string"] + } + } + }, + "klarna_pay_now": { + "id": { + "type": ["null", "string"] + } + }, + "venmo": { + "email": { + "type": ["null", "string"] + } + } + } +} diff --git a/tap_chargebee/schemas/common/promotional_credits.json b/tap_chargebee/schemas/common/promotional_credits.json new file mode 100644 index 0000000..66efe00 --- /dev/null +++ b/tap_chargebee/schemas/common/promotional_credits.json @@ -0,0 +1,46 @@ +{ + "type": ["null", "object"], + "additionalProperties": false, + "properties": { + "id": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "type":{ + "type": ["null", "string"] + }, + "amount": { + "type": ["null", "string"] + }, + "amount_in_decimal": { + "type": ["null", "string"] + }, + "currency_code": { + "type": ["null", "string"] + }, + "description": { + "type": ["null", "string"] + }, + "credit_type": { + "type": ["null", "string"] + }, + "reference": { + "type": ["null", "string"] + }, + "closing_balance": { + "type": ["null", "integer"] + }, + "done_by": { + "type": ["null", "string"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "object": { + "type": ["null", "string"] + } + } +} \ No newline at end of file diff --git a/tap_chargebee/schemas/common/quotes.json b/tap_chargebee/schemas/common/quotes.json new file mode 100644 index 0000000..8f7b3b8 --- /dev/null +++ b/tap_chargebee/schemas/common/quotes.json @@ -0,0 +1,389 @@ +{ + "type": ["null", "object"], + "additionalProperties": false, + "properties": { + "id": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "po_number": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "invoice_id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "operation_type": { + "type": ["null", "string"] + }, + "vat_number": { + "type": ["null", "string"] + }, + "price_type": { + "type": ["null", "string"] + }, + "valid_till": { + "type": ["null", "string"], + "format": "date-time" + }, + "date": { + "type": ["null", "string"], + "format": "date-time" + }, + "total_payable": { + "type": ["null", "integer"] + }, + "charge_on_acceptance": { + "type": ["null", "integer"] + }, + "sub_total": { + "type": ["null", "integer"] + }, + "total": { + "type": ["null", "integer"] + }, + "credits_applied": { + "type": ["null", "integer"] + }, + "amount_paid": { + "type": ["null", "integer"] + }, + "amount_due": { + "type": ["null", "integer"] + }, + "version": { + "type": ["null", "integer"] + }, + "resource_version": { + "type": ["null", "integer"] + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "vat_number_prefix": { + "type": ["null", "string"] + }, + "currency_code": { + "type": ["null", "string"] + }, + "tax_category": { + "type": ["null", "string"] + }, + "notes": { + "type": ["null", "string"] + }, + "business_entity_id": { + "type": ["null", "string"] + }, + "contract_term_start": { + "type": ["null", "string"], + "format": "date-time" + }, + "contract_term_end": { + "type": ["null", "string"], + "format": "date-time" + }, + "contract_term_termination_fee": { + "type": ["null", "integer"] + }, + "line_items": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "date_from": { + "type": ["null", "string"], + "format": "date-time" + }, + "date_to": { + "type": ["null", "string"], + "format": "date-time" + }, + "unit_amount": { + "type": ["null", "integer"] + }, + "quantity": { + "type": ["null", "integer"] + }, + "is_taxed": { + "type": ["null", "boolean"] + }, + "tax_amount": { + "type": ["null", "integer"] + }, + "tax_rate": { + "type": ["null", "integer"] + }, + "amount": { + "type": ["null", "integer"] + }, + "discount_amount": { + "type": ["null", "integer"] + }, + "item_level_discount_amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "tax_exempt_reason": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "pricing_model": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + }, + "discounts": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + }, + "line_item_discounts": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "discount_type": { + "type": ["null", "string"] + }, + "coupon_id": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "discount_amount": { + "type": ["null", "integer"] + } + } + } + }, + "taxes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "name": { + "type": ["null", "string"] + }, + "amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + } + } + } + }, + "line_item_taxes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "tax_name": { + "type": ["null", "string"] + }, + "tax_rate": { + "type": ["null", "integer"] + }, + "is_partial_tax_applied": { + "type": ["null", "boolean"] + }, + "is_non_compliance_tax": { + "type": ["null", "boolean"] + }, + "taxable_amount": { + "type": ["null", "integer"] + }, + "tax_amount": { + "type": ["null", "integer"] + }, + "tax_juris_type": { + "type": ["null", "string"] + }, + "tax_juris_name": { + "type": ["null", "string"] + }, + "tax_juris_code": { + "type": ["null", "string"] + } + } + } + }, + "line_item_tiers": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "starting_unit": { + "type": ["null", "integer"] + }, + "ending_unit": { + "type": ["null", "integer"] + }, + "quantity_used": { + "type": ["null", "integer"] + }, + "unit_amount": { + "type": ["null", "integer"] + } + } + } + }, + "shipping_address": { + "type": ["null", "object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status,": { + "type": ["null", "string"] + }, + "index": { + "type": ["null", "string"] + } + } + }, + "billing_address": { + "type": ["null", "object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + } +} \ No newline at end of file diff --git a/tap_chargebee/schemas/transactions.json b/tap_chargebee/schemas/common/transactions.json similarity index 61% rename from tap_chargebee/schemas/transactions.json rename to tap_chargebee/schemas/common/transactions.json index b2b9036..6729f49 100644 --- a/tap_chargebee/schemas/transactions.json +++ b/tap_chargebee/schemas/common/transactions.json @@ -20,6 +20,12 @@ "payment_method": { "type": ["null", "string"] }, + "iin": { + "type": ["null", "string"] + }, + "last4": { + "type": ["null", "string"] + }, "reference_number": { "type": ["null", "string"] }, @@ -52,6 +58,12 @@ "fraud_flag": { "type": ["null", "string"] }, + "initiator_type": { + "type": ["null", "string"] + }, + "three_d_secure": { + "type": ["null", "boolean"] + }, "error_code": { "type": ["null", "string"] }, @@ -99,6 +111,37 @@ "refunded_txn_id": { "type": ["null", "string"] }, + "authorization_reason": { + "type": ["null", "string"] + }, + "voided_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "reversal_transaction_id": { + "type": ["null", "string"] + }, + "reference_authorization_id": { + "type": ["null", "string"] + }, + "amount_capturable": { + "type": ["null", "string"] + }, + "merchant_reference_id": { + "type": ["null", "string"] + }, + "custom_payment_method_id": { + "type": ["null", "string"] + }, + "business_entity_id": { + "type": ["null", "string"] + }, + "payment_method_details": { + "type": ["null", "string"] + }, + "custom_payment_method_name": { + "type": ["null", "string"] + }, "linked_invoices": { "type": ["null", "array"], "items": { @@ -116,7 +159,7 @@ }, "invoice_date": { "type": ["null", "string"], - "format": "date-times" + "format": "date-time" }, "invoice_total": { "type": ["null", "integer"] @@ -145,6 +188,9 @@ "cn_reason_code": { "type": ["null", "string"] }, + "cn_create_reason_code": { + "type": ["null", "string"] + }, "cn_date": { "type": ["null", "string"], "format": "date-time" @@ -181,6 +227,71 @@ } } } + }, + "linked_payments": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "amount": { + "type": ["null", "integer"] + }, + "date": { + "type": ["null", "string"], + "format": "date-time" + } + } + } + } + }, + "error_detail": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "request_id": { + "type": ["null", "string"] + }, + "error_category": { + "type": ["null", "string"] + }, + "error_message": { + "type": ["null", "integer"] + }, + "decline_code": { + "type": ["null", "string"] + }, + "decline_message": { + "type": ["null", "string"] + }, + "network_error_code": { + "type": ["null", "integer"] + }, + "network_error_message": { + "type": ["null", "string"] + }, + "error_field": { + "type": ["null", "string"] + }, + "recommendation_code": { + "type": ["null", "integer"] + }, + "recommendation_message": { + "type": ["null", "string"] + }, + "processor_error_code": { + "type": ["null", "string"] + }, + "processor_error_message": { + "type": ["null", "integer"] + } + } } } } diff --git a/tap_chargebee/schemas/plans.json b/tap_chargebee/schemas/common/virtual_bank_accounts.json similarity index 57% rename from tap_chargebee/schemas/plans.json rename to tap_chargebee/schemas/common/virtual_bank_accounts.json index 2dcf389..8cb22c5 100644 --- a/tap_chargebee/schemas/plans.json +++ b/tap_chargebee/schemas/common/virtual_bank_accounts.json @@ -5,59 +5,51 @@ "id": { "type": ["null", "string"] }, - "name": { + "customer_id": { "type": ["null", "string"] }, - "description": { + "email": { "type": ["null", "string"] }, - "price": { - "type": ["null", "integer"] - }, - "period": { - "type": ["null", "integer"] - }, - "period_unit": { + "scheme": { "type": ["null", "string"] }, - "trial_period": { - "type": ["null", "integer"] - }, - "trial_period_unit": { + "bank_name": { "type": ["null", "string"] }, - "charge_model": { + "account_number": { "type": ["null", "string"] }, - "free_quantity": { - "type": ["null", "integer"] - }, - "setup_cost": { - "type": ["null", "integer"] - }, - "status": { + "routing_number": { "type": ["null", "string"] }, - "billing_cycles": { - "type": ["null", "integer"] + "swift_code": { + "type": ["null", "string"] }, - "redirect_url": { + "gateway": { "type": ["null", "string"] }, - "sku": { + "gateway_account_id": { "type": ["null", "string"] }, + "resource_version": { + "type": ["null", "integer"] + }, "updated_at": { "type": ["null", "string"], "format": "date-time" }, - "invoice_notes": { + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "reference_id": { "type": ["null", "string"] }, - "taxable": { + "deleted": { "type": ["null", "boolean"] }, - "tax_profile_id": { + "object": { "type": ["null", "string"] } } diff --git a/tap_chargebee/schemas/credit_notes.json b/tap_chargebee/schemas/credit_notes.json deleted file mode 100644 index c17728d..0000000 --- a/tap_chargebee/schemas/credit_notes.json +++ /dev/null @@ -1,354 +0,0 @@ -{ - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "customer_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "subscription_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "reference_invoice_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "type": { - "type": ["null", "string"] - }, - "reason_code": { - "type": ["null", "string"] - }, - "status": { - "type": ["null", "string"] - }, - "vat_number": { - "type": ["null", "string"], - "maxLength": 20 - }, - "date": { - "type": ["null", "string"], - "format": "date-time" - }, - "price_type": { - "type": ["null", "string"] - }, - "currency_code": { - "type": ["null", "string"], - "maxLength": 3 - }, - "total": { - "type": ["null", "integer"], - "minimum": 0 - }, - "amount_allocated": { - "type": ["null", "integer"], - "minimum": 0 - }, - "amount_refunded": { - "type": ["null", "integer"], - "minimum": 0 - }, - "amount_available": { - "type": ["null", "integer"], - "minimum": 0 - }, - "refunded_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "voided_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "resource_version": { - "type": ["null", "integer"] - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "sub_total": { - "type": ["null", "integer"], - "minimum": 0 - }, - "round_off_amount": { - "type": ["null", "integer"], - "minimum": 0 - }, - "deleted": { - "type": ["null", "boolean"] - }, - "line_items": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "id": { - "type": ["null", "string"], - "maxLength": 40 - }, - "subscription_id": { - "type": ["null", "string"] - }, - "date_from": { - "type": ["null", "string"], - "format": "date-time" - }, - "date_to": { - "type": ["null", "string"], - "format": "date-time" - }, - "unit_amount": { - "type": ["null", "integer"] - }, - "quantity": { - "type": ["null", "integer"] - }, - "amount": { - "type": ["null", "integer"] - }, - "pricing_model": { - "type": ["null", "string"] - }, - "is_taxed": { - "type": ["null", "boolean"] - }, - "tax_amount": { - "type": ["null", "integer"], - "minimum": 0 - }, - "tax_rate": { - "type": ["null", "number"], - "minimum": 0, - "maximum": 100 - }, - "discount_amount": { - "type": ["null", "integer"], - "minimum": 0 - }, - "item_level_discount_amount": { - "type": ["null", "integer"], - "minimum": 0 - }, - "description": { - "type": ["null", "string"], - "maxLength": 250 - }, - "entity_type": { - "type": ["null", "string"] - }, - "tax_exempt_reason": { - "type": ["null", "string"] - }, - "entity_id": { - "type": ["null", "string"], - "maxLength": 100 - } - } - } - }, - "discounts": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "amount": { - "type": ["null", "integer"], - "minimum": 0 - }, - "description": { - "type": ["null", "string"], - "maxLength": 250 - }, - "entity_type": { - "type": ["null", "string"] - }, - "entity_id": { - "type": ["null", "string"], - "maxLength": 100 - } - } - } - }, - "line_item_discounts": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "line_item_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "discount_type": { - "type": ["null", "string"] - }, - "coupon_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "discount_amount": { - "type": ["null", "integer"], - "minimum": 0 - } - } - } - }, - "line_item_tiers": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "line_item_id": { - "type": ["null", "string"], - "maxLength": 40 - }, - "starting_unit": { - "type": ["null", "integer"], - "minimum": 1 - }, - "ending_unit": { - "type": ["null", "integer"] - }, - "quantity_used": { - "type": ["null", "integer"], - "minimum": 1 - }, - "unit_amount": { - "type": ["null", "integer"], - "minimum": 0 - } - } - } - }, - "taxes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "name": { - "type": ["null", "string"], - "maxLength": 100 - }, - "amount": { - "type": ["null", "integer"], - "minimum": 0 - }, - "description": { - "type": ["null", "string"], - "maxLength": 250 - } - } - } - }, - "line_item_taxes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "line_item_id": { - "type": ["null", "string"], - "maxLength": 40 - }, - "tax_name": { - "type": ["null", "string"], - "maxLength": 100 - }, - "tax_rate": { - "type": ["null", "number"], - "minimum": 0, - "maximum": 100 - }, - "is_partial_tax_applied": { - "type": ["null", "boolean"] - }, - "is_non_compliance_tax": { - "type": ["null", "boolean"] - }, - "taxable_amount": { - "type": ["null", "integer"], - "minimum": 0 - }, - "tax_amount": { - "type": ["null", "integer"], - "minimum": 0 - }, - "tax_juris_type": { - "type": ["null", "string"] - }, - "tax_juris_name": { - "type": ["null", "string"], - "maxLength": 250 - }, - "tax_juris_code": { - "type": ["null", "string"], - "maxLength": 250 - } - } - } - }, - "linked_refunds": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "txn_id": { - "type": ["null", "string"], - "maxLength": 40 - }, - "applied_amount": { - "type": ["null", "integer"], - "minimum": 0 - }, - "applied_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "txn_status": { - "type": ["null", "string"] - }, - "txn_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "txn_amount": { - "type": ["null", "integer"], - "minimum": 1 - } - } - } - }, - "allocations": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "invoice_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "allocated_amount": { - "type": ["null", "integer"], - "minimum": 0 - }, - "allocated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "invoice_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "invoice_status": { - "type": ["null", "string"] - } - } - } - } - } -} diff --git a/tap_chargebee/schemas/events.json b/tap_chargebee/schemas/events.json deleted file mode 100644 index 5b06384..0000000 --- a/tap_chargebee/schemas/events.json +++ /dev/null @@ -1,1472 +0,0 @@ -{ - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"] - }, - "occurred_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "source": { - "type": ["null", "string"] - }, - "user": { - "type": ["null", "string"] - }, - "event_type": { - "type": ["null", "string"] - }, - "api_version": { - "type": ["null", "string"] - }, - "object": { - "type": ["null", "string"] - }, - "webhook_status": { - "type": ["null", "string"] - }, - "content": { - "type": ["null", "object"], - "properties" : { - "customer": { - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"] - }, - "first_name": { - "type": ["null", "string"] - }, - "last_name": { - "type": ["null", "string"] - }, - "email": { - "type": ["null", "string"] - }, - "phone": { - "type": ["null", "string"] - }, - "company": { - "type": ["null", "string"] - }, - "vat_number": { - "type": ["null", "string"] - }, - "auto_collection": { - "type": ["null", "string"] - }, - "net_term_days": { - "type": ["null", "integer"] - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "locale": { - "type": ["null", "string"] - }, - "consolidated_invoicing": { - "type": ["null", "boolean"] - }, - "billing_date": { - "type": ["null", "boolean"] - }, - "billing_date_mode": { - "type": ["null", "string"] - }, - "billing_day_of_week": { - "type": ["null", "boolean"] - }, - "billing_day_of_week_mode": { - "type": ["null", "string"] - }, - "primary_payment_source_id": { - "type": ["null", "string"] - }, - "invoice_notes": { - "type": ["null", "string"] - }, - "promotional_credits": { - "type": ["null", "integer"] - }, - "unbilled_charges": { - "type": ["null", "integer"] - }, - "refundable_credits": { - "type": ["null", "integer"] - }, - "excess_payments": { - "type": ["null", "integer"] - }, - "deleted": { - "type": ["null", "boolean"] - }, - "cf_company_id": { - "type": ["null","integer"] - }, - "allow_direct_debit": { - "type": ["null", "boolean"] - }, - "card_status": { - "type": ["null", "string"] - }, - "object": { - "type": ["null", "string"] - }, - "pii_cleared": { - "type": ["null", "string"] - }, - "preferred_currency_code": { - "type": ["null", "string"] - }, - "resource_version": { - "type": ["null", "integer"] - }, - "taxability": { - "type": ["null", "string"] - }, - "billing_address": { - "type": ["null","object"], - "properties": { - "first_name": { - "type": ["null", "string"] - }, - "last_name": { - "type": ["null", "string"] - }, - "email": { - "type": ["null", "string"] - }, - "company": { - "type": ["null", "string"] - }, - "phone": { - "type": ["null", "string"] - }, - "line1": { - "type": ["null", "string"] - }, - "line2": { - "type": ["null", "string"] - }, - "line3": { - "type": ["null", "string"] - }, - "city": { - "type": ["null", "string"] - }, - "state_code": { - "type": ["null", "string"] - }, - "state": { - "type": ["null", "string"] - }, - "country": { - "type": ["null", "string"] - }, - "zip": { - "type": ["null", "string"] - }, - "validation_status,": { - "type": ["null", "string"] - } - } - }, - "referral_urls": { - "type": ["null", "array"], - "items": { - "type": ["null","object"], - "properties": { - "external_customer_id": { - "type": ["null", "string"] - }, - "referral_sharing_url": { - "type": ["null", "string"] - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "referral_campaign_id": { - "type": ["null", "string"] - }, - "referral_account_id": { - "type": ["null", "string"] - }, - "referral_external_account_id": { - "type": ["null", "string"] - }, - "referral_system": { - "type": ["null", "string"] - } - } - } - }, - "contacts": { - "type": ["null", "array"], - "items": { - "type": ["null","object"], - "properties": { - "id": { - "type": ["null", "string"] - }, - "first_name": { - "type": ["null", "string"] - }, - "last_name": { - "type": ["null", "string"] - }, - "email": { - "type": ["null", "string"] - }, - "phone": { - "type": ["null", "string"] - }, - "label": { - "type": ["null", "string"] - }, - "enabled": { - "type": ["null", "boolean"] - }, - "send_account_email": { - "type": ["null", "string"] - }, - "send_billing_email": { - "type": ["null", "string"] - }, - "object": { - "type": ["null", "string"] - } - } - } - }, - "payment_method": { - "type": ["null","object"], - "properties": { - "type": { - "type": ["null", "string"] - }, - "gateway": { - "type": ["null", "string"] - }, - "gateway_account_id ": { - "type": ["null", "string"] - }, - "status": { - "type": ["null", "string"] - }, - "reference_id": { - "type": ["null", "string"] - }, - "gateway_account_id": { - "type": ["null", "string"] - }, - "object": { - "type": ["null", "string"] - } - } - }, - "balances": { - "type": ["null", "array"], - "items": { - "type": ["null","object"], - "properties": { - "promotional_credits": { - "type": ["null", "integer"] - }, - "excess_payments": { - "type": ["null", "integer"] - }, - "refundable_credits ": { - "type": ["null", "integer"] - }, - "unbilled_charges": { - "type": ["null", "integer"] - }, - "currency_code": { - "type": ["null", "string"] - } - } - } - } - } - }, - "subscription": { - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"] - }, - "customer_id": { - "type": ["null", "string"] - }, - "currency_code": { - "type": ["null", "string"] - }, - "plan_id": { - "type": ["null", "string"] - }, - "plan_quantity": { - "type": ["null", "integer"] - }, - "plan_unit_price": { - "type": ["null", "integer"] - }, - "billing_period": { - "type": ["null", "integer"] - }, - "mrr": { - "type": ["null", "integer"] - }, - "billing_period_unit": { - "type": ["null", "string"] - }, - "status": { - "type": ["null", "string"] - }, - "coupon": { - "type": ["null", "string"] - }, - "trial_start": { - "type": ["null", "string"], - "format": "date-time" - }, - "trial_end": { - "type": ["null", "string"], - "format": "date-time" - }, - "current_term_start": { - "type": ["null", "string"], - "format": "date-time" - }, - "current_term_end": { - "type": ["null", "string"], - "format": "date-time" - }, - "next_billing_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "remaining_billing_cycles": { - "type": ["null", "integer"] - }, - "po_number": { - "type": ["null", "string"] - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "started_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "activated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "cancelled_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "cancel_reason": { - "type": ["null", "string"] - }, - "affiliate_token": { - "type": ["null", "string"] - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "payment_source_id": { - "type": ["null", "string"] - }, - "auto_collection": { - "type": ["null", "string"] - }, - "start_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "invoice_notes": { - "type": ["null", "string"] - }, - "deleted": { - "type": ["null", "boolean"] - }, - "base_currency_code": { - "type": ["null", "string"] - }, - "due_invoices_count": { - "type": ["null", "integer"] - }, - "exchange_rate": { - "type": ["null", "number"] - }, - "has_scheduled_changes": { - "type": ["null", "boolean"] - }, - "plan_amount": { - "type": ["null", "integer"] - }, - "plan_free_quantity": { - "type": ["null", "integer"] - }, - "resource_version": { - "type": ["null", "integer"] - }, - "object": { - "type": ["null", "string"] - }, - "addons": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "id": { - "type": ["null", "string"] - }, - "quantity": { - "type": ["null", "integer"] - }, - "unit_price": { - "type": ["null", "integer"] - }, - "trial_end": { - "type": ["null", "string"], - "format": "date-time" - }, - "remaining_billing_cycles": { - "type": ["null", "integer"] - }, - "object": { - "type": ["null", "string"] - } - } - } - }, - "event_based_addons": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "id": { - "type": ["null", "string"] - }, - "quantity": { - "type": ["null", "integer"] - }, - "unit_price": { - "type": ["null", "integer"] - }, - "on_event": { - "type": ["null", "string"] - }, - "charge_once": { - "type": ["null", "boolean"] - }, - "object": { - "type": ["null", "string"] - } - } - } - }, - "charged_event_based_addons": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "id": { - "type": ["null", "string"] - }, - "last_charged_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "object": { - "type": ["null", "string"] - } - } - } - }, - "coupons": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "coupon_id": { - "type": ["null", "string"] - }, - "apply_till": { - "type": ["null", "string"], - "format": "date-time" - }, - "applied_count": { - "type": ["null", "integer"] - }, - "coupon_code": { - "type": ["null", "string"] - }, - "object": { - "type": ["null", "string"] - } - } - } - }, - "shipping_address": { - "type": ["null","object"], - "properties": { - "first_name": { - "type": ["null", "string"] - }, - "last_name": { - "type": ["null", "string"] - }, - "email": { - "type": ["null", "string"] - }, - "company": { - "type": ["null", "string"] - }, - "phone": { - "type": ["null", "string"] - }, - "line1": { - "type": ["null", "string"] - }, - "line2": { - "type": ["null", "string"] - }, - "line3": { - "type": ["null", "string"] - }, - "city": { - "type": ["null", "string"] - }, - "state_code": { - "type": ["null", "string"] - }, - "state": { - "type": ["null", "string"] - }, - "country": { - "type": ["null", "string"] - }, - "zip": { - "type": ["null", "string"] - }, - "validation_status,": { - "type": ["null", "string"] - } - } - }, - "referral_info": { - "type": ["null","object"], - "properties": { - "referral_code": { - "type": ["null", "string"] - }, - "coupon_code": { - "type": ["null", "string"] - }, - "referrer_id": { - "type": ["null", "string"] - }, - "external_reference_id": { - "type": ["null", "string"] - }, - "reward_status": { - "type": ["null", "string"] - }, - "referral_status": { - "type": ["null", "string"] - }, - "account_id": { - "type": ["null", "string"] - }, - "campaign_id": { - "type": ["null", "string"] - }, - "external_campaign_id": { - "type": ["null", "string"] - }, - "friend_offer_type": { - "type": ["null", "string"] - }, - "referrer_reward_type": { - "type": ["null", "string"] - }, - "notify_referral_system": { - "type": ["null", "string"] - }, - "destination_url": { - "type": ["null", "string"] - }, - "post_purchase_widget_enabled": { - "type": ["null", "boolean"] - } - } - } - } - }, - "plan": { - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"] - }, - "name": { - "type": ["null", "string"] - }, - "description": { - "type": ["null", "string"] - }, - "price": { - "type": ["null", "integer"] - }, - "period": { - "type": ["null", "integer"] - }, - "period_unit": { - "type": ["null", "string"] - }, - "trial_period": { - "type": ["null", "integer"] - }, - "trial_period_unit": { - "type": ["null", "string"] - }, - "charge_model": { - "type": ["null", "string"] - }, - "free_quantity": { - "type": ["null", "integer"] - }, - "setup_cost": { - "type": ["null", "integer"] - }, - "status": { - "type": ["null", "string"] - }, - "billing_cycles": { - "type": ["null", "integer"] - }, - "redirect_url": { - "type": ["null", "string"] - }, - "sku": { - "type": ["null", "string"] - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "invoice_notes": { - "type": ["null", "string"] - }, - "taxable": { - "type": ["null", "boolean"] - }, - "tax_profile_id": { - "type": ["null", "string"] - } - } - }, - "invoice": { - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"] - }, - "po_number": { - "type": ["null", "string"] - }, - "customer_id": { - "type": ["null", "string"] - }, - "recurring": { - "type": ["null", "boolean"] - }, - "status": { - "type": ["null", "string"] - }, - "vat_number": { - "type": ["null", "string"] - }, - "price_type": { - "type": ["null", "string"] - }, - "date": { - "type": ["null", "string"], - "format": "date-time" - }, - "due_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "net_term_days": { - "type": ["null", "integer"] - }, - "currency_code": { - "type": ["null", "string"] - }, - "total": { - "type": ["null", "integer"] - }, - "amount_paid": { - "type": ["null", "integer"] - }, - "amount_adjusted": { - "type": ["null", "integer"] - }, - "write_off_amount": { - "type": ["null", "integer"] - }, - "credits_applied": { - "type": ["null", "integer"] - }, - "amount_due": { - "type": ["null", "integer"] - }, - "paid_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "dunning_status": { - "type": ["null", "string"] - }, - "next_retry_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "resource_version": { - "type": ["null", "integer"] - }, - "voided_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "sub_total": { - "type": ["null", "integer"] - }, - "tax": { - "type": ["null", "integer"] - }, - "first_invoice": { - "type": ["null", "boolean"] - }, - "has_advance_charges": { - "type": ["null", "boolean"] - }, - "expected_payment_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "amount_to_collect": { - "type": ["null", "integer"] - }, - "round_off_amount": { - "type": ["null", "integer"] - }, - "deleted": { - "type": ["null", "boolean"] - }, - "is_gifted": { - "type": ["null", "boolean"] - }, - "term_finalized": { - "type": ["null", "boolean"] - }, - "line_items": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "id": { - "type": ["null", "string"] - }, - "subscription_id": { - "type": ["null", "string"] - }, - "date_from": { - "type": ["null", "string"], - "format": "date-time" - }, - "date_to": { - "type": ["null", "string"], - "format": "date-time" - }, - "unit_amount": { - "type": ["null", "integer"] - }, - "quantity": { - "type": ["null", "integer"] - }, - "is_taxed": { - "type": ["null", "boolean"] - }, - "tax_amount": { - "type": ["null", "integer"] - }, - "tax_rate": { - "type": ["null", "integer"] - }, - "amount": { - "type": ["null", "integer"] - }, - "discount_amount": { - "type": ["null", "integer"] - }, - "item_level_discount_amount": { - "type": ["null", "integer"] - }, - "description": { - "type": ["null", "string"] - }, - "entity_type": { - "type": ["null", "string"] - }, - "tax_exempt_reason": { - "type": ["null", "string"] - }, - "entity_id": { - "type": ["null", "string"] - }, - "pricing_model": { - "type": ["null", "string"] - }, - "object": { - "type": ["null", "string"] - } - } - } - }, - "discounts": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "amount": { - "type": ["null", "integer"] - }, - "description": { - "type": ["null", "string"] - }, - "entity_type": { - "type": ["null", "string"] - }, - "entity_id": { - "type": ["null", "string"] - }, - "object": { - "type": ["null", "string"] - } - } - } - }, - "line_item_discounts": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "line_item_id": { - "type": ["null", "string"] - }, - "discount_type": { - "type": ["null", "string"] - }, - "coupon_id": { - "type": ["null", "string"] - }, - "discount_amount": { - "type": ["null", "integer"] - } - } - } - }, - "taxes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "name": { - "type": ["null", "string"] - }, - "amount": { - "type": ["null", "integer"] - }, - "description": { - "type": ["null", "string"] - } - } - } - }, - "line_item_taxes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "line_item_id": { - "type": ["null", "string"] - }, - "tax_name": { - "type": ["null", "string"] - }, - "tax_rate": { - "type": ["null", "integer"] - }, - "tax_amount": { - "type": ["null", "integer"] - }, - "tax_juris_type": { - "type": ["null", "string"] - }, - "tax_juris_name": { - "type": ["null", "string"] - }, - "tax_juris_code": { - "type": ["null", "string"] - } - } - } - }, - "linked_payments": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "txn_id": { - "type": ["null", "string"] - }, - "applied_amount": { - "type": ["null", "integer"] - }, - "applied_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "txn_status": { - "type": ["null", "string"] - }, - "txn_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "txn_amount": { - "type": ["null", "integer"] - } - } - } - }, - "applied_credits": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "cn_id": { - "type": ["null", "string"] - }, - "applied_amount": { - "type": ["null", "integer"] - }, - "applied_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "cn_reason_code": { - "type": ["null", "string"] - }, - "cn_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "cn_status": { - "type": ["null", "string"] - } - } - } - }, - "adjustment_credit_notes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "cn_id": { - "type": ["null", "string"] - }, - "cn_reason_code": { - "type": ["null", "string"] - }, - "cn_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "cn_total": { - "type": ["null", "integer"] - }, - "cn_status": { - "type": ["null", "string"] - } - } - } - }, - "issued_credit_notes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "cn_id": { - "type": ["null", "string"] - }, - "cn_reason_code": { - "type": ["null", "string"] - }, - "cn_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "cn_total": { - "type": ["null", "integer"] - }, - "cn_status": { - "type": ["null", "string"] - } - } - } - }, - "linked_orders": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "id": { - "type": ["null", "string"] - }, - "status": { - "type": ["null", "string"] - }, - "reference_id": { - "type": ["null", "string"] - }, - "fulfillment_status": { - "type": ["null", "string"] - }, - "batch_id": { - "type": ["null", "string"] - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - } - } - } - }, - "notes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "entity_type": { - "type": ["null", "string"] - }, - "note": { - "type": ["null", "string"] - }, - "entity_id": { - "type": ["null", "string"] - } - } - } - }, - "shipping_address": { - "type": ["null","object"], - "properties": { - "first_name": { - "type": ["null", "string"] - }, - "last_name": { - "type": ["null", "string"] - }, - "email": { - "type": ["null", "string"] - }, - "company": { - "type": ["null", "string"] - }, - "phone": { - "type": ["null", "string"] - }, - "line1": { - "type": ["null", "string"] - }, - "line2": { - "type": ["null", "string"] - }, - "line3": { - "type": ["null", "string"] - }, - "city": { - "type": ["null", "string"] - }, - "state_code": { - "type": ["null", "string"] - }, - "state": { - "type": ["null", "string"] - }, - "country": { - "type": ["null", "string"] - }, - "zip": { - "type": ["null", "string"] - }, - "validation_status,": { - "type": ["null", "string"] - } - } - }, - "billing_address": { - "type": ["null","object"], - "properties": { - "first_name": { - "type": ["null", "string"] - }, - "last_name": { - "type": ["null", "string"] - }, - "email": { - "type": ["null", "string"] - }, - "company": { - "type": ["null", "string"] - }, - "phone": { - "type": ["null", "string"] - }, - "line1": { - "type": ["null", "string"] - }, - "line2": { - "type": ["null", "string"] - }, - "line3": { - "type": ["null", "string"] - }, - "city": { - "type": ["null", "string"] - }, - "state_code": { - "type": ["null", "string"] - }, - "state": { - "type": ["null", "string"] - }, - "country": { - "type": ["null", "string"] - }, - "zip": { - "type": ["null", "string"] - }, - "validation_status": { - "type": ["null", "string"] - }, - "object": { - "type": ["null", "string"] - } - } - }, - "exchange_rate": { - "type": ["null", "number"] - }, - "base_currency_code": { - "type": ["null", "string"] - }, - "new_sales_amount": { - "type": ["null", "integer"] - }, - "object": { - "type": ["null", "string"] - }, - "subscription_id": { - "type": ["null", "string"] - }, - "total": { - "type": ["null", "integer"] - } - } - }, - "coupon": { - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"] - }, - "name": { - "type": ["null", "string"] - }, - "discount_type": { - "type": ["null", "string"] - }, - "discount_percentage": { - "type": ["null", "number"] - }, - "discount_amount": { - "type": ["null", "number"] - }, - "duration_type": { - "type": ["null", "string"] - }, - "duration_month": { - "type": ["null", "integer"] - }, - "max_redemptions": { - "type": ["null", "integer"] - }, - "status": { - "type": ["null", "string"] - }, - "apply_discount_on": { - "type": ["null", "string"] - }, - "apply_on": { - "type": ["null", "string"] - }, - "plan_constraint": { - "type": ["null", "string"] - }, - "addon_constraint": { - "type": ["null", "string"] - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "archived_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "resource_version": { - "type": ["null", "integer"] - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "object": { - "type": ["null", "string"] - }, - "redemptions": { - "type": ["null", "integer"] - } - } - }, - "transaction": { - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"] - }, - "customer_id": { - "type": ["null", "string"] - }, - "subscription_id": { - "type": ["null", "string"] - }, - "gateway_account_id": { - "type": ["null", "string"] - }, - "payment_source_id": { - "type": ["null", "string"] - }, - "payment_method": { - "type": ["null", "string"] - }, - "reference_number": { - "type": ["null", "string"] - }, - "gateway": { - "type": ["null", "string"] - }, - "type": { - "type": ["null", "string"] - }, - "date": { - "type": ["null", "string"], - "format": "date-time" - }, - "settled_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "currency_code": { - "type": ["null", "string"] - }, - "amount": { - "type": ["null", "integer"] - }, - "id_at_gateway": { - "type": ["null", "string"] - }, - "status": { - "type": ["null", "string"] - }, - "fraud_flag": { - "type": ["null", "string"] - }, - "error_code": { - "type": ["null", "string"] - }, - "error_text": { - "type": ["null", "string"] - }, - "validated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "fraud_reason": { - "type": ["null", "string"] - }, - "amount_unused": { - "type": ["null", "integer"] - }, - "masked_card_number": { - "type": ["null", "string"] - }, - "reference_transaction_id": { - "type": ["null", "string"] - }, - "reversal_txn_id": { - "type": ["null", "string"] - }, - "deleted": { - "type": ["null", "boolean"] - }, - "exchange_rate": { - "type": ["null", "number"] - }, - "base_currency_code": { - "type": ["null", "string"] - }, - "resource_version": { - "type": ["null", "integer"] - }, - "object": { - "type": ["null", "string"] - }, - "refunded_txn_id": { - "type": ["null", "string"] - }, - "linked_invoices": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "invoice_id": { - "type": ["null", "string"] - }, - "applied_amount": { - "type": ["null", "integer"] - }, - "applied_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "invoice_date": { - "type": ["null", "string"], - "format": "date-times" - }, - "invoice_total": { - "type": ["null", "integer"] - }, - "invoice_status": { - "type": ["null", "string"] - } - } - } - }, - "linked_credit_notes": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "cn_id": { - "type": ["null", "string"] - }, - "applied_amount": { - "type": ["null", "integer"] - }, - "applied_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "cn_reason_code": { - "type": ["null", "string"] - }, - "cn_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "cn_total": { - "type": ["null", "integer"] - }, - "cn_status": { - "type": ["null", "string"] - }, - "cn_reference_invoice_id": { - "type": ["null", "string"] - } - } - } - }, - "linked_refunds": { - "type": ["null", "array"], - "items": { - "type": ["null", "object"], - "properties": { - "txn_id": { - "type": ["null", "string"] - }, - "txn_status": { - "type": ["null", "string"] - }, - "txn_date": { - "type": ["null", "string"], - "format": "date-time" - }, - "txn_amount": { - "type": ["null", "integer"] - } - } - } - } - } - } - } - } - } - } diff --git a/tap_chargebee/schemas/item_model/coupons.json b/tap_chargebee/schemas/item_model/coupons.json new file mode 100644 index 0000000..444676e --- /dev/null +++ b/tap_chargebee/schemas/item_model/coupons.json @@ -0,0 +1,251 @@ +{ + "type":[ + "null", + "object" + ], + "additionalProperties":false, + "properties":{ + "id":{ + "type":[ + "null", + "string" + ] + }, + "name":{ + "type":[ + "null", + "string" + ] + }, + "invoice_name":{ + "type":[ + "null", + "string" + ] + }, + "discount_type":{ + "type":[ + "null", + "string" + ] + }, + "discount_percentage":{ + "type":[ + "null", + "number" + ] + }, + "discount_amount":{ + "type":[ + "null", + "number" + ] + }, + "currency_code":{ + "type":[ + "null", + "string" + ] + }, + "duration_type":{ + "type":[ + "null", + "string" + ] + }, + "valid_till":{ + "type":[ + "null", + "string" + ], + "format":"date-time" + }, + "duration_month":{ + "type":[ + "null", + "integer" + ] + }, + "max_redemptions":{ + "type":[ + "null", + "integer" + ] + }, + "status":{ + "type":[ + "null", + "string" + ] + }, + "apply_discount_on":{ + "type":[ + "null", + "string" + ] + }, + "apply_on":{ + "type":[ + "null", + "string" + ] + }, + "created_at":{ + "type":[ + "null", + "string" + ], + "format":"date-time" + }, + "archived_at":{ + "type":[ + "null", + "string" + ], + "format":"date-time" + }, + "resource_version":{ + "type":[ + "null", + "integer" + ] + }, + "updated_at":{ + "type":[ + "null", + "string" + ], + "format":"date-time" + }, + "period": { + "type": [ + "null", + "integer" + ] + }, + "period_unit": { + "type": [ + "null", + "string" + ] + }, + "object":{ + "type":[ + "null", + "string" + ] + }, + "redemptions":{ + "type":[ + "null", + "integer" + ] + }, + "invoice_notes":{ + "type":[ + "null", + "string" + ] + }, + "meta_data":{ + "type":[ + "null", + "string" + ] + }, + "item_constraints":{ + "type":[ + "null", + "array" + ], + "items":{ + "type":[ + "null", + "object" + ], + "properties":{ + "item_type":{ + "type":[ + "null", + "string" + ] + }, + "constraint":{ + "type":[ + "null", + "string" + ] + }, + "item_price_ids":{ + "type":[ + "null", + "array" + ], + "items":{ + "type":[ + "null", + "string" + ] + } + } + } + } + }, + "item_constraint_criteria":{ + "type":[ + "null", + "array" + ], + "items":{ + "type":[ + "null", + "object" + ], + "properties":{ + "item_type":{ + "type":[ + "null", + "string" + ] + }, + "currencies":{ + "type":[ + "null", + "array" + ], + "items":{ + "type":[ + "null", + "string" + ] + } + }, + "item_family_ids":{ + "type":[ + "null", + "array" + ], + "items":{ + "type":[ + "null", + "string" + ] + } + }, + "item_price_periods":{ + "type":[ + "null", + "array" + ], + "items":{ + "type":[ + "null", + "string" + ] + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tap_chargebee/schemas/item_model/events.json b/tap_chargebee/schemas/item_model/events.json new file mode 100644 index 0000000..b333164 --- /dev/null +++ b/tap_chargebee/schemas/item_model/events.json @@ -0,0 +1,653 @@ +{ + "type": ["null", "object"], + "additionalProperties": false, + "properties": { + "id": { + "type": ["null", "string"] + }, + "occurred_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "source": { + "type": ["null", "string"] + }, + "user": { + "type": ["null", "string"] + }, + "event_type": { + "type": ["null", "string"] + }, + "api_version": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + }, + "webhook_status": { + "type": ["null", "string"] + }, + "content": { + "type": ["null", "object"], + "properties" : { + "coupon": { + "$ref": "coupons.json" + }, + "credit_note": { + "$ref": "credit_notes.json" + }, + "customer": { + "$ref": "customers.json" + }, + "gift": { + "$ref": "gifts.json" + }, + "invoice": { + "$ref": "invoices.json" + }, + "order": { + "$ref": "orders.json" + }, + "payment_source": { + "$ref": "payment_sources.json" + }, + "item": { + "$ref": "items.json" + }, + "item_price": { + "$ref": "item_prices.json" + }, + "item_family": { + "$ref": "item_families.json" + }, + "promotional_credit": { + "$ref": "promotional_credits.json" + }, + "subscription":{ + "$ref": "subscriptions.json" + }, + "transaction": { + "$ref": "transactions.json" + }, + "virtual_bank_account":{ + "$ref": "virtual_bank_accounts.json" + }, + "unbilled_charges": { + "type": ["null", "array"], + "items": { + "type": ["null","object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "date_from": { + "type": ["null", "string"], + "format": "date-time" + }, + "date_to": { + "type": ["null", "string"], + "format": "date-time" + }, + "unit_amount": { + "type": ["null", "integer"] + }, + "pricing_model": { + "type": ["null", "string"] + }, + "quantity": { + "type": ["null", "integer"] + }, + "amount": { + "type": ["null", "integer"] + }, + "currency_code": { + "type": ["null", "string"] + }, + "discount_amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "is_voided": { + "type": ["null", "boolean"] + }, + "voided_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + }, + "amount_in_decimal": { + "type": ["null", "string"] + }, + "deleted": { + "type": ["null", "boolean"] + }, + "tiers": { + "type": ["null", "array"], + "items": { + "type": ["null","object"], + "properties": { + "starting_unit": { + "type": ["null", "integer"] + }, + "ending_unit": { + "type": ["null", "integer"] + }, + "quantity_used": { + "type": ["null", "integer"] + }, + "unit_amount ": { + "type": ["null", "integer"] + }, + "starting_unit_in_decimal": { + "type": ["null", "string"] + }, + "ending_unit_in_decimal": { + "type": ["null", "string"] + }, + "quantity_used_in_decimal": { + "type": ["null", "string"] + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] + } + } + } + } + } + + } + }, + "coupon_code":{ + "type":["null", "object"], + "additionalProperties": false, + "properties":{ + "code": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "coupon_id": { + "type": ["null", "string"] + }, + "coupon_site_id": { + "type": ["null", "string"] + }, + "coupon_set_id": { + "type": ["null", "string"] + }, + "coupon_set_name": { + "type": ["null", "string"] + } + } + }, + "coupon_set":{ + "type":["null", "object"], + "additionalProperties": false, + "properties":{ + "id": { + "type": ["null", "string"] + }, + "coupon_id": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "total_count": { + "type": ["null", "integer"] + }, + "redeemed_count": { + "type": ["null", "integer"] + }, + "archived_count": { + "type": ["null", "integer"] + }, + "meta_data": { + "type": ["null", "string"] + } + } + }, + "quote": { + "type":["null", "object"], + "additionalProperties": false, + "properties":{ + "id": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "po_number": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "invoice_id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "operation_type": { + "type": ["null", "string"] + }, + "vat_number": { + "type": ["null", "string"] + }, + "price_type": { + "type": ["null", "string"] + }, + "valid_till": { + "type": ["null", "string"], + "format": "date-time" + }, + "date": { + "type": ["null", "string"], + "format": "date-time" + }, + "total_payable": { + "type": ["null", "integer"] + }, + "charge_on_acceptance": { + "type": ["null", "integer"] + }, + "sub_total": { + "type": ["null", "integer"] + }, + "total": { + "type": ["null", "integer"] + }, + "credits_applied": { + "type": ["null", "integer"] + }, + "amount_paid": { + "type": ["null", "integer"] + }, + "amount_due": { + "type": ["null", "integer"] + }, + "version": { + "type": ["null", "integer"] + }, + "resource_version": { + "type": ["null", "integer"] + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "vat_number_prefix": { + "type": ["null", "string"] + }, + "currency_code": { + "type": ["null", "string"] + }, + "notes": { + "type":["null", "array"], + "items":{ + "type":["null","string"] + } + }, + "contract_term_start": { + "type": ["null", "string"], + "format": "date-time" + }, + "contract_term_end": { + "type": ["null", "string"], + "format": "date-time" + }, + "contract_term_termination_fee": { + "type": ["null", "integer"] + }, + "object": { + "type": ["null", "string"] + }, + "line_items": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "date_from": { + "type": ["null", "string"], + "format": "date-time" + }, + "date_to": { + "type": ["null", "string"], + "format": "date-time" + }, + "unit_amount": { + "type": ["null", "integer"] + }, + "quantity": { + "type": ["null", "integer"] + }, + "amount": { + "type": ["null", "integer"] + }, + "pricing_model": { + "type": ["null", "string"] + }, + "is_taxed": { + "type": ["null", "boolean"] + }, + "tax_amount": { + "type": ["null", "integer"] + }, + "tax_rate": { + "type": ["null", "number"] + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + }, + "amount_in_decimal": { + "type": ["null", "string"] + }, + "discount_amount": { + "type": ["null", "integer"] + }, + "item_level_discount_amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "entity_description": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "tax_exempt_reason": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + }, + "discounts": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + } + } + } + }, + "line_item_discounts": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "discount_type": { + "type": ["null", "string"] + }, + "coupon_id": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "discount_amount": { + "type": ["null", "integer"] + } + } + } + }, + "taxes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "name": { + "type": ["null", "string"] + }, + "amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + } + } + } + }, + "line_item_taxes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "tax_name": { + "type": ["null", "string"] + }, + "tax_rate": { + "type": ["null", "number"] + }, + "is_partial_tax_applied": { + "type": ["null", "boolean"] + }, + "is_non_compliance_tax": { + "type": ["null", "boolean"] + }, + "taxable_amount": { + "type": ["null", "integer"] + }, + "tax_amount": { + "type": ["null", "integer"] + }, + "tax_juris_type": { + "type": ["null", "string"] + }, + "tax_juris_name": { + "type": ["null", "string"] + }, + "tax_juris_code": { + "type": ["null", "string"] + }, + "tax_amount_in_local_currency": { + "type": ["null", "integer"] + }, + "local_currency_code": { + "type": ["null", "string"] + } + } + } + }, + "line_item_tiers": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "starting_unit": { + "type": ["null", "integer"] + }, + "ending_unit": { + "type": ["null", "integer"] + }, + "quantity_used": { + "type": ["null", "integer"] + }, + "unit_amount": { + "type": ["null", "integer"] + }, + "starting_unit_in_decimal": { + "type": ["null", "string"] + }, + "ending_unit_in_decimal": { + "type": ["null", "string"] + }, + "quantity_used_in_decimal": { + "type": ["null", "string"] + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] + } + } + } + }, + "shipping_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status,": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + }, + "billing_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + } + } + + } + } + } +} diff --git a/tap_chargebee/schemas/item_model/item_families.json b/tap_chargebee/schemas/item_model/item_families.json new file mode 100644 index 0000000..1a36682 --- /dev/null +++ b/tap_chargebee/schemas/item_model/item_families.json @@ -0,0 +1,63 @@ +{ + "type":[ + "null", + "object" + ], + "properties":{ + "id":{ + "type":[ + "null", + "string" + ] + }, + "name":{ + "type":[ + "null", + "string" + ] + }, + "channel":{ + "type":[ + "null", + "string" + ] + }, + "description":{ + "type":[ + "null", + "string" + ] + }, + "status":{ + "type":[ + "null", + "string" + ] + }, + "resource_version":{ + "type":[ + "null", + "integer" + ] + }, + "updated_at":{ + "type":[ + "null", + "string" + ], + "format":"date-time" + }, + "object":{ + "type":[ + "null", + "string" + ] + }, + "custom_fields": { + "type": [ + "null", + "string" + ] + } + } +} \ No newline at end of file diff --git a/tap_chargebee/schemas/item_model/item_prices.json b/tap_chargebee/schemas/item_model/item_prices.json new file mode 100644 index 0000000..671aba4 --- /dev/null +++ b/tap_chargebee/schemas/item_model/item_prices.json @@ -0,0 +1,342 @@ +{ + "type":[ + "null", + "object" + ], + "properties":{ + "id":{ + "type":[ + "null", + "string" + ] + }, + "name":{ + "type":[ + "null", + "string" + ] + }, + "channel":{ + "type":[ + "null", + "string" + ] + }, + "item_family_id":{ + "type":[ + "null", + "string" + ] + }, + "item_id":{ + "type":[ + "null", + "string" + ] + }, + "description":{ + "type":[ + "null", + "string" + ] + }, + "status":{ + "type":[ + "null", + "string" + ] + }, + "external_name":{ + "type":[ + "null", + "string" + ] + }, + "pricing_model":{ + "type":[ + "null", + "string" + ] + }, + "price":{ + "type":[ + "null", + "integer" + ] + }, + "price_in_decimal":{ + "type":[ + "null", + "string" + ] + }, + "period":{ + "type":[ + "null", + "integer" + ] + }, + "currency_code":{ + "type":[ + "null", + "string" + ] + }, + "period_unit":{ + "type":[ + "null", + "string" + ] + }, + "trial_period":{ + "type":[ + "null", + "integer" + ] + }, + "trial_period_unit":{ + "type":[ + "null", + "string" + ] + }, + "trial_end_action":{ + "type":[ + "null", + "string" + ] + }, + "shipping_period":{ + "type":[ + "null", + "integer" + ] + }, + "shipping_period_unit":{ + "type":[ + "null", + "string" + ] + }, + "billing_cycles":{ + "type":[ + "null", + "integer" + ] + }, + "free_quantity":{ + "type":[ + "null", + "integer" + ] + }, + "free_quantity_in_decimal":{ + "type":[ + "null", + "string" + ] + }, + "resource_version":{ + "type":[ + "null", + "integer" + ] + }, + "updated_at":{ + "type":[ + "null", + "string" + ], + "format":"date-time" + }, + "created_at":{ + "type":[ + "null", + "string" + ], + "format":"date-time" + }, + "archived_at":{ + "type":[ + "null", + "string" + ], + "format":"date-time" + }, + "invoice_notes":{ + "type":[ + "null", + "string" + ] + }, + "is_taxable":{ + "type":[ + "null", + "boolean" + ] + }, + "metadata":{ + "type":[ + "null", + "string" + ] + }, + "item_type":{ + "type":[ + "null", + "string" + ] + }, + "show_description_in_invoices":{ + "type":[ + "null", + "boolean" + ] + }, + "show_description_in_quotes":{ + "type":[ + "null", + "boolean" + ] + }, + "archivable":{ + "type":[ + "null", + "boolean", + "string" + ] + }, + "object":{ + "type":[ + "null", + "string" + ] + }, + "custom_fields": { + "type": [ + "null", + "string" + ] + }, + "tiers":{ + "type":[ + "null", + "array" + ], + "items":{ + "type":[ + "null", + "object" + ], + "properties":{ + "starting_unit":{ + "type":[ + "null", + "integer" + ] + }, + "ending_unit":{ + "type":[ + "null", + "integer" + ] + }, + "price":{ + "type":[ + "null", + "integer" + ] + } + } + } + }, + "tax_detail":{ + "type":[ + "null", + "object" + ], + "properties":{ + "tax_profile_id":{ + "type":[ + "null", + "string" + ] + }, + "avalara_sale_type":{ + "type":[ + "null", + "string" + ] + }, + "avalara_transaction_type":{ + "type":[ + "null", + "integer" + ] + }, + "avalara_service_type":{ + "type":[ + "null", + "integer" + ] + }, + "avalara_tax_code":{ + "type":[ + "null", + "string" + ] + }, + "taxjar_product_code":{ + "type":[ + "null", + "string" + ] + } + } + }, + "accounting_detail":{ + "type":[ + "null", + "object" + ], + "properties":{ + "sku":{ + "type":[ + "null", + "string" + ] + }, + "accounting_code":{ + "type":[ + "null", + "string" + ] + }, + "accounting_category1":{ + "type":[ + "null", + "string" + ] + }, + "accounting_category2":{ + "type":[ + "null", + "string" + ] + }, + "accounting_category3":{ + "type":[ + "null", + "string" + ] + }, + "accounting_category4":{ + "type":[ + "null", + "string" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tap_chargebee/schemas/item_model/items.json b/tap_chargebee/schemas/item_model/items.json new file mode 100644 index 0000000..5e8200c --- /dev/null +++ b/tap_chargebee/schemas/item_model/items.json @@ -0,0 +1,187 @@ +{ + "type":[ + "null", + "object" + ], + "properties":{ + "id":{ + "type":[ + "null", + "string" + ] + }, + "name":{ + "type":[ + "null", + "string" + ] + }, + "channel":{ + "type":[ + "null", + "string" + ] + }, + "external_name":{ + "type":[ + "null", + "string" + ] + }, + "description":{ + "type":[ + "null", + "string" + ] + }, + "status":{ + "type":[ + "null", + "string" + ] + }, + "resource_version":{ + "type":[ + "null", + "integer" + ] + }, + "updated_at":{ + "type":[ + "null", + "string" + ], + "format":"date-time" + }, + "item_family_id":{ + "type":[ + "null", + "string" + ] + }, + "type":{ + "type":[ + "null", + "string" + ] + }, + "is_shippable":{ + "type":[ + "null", + "boolean" + ] + }, + "is_giftable":{ + "type":[ + "null", + "boolean" + ] + }, + "redirect_url":{ + "type":[ + "null", + "string" + ] + }, + "enabled_for_checkout":{ + "type":[ + "null", + "boolean" + ] + }, + "enabled_in_portal":{ + "type":[ + "null", + "boolean" + ] + }, + "included_in_mrr":{ + "type":[ + "null", + "boolean" + ] + }, + "item_applicability":{ + "type":[ + "null", + "string" + ] + }, + "gift_claim_redirect_url":{ + "type":[ + "null", + "string" + ] + }, + "unit":{ + "type":[ + "null", + "string" + ] + }, + "metered":{ + "type":[ + "null", + "boolean" + ] + }, + "usage_calculation":{ + "type":[ + "null", + "string" + ] + }, + "archived_at":{ + "type":[ + "null", + "string" + ], + "format":"date-time" + }, + "metadata":{ + "type":[ + "null", + "string" + ] + }, + "archivable":{ + "type":[ + "null", + "boolean", + "string" + ] + }, + "object":{ + "type":[ + "null", + "string" + ] + }, + "custom_fields": { + "type": [ + "null", + "string" + ] + }, + "applicable_items":{ + "type":[ + "null", + "array" + ], + "items":{ + "type":[ + "null", + "object" + ], + "properties":{ + "id":{ + "type":[ + "null", + "string" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/tap_chargebee/schemas/item_model/subscriptions.json b/tap_chargebee/schemas/item_model/subscriptions.json new file mode 100644 index 0000000..0fc1d5a --- /dev/null +++ b/tap_chargebee/schemas/item_model/subscriptions.json @@ -0,0 +1,1025 @@ +{ + "type": [ + "null", + "object" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "currency_code": { + "type": [ + "null", + "string" + ] + }, + "start_date": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "changes_scheduled_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "trial_end": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "remaining_billing_cycles": { + "type": [ + "null", + "integer" + ] + }, + "po_number": { + "type": [ + "null", + "string" + ] + }, + "plan_quantity_in_decimal": { + "type": [ + "null", + "string" + ] + }, + "plan_unit_price_in_decimal": { + "type": [ + "null", + "string" + ] + }, + "customer_id": { + "type": [ + "null", + "string" + ] + }, + "status": { + "type": [ + "null", + "string" + ] + }, + "coupon": { + "type": [ + "null", + "string" + ] + }, + "trial_start": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "trial_end_action": { + "type": [ + "null", + "string" + ] + }, + "current_term_start": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "current_term_end": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "next_billing_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "started_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "activated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "contract_term_billing_cycle_on_renewal": { + "type": [ + "null", + "integer" + ] + }, + "override_relationship": { + "type": [ + "null", + "boolean" + ] + }, + "pause_date": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "resume_date": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "cancelled_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "cancel_reason": { + "type": [ + "null", + "string" + ] + }, + "created_from_ip": { + "type": [ + "null", + "string" + ] + }, + "resource_version": { + "type": [ + "null", + "integer" + ] + }, + "object": { + "type": [ + "null", + "string" + ] + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "has_scheduled_advance_invoices": { + "type": [ + "null", + "boolean" + ] + }, + "has_scheduled_changes": { + "type": [ + "null", + "boolean" + ] + }, + "payment_source_id": { + "type": [ + "null", + "string" + ] + }, + "plan_free_quantity_in_decimal": { + "type": [ + "null", + "string" + ] + }, + "plan_amount_in_decimal": { + "type": [ + "null", + "string" + ] + }, + "auto_collection": { + "type": [ + "null", + "string" + ] + }, + "billing_period": { + "type": [ + "null", + "integer" + ] + }, + "billing_period_unit": { + "type": [ + "null", + "string" + ] + }, + "due_invoices_count": { + "type": [ + "null", + "integer" + ] + }, + "due_since": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "total_dues": { + "type": [ + "null", + "integer" + ] + }, + "mrr": { + "type": [ + "null", + "integer" + ] + }, + "exchange_rate": { + "type": [ + "null", + "number" + ] + }, + "base_currency_code": { + "type": [ + "null", + "string" + ] + }, + "invoice_notes": { + "type": [ + "null", + "string" + ] + }, + "meta_data": { + "type": [ + "null", + "string" + ] + }, + "deleted": { + "type": [ + "null", + "boolean" + ] + }, + "cancel_schedule_created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "cancel_reason_code": { + "type": [ + "null", + "string" + ] + }, + "free_period": { + "type": [ + "null", + "integer" + ] + }, + "free_period_unit": { + "type": [ + "null", + "string" + ] + }, + "business_entity_id": { + "type": [ + "null", + "string" + ] + }, + "create_pending_invoices": { + "type": [ + "null", + "boolean" + ] + }, + "auto_close_invoices": { + "type": [ + "null", + "boolean" + ] + }, + "channel": { + "type": [ + "null", + "string" + ] + }, + "active_id": { + "type": [ + "null", + "string" + ] + }, + "custom_fields": { + "type": [ + "null", + "string" + ] + }, + "discounts": { + "type": [ + "null", + "array" + ], + "items": { + "type": [ + "null", + "object" + ], + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "invoice_name": { + "type": [ + "null", + "string" + ] + }, + "type": { + "type": [ + "null", + "string" + ] + }, + "percentage": { + "type": [ + "null", + "number" + ] + }, + "amount": { + "type": [ + "null", + "number" + ] + }, + "currency_code": { + "type": [ + "null", + "string" + ] + }, + "duration_type": { + "type": [ + "null", + "string" + ] + }, + "period": { + "type": [ + "null", + "integer" + ] + }, + "period_unit": { + "type": [ + "null", + "string" + ] + }, + "included_in_mrr": { + "type": [ + "null", + "boolean" + ] + }, + "apply_on": { + "type": [ + "null", + "string" + ] + }, + "item_price_id": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "integer" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "updated_at": { + "type": [ + "null", + "integer" + ] + }, + "resource_version": { + "type": [ + "null", + "integer" + ] + }, + "applied_count": { + "type": [ + "null", + "integer" + ] + }, + "coupon_id": { + "type": [ + "null", + "string" + ] + }, + "index": { + "type": [ + "null", + "integer" + ] + }, + "apply_till": { + "type": [ + "null", + "integer" + ] + }, + "object": { + "type": [ + "null", + "string" + ] + } + } + } + }, + "subscription_items": { + "type": [ + "null", + "array" + ], + "items": { + "type": [ + "null", + "object" + ], + "properties": { + "item_price_id": { + "type": [ + "null", + "string" + ] + }, + "item_type": { + "type": [ + "null", + "string" + ] + }, + "quantity": { + "type": [ + "null", + "integer" + ] + }, + "quantity_in_decimal": { + "type": [ + "null", + "string" + ] + }, + "unit_price": { + "type": [ + "null", + "integer" + ] + }, + "unit_price_in_decimal": { + "type": [ + "null", + "string" + ] + }, + "amount": { + "type": [ + "null", + "integer" + ] + }, + "amount_in_decimal": { + "type": [ + "null", + "string" + ] + }, + "free_quantity": { + "type": [ + "null", + "integer" + ] + }, + "free_quantity_in_decimal": { + "type": [ + "null", + "string" + ] + }, + "trial_end": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "billing_cycles": { + "type": [ + "null", + "integer" + ] + }, + "service_period_days": { + "type": [ + "null", + "integer" + ] + }, + "charge_on_event": { + "type": [ + "null", + "string" + ] + }, + "charge_once": { + "type": [ + "null", + "boolean" + ] + }, + "charge_on_option": { + "type": [ + "null", + "string" + ] + }, + "object": { + "type": [ + "null", + "string" + ] + } + } + } + }, + "item_tiers": { + "type": [ + "null", + "array" + ], + "items": { + "type": [ + "null", + "object" + ], + "properties": { + "item_price_id": { + "type": [ + "null", + "string" + ] + }, + "starting_unit": { + "type": [ + "null", + "integer" + ] + }, + "ending_unit": { + "type": [ + "null", + "integer" + ] + }, + "price": { + "type": [ + "null", + "integer" + ] + }, + "starting_unit_in_decimal": { + "type": [ + "null", + "string" + ] + }, + "ending_unit_in_decimal": { + "type": [ + "null", + "string" + ] + }, + "price_in_decimal": { + "type": [ + "null", + "string" + ] + } + } + } + }, + "charged_items": { + "type": [ + "null", + "array" + ], + "items": { + "type": [ + "null", + "object" + ], + "properties": { + "item_price_id": { + "type": [ + "null", + "string" + ] + }, + "last_charged_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "object": { + "type": [ + "null", + "string" + ] + } + } + } + }, + "coupons": { + "type": [ + "null", + "array" + ], + "items": { + "type": [ + "null", + "object" + ], + "properties": { + "coupon_id": { + "type": [ + "null", + "string" + ] + }, + "apply_till": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "applied_count": { + "type": [ + "null", + "integer" + ] + }, + "coupon_code": { + "type": [ + "null", + "string" + ] + }, + "object": { + "type": [ + "null", + "string" + ] + } + } + } + }, + "shipping_address": { + "type": [ + "null", + "object" + ], + "properties": { + "first_name": { + "type": [ + "null", + "string" + ] + }, + "last_name": { + "type": [ + "null", + "string" + ] + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "company": { + "type": [ + "null", + "string" + ] + }, + "phone": { + "type": [ + "null", + "string" + ] + }, + "line1": { + "type": [ + "null", + "string" + ] + }, + "line2": { + "type": [ + "null", + "string" + ] + }, + "line3": { + "type": [ + "null", + "string" + ] + }, + "city": { + "type": [ + "null", + "string" + ] + }, + "state_code": { + "type": [ + "null", + "string" + ] + }, + "state": { + "type": [ + "null", + "string" + ] + }, + "country": { + "type": [ + "null", + "string" + ] + }, + "zip": { + "type": [ + "null", + "string" + ] + }, + "validation_status": { + "type": [ + "null", + "string" + ] + }, + "object": { + "type": [ + "null", + "string" + ] + } + } + }, + "referral_info": { + "type": [ + "null", + "object" + ], + "properties": { + "referral_code": { + "type": [ + "null", + "string" + ] + }, + "coupon_code": { + "type": [ + "null", + "string" + ] + }, + "referrer_id": { + "type": [ + "null", + "string" + ] + }, + "external_reference_id": { + "type": [ + "null", + "string" + ] + }, + "reward_status": { + "type": [ + "null", + "string" + ] + }, + "referral_system": { + "type": [ + "null", + "string" + ] + }, + "account_id": { + "type": [ + "null", + "string" + ] + }, + "campaign_id": { + "type": [ + "null", + "string" + ] + }, + "external_campaign_id": { + "type": [ + "null", + "string" + ] + }, + "friend_offer_type": { + "type": [ + "null", + "string" + ] + }, + "referrer_reward_type": { + "type": [ + "null", + "string" + ] + }, + "notify_referral_system": { + "type": [ + "null", + "string" + ] + }, + "destination_url": { + "type": [ + "null", + "string" + ] + }, + "post_purchase_widget_enabled": { + "type": [ + "null", + "boolean" + ] + } + } + }, + "contract_term": { + "type": [ + "null", + "object" + ], + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "status": { + "type": [ + "null", + "string" + ] + }, + "contract_start": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "contract_end": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "billing_cycle": { + "type": [ + "null", + "integer" + ] + }, + "action_at_term_end": { + "type": [ + "null", + "string" + ] + }, + "total_contract_value": { + "type": [ + "null", + "integer" + ] + }, + "cancellation_cutoff_period": { + "type": [ + "null", + "integer" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "subscription_id": { + "type": [ + "null", + "string" + ] + }, + "remaining_billing_cycles": { + "type": [ + "null", + "integer" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tap_chargebee/schemas/payment_sources.json b/tap_chargebee/schemas/payment_sources.json deleted file mode 100644 index a307f36..0000000 --- a/tap_chargebee/schemas/payment_sources.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"] - }, - "resource_version": { - "type": ["null", "integer"] - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "customer_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "type": { - "type": ["null", "string"] - }, - "reference_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "status": { - "type": ["null", "string"] - }, - "gateway": { - "type": ["null", "string"] - }, - "gateway_account_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "ip_address": { - "type": ["null", "string"], - "maxLength": 50 - }, - "issuing_country": { - "type": ["null", "string"], - "maxLength": 50 - }, - "deleted": { - "type": ["null", "boolean"] - }, - "object": { - "type": ["null", "string"] - }, - "card": { - "$ref": "cards.json" - }, - "bank_account": { - "type": ["null", "object"], - "properties": { - "last4": { - "type": ["null", "string"], - "minLength": 4, - "maxLength": 4 - }, - "name_on_account": { - "type": ["null", "string"], - "maxLength": 300 - }, - "bank_name": { - "type": ["null", "string"], - "maxLength": 100 - }, - "mandate_id": { - "type": ["null", "string"], - "minLength": 5, - "maxLength": 17 - }, - "account_type": { - "type": ["null", "string"] - }, - "echeck_type": { - "type": ["null", "string"] - }, - "account_holder_type": { - "type": ["null", "string"] - } - } - }, - "amazon_payment": { - "type": ["null", "object"], - "properties": { - "email": { - "type": ["null", "string"], - "maxLength": 70 - }, - "agreement_id": { - "type": ["null", "string"], - "maxLength": 50 - } - } - }, - "paypal": { - "type": ["null", "object"], - "properties": { - "email": { - "type": ["null", "string"], - "maxLength": 70 - }, - "agremeent_id": { - "type": ["null", "string"], - "maxLength": 50 - } - } - } - } -} diff --git a/tap_chargebee/schemas/plan_model/addons.json b/tap_chargebee/schemas/plan_model/addons.json new file mode 100644 index 0000000..2345932 --- /dev/null +++ b/tap_chargebee/schemas/plan_model/addons.json @@ -0,0 +1,169 @@ +{ + "type": ["null", "object"], + "additionalProperties": false, + "properties": { + "id": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "channel":{ + "type": ["null", "string"] + }, + "proration_type":{ + "type": ["null", "string"] + }, + "invoice_name": { + "type": ["null", "string"] + }, + "description": { + "type": ["null", "string"] + }, + "pricing_model": { + "type": ["null", "string"] + }, + "charge_type": { + "type": ["null", "string"] + }, + "price": { + "type": ["null", "integer"] + }, + "currency_code": { + "type": ["null", "string"] + }, + "period": { + "type": ["null", "integer"] + }, + "period_unit": { + "type": ["null", "string"] + }, + "unit": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "archived_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "enabled_in_portal": { + "type": ["null", "boolean"] + }, + "tax_code": { + "type": ["null", "string"] + }, + "taxjar_product_code": { + "type": ["null", "string"] + }, + "hsn_code": { + "type": ["null", "string"] + }, + "avalara_sale_type": { + "type": ["null", "string"] + }, + "avalara_transaction_type": { + "type": ["null", "integer"] + }, + "avalara_service_type": { + "type": ["null", "integer"] + }, + "sku": { + "type": ["null", "string"] + }, + "accounting_code": { + "type": ["null", "string"] + }, + "accounting_category1": { + "type": ["null", "string"] + }, + "accounting_category2": { + "type": ["null", "string"] + }, + "accounting_category3": { + "type": ["null", "string"] + }, + "accounting_category4": { + "type": ["null", "string"] + }, + "is_shippable": { + "type": ["null", "boolean"] + }, + "shipping_frequency_period": { + "type": ["null", "integer"] + }, + "shipping_frequency_period_unit": { + "type": ["null", "string"] + }, + "resource_version": { + "type": ["null", "integer"] + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "included_in_mrr": { + "type": ["null", "boolean"] + }, + "invoice_notes": { + "type": ["null", "string"] + }, + "taxable": { + "type": ["null", "boolean"] + }, + "tax_profile_id": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + }, + "type": { + "type": ["null", "string"] + }, + "meta_data": { + "type": ["null", "string"] + }, + "custom_fields": { + "type": ["null", "string"] + }, + "show_description_in_invoices": { + "type": ["null", "boolean"] + }, + "show_description_in_quotes": { + "type": ["null", "boolean"] + }, + "price_in_decimal": { + "type": ["null", "string"] + }, + "tiers": { + "type": ["null", "array"], + "items": { + "type": ["null","object"], + "properties": { + "starting_unit": { + "type": ["null", "integer"] + }, + "ending_unit": { + "type": ["null", "integer"] + }, + "price": { + "type": ["null", "integer"] + }, + "object": { + "type": ["null", "string"] + }, + "starting_unit_in_decimal": { + "type": ["null", "string"] + }, + "ending_unit_in_decimal": { + "type": ["null", "string"] + }, + "price_in_decimal": { + "type": ["null", "string"] + } + } + } + } + } +} diff --git a/tap_chargebee/schemas/coupons.json b/tap_chargebee/schemas/plan_model/coupons.json similarity index 64% rename from tap_chargebee/schemas/coupons.json rename to tap_chargebee/schemas/plan_model/coupons.json index fb00e4f..b7854d1 100644 --- a/tap_chargebee/schemas/coupons.json +++ b/tap_chargebee/schemas/plan_model/coupons.json @@ -8,6 +8,9 @@ "name": { "type": ["null", "string"] }, + "invoice_name": { + "type": ["null", "string"] + }, "discount_type": { "type": ["null", "string"] }, @@ -17,9 +20,16 @@ "discount_amount": { "type": ["null", "number"] }, + "currency_code": { + "type": ["null", "string"] + }, "duration_type": { "type": ["null", "string"] }, + "valid_till": { + "type": ["null", "string"], + "format": "date-time" + }, "duration_month": { "type": ["null", "integer"] }, @@ -56,11 +66,38 @@ "type": ["null", "string"], "format": "date-time" }, + "included_in_mrr": { + "type": ["null", "boolean"] + }, + "period": { + "type": ["null", "integer"] + }, + "period_unit": { + "type": ["null", "string"] + }, "object": { "type": ["null", "string"] }, "redemptions": { "type": ["null", "integer"] + }, + "plan_ids": { + "type": ["null", "array"], + "items": { + "type": ["null", "string"] + } + }, + "addon_ids": { + "type": ["null", "array"], + "items": { + "type": ["null", "string"] + } + }, + "invoice_notes": { + "type": ["null", "string"] + }, + "meta_data": { + "type": ["null", "string"] } } } diff --git a/tap_chargebee/schemas/plan_model/events.json b/tap_chargebee/schemas/plan_model/events.json new file mode 100644 index 0000000..1e95e3c --- /dev/null +++ b/tap_chargebee/schemas/plan_model/events.json @@ -0,0 +1,647 @@ +{ + "type": ["null", "object"], + "additionalProperties": false, + "properties": { + "id": { + "type": ["null", "string"] + }, + "occurred_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "source": { + "type": ["null", "string"] + }, + "user": { + "type": ["null", "string"] + }, + "event_type": { + "type": ["null", "string"] + }, + "api_version": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + }, + "webhook_status": { + "type": ["null", "string"] + }, + "content": { + "type": ["null", "object"], + "properties" : { + "addon": { + "$ref": "addons.json" + }, + "coupon": { + "$ref": "coupons.json" + }, + "credit_note": { + "$ref": "credit_notes.json" + }, + "customer": { + "$ref": "customers.json" + }, + "gift": { + "$ref": "gifts.json" + }, + "invoice": { + "$ref": "invoices.json" + }, + "order": { + "$ref": "orders.json" + }, + "payment_source": { + "$ref": "payment_sources.json" + }, + "plan": { + "$ref": "plans.json" + }, + "promotional_credit": { + "$ref": "promotional_credits.json" + }, + "subscription":{ + "$ref": "subscriptions.json" + }, + "transaction": { + "$ref": "transactions.json" + }, + "virtual_bank_account":{ + "$ref": "virtual_bank_accounts.json" + }, + "unbilled_charges": { + "type": ["null", "array"], + "items": { + "type": ["null","object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "date_from": { + "type": ["null", "string"], + "format": "date-time" + }, + "date_to": { + "type": ["null", "string"], + "format": "date-time" + }, + "unit_amount": { + "type": ["null", "integer"] + }, + "pricing_model": { + "type": ["null", "string"] + }, + "quantity": { + "type": ["null", "integer"] + }, + "amount": { + "type": ["null", "integer"] + }, + "currency_code": { + "type": ["null", "string"] + }, + "discount_amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "is_voided": { + "type": ["null", "boolean"] + }, + "voided_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + }, + "amount_in_decimal": { + "type": ["null", "string"] + }, + "deleted": { + "type": ["null", "boolean"] + }, + "tiers": { + "type": ["null", "array"], + "items": { + "type": ["null","object"], + "properties": { + "starting_unit": { + "type": ["null", "integer"] + }, + "ending_unit": { + "type": ["null", "integer"] + }, + "quantity_used": { + "type": ["null", "integer"] + }, + "unit_amount ": { + "type": ["null", "integer"] + }, + "starting_unit_in_decimal": { + "type": ["null", "string"] + }, + "ending_unit_in_decimal": { + "type": ["null", "string"] + }, + "quantity_used_in_decimal": { + "type": ["null", "string"] + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] + } + } + } + } + } + + } + }, + "coupon_code":{ + "type":["null", "object"], + "additionalProperties": false, + "properties":{ + "code": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "coupon_id": { + "type": ["null", "string"] + }, + "coupon_site_id": { + "type": ["null", "string"] + }, + "coupon_set_id": { + "type": ["null", "string"] + }, + "coupon_set_name": { + "type": ["null", "string"] + } + } + }, + "coupon_set":{ + "type":["null", "object"], + "additionalProperties": false, + "properties":{ + "id": { + "type": ["null", "string"] + }, + "coupon_id": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "total_count": { + "type": ["null", "integer"] + }, + "redeemed_count": { + "type": ["null", "integer"] + }, + "archived_count": { + "type": ["null", "integer"] + }, + "meta_data": { + "type": ["null", "string"] + } + } + }, + "quote": { + "type":["null", "object"], + "additionalProperties": false, + "properties":{ + "id": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "po_number": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "invoice_id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "operation_type": { + "type": ["null", "string"] + }, + "vat_number": { + "type": ["null", "string"] + }, + "price_type": { + "type": ["null", "string"] + }, + "valid_till": { + "type": ["null", "string"], + "format": "date-time" + }, + "date": { + "type": ["null", "string"], + "format": "date-time" + }, + "total_payable": { + "type": ["null", "integer"] + }, + "charge_on_acceptance": { + "type": ["null", "integer"] + }, + "sub_total": { + "type": ["null", "integer"] + }, + "total": { + "type": ["null", "integer"] + }, + "credits_applied": { + "type": ["null", "integer"] + }, + "amount_paid": { + "type": ["null", "integer"] + }, + "amount_due": { + "type": ["null", "integer"] + }, + "version": { + "type": ["null", "integer"] + }, + "resource_version": { + "type": ["null", "integer"] + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "vat_number_prefix": { + "type": ["null", "string"] + }, + "currency_code": { + "type": ["null", "string"] + }, + "notes": { + "type":["null", "array"], + "items":{ + "type":["null","string"] + } + }, + "contract_term_start": { + "type": ["null", "string"], + "format": "date-time" + }, + "contract_term_end": { + "type": ["null", "string"], + "format": "date-time" + }, + "contract_term_termination_fee": { + "type": ["null", "integer"] + }, + "object": { + "type": ["null", "string"] + }, + "line_items": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "subscription_id": { + "type": ["null", "string"] + }, + "date_from": { + "type": ["null", "string"], + "format": "date-time" + }, + "date_to": { + "type": ["null", "string"], + "format": "date-time" + }, + "unit_amount": { + "type": ["null", "integer"] + }, + "quantity": { + "type": ["null", "integer"] + }, + "amount": { + "type": ["null", "integer"] + }, + "pricing_model": { + "type": ["null", "string"] + }, + "is_taxed": { + "type": ["null", "boolean"] + }, + "tax_amount": { + "type": ["null", "integer"] + }, + "tax_rate": { + "type": ["null", "number"] + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + }, + "amount_in_decimal": { + "type": ["null", "string"] + }, + "discount_amount": { + "type": ["null", "integer"] + }, + "item_level_discount_amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "entity_description": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "tax_exempt_reason": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + }, + "customer_id": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + }, + "discounts": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + }, + "entity_type": { + "type": ["null", "string"] + }, + "entity_id": { + "type": ["null", "string"] + } + } + } + }, + "line_item_discounts": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "discount_type": { + "type": ["null", "string"] + }, + "coupon_id": { + "type": ["null", "string"] + }, + "discount_amount": { + "type": ["null", "integer"] + } + } + } + }, + "taxes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "name": { + "type": ["null", "string"] + }, + "amount": { + "type": ["null", "integer"] + }, + "description": { + "type": ["null", "string"] + } + } + } + }, + "line_item_taxes": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "tax_name": { + "type": ["null", "string"] + }, + "tax_rate": { + "type": ["null", "number"] + }, + "is_partial_tax_applied": { + "type": ["null", "boolean"] + }, + "is_non_compliance_tax": { + "type": ["null", "boolean"] + }, + "taxable_amount": { + "type": ["null", "integer"] + }, + "tax_amount": { + "type": ["null", "integer"] + }, + "tax_juris_type": { + "type": ["null", "string"] + }, + "tax_juris_name": { + "type": ["null", "string"] + }, + "tax_juris_code": { + "type": ["null", "string"] + }, + "tax_amount_in_local_currency": { + "type": ["null", "integer"] + }, + "local_currency_code": { + "type": ["null", "string"] + } + } + } + }, + "line_item_tiers": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "line_item_id": { + "type": ["null", "string"] + }, + "starting_unit": { + "type": ["null", "integer"] + }, + "ending_unit": { + "type": ["null", "integer"] + }, + "quantity_used": { + "type": ["null", "integer"] + }, + "unit_amount": { + "type": ["null", "integer"] + }, + "starting_unit_in_decimal": { + "type": ["null", "string"] + }, + "ending_unit_in_decimal": { + "type": ["null", "string"] + }, + "quantity_used_in_decimal": { + "type": ["null", "string"] + }, + "unit_amount_in_decimal": { + "type": ["null", "string"] + } + } + } + }, + "shipping_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status,": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + }, + "billing_address": { + "type": ["null","object"], + "properties": { + "first_name": { + "type": ["null", "string"] + }, + "last_name": { + "type": ["null", "string"] + }, + "email": { + "type": ["null", "string"] + }, + "company": { + "type": ["null", "string"] + }, + "phone": { + "type": ["null", "string"] + }, + "line1": { + "type": ["null", "string"] + }, + "line2": { + "type": ["null", "string"] + }, + "line3": { + "type": ["null", "string"] + }, + "city": { + "type": ["null", "string"] + }, + "state_code": { + "type": ["null", "string"] + }, + "state": { + "type": ["null", "string"] + }, + "country": { + "type": ["null", "string"] + }, + "zip": { + "type": ["null", "string"] + }, + "validation_status": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + } + } + } + } + } + + } + } + } +} diff --git a/tap_chargebee/schemas/plan_model/plans.json b/tap_chargebee/schemas/plan_model/plans.json new file mode 100644 index 0000000..c3f95c4 --- /dev/null +++ b/tap_chargebee/schemas/plan_model/plans.json @@ -0,0 +1,270 @@ +{ + "type": ["null", "object"], + "additionalProperties": false, + "properties": { + "id": { + "type": ["null", "string"] + }, + "name": { + "type": ["null", "string"] + }, + "invoice_name": { + "type": ["null", "string"] + }, + "channel":{ + "type": ["null", "string"] + }, + "description": { + "type": ["null", "string"] + }, + "price": { + "type": ["null", "integer"] + }, + "currency_code": { + "type": ["null", "string"] + }, + "period": { + "type": ["null", "integer"] + }, + "period_unit": { + "type": ["null", "string"] + }, + "trial_period": { + "type": ["null", "integer"] + }, + "trial_period_unit": { + "type": ["null", "string"] + }, + "trial_end_action": { + "type": ["null", "string"] + }, + "charge_model": { + "type": ["null", "string"] + }, + "pricing_model": { + "type": ["null", "string"] + }, + "free_quantity": { + "type": ["null", "integer"] + }, + "setup_cost": { + "type": ["null", "integer"] + }, + "status": { + "type": ["null", "string"] + }, + "archived_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "billing_cycles": { + "type": ["null", "integer"] + }, + "redirect_url": { + "type": ["null", "string"] + }, + "sku": { + "type": ["null", "string"] + }, + "updated_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "invoice_notes": { + "type": ["null", "string"] + }, + "taxable": { + "type": ["null", "boolean"] + }, + "tax_profile_id": { + "type": ["null", "string"] + }, + "enabled_in_hosted_pages": { + "type": ["null", "boolean"] + }, + "enabled_in_portal": { + "type": ["null", "boolean"] + }, + "addon_applicability": { + "type": ["null", "string"] + }, + "hsn_code": { + "type": ["null", "string"] + }, + "tax_code": { + "type": ["null", "string"] + }, + "taxjar_product_code": { + "type": ["null", "string"] + }, + "avalara_sale_type": { + "type": ["null", "string"] + }, + "avalara_transaction_type": { + "type": ["null", "integer"] + }, + "avalara_service_type": { + "type": ["null", "integer"] + }, + "account_code": { + "type": ["null", "string"] + }, + "accounting_code": { + "type": ["null", "string"] + }, + "accounting_category1": { + "type": ["null", "string"] + }, + "accounting_category2": { + "type": ["null", "string"] + }, + "accounting_category3": { + "type": ["null", "string"] + }, + "accounting_category4": { + "type": ["null", "string"] + }, + "is_shippable": { + "type": ["null", "boolean"] + }, + "shipping_frequency_period": { + "type": ["null", "integer"] + }, + "shipping_frequency_period_unit": { + "type": ["null", "string"] + }, + "resource_version": { + "type": ["null", "integer"] + }, + "giftable": { + "type": ["null", "boolean"] + }, + "claim_url": { + "type": ["null", "string"] + }, + "meta_data": { + "type": ["null", "string"] + }, + "show_description_in_invoices": { + "type": ["null", "boolean"] + }, + "show_description_in_quotes": { + "type": ["null", "boolean"] + }, + "custom_fields": { + "type": ["null", "string"] + }, + "free_quantity_in_decimal": { + "type": ["null", "string"] + }, + "price_in_decimal": { + "type": ["null", "string"] + }, + "object": { + "type": ["null", "string"] + }, + "tiers": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "starting_unit": { + "type": ["null", "integer"] + }, + "ending_unit": { + "type": ["null", "integer"] + }, + "price": { + "type": ["null", "integer"] + }, + "starting_unit_in_decimal": { + "type": ["null", "string"] + }, + "ending_unit_in_decimal": { + "type": ["null", "string"] + }, + "price_in_decimal": { + "type": ["null", "string"] + } + } + } + }, + "tax_providers_fields": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "provider_name": { + "type": ["null", "string"] + }, + "field_id": { + "type": ["null", "string"] + }, + "field_value": { + "type": ["null", "string"] + } + } + } + }, + "applicable_addons": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + } + } + } + }, + "attached_addons": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "quantity": { + "type": ["null", "integer"] + }, + "billing_cycles": { + "type": ["null", "integer"] + }, + "type": { + "type": ["null", "string"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + } + } + } + }, + "event_based_addons": { + "type": ["null", "array"], + "items": { + "type": ["null", "object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "quantity": { + "type": ["null", "integer"] + }, + "on_event": { + "type": ["null", "string"] + }, + "charge_once": { + "type": ["null", "boolean"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + } + } + } + } + } +} diff --git a/tap_chargebee/schemas/subscriptions.json b/tap_chargebee/schemas/plan_model/subscriptions.json similarity index 66% rename from tap_chargebee/schemas/subscriptions.json rename to tap_chargebee/schemas/plan_model/subscriptions.json index 1e08db5..070bb5a 100644 --- a/tap_chargebee/schemas/subscriptions.json +++ b/tap_chargebee/schemas/plan_model/subscriptions.json @@ -43,6 +43,9 @@ "type": ["null", "string"], "format": "date-time" }, + "trial_end_action": { + "type": ["null", "string"] + }, "current_term_start": { "type": ["null", "string"], "format": "date-time" @@ -97,6 +100,10 @@ "type": ["null", "string"], "format": "date-time" }, + "changes_scheduled_at": { + "type": ["null", "string"], + "format": "date-time" + }, "invoice_notes": { "type": ["null", "string"] }, @@ -112,6 +119,9 @@ "exchange_rate": { "type": ["null", "number"] }, + "has_scheduled_advance_invoices": { + "type": ["null", "boolean"] + }, "has_scheduled_changes": { "type": ["null", "boolean"] }, @@ -127,6 +137,85 @@ "object": { "type": ["null", "string"] }, + "setup_fee": { + "type": ["null", "integer"] + }, + "gift_id": { + "type": ["null", "string"] + }, + "contract_term_billing_cycle_on_renewal": { + "type": ["null", "integer"] + }, + "override_relationship": { + "type": ["null", "boolean"] + }, + "pause_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "resume_date": { + "type": ["null", "string"], + "format": "date-time" + }, + "created_from_ip": { + "type": ["null", "string"] + }, + "due_since": { + "type": ["null", "string"], + "format": "date-time" + }, + "total_dues": { + "type": ["null", "integer"] + }, + "meta_data": { + "type": ["null", "string"] + }, + "custom_fields": { + "type": ["null", "string"] + }, + "plan_amount_in_decimal" : { + "type": ["null", "string"] + }, + "plan_free_quantity_in_decimal" : { + "type": ["null", "string"] + }, + "plan_quantity_in_decimal" : { + "type": ["null", "string"] + }, + "plan_unit_price_in_decimal" : { + "type": ["null", "string"] + }, + "cancel_schedule_created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "offline_payment_method" : { + "type": ["null", "string"] + }, + "business_entity_id" : { + "type": ["null", "string"] + }, + "active_id" : { + "type": ["null", "string"] + }, + "cancel_reason_code" : { + "type": ["null", "string"] + }, + "free_period": { + "type": ["null", "integer"] + }, + "free_period_unit" : { + "type": ["null", "string"] + }, + "create_pending_invoices": { + "type": ["null", "boolean"] + }, + "auto_close_invoices": { + "type": ["null", "boolean"] + }, + "channel" : { + "type": ["null", "string"] + }, "addons": { "type": ["null", "array"], "items": { @@ -141,6 +230,9 @@ "unit_price": { "type": ["null", "integer"] }, + "amount": { + "type": ["null", "integer"] + }, "trial_end": { "type": ["null", "string"], "format": "date-time" @@ -150,6 +242,15 @@ }, "object": { "type": ["null", "string"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + }, + "unit_price_in_decimal": { + "type": ["null", "string"] + }, + "amount_in_decimal": { + "type": ["null", "string"] } } } @@ -176,6 +277,12 @@ }, "object": { "type": ["null", "string"] + }, + "quantity_in_decimal": { + "type": ["null", "string"] + }, + "unit_price_in_decimal": { + "type": ["null", "string"] } } } @@ -264,7 +371,10 @@ "zip": { "type": ["null", "string"] }, - "validation_status,": { + "validation_status": { + "type": ["null", "string"] + }, + "object": { "type": ["null", "string"] } } @@ -287,7 +397,7 @@ "reward_status": { "type": ["null", "string"] }, - "referral_status": { + "referral_system": { "type": ["null", "string"] }, "account_id": { @@ -315,6 +425,47 @@ "type": ["null", "boolean"] } } + }, + "contract_term": { + "type": ["null","object"], + "properties": { + "id": { + "type": ["null", "string"] + }, + "status": { + "type": ["null", "string"] + }, + "contract_start": { + "type": ["null", "string"], + "format": "date-time" + }, + "contract_end": { + "type": ["null", "string"], + "format": "date-time" + }, + "billing_cycle": { + "type": ["null", "integer"] + }, + "action_at_term_end": { + "type": ["null", "string"] + }, + "total_contract_value": { + "type": ["null", "integer"] + }, + "cancellation_cutoff_period": { + "type": ["null", "integer"] + }, + "created_at": { + "type": ["null", "string"], + "format": "date-time" + }, + "subscription_id": { + "type": ["null", "string"] + }, + "remaining_billing_cycles": { + "type": ["null", "integer"] + } + } } } } diff --git a/tap_chargebee/schemas/virtual_bank_accounts.json b/tap_chargebee/schemas/virtual_bank_accounts.json deleted file mode 100644 index a87772b..0000000 --- a/tap_chargebee/schemas/virtual_bank_accounts.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "type": ["null", "object"], - "additionalProperties": false, - "properties": { - "id": { - "type": ["null", "string"], - "maxLength": 40 - }, - "customer_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "email": { - "type": ["null", "string"], - "maxLength": 70 - }, - "bank_name": { - "type": ["null", "string"], - "maxLength": 100 - }, - "account_number": { - "type": ["null", "string"], - "minLength": 5, - "maxLength": 50 - }, - "routing_number": { - "type": ["null", "string"], - "minLength": 9, - "maxLength": 50 - }, - "swift_code": { - "type": ["null", "string"], - "minLength": 8, - "maxLength": 11 - }, - "gateway": { - "type": ["null", "string"] - }, - "gateway_account_id": { - "type": ["null", "string"], - "maxLength": 50 - }, - "resource_version": { - "type": ["null", "integer"] - }, - "updated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "created_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "reference_id": { - "type": ["null", "string"], - "maxLength": 150 - }, - "deleted": { - "type": ["null", "boolean"] - }, - "object": { - "type": ["null", "string"] - } - } -} diff --git a/tap_chargebee/state.py b/tap_chargebee/state.py deleted file mode 100644 index 6c14cf0..0000000 --- a/tap_chargebee/state.py +++ /dev/null @@ -1,63 +0,0 @@ -import datetime -import json -import singer - -LOGGER = singer.get_logger() - - -def get_last_record_value_for_table(state, table, field): - last_value = state.get('bookmarks', {}) \ - .get(table, {}) \ - .get(field) - - if last_value is None: - return None - - return last_value - - -def incorporate(state, table, key, value, force=False): - if value is None: - return state - - if isinstance(value, datetime.datetime): - value = value.strftime('%Y-%m-%dT%H:%M:%SZ') - - if state is None: - new_state = {} - else: - new_state = state.copy() - - if 'bookmarks' not in new_state: - new_state['bookmarks'] = {} - - if table not in new_state['bookmarks']: - new_state['bookmarks'][table] = {} - - if(new_state['bookmarks'].get(table, {}).get(key) is None or - new_state['bookmarks'].get(table, {}).get(key) < value or - force): - new_state['bookmarks'][table][key] = value - - return new_state - - -def save_state(state): - if not state: - return - - LOGGER.info('Updating state.') - - singer.write_state(state) - - -def load_state(filename): - if filename is None: - return {} - - try: - with open(filename) as handle: - return json.load(handle) - except: - LOGGER.fatal("Failed to decode state file. Is it valid json?") - raise RuntimeError diff --git a/tap_chargebee/streams/__init__.py b/tap_chargebee/streams/__init__.py index 9467942..f94e278 100644 --- a/tap_chargebee/streams/__init__.py +++ b/tap_chargebee/streams/__init__.py @@ -1,25 +1,70 @@ from .addons import AddonsStream from .coupons import CouponsStream +from .comments import CommentsStream from .credit_notes import CreditNotesStream from .customers import CustomersStream from .events import EventsStream from .invoices import InvoicesStream +from .item_families import ItemFamiliesStream +from .item_prices import ItemPricesStream +from .items import ItemsStream from .payment_sources import PaymentSourcesStream from .plans import PlansStream from .subscriptions import SubscriptionsStream from .transactions import TransactionsStream from .virtual_bank_accounts import VirtualBankAccountsStream +from .credit_notes import CreditNotesStream +from .gifts import GiftsStream +from .orders import OrdersStream +from .quotes import QuotesStream +from .promotional_credits import PromotionalCreditsStream -AVAILABLE_STREAMS = [ - AddonsStream, +COMMON_AVAILABLE_STREAMS = [ + EventsStream, + CommentsStream, CouponsStream, CreditNotesStream, CustomersStream, + GiftsStream, InvoicesStream, + OrdersStream, PaymentSourcesStream, - PlansStream, + QuotesStream, + PromotionalCreditsStream, SubscriptionsStream, TransactionsStream, - EventsStream, VirtualBankAccountsStream ] + +PLAN_MODEL_AVAILABLE_STREAMS = COMMON_AVAILABLE_STREAMS + [ + AddonsStream, + PlansStream +] + +ITEM_MODEL_AVAILABLE_STREAMS = COMMON_AVAILABLE_STREAMS + [ + ItemsStream, + ItemPricesStream, + ItemFamiliesStream +] + +STREAMS = { + 'events' : EventsStream, + 'comments' : CommentsStream, + 'coupons' : CouponsStream, + 'credit_notes' : CreditNotesStream, + 'customers' : CustomersStream, + 'gifts' : GiftsStream, + 'invoices' : InvoicesStream, + 'orders' : OrdersStream, + 'payment_sources' : PaymentSourcesStream, + 'quotes' : QuotesStream, + 'promotional_credits' : PromotionalCreditsStream, + 'subscriptions' : SubscriptionsStream, + 'transactions' : TransactionsStream, + 'virtual_bank_accounts' : VirtualBankAccountsStream, + 'addons' : AddonsStream, + 'plans' : PlansStream, + 'items' : ItemsStream, + 'item_prices' : ItemPricesStream, + 'item_families': ItemFamiliesStream +} \ No newline at end of file diff --git a/tap_chargebee/streams/addons.py b/tap_chargebee/streams/addons.py index 8879200..5b36e38 100644 --- a/tap_chargebee/streams/addons.py +++ b/tap_chargebee/streams/addons.py @@ -1,16 +1,43 @@ +import json from tap_chargebee.streams.base import BaseChargebeeStream - +from .util import Util class AddonsStream(BaseChargebeeStream): - TABLE = 'addons' - ENTITY = 'addon' + STREAM = 'addons' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['updated_at'] + ENTITY = 'addon' SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['updated_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'plan_model/addons' + SORT_BY = None def get_url(self): return 'https://{}.chargebee.com/api/v2/addons'.format(self.config.get('site')) + + def handle_deleted_items(self, records: dict): + """ + Handle deleted records based on the include_deleted config. + """ + deleted_records = [] + if self.include_deleted: + for addon in Util.addons: + deleted_records.append(addon) + return deleted_records + + def add_custom_fields(self, record: dict): + """ + Adds custom fields to the record. + """ + custom_fields = {} + for key in record.keys(): + if "cf_" in key: + custom_fields[key] = record[key] + + if custom_fields: + record["custom_fields"] = json.dumps(custom_fields) + + return record diff --git a/tap_chargebee/streams/base.py b/tap_chargebee/streams/base.py index 8dbccb8..83f4fa8 100644 --- a/tap_chargebee/streams/base.py +++ b/tap_chargebee/streams/base.py @@ -1,141 +1,272 @@ import singer +import json +import os +import pytz +from singer import metadata, metrics +from singer import Transformer +from singer.catalog import Catalog +from tap_chargebee.client import ChargebeeClient +from datetime import datetime -from dateutil.parser import parse -from tap_framework.streams import BaseStream -from tap_framework.config import get_config_start_date -from tap_chargebee.state import get_last_record_value_for_table, incorporate, \ - save_state LOGGER = singer.get_logger() +UNIX_SECONDS_INTEGER_DATETIME_PARSING = "unix-seconds-integer-datetime-parsing" +DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" +# 2 minutes lookback window to avoid missing records +LOOKBACK_WINDOW = 2 -class BaseChargebeeStream(BaseStream): +class BaseChargebeeStream: - def write_schema(self): - singer.write_schema( - self.catalog.stream, - self.catalog.schema.to_dict(), - key_properties=self.catalog.key_properties, - bookmark_properties=self.BOOKMARK_PROPERTIES) + STREAM = None + KEY_PROPERTIES = [] + API_METHOD = "GET" + REQUIRES = [] + REPLICATION_METHOD = None + REPLICATION_KEY = None + ENTITY = None + SELECTED_BY_DEFAULT = True + VALID_REPLICATION_KEYS = [] + INCLUSION = None + SCHEMA = None + SORT_BY = None + + def __init__( + self, config: dict, state: dict, catalog: Catalog, client: ChargebeeClient + ): + self.config = config + self.state = state + self.catalog = catalog + self.client = client + self.include_deleted = self.get_include_deleted() + + def get_abs_path(self, path: str): + return os.path.join(os.path.dirname(os.path.realpath(__file__)), path) + + def handle_deleted_items(self, records: list): + return [] + + def get_include_deleted(self): + """ + Returns whether to include deleted records based on config. + """ + return self.config.get("include_deleted") not in ["false", "False", False] + + def add_custom_fields(self, record: dict): + """ + Placeholder for adding custom fields. Should be overridden by subclasses if needed. + """ + return record + + def load_shared_schema_refs(self): + """ + Loads shared schema references from common and version-specific folders. + Returns a dictionary of shared schemas. + """ + shared_schema_refs = {} + schema_folders = ["common"] + + if self.config["item_model"]: + # Chosen streams of product catalog v2 + schema_folders.append("item_model") + else: + # Chosen streams of product catalog v1 + schema_folders.append("plan_model") + + for schema_folder in schema_folders: + shared_schema_refs.update(self.load_shared_schema_ref(schema_folder)) + + return shared_schema_refs + + def load_shared_schema_ref(self, folder_name: str): + """ + Loads schema references from a specified folder. + Returns a dictionary of schema references. + """ + shared_schema_refs = {} + shared_schemas_path = self.get_abs_path("../schemas/" + folder_name) + + try: + shared_file_names = [ + f + for f in os.listdir(shared_schemas_path) + if os.path.isfile(os.path.join(shared_schemas_path, f)) + ] + except FileNotFoundError as e: + LOGGER.error("Schema folder not found: %s", shared_schemas_path) + return {} + + for shared_file in shared_file_names: + # Excluded event stream as it is not used as a reference in any other stream + if shared_file == "events.json": + continue + with open(os.path.join(shared_schemas_path, shared_file)) as data_file: + shared_schema_refs[shared_file] = json.load(data_file) + + return shared_schema_refs + + def load_schema(self): + """ + Loads the schema for the current stream. + """ + schema_file = "../schemas/{}.json".format(self.SCHEMA) + with open(self.get_abs_path(schema_file), encoding="UTF-8") as f: + schema = json.load(f) + + return schema def generate_catalog(self): - schema = self.get_schema() - mdata = singer.metadata.new() + """ + Generates the catalog for the stream, including schema and metadata. + """ + schema = self.load_schema() + mdata = metadata.new() - metadata = { - "selected": self.SELECTED, - "inclusion": self.INCLUSION, + # Metadata to add to the catalog + metadata_to_add = { + "forced-replication-method": self.REPLICATION_METHOD, "valid-replication-keys": self.VALID_REPLICATION_KEYS, - "selected-by-default": self.SELECTED_BY_DEFAULT, - "schema-name": self.TABLE + "inclusion": self.INCLUSION, + "table-key-properties": self.KEY_PROPERTIES, } - for k, v in metadata.items(): - mdata = singer.metadata.write( - mdata, - (), - k, - v - ) + # Assign metadata at the stream level + for k, v in metadata_to_add.items(): + mdata = metadata.write(mdata, (), k, v) - for field_name, field_schema in schema.get('properties').items(): - inclusion = 'available' + # Assign metadata for each property in the schema + for field_name, field_schema in schema.get("properties").items(): + inclusion = "available" - if field_name in self.KEY_PROPERTIES: - inclusion = 'automatic' + # Set inclusion to automatic for key properties and replication keys + if ( + field_name in self.KEY_PROPERTIES + or field_name in self.VALID_REPLICATION_KEYS + ): + inclusion = "automatic" - mdata = singer.metadata.write( - mdata, - ('properties', field_name), - 'inclusion', - inclusion + mdata = metadata.write( + mdata, ("properties", field_name), "inclusion", inclusion ) - return [{ - 'tap_stream_id': self.TABLE, - 'stream': self.TABLE, - 'key_properties': self.KEY_PROPERTIES, - 'bookmark_properties': self.BOOKMARK_PROPERTIES, - 'schema': self.get_schema(), - 'metadata': singer.metadata.to_list(mdata) - }] - - # This overrides the transform_record method in the Fistown Analytics tap-framework package - def transform_record(self, record): - with singer.Transformer(integer_datetime_fmt="unix-seconds-integer-datetime-parsing") as tx: - metadata = {} - - if self.catalog.metadata is not None: - metadata = singer.metadata.to_map(self.catalog.metadata) - - return tx.transform( - record, - self.catalog.schema.to_dict(), - metadata) - - def get_stream_data(self, data): - entity = self.ENTITY - return [self.transform_record(item.get(entity)) for item in data] - - def sync_data(self): - table = self.TABLE - api_method = self.API_METHOD - done = False - - # Attempt to get the bookmark date from the state file (if one exists and is supplied). - LOGGER.info('Attempting to get the most recent bookmark_date for entity {}.'.format(self.ENTITY)) - bookmark_date = get_last_record_value_for_table(self.state, table, 'bookmark_date') - - # If there is no bookmark date, fall back to using the start date from the config file. - if bookmark_date is None: - LOGGER.info('Could not locate bookmark_date from STATE file. Falling back to start_date from config.json instead.') - bookmark_date = get_config_start_date(self.config) - else: - bookmark_date = parse(bookmark_date) + # Resolve shared schema references + refs = self.load_shared_schema_refs() - # Convert bookmarked start date to POSIX. - bookmark_date_posix = int(bookmark_date.timestamp()) + return [ + { + "tap_stream_id": self.STREAM, + "stream": self.STREAM, + "schema": singer.resolve_schema_references(schema, refs), + "metadata": metadata.to_list(mdata), + } + ] - # Create params for filtering - if self.ENTITY == 'event': - params = {"occurred_at[after]": bookmark_date_posix} - bookmark_key = 'occurred_at' - else: - params = {"updated_at[after]": bookmark_date_posix} - bookmark_key = 'updated_at' + def appendCustomFields(self, record: dict): + """ + Prepare custom fields for the record for objects like "addon", "plan", "subscription", "customer" from the /events endpoint + """ + listOfCustomFieldObj = ["addon", "plan", "subscription", "customer"] + custom_fields = {} + event_custom_fields = {} - LOGGER.info("Querying {} starting at {}".format(table, bookmark_date)) + if self.ENTITY == "event": + # Extracting the object name from the event_type and adding custom fields for the object + words = record["event_type"].split("_") + content_obj = "_".join(words[:-1]) + content_data = record["content"].get(content_obj, {}) - while not done: - max_date = bookmark_date + # Add custom fields for specific objects + if content_obj in listOfCustomFieldObj: + for k, v in content_data.items(): + if "cf_" in k: + event_custom_fields[k] = v + record["content"][content_obj]["custom_fields"] = json.dumps( + event_custom_fields + ) - response = self.client.make_request( - url=self.get_url(), - method=api_method, - params=params) + for k, v in record.items(): + if "cf_" in k: + custom_fields[k] = v + record["custom_fields"] = json.dumps(custom_fields) + return record - to_write = self.get_stream_data(response.get('list')) + def update_bookmark(self, bookmark_value: str): + """ + Updates the bookmark in the state if the new bookmark value is greater than the current one. + """ + start_date = self.config.get("start_date") + current_bookmark = singer.get_bookmark( + self.state, self.STREAM, self.REPLICATION_KEY, default=start_date + ) + if bookmark_value and bookmark_value > current_bookmark: + self.state = singer.write_bookmark( + self.state, self.STREAM, self.REPLICATION_KEY, bookmark_value + ) - with singer.metrics.record_counter(endpoint=table) as ctr: - singer.write_records(table, to_write) + def evaluate_bookmark_based_on_lookback( + self, last_bookmark_value: str, lookback_window: int + ): + """ + Adjusts the bookmark value based on the lookback window. + """ + bookmark_value_timestamp = int( + singer.utils.strptime_to_utc(last_bookmark_value).timestamp() + ) + lookback_evaluated_bookmark = bookmark_value_timestamp - lookback_window + return lookback_evaluated_bookmark - ctr.increment(amount=len(to_write)) + def sync(self): + """ + Extract data from the Chargebee API and write it to the singer stream. + """ + start_date = self.config.get("start_date") + last_bookmark_value = singer.get_bookmark( + self.state, self.STREAM, self.REPLICATION_KEY, default=start_date + ) - for item in to_write: - max_date = max( - max_date, - parse(item.get(bookmark_key)) - ) + # Adjust the bookmark value based on the lookback window + # Ref: https://github.com/singer-io/tap-chargebee/pull/54#issuecomment-1138439726 + lookback_evaluated_bookmark = self.evaluate_bookmark_based_on_lookback( + last_bookmark_value, LOOKBACK_WINDOW + ) - self.state = incorporate( - self.state, table, 'bookmark_date', max_date) + # Filtering parameters + params = {f"{self.REPLICATION_KEY}[after]": lookback_evaluated_bookmark} - if not response.get('next_offset'): - LOGGER.info("Final offset reached. Ending sync.") - done = True - else: - LOGGER.info("Advancing by one offset.") - params['offset'] = response.get('next_offset') - bookmark_date = max_date + LOGGER.info(f"Querying stream - {self.STREAM} starting at {self.STREAM}") + + pages = self.client.get_offset_based_pages( + self.get_url(), self.API_METHOD, self.SORT_BY, params + ) + + with metrics.record_counter(self.STREAM) as counter, Transformer( + integer_datetime_fmt=UNIX_SECONDS_INTEGER_DATETIME_PARSING + ) as transformer: + for page in pages: + # Extract deleted records of "plans, addons and coupons" streams from events + deleted_records = self.handle_deleted_items(page) + # Combine current page records with deleted records + records_to_write = page + deleted_records + + for record in records_to_write: + record = self.add_custom_fields(record[self.ENTITY]) + + replication_key_value = singer.utils.strftime( + datetime.fromtimestamp( + record.get(self.REPLICATION_KEY), pytz.UTC + ), + format_str=DATETIME_FORMAT, + ) + + transformed_record = transformer.transform( + record, + self.catalog.schema.to_dict(), + metadata.to_map(self.catalog.metadata), + ) + singer.write_record(self.STREAM, transformed_record) + self.update_bookmark(replication_key_value) + counter.increment() - save_state(self.state) + # Write state after each page + singer.write_state(self.state) + return counter.value diff --git a/tap_chargebee/streams/comments.py b/tap_chargebee/streams/comments.py new file mode 100644 index 0000000..9a9d974 --- /dev/null +++ b/tap_chargebee/streams/comments.py @@ -0,0 +1,17 @@ +from tap_chargebee.streams.base import BaseChargebeeStream + +class CommentsStream(BaseChargebeeStream): + STREAM = 'comments' + ENTITY = 'comment' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'created_at' + KEY_PROPERTIES = ['id'] + SELECTED_BY_DEFAULT = True + VALID_REPLICATION_KEYS = ['created_at'] + INCLUSION = 'available' + API_METHOD = 'GET' + SCHEMA = 'common/comments' + SORT_BY = 'created_at' + + def get_url(self): + return 'https://{}.chargebee.com/api/v2/comments'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/coupons.py b/tap_chargebee/streams/coupons.py index 3c25b1e..5cdee24 100644 --- a/tap_chargebee/streams/coupons.py +++ b/tap_chargebee/streams/coupons.py @@ -1,16 +1,33 @@ from tap_chargebee.streams.base import BaseChargebeeStream - +from .util import Util class CouponsStream(BaseChargebeeStream): - TABLE = 'coupons' - ENTITY = 'coupon' + STREAM = 'coupons' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['updated_at'] + ENTITY = 'coupon' SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['updated_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'plan_model/coupons' + SORT_BY = 'created_at' + + def __init__(self, config, state, catalog, client): + BaseChargebeeStream.__init__(self, config, state, catalog, client) + if self.config['item_model']: + self.SCHEMA = 'item_model/coupons' def get_url(self): return 'https://{}.chargebee.com/api/v2/coupons'.format(self.config.get('site')) + + def handle_deleted_items(self, records: dict): + """ + Handle deleted records based on the include_deleted config. + """ + deleted_records = [] + if self.include_deleted: + for coupon in Util.coupons: + deleted_records.append(coupon) + return deleted_records \ No newline at end of file diff --git a/tap_chargebee/streams/credit_notes.py b/tap_chargebee/streams/credit_notes.py index 4adbf35..0fbf04e 100644 --- a/tap_chargebee/streams/credit_notes.py +++ b/tap_chargebee/streams/credit_notes.py @@ -2,15 +2,17 @@ class CreditNotesStream(BaseChargebeeStream): - TABLE = 'credit_notes' - ENTITY = 'credit_note' + STREAM = 'credit_notes' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['updated_at'] + ENTITY = 'credit_note' SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['updated_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'common/credit_notes' + SORT_BY = 'date' def get_url(self): return 'https://{}.chargebee.com/api/v2/credit_notes'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/customers.py b/tap_chargebee/streams/customers.py index e0849da..c4304ab 100644 --- a/tap_chargebee/streams/customers.py +++ b/tap_chargebee/streams/customers.py @@ -1,16 +1,33 @@ +import json from tap_chargebee.streams.base import BaseChargebeeStream class CustomersStream(BaseChargebeeStream): - TABLE = 'customers' - ENTITY = 'customer' + STREAM = 'customers' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['updated_at'] + ENTITY = 'customer' SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['updated_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'common/customers' + SORT_BY = 'updated_at' def get_url(self): return 'https://{}.chargebee.com/api/v2/customers'.format(self.config.get('site')) + + def add_custom_fields(self, record: dict): + """ + Adds custom fields to the record. + """ + custom_fields = {} + for key in record.keys(): + if "cf_" in key: + custom_fields[key] = record[key] + + if custom_fields: + record["custom_fields"] = json.dumps(custom_fields) + + return record diff --git a/tap_chargebee/streams/events.py b/tap_chargebee/streams/events.py index 80307b3..e17b4d5 100644 --- a/tap_chargebee/streams/events.py +++ b/tap_chargebee/streams/events.py @@ -1,16 +1,64 @@ +import json from tap_chargebee.streams.base import BaseChargebeeStream - +from .util import Util class EventsStream(BaseChargebeeStream): - TABLE = 'events' - ENTITY = 'event' + STREAM = 'events' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'occurred_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['occurred_at'] + ENTITY = 'event' SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['occurred_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'plan_model/events' + SORT_BY = 'occurred_at' + + def __init__(self, config, state, catalog, client): + BaseChargebeeStream.__init__(self, config, state, catalog, client) + if self.config['item_model']: + self.SCHEMA = 'item_model/events' + + def handle_deleted_items(self, records): + if self.include_deleted: + # Parse "event_type" from events records and collect deleted plan/addon/coupon from events + # Ref: https://github.com/singer-io/tap-chargebee/pull/58/files#r666092906 + for record in records: + event = record[self.ENTITY] + if event["event_type"] == 'plan_deleted': + Util.plans.append({'plan': event['content']['plan']}) + elif event['event_type'] == 'addon_deleted': + Util.addons.append({'addon': event['content']['addon']}) + elif event['event_type'] == 'coupon_deleted': + Util.coupons.append({'coupon': event['content']['coupon']}) + + return super().handle_deleted_items(records) def get_url(self): return 'https://{}.chargebee.com/api/v2/events'.format(self.config.get('site')) + + def add_custom_fields(self, record): + + listOfCustomFieldObj = ['addon', 'plan', 'subscription', 'customer'] + custom_fields = {} + event_custom_fields = {} + if self.ENTITY == 'event': + # payment_source_added -> payment_source, customer_created -> customer + content_obj = record['event_type'].rsplit("_", 1)[0] + + + if content_obj in listOfCustomFieldObj: + event_custom_fields = { + k: v for k, v in record['content'][content_obj].items() if 'cf_' in k + } + record['content'][content_obj]['custom_fields'] = json.dumps(event_custom_fields) + + for key in record.keys(): + if "cf_" in key: + custom_fields[key] = record[key] + + if custom_fields: + record['custom_fields'] = json.dumps(custom_fields) + + return record \ No newline at end of file diff --git a/tap_chargebee/streams/gifts.py b/tap_chargebee/streams/gifts.py new file mode 100644 index 0000000..2c4bcf7 --- /dev/null +++ b/tap_chargebee/streams/gifts.py @@ -0,0 +1,18 @@ +from tap_chargebee.streams.base import BaseChargebeeStream + + +class GiftsStream(BaseChargebeeStream): + STREAM = 'gifts' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' + KEY_PROPERTIES = ['id'] + ENTITY = 'gift' + SELECTED_BY_DEFAULT = True + VALID_REPLICATION_KEYS = ['updated_at'] + INCLUSION = 'available' + API_METHOD = 'GET' + SCHEMA = 'common/gifts' + SORT_BY = None + + def get_url(self): + return 'https://{}.chargebee.com/api/v2/gifts'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/invoices.py b/tap_chargebee/streams/invoices.py index 7ec576f..ed921b1 100644 --- a/tap_chargebee/streams/invoices.py +++ b/tap_chargebee/streams/invoices.py @@ -2,15 +2,17 @@ class InvoicesStream(BaseChargebeeStream): - TABLE = 'invoices' - ENTITY = 'invoice' + STREAM = 'invoices' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['updated_at'] + ENTITY = 'invoice' SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['updated_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'common/invoices' + SORT_BY = 'updated_at' def get_url(self): return 'https://{}.chargebee.com/api/v2/invoices'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/item_families.py b/tap_chargebee/streams/item_families.py new file mode 100644 index 0000000..81f092c --- /dev/null +++ b/tap_chargebee/streams/item_families.py @@ -0,0 +1,18 @@ +from tap_chargebee.streams.base import BaseChargebeeStream + + +class ItemFamiliesStream(BaseChargebeeStream): + STREAM = 'item_families' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' + KEY_PROPERTIES = ['id'] + ENTITY = 'item_family' + SELECTED_BY_DEFAULT = True + VALID_REPLICATION_KEYS = ['updated_at'] + INCLUSION = 'available' + API_METHOD = 'GET' + SCHEMA = 'item_model/item_families' + SORT_BY = None + + def get_url(self): + return 'https://{}.chargebee.com/api/v2/item_families'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/item_prices.py b/tap_chargebee/streams/item_prices.py new file mode 100644 index 0000000..695d81a --- /dev/null +++ b/tap_chargebee/streams/item_prices.py @@ -0,0 +1,18 @@ +from tap_chargebee.streams.base import BaseChargebeeStream + + +class ItemPricesStream(BaseChargebeeStream): + STREAM = 'item_prices' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' + KEY_PROPERTIES = ['id'] + ENTITY = 'item_price' + SELECTED_BY_DEFAULT = True + VALID_REPLICATION_KEYS = ['updated_at'] + INCLUSION = 'available' + API_METHOD = 'GET' + SCHEMA = 'item_model/item_prices' + SORT_BY = 'updated_at' + + def get_url(self): + return 'https://{}.chargebee.com/api/v2/item_prices'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/items.py b/tap_chargebee/streams/items.py new file mode 100644 index 0000000..051fb19 --- /dev/null +++ b/tap_chargebee/streams/items.py @@ -0,0 +1,18 @@ +from tap_chargebee.streams.base import BaseChargebeeStream + + +class ItemsStream(BaseChargebeeStream): + STREAM = 'items' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' + KEY_PROPERTIES = ['id'] + ENTITY = 'item' + SELECTED_BY_DEFAULT = True + VALID_REPLICATION_KEYS = ['updated_at'] + INCLUSION = 'available' + API_METHOD = 'GET' + SCHEMA = 'item_model/items' + SORT_BY = 'updated_at' + + def get_url(self): + return 'https://{}.chargebee.com/api/v2/items'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/orders.py b/tap_chargebee/streams/orders.py new file mode 100644 index 0000000..d49d9fa --- /dev/null +++ b/tap_chargebee/streams/orders.py @@ -0,0 +1,18 @@ +from tap_chargebee.streams.base import BaseChargebeeStream + + +class OrdersStream(BaseChargebeeStream): + STREAM = 'orders' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' + KEY_PROPERTIES = ['id'] + ENTITY = 'order' + SELECTED_BY_DEFAULT = True + VALID_REPLICATION_KEYS = ['updated_at'] + INCLUSION = 'available' + API_METHOD = 'GET' + SCHEMA = 'common/orders' + SORT_BY = 'updated_at' + + def get_url(self): + return 'https://{}.chargebee.com/api/v2/orders'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/payment_sources.py b/tap_chargebee/streams/payment_sources.py index 9929504..d0b9167 100644 --- a/tap_chargebee/streams/payment_sources.py +++ b/tap_chargebee/streams/payment_sources.py @@ -2,15 +2,17 @@ class PaymentSourcesStream(BaseChargebeeStream): - TABLE = 'payment_sources' - ENTITY = 'payment_source' + STREAM = 'payment_sources' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['updated_at'] + ENTITY = 'payment_source' SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['updated_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'common/payment_sources' + SORT_BY = None def get_url(self): return 'https://{}.chargebee.com/api/v2/payment_sources'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/plans.py b/tap_chargebee/streams/plans.py index a413e20..c6511cd 100644 --- a/tap_chargebee/streams/plans.py +++ b/tap_chargebee/streams/plans.py @@ -1,16 +1,43 @@ +import json from tap_chargebee.streams.base import BaseChargebeeStream - +from .util import Util class PlansStream(BaseChargebeeStream): - TABLE = 'plans' - ENTITY = 'plan' + STREAM = 'plans' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['updated_at'] + ENTITY = 'plan' SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['updated_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'plan_model/plans' + SORT_BY = None def get_url(self): return 'https://{}.chargebee.com/api/v2/plans'.format(self.config.get('site')) + + def handle_deleted_items(self, records: dict): + """ + Handle deleted records based on the include_deleted config. + """ + deleted_records = [] + if self.include_deleted: + for plan in Util.plans: + deleted_records.append(plan) + return deleted_records + + def add_custom_fields(self, record: dict): + """ + Adds custom fields to the record. + """ + custom_fields = {} + for key in record.keys(): + if "cf_" in key: + custom_fields[key] = record[key] + + if custom_fields: + record['custom_fields'] = json.dumps(custom_fields) + + return record \ No newline at end of file diff --git a/tap_chargebee/streams/promotional_credits.py b/tap_chargebee/streams/promotional_credits.py new file mode 100644 index 0000000..eae6baf --- /dev/null +++ b/tap_chargebee/streams/promotional_credits.py @@ -0,0 +1,18 @@ +from tap_chargebee.streams.base import BaseChargebeeStream + + +class PromotionalCreditsStream(BaseChargebeeStream): + STREAM = 'promotional_credits' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'created_at' + KEY_PROPERTIES = ['id'] + ENTITY = 'promotional_credit' + SELECTED_BY_DEFAULT = True + VALID_REPLICATION_KEYS = ['created_at'] + INCLUSION = 'available' + API_METHOD = 'GET' + SCHEMA = 'common/promotional_credits' + SORT_BY = None + + def get_url(self): + return 'https://{}.chargebee.com/api/v2/promotional_credits'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/quotes.py b/tap_chargebee/streams/quotes.py new file mode 100644 index 0000000..600c44b --- /dev/null +++ b/tap_chargebee/streams/quotes.py @@ -0,0 +1,18 @@ +from tap_chargebee.streams.base import BaseChargebeeStream + + +class QuotesStream(BaseChargebeeStream): + STREAM = 'quotes' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' + KEY_PROPERTIES = ['id'] + ENTITY = 'quote' + SELECTED_BY_DEFAULT = True + VALID_REPLICATION_KEYS = ['updated_at'] + INCLUSION = 'available' + API_METHOD = 'GET' + SCHEMA = 'common/quotes' + SORT_BY = 'date' + + def get_url(self): + return 'https://{}.chargebee.com/api/v2/quotes'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/subscriptions.py b/tap_chargebee/streams/subscriptions.py index b6d6499..b550d81 100644 --- a/tap_chargebee/streams/subscriptions.py +++ b/tap_chargebee/streams/subscriptions.py @@ -1,16 +1,38 @@ +import json from tap_chargebee.streams.base import BaseChargebeeStream class SubscriptionsStream(BaseChargebeeStream): - TABLE = 'subscriptions' - ENTITY = 'subscription' + STREAM = 'subscriptions' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['updated_at'] + ENTITY = 'subscription' SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['updated_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'plan_model/subscriptions' + SORT_BY = 'updated_at' + + def __init__(self, config, state, catalog, client): + BaseChargebeeStream.__init__(self, config, state, catalog, client) + if self.config['item_model']: + self.SCHEMA = 'item_model/subscriptions' def get_url(self): return 'https://{}.chargebee.com/api/v2/subscriptions'.format(self.config.get('site')) + + def add_custom_fields(self, record: dict): + """ + Adds custom fields to the record. + """ + custom_fields = {} + for key in record.keys(): + if "cf_" in key: + custom_fields[key] = record[key] + + if custom_fields: + record["custom_fields"] = json.dumps(custom_fields) + + return record diff --git a/tap_chargebee/streams/transactions.py b/tap_chargebee/streams/transactions.py index 7ee0636..ad1f000 100644 --- a/tap_chargebee/streams/transactions.py +++ b/tap_chargebee/streams/transactions.py @@ -2,15 +2,17 @@ class TransactionsStream(BaseChargebeeStream): - TABLE = 'transactions' + STREAM = 'transactions' ENTITY = 'transaction' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['updated_at'] SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['updated_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'common/transactions' + SORT_BY = 'updated_at' def get_url(self): return 'https://{}.chargebee.com/api/v2/transactions'.format(self.config.get('site')) diff --git a/tap_chargebee/streams/util.py b/tap_chargebee/streams/util.py new file mode 100644 index 0000000..558acae --- /dev/null +++ b/tap_chargebee/streams/util.py @@ -0,0 +1,6 @@ +class Util: + + plans = [] + addons = [] + coupons = [] + diff --git a/tap_chargebee/streams/virtual_bank_accounts.py b/tap_chargebee/streams/virtual_bank_accounts.py index e8a78ea..86025d6 100644 --- a/tap_chargebee/streams/virtual_bank_accounts.py +++ b/tap_chargebee/streams/virtual_bank_accounts.py @@ -2,15 +2,17 @@ class VirtualBankAccountsStream(BaseChargebeeStream): - TABLE = 'virtual_bank_accounts' + STREAM = 'virtual_bank_accounts' ENTITY = 'virtual_bank_account' + REPLICATION_METHOD = 'INCREMENTAL' + REPLICATION_KEY = 'updated_at' KEY_PROPERTIES = ['id'] - BOOKMARK_PROPERTIES = ['updated_at'] SELECTED_BY_DEFAULT = True VALID_REPLICATION_KEYS = ['updated_at'] INCLUSION = 'available' - SELECTED = True API_METHOD = 'GET' + SCHEMA = 'common/virtual_bank_accounts' + SORT_BY = None def get_url(self): return 'https://{}.chargebee.com/api/v2/virtual_bank_accounts'.format(self.config.get('site')) diff --git a/tests/base.py b/tests/base.py new file mode 100644 index 0000000..18e2958 --- /dev/null +++ b/tests/base.py @@ -0,0 +1,384 @@ +import unittest +import os +from datetime import timedelta +from datetime import datetime as dt +import time + +import singer +from tap_tester import connections, menagerie, runner, LOGGER + + +class ChargebeeBaseTest(unittest.TestCase): + """ + Setup expectations for test sub classes. + Metadata describing streams. + A bunch of shared methods that are used in tap-tester tests. + Shared tap-specific methods (as needed). + """ + AUTOMATIC_FIELDS = "automatic" + REPLICATION_KEYS = "valid-replication-keys" + PRIMARY_KEYS = "table-key-properties" + REPLICATION_METHOD = "forced-replication-method" + INCREMENTAL = "INCREMENTAL" + FULL_TABLE = "FULL_TABLE" + START_DATE_FORMAT = "%Y-%m-%dT00:00:00Z" + BOOKMARK_FORMAT = "%Y-%m-%dT%H:%M:%SZ" + RECORD_REPLICATION_KEY_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" + DATETIME_FMT = { + "%Y-%m-%dT%H:%M:%SZ", + "%Y-%m-%dT%H:%M:%S.000000Z" + } + start_date = "" + is_product_catalog_v1 = True + properties_v1 = { + "site": "TAP_CHARGEBEE_SITE" + } + properties_v2 = { + "site": "TAP_CHARGEBEE_SITE_V2" + } + credentials_v1 = { + "api_key": "TAP_CHARGEBEE_API_KEY", + } + credentials_v2 = { + "api_key": "TAP_CHARGEBEE_API_KEY_V2", + } + + @staticmethod + def tap_name(): + """The name of the tap""" + return 'tap-chargebee' + + @staticmethod + def get_type(): + return 'platform.chargebee' + + def get_properties(self, original: bool = True): + """Configuration properties required for the tap.""" + properties_dict = { + 'start_date': '2019-06-24T00:00:00Z' + } + props = self.properties_v2 + if self.is_product_catalog_v1: + props = self.properties_v1 + for prop in props: + properties_dict[prop] = os.getenv(props[prop]) + + if original: + return properties_dict + + properties_dict["start_date"] = self.start_date + return properties_dict + + def get_credentials(self): + """Authentication information for the test account.""" + credentials_dict = {} + creds = self.credentials_v2 + if self.is_product_catalog_v1: + creds = self.credentials_v1 + for cred in creds: + credentials_dict[cred] = os.getenv(creds[cred]) + + return credentials_dict + + def common_metadata(self): + """Metadata of common streams""" + return { + "events": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"occurred_at"} + }, + "coupons": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "credit_notes": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "customers": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "gifts": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "invoices": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "orders": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "payment_sources": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "promotional_credits": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"created_at"} + }, + "comments": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"created_at"} + }, + "quotes": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "subscriptions": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "transactions": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "virtual_bank_accounts": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + } + } + + def plan_model_metadata(self): + return { + "addons": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "plans": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + } + } + + def item_model_metadata(self): + return { + "items": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "item_families": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + }, + "item_prices": { + self.PRIMARY_KEYS: {"id"}, + self.REPLICATION_METHOD: self.INCREMENTAL, + self.REPLICATION_KEYS: {"updated_at"} + } + } + + def expected_metadata(self): + """The expected primary key of the streams""" + common_streams = self.common_metadata() + if self.is_product_catalog_v1: + plan_model_stream = self.plan_model_metadata() + return {**common_streams, **plan_model_stream} + item_model_stream = self.item_model_metadata() + return {**common_streams, **item_model_stream} + + def expected_streams(self): + """A set of expected stream names""" + return set(self.expected_metadata().keys()) + + def expected_primary_keys(self): + """return a dictionary with key of table name and value as a set of primary key fields""" + return {table: properties.get(self.PRIMARY_KEYS, set()) + for table, properties + in self.expected_metadata().items()} + + def expected_replication_keys(self): + """return a dictionary with key of table name and value as a set of replication key fields""" + return {table: properties.get(self.REPLICATION_KEYS, set()) + for table, properties + in self.expected_metadata().items()} + + def expected_automatic_fields(self): + """return a dictionary with key of table name and set of value of automatic(primary key and bookmark field) fields""" + auto_fields = {} + for k, v in self.expected_metadata().items(): + auto_fields[k] = v.get(self.PRIMARY_KEYS, set()) | v.get(self.REPLICATION_KEYS, set()) + return auto_fields + + def expected_replication_method(self): + """return a dictionary with key of table name and value of replication method""" + return {table: properties.get(self.REPLICATION_METHOD, None) + for table, properties + in self.expected_metadata().items()} + + def setUp(self): + missing_envs = [] + props_v1 = self.properties_v1 + props_v2 = self.properties_v2 + props = {**props_v1, **props_v2} + creds_v1 = self.credentials_v1 + creds_v2 = self.credentials_v2 + creds = {**creds_v1, **creds_v2} + + for prop in props: + if os.getenv(props[prop]) == None: + missing_envs.append(prop) + for cred in creds: + if os.getenv(creds[cred]) == None: + missing_envs.append(cred) + + if len(missing_envs) != 0: + raise Exception("set " + ", ".join(missing_envs)) + + ######################### + # Helper Methods # + ######################### + + def run_and_verify_check_mode(self, conn_id): + """ + Run the tap in check mode and verify it succeeds. + This should be ran prior to field selection and initial sync. + Return the connection id and found catalogs from menagerie. + """ + # run in check mode + check_job_name = runner.run_check_mode(self, conn_id) + + # verify check exit codes + exit_status = menagerie.get_exit_status(conn_id, check_job_name) + menagerie.verify_check_exit_status(self, exit_status, check_job_name) + + found_catalogs = menagerie.get_catalogs(conn_id) + self.assertGreater(len(found_catalogs), 0, msg="unable to locate schemas for connection {}".format(conn_id)) + + found_catalog_names = set(map(lambda c: c['stream_name'], found_catalogs)) + LOGGER.info(found_catalog_names) + self.assertSetEqual(self.expected_streams(), found_catalog_names, msg="discovered schemas do not match") + LOGGER.info("discovered schemas are OK") + + return found_catalogs + + def run_and_verify_sync(self, conn_id): + """ + Run a sync job and make sure it exited properly. + Return a dictionary with keys of streams synced + and values of records synced for each stream + """ + # Run a sync job using orchestrator + sync_job_name = runner.run_sync_mode(self, conn_id) + + # Verify tap and target exit codes + exit_status = menagerie.get_exit_status(conn_id, sync_job_name) + menagerie.verify_sync_exit_status(self, exit_status, sync_job_name) + + # Verify actual rows were synced + sync_record_count = runner.examine_target_output_file( + self, conn_id, self.expected_streams(), self.expected_primary_keys()) + self.assertGreater( + sum(sync_record_count.values()), 0, + msg="failed to replicate any data: {}".format(sync_record_count) + ) + LOGGER.info("total replicated row count: {}".format(sum(sync_record_count.values()))) + + return sync_record_count + + def perform_and_verify_table_and_field_selection(self, + conn_id, + test_catalogs, + select_all_fields=True): + """ + Perform table and field selection based off of the streams to select + set and field selection parameters. + Verify this results in the expected streams selected and all or no + fields selected for those streams. + """ + + # Select all available fields or select no fields from all testable streams + self.select_all_streams_and_fields( + conn_id=conn_id, catalogs=test_catalogs, select_all_fields=select_all_fields + ) + + catalogs = menagerie.get_catalogs(conn_id) + + # Ensure our selection affects the catalog + expected_selected = [tc.get('stream_name') for tc in test_catalogs] + for cat in catalogs: + catalog_entry = menagerie.get_annotated_schema(conn_id, cat['stream_id']) + + # Verify all testable streams are selected + selected = catalog_entry.get('annotated-schema').get('selected') + LOGGER.info("Validating selection on {}: {}".format(cat['stream_name'], selected)) + if cat['stream_name'] not in expected_selected: + self.assertFalse(selected, msg="Stream selected, but not testable.") + continue # Skip remaining assertions if we aren't selecting this stream + self.assertTrue(selected, msg="Stream not selected.") + + if select_all_fields: + # Verify all fields within each selected stream are selected + for field, field_props in catalog_entry.get('annotated-schema').get('properties').items(): + field_selected = field_props.get('selected') + LOGGER.info("\tValidating selection on {}.{}: {}".format( + cat['stream_name'], field, field_selected)) + self.assertTrue(field_selected, msg="Field not selected.") + else: + # Verify only automatic fields are selected + expected_automatic_fields = self.expected_automatic_fields().get(cat['stream_name']) + selected_fields = self.get_selected_fields_from_metadata(catalog_entry['metadata']) + + self.assertEqual(expected_automatic_fields, selected_fields) + + @staticmethod + def get_selected_fields_from_metadata(metadata): + """return selected fields from metedata""" + selected_fields = set() + for field in metadata: + is_field_metadata = len(field['breadcrumb']) > 1 + inclusion_automatic_or_selected = ( + field['metadata']['selected'] is True or \ + field['metadata']['inclusion'] == 'automatic' + ) + if is_field_metadata and inclusion_automatic_or_selected: + selected_fields.add(field['breadcrumb'][1]) + return selected_fields + + @staticmethod + def select_all_streams_and_fields(conn_id, catalogs, select_all_fields: bool = True): + """Select all streams and all fields within streams""" + for catalog in catalogs: + schema = menagerie.get_annotated_schema(conn_id, catalog['stream_id']) + + non_selected_properties = [] + if not select_all_fields: + # get a list of all properties so that none are selected + non_selected_properties = schema.get('annotated-schema', {}).get( + 'properties', {}).keys() + + connections.select_catalog_and_fields_via_metadata( + conn_id, catalog, schema, [], non_selected_properties) + + ########################################################################## + ### Tap Specific Methods + ########################################################################## + + def is_incremental(self, stream): + """return is stream is incremental or not""" + return self.expected_metadata()[stream][self.REPLICATION_METHOD] == self.INCREMENTAL + + def dt_to_ts(self, dtime, format): + """convert datetime with a format to timestamp""" + date_stripped = int(time.mktime(dt.strptime(dtime, format).timetuple())) + return date_stripped diff --git a/tests/test_chargebee_all_fields.py b/tests/test_chargebee_all_fields.py new file mode 100644 index 0000000..fc036a6 --- /dev/null +++ b/tests/test_chargebee_all_fields.py @@ -0,0 +1,349 @@ +from tap_tester import connections, runner, menagerie +from base import ChargebeeBaseTest + + +class ChargebeeAllFieldsTest(ChargebeeBaseTest): + + # list of fields that are common between V1 and V2 for which data is not generated + # we are removing this because we cannot find some fields in the UI, some fields require + # to enable Monthly Recurring Revenue setting, TaxJar, Contract terms feature, + # configure Avatax for Communications, Configure Avatax for Sales, Multi decimal feature + fields_to_remove_common = { + 'promotional_credits': {'amount_in_decimal'}, # not found in the UI + 'item_prices': {'custom_fields'}, + 'items': {'custom_fields'}, + 'item_families': {'custom_fields'}, + 'invoices': { # not found in the UI + 'void_reason_code', + 'expected_payment_date', + 'voided_at', + 'payment_owner', + 'vat_number_prefix', + 'total_in_local_currency', + 'sub_total_in_local_currency', + 'local_currency_code', + 'next_retry_at', + 'einvoice', + 'business_entity_id', + 'tax_origin', + 'local_currency_exchange_rate', + 'linked_taxes_withheld', + 'statement_descriptor', + }, + 'subscriptions': { # not found in the UI + 'create_pending_invoices', + 'free_period', + 'contract_term', + 'plan_free_quantity_in_decimal', + 'resume_date', + 'override_relationship', + 'auto_close_invoices', + 'contract_term_billing_cycle_on_renewal', # Enable Contract terms feature + 'plan_amount_in_decimal', + 'plan_quantity_in_decimal', + 'free_period_unit', + 'referral_info', + 'pause_date', + 'plan_unit_price_in_decimal', + 'trial_end_action', # Enable Trial End Action feature + 'changes_scheduled_at', + 'discounts', + 'event_based_addons', + 'start_date', + 'active_id', + 'business_entity_id', + 'remaining_billing_cycles' + }, + 'customers': { # not found in the UI + 'vat_number_validated_time', + 'referral_urls', + 'offline_payment_method', + 'entity_code', # Configure Avatax for Sales + 'billing_day_of_week_mode', + 'billing_date', + 'use_default_hierarchy_settings', + 'registered_for_gst', + 'exemption_details', # Configure Avatax for Communications + 'fraud_flag', + 'exempt_number', # Configure Avatax for Sales + 'vat_number_status', + 'billing_day_of_week', + 'parent_account_access', + 'child_account_access', + 'client_profile_id', # Configure Avatax for Communications + 'is_location_valid', + 'relationship', + 'billing_date_mode', + 'customer_type', # Configure Avatax for Communications + 'auto_close_invoices', # Metered Billing must be enabled + 'vat_number_prefix', + 'business_customer_without_vat_number', # Validate VAT + 'entity_identifier_standard', + 'is_einvoice_enabled', + 'entity_identifiers', + 'entity_identifier_scheme', + 'cf_people_id', + 'invoice_notes', + 'tax_providers_fields', + 'business_entity_id', + 'created_from_ip', + 'active_id', + 'billing_month', + 'einvoicing_method' + }, + 'credit_notes': { # not found in the UI + 'line_item_tiers', + 'vat_number_prefix', + 'total_in_local_currency', + 'sub_total_in_local_currency', + 'local_currency_code', + 'einvoice', + 'business_entity_id', + 'linked_tax_withheld_refunds', + 'tax_origin', + 'local_currency_exchange_rate', + 'tax_category', + 'shipping_address' + }, + 'payment_sources': { # not found in the UI + 'issuing_country', + 'paypal', + 'ip_address', + 'bank_account', + 'amazon_payment', + 'upi', + 'mandates', + 'business_entity_id', + 'billing_address', + 'klarna_pay_now', + 'venmo', + 'boleto' + }, + 'transactions': { + 'fraud_flag', + 'authorization_reason', + 'voided_at', + 'reversal_txn_id', + 'initiator_type', + 'linked_payments', + 'three_d_secure', + 'merchant_reference_id', + 'settled_at', + 'reference_authorization_id', + 'reversal_transaction_id', + 'validated_at', + 'fraud_reason', + 'amount_capturable', + 'reference_transaction_id', + 'iin', + 'last4', + 'custom_payment_method_name', + 'custom_payment_method_id', + 'business_entity_id' + }, + } + + # fields to remove for V2, we cannot find some fields in the UI + fields_to_remove_V2 = { + 'item_families': {'channel'}, + 'item_prices': { # not found in the UI + 'free_quantity_in_decimal', + 'archivable', + 'tax_detail', + 'trial_end_action', + 'price_in_decimal', + 'accounting_detail', + 'shipping_period_unit', + 'shipping_period', + 'archived_at' + }, + 'invoices': { # not found in the UI + 'line_item_taxes', + 'taxes', + 'dunning_status', + 'vat_number' + }, + 'credit_notes': { # not found in the UI + 'voided_at', + 'vat_number', + 'discounts' + }, + 'items': { # not found in the UI + 'archivable', + 'gift_claim_redirect_url', + 'applicable_items', + 'usage_calculation', + 'included_in_mrr' # Enable Monthly Recurring Revenue setting + }, + 'coupons': { # not found in the UI + 'archived_at' + }, + 'customers': { # not found in the UI + 'backup_payment_source_id', + 'cf_company_id', + 'consolidated_invoicing', + 'billing_day_of_week', + 'vat_number' + }, + 'subscriptions': { # not found in the UI + 'cancel_reason', + 'payment_source_id', + 'invoice_notes', + 'created_from_ip', + 'cancel_reason_code', + 'coupon', + 'coupons' + }, + 'transactions': { # not found in the UI + 'error_text', + 'reference_number', + 'error_code', + 'refunded_txn_id' + } + } + + # fields to remove for V1 + # we are removing this because we cannot find some fields in the UI, some fields require to enable + # Monthly Recurring Revenue setting, TaxJar, configure Avatax for Communications, Multi decimal feature + fields_to_remove_V1 = { + 'coupons': { + 'included_in_mrr' # Enable Monthly Recurring Revenue setting + }, + 'addons': { # not found in the UI + 'avalara_service_type', # configure Avatax for Communications + 'accounting_category3', + 'taxjar_product_code', # TaxJar should be enabled + 'accounting_category4', + 'avalara_transaction_type', # configure Avatax for Communications + 'tiers', + 'tax_code', + 'price_in_decimal', # Multi decimal feature is disabled + 'included_in_mrr', # Enable Monthly Recurring Revenue setting + 'tax_profile_id', + 'avalara_sale_type' # configure Avatax for Communications + }, + 'quotes': { # not found in the UI + 'contract_term_start', + 'line_item_tiers', + 'vat_number_prefix', + 'invoice_id', + 'contract_term_termination_fee', + 'contract_term_end' + }, + 'events': {'user'}, + 'invoices': {'line_item_tiers'}, + 'plans': { # not found in the UI + 'avalara_service_type', # configure Avatax for Communications + 'account_code', + 'event_based_addons', + 'free_quantity_in_decimal', + 'taxjar_product_code', # configure Avatax for Communications + 'applicable_addons', + 'accounting_category4', + 'avalara_transaction_type', # configure Avatax for Communications + 'claim_url', + 'tiers', + 'tax_profile_id', + 'tax_code', + 'accounting_category3', + 'price_in_decimal', # Multi decimal feature is disabled + 'archived_at', + 'attached_addons', + 'avalara_sale_type', # configure Avatax for Sales + 'trial_end_action' + }, + 'subscriptions': { # not found in the UI + 'offline_payment_method', + 'gift_id' + } + } + + def name(self): + return 'chargebee_all_fields_test' + + def all_fields_test_run(self): + """ + • Verify no unexpected streams were replicated + • Verify that more than just the automatic fields are replicated for each stream. + • verify all fields for each stream are replicated + """ + + untestable_streams_of_v2 = {'quotes'} # For V2, we have 0 records for 'quotes' stream + # Skipping streams virtual_bank_accounts, gifts and orders as we are not able to generate data + expected_streams = self.expected_streams() - {'virtual_bank_accounts', 'gifts', 'orders'} + + # skip quotes for product catalog V2 + if not self.is_product_catalog_v1: + expected_streams = expected_streams - untestable_streams_of_v2 + + expected_automatic_fields = self.expected_automatic_fields() + conn_id = connections.ensure_connection(self) + + found_catalogs = self.run_and_verify_check_mode(conn_id) + # table and field selection + catalog_entries = [catalog for catalog in found_catalogs + if catalog.get('tap_stream_id') in expected_streams] + self.perform_and_verify_table_and_field_selection(conn_id, catalog_entries) + + # grab metadata after performing table-and-field selection to set expectations + # used for asserting all fields are replicated + stream_to_all_catalog_fields = dict() + for catalog in catalog_entries: + stream_id, stream_name = catalog["stream_id"], catalog["stream_name"] + catalog_entry = menagerie.get_annotated_schema(conn_id, stream_id) + fields_from_field_level_md = [md_entry["breadcrumb"][1] for md_entry in catalog_entry["metadata"] + if md_entry["breadcrumb"] != []] + stream_to_all_catalog_fields[stream_name] = set(fields_from_field_level_md) + + record_count_by_stream = self.run_and_verify_sync(conn_id) + synced_records = runner.get_records_from_target_output() + + # Verify no unexpected streams were replicated + synced_stream_names = set(synced_records.keys()) + self.assertSetEqual(expected_streams, synced_stream_names) + + for stream in expected_streams: + with self.subTest(stream=stream): + + # expected values + expected_automatic_keys = expected_automatic_fields.get(stream, set()) + + # get all expected keys + expected_all_keys = stream_to_all_catalog_fields[stream] + + # collect actual values + messages = synced_records.get(stream) + + actual_all_keys = set() + # collect actual values + for message in messages['messages']: + if message['action'] == 'upsert': + actual_all_keys.update(message['data'].keys()) + + # Verify that you get some records for each stream + self.assertGreater(record_count_by_stream.get(stream, -1), 0) + + # verify all fields for a stream were replicated + self.assertGreater(len(expected_all_keys), len(expected_automatic_keys)) + self.assertTrue(expected_automatic_keys.issubset(expected_all_keys), msg=f'{expected_automatic_keys-expected_all_keys} is not in "expected_all_keys"') + + # get fields to remove for the version + if self.is_product_catalog_v1: + stream_fields_as_per_version = self.fields_to_remove_V1.get(stream, set()) + else: + stream_fields_as_per_version = self.fields_to_remove_V2.get(stream, set()) + # remove some fields as data cannot be generated / retrieved + fields_to_remove = self.fields_to_remove_common.get(stream, set()) | stream_fields_as_per_version + expected_all_keys = expected_all_keys - fields_to_remove + + self.assertSetEqual(expected_all_keys, actual_all_keys) + + def test_run(self): + + # All fields test for Product Catalog version 1 + # self.is_product_catalog_v1 = True + # self.all_fields_test_run() + + # All fields test for Product Catalog version 2 + self.is_product_catalog_v1 = False + self.all_fields_test_run() diff --git a/tests/test_chargebee_automatic_fields.py b/tests/test_chargebee_automatic_fields.py new file mode 100644 index 0000000..fc381cc --- /dev/null +++ b/tests/test_chargebee_automatic_fields.py @@ -0,0 +1,66 @@ +from tap_tester import connections,runner +from base import ChargebeeBaseTest + +class ChargebeeAutomaticFieldsTest(ChargebeeBaseTest): + + def name(self): + return "chargebee_automatic_fields_test" + + def automatic_fields_test_run(self): + """ + Testing that all the automatic fields are replicated despite de-selecting them + - Verify that only the automatic fields are sent to the target. + - Verify that all replicated records have unique primary key values. + """ + + untestable_streams = {'quotes'} # For V2, we have 0 records for 'quotes' stream + # Skipping streams virtual_bank_accounts, gifts and orders as we are not able to generate data + expected_streams = self.expected_streams() - {'virtual_bank_accounts', 'gifts', 'orders'} + + # skip quotes for product catalog V2 + if not self.is_product_catalog_v1: + expected_streams = expected_streams - untestable_streams + + conn_id = connections.ensure_connection(self) + + found_catalogs = self.run_and_verify_check_mode(conn_id) + + # Select all streams and no fields within streams + self.perform_and_verify_table_and_field_selection(conn_id, found_catalogs, select_all_fields=False) + + record_count_by_stream = self.run_and_verify_sync(conn_id) + synced_records = runner.get_records_from_target_output() + + for stream in expected_streams: + with self.subTest(stream=stream): + + # expected values + expected_primary_keys = self.expected_primary_keys()[stream] + expected_keys = self.expected_automatic_fields().get(stream) + + # collect actual values + messages = synced_records.get(stream) + record_messages_keys = [set(row['data'].keys()) for row in messages['messages']] + + # check if the stream has collected some records + self.assertGreater(record_count_by_stream.get(stream, 0), 0) + + # Verify that only the automatic fields are sent to the target + for actual_keys in record_messages_keys: + self.assertSetEqual(expected_keys, actual_keys) + + # Verify we did not duplicate any records across pages + records_pks_list = [tuple([message.get('data').get(primary_key) for primary_key in expected_primary_keys]) + for message in messages.get('messages')] + self.assertCountEqual(records_pks_list, set(records_pks_list), + msg="We have duplicate records for {}".format(stream)) + + def test_run(self): + + # Automatic fields test for Product Catalog v1 + # self.is_product_catalog_v1 = True + # self.automatic_fields_test_run() + + # Automatic fields test for Product Catalog v2 + self.is_product_catalog_v1 = False + self.automatic_fields_test_run() \ No newline at end of file diff --git a/tests/test_chargebee_bookmark.py b/tests/test_chargebee_bookmark.py new file mode 100644 index 0000000..5f6b5ae --- /dev/null +++ b/tests/test_chargebee_bookmark.py @@ -0,0 +1,167 @@ +from copy import deepcopy +from datetime import timedelta, datetime +from tap_tester import connections, runner, menagerie +from base import ChargebeeBaseTest +import dateutil.parser + +class ChargebeeBookmarkTest(ChargebeeBaseTest): + + def name(self): + return "chargebee_bookmark_test" + + def get_max_replication_value(self, records, replication_key): + """ + Return maximum replication value for the stream + """ + max_val = records[0].get(replication_key) + for record in records[1:]: + max_val = max(max_val, record.get(replication_key)) + return max_val + + def get_simulated_states(self, state, records): + """ + Subtract 12 hours from all bookmarks in the state file + """ + new_state = deepcopy(state) + for stream_name, bookmark in new_state.get("bookmarks").items(): + stream_records = [record.get('data') for record in records.get(stream_name, {}).get('messages', []) + if record.get('action') == 'upsert'] + # as these streams are skipped the state file will contain the start date as bookmark + if stream_name in self.streams_to_skip | {'quotes'}: + bookmark_date = bookmark["bookmark_date"] + else: + bookmark_date = self.get_max_replication_value(stream_records, list(self.expected_replication_keys().get(stream_name))[0]) + new_bookmark_date = dateutil.parser.parse(bookmark_date) - timedelta(minutes=1) + bookmark["bookmark_date"] = datetime.strftime(new_bookmark_date, self.BOOKMARK_FORMAT) + + return new_state + + def bookmark_test_run(self): + """ + Testing that the bookmarking for the tap works as expected + - Verify for each incremental stream you can do a sync which records bookmarks + - Verify that a bookmark doesn't exist for full table streams. + - Verify the bookmark is the max value sent to the target for the a given replication key. + - Verify 2nd sync respects the bookmark + - All data of the 2nd sync is >= the bookmark from the first sync + - The number of records in the 2nd sync is less then the first + """ + + untestable_streams = {'quotes'} # For V2, we have 0 records for 'quotes' stream + # Skipping streams virtual_bank_accounts, gifts and orders as we are not able to generate data + self.streams_to_skip = {'virtual_bank_accounts', 'gifts', 'orders'} + expected_streams = self.expected_streams() - self.streams_to_skip + + # skip quotes for product catalog V2 + if not self.is_product_catalog_v1: + expected_streams = expected_streams - untestable_streams + + expected_replication_keys = self.expected_replication_keys() + + ################################# First Sync ######################################### + + conn_id = connections.ensure_connection(self) + + # Run in check mode + found_catalogs = self.run_and_verify_check_mode(conn_id) + + # table and field selection + self.perform_and_verify_table_and_field_selection(conn_id, found_catalogs) + + # Run a first sync job using orchestrator + first_sync_record_count = self.run_and_verify_sync(conn_id) + first_sync_records = runner.get_records_from_target_output() + first_sync_bookmarks = menagerie.get_state(conn_id) + + ####################### Update State Between Syncs ######################### + + new_states = self.get_simulated_states(first_sync_bookmarks, first_sync_records) + menagerie.set_state(conn_id, new_states) + + ################################# Second Sync ######################################### + + second_sync_record_count = self.run_and_verify_sync(conn_id) + second_sync_records = runner.get_records_from_target_output() + second_sync_bookmarks = menagerie.get_state(conn_id) + + ########################### Test by Stream ########################################### + + for stream in expected_streams: + with self.subTest(stream=stream): + + # collect information for assertions from syncs 1 & 2 base on expected values + first_sync_count = first_sync_record_count.get(stream, 0) + second_sync_count = second_sync_record_count.get(stream, 0) + + first_sync_messages = [record.get('data') for record in first_sync_records.get(stream, {}).get('messages', []) + if record.get('action') == 'upsert'] + second_sync_messages = [record.get('data') for record in second_sync_records.get(stream, {}).get('messages', []) + if record.get('action') == 'upsert'] + + first_bookmark_key_value = first_sync_bookmarks.get('bookmarks', {stream: None}).get(stream) + second_bookmark_key_value = second_sync_bookmarks.get('bookmarks', {stream: None}).get(stream) + + if self.is_incremental(stream): + + # collect information specific to incremental streams from syncs 1 & 2 + # Tap is using key as 'bookmark_date' while writing the states + replication_key = list(expected_replication_keys[stream])[0] + + first_bookmark_value = first_bookmark_key_value.get(replication_key) + second_bookmark_value = second_bookmark_key_value.get(replication_key) + + first_bookmark_value_ts = self.dt_to_ts(first_bookmark_value, self.BOOKMARK_FORMAT) + second_bookmark_value_ts = self.dt_to_ts(second_bookmark_value, self.BOOKMARK_FORMAT) + + simulated_bookmark_value = self.dt_to_ts(new_states['bookmarks'][stream][replication_key], self.BOOKMARK_FORMAT) + simulated_bookmark_value = simulated_bookmark_value - 2 # Lookback of 2 minutes + # Verify the first sync sets a bookmark of the expected form + self.assertIsNotNone(first_bookmark_key_value) + self.assertIsNotNone(first_bookmark_value) + + self.assertIsNotNone(second_bookmark_key_value) + self.assertIsNotNone(second_bookmark_value) + + # Verify the second sync bookmark is Equal to the first sync bookmark + # As we have implemented (now - 2 minutes) as bookmark, thus, bookmark will not be same for both syncs + self.assertEqual(second_bookmark_value, first_bookmark_value) # assumes no changes to data during test + + for record in first_sync_messages: + # Verify the first sync bookmark value is the max replication key value for a given stream + replication_key_value = self.dt_to_ts(record.get(replication_key), self.RECORD_REPLICATION_KEY_FORMAT) + self.assertLessEqual(replication_key_value, first_bookmark_value_ts, + msg="First sync bookmark was set incorrectly, a record with a greater replication-key value was synced." + ) + + for record in second_sync_messages: + # Verify the second sync replication key value is Greater or Equal to the first sync bookmark + replication_key_value = self.dt_to_ts(record.get(replication_key), self.RECORD_REPLICATION_KEY_FORMAT) + self.assertGreaterEqual(replication_key_value, simulated_bookmark_value, + msg="Second sync records do not respect the previous bookmark.") + + # Verify the second sync bookmark value is the max replication key value for a given stream + self.assertLessEqual(replication_key_value, second_bookmark_value_ts, + msg="Second sync bookmark was set incorrectly, a record with a greater replication-key value was synced." + ) + + # verify that you get less data the 2nd time around + self.assertLess(second_sync_count, first_sync_count, + msg="second sync didn't have less records, bookmark usage not verified") + + else: + # Verify the syncs do not set a bookmark for full table streams + self.assertIsNone(first_bookmark_key_value) + self.assertIsNone(second_bookmark_key_value) + + # Verify the number of records in the second sync is the same as the first + self.assertEqual(second_sync_count, first_sync_count) + + def test_run(self): + + # Bookmark test for Product Catalog v1 + # self.is_product_catalog_v1 = True + # self.bookmark_test_run() + + # Bookmark test for Product Catalog v2 + self.is_product_catalog_v1 = False + self.bookmark_test_run() \ No newline at end of file diff --git a/tests/test_chargebee_discovery.py b/tests/test_chargebee_discovery.py new file mode 100644 index 0000000..b0e323b --- /dev/null +++ b/tests/test_chargebee_discovery.py @@ -0,0 +1,135 @@ +"""Test tap discovery mode and metadata.""" +import re + +from tap_tester import menagerie, connections + +from base import ChargebeeBaseTest + +class ChargebeeDiscoveryTest(ChargebeeBaseTest): + + def name(self): + return "tap_tester_chargebee_discovery_test" + + def discovery_test_run(self): + """ + Testing that discovery creates the appropriate catalog with valid metadata. + • Verify number of actual streams discovered match expected + • Verify the stream names discovered were what we expect + • Verify stream names follow naming convention + streams should only have lowercase alphas and underscores + • verify there is only 1 top level breadcrumb + • verify replication key(s) + • verify primary key(s) + • verify that if there is a replication key we are doing INCREMENTAL otherwise FULL + • verify the actual replication matches our expected replication method + • verify that primary, replication and foreign keys + are given the inclusion of automatic. + • verify that all other fields have inclusion of available metadata. + """ + streams_to_test = self.expected_streams() + + conn_id = connections.ensure_connection(self) + + found_catalogs = self.run_and_verify_check_mode(conn_id) + # Verify stream names follow naming convention + # streams should only have lowercase alphas and underscores + found_catalog_names = {c['tap_stream_id'] for c in found_catalogs} + self.assertTrue(all([re.fullmatch(r"[a-z_]+", name) for name in found_catalog_names]), + msg="One or more streams don't follow standard naming") + + for stream in streams_to_test: + with self.subTest(stream=stream): + + # Verify ensure the catalog is found for a given stream + catalog = next(iter([catalog for catalog in found_catalogs + if catalog["stream_name"] == stream])) + self.assertIsNotNone(catalog) + + # collecting expected values + expected_primary_keys = self.expected_primary_keys()[stream] + expected_replication_keys = self.expected_replication_keys()[stream] + expected_automatic_fields = expected_primary_keys | expected_replication_keys + expected_replication_method = self.expected_replication_method()[stream] + + # collecting actual values... + schema_and_metadata = menagerie.get_annotated_schema(conn_id, catalog['stream_id']) + metadata = schema_and_metadata["metadata"] + stream_properties = [item for item in metadata if item.get("breadcrumb") == []] + actual_primary_keys = set( + stream_properties[0].get( + "metadata", {self.PRIMARY_KEYS: []}).get(self.PRIMARY_KEYS, []) + ) + actual_replication_keys = set( + stream_properties[0].get( + "metadata", {self.REPLICATION_KEYS: []}).get(self.REPLICATION_KEYS, []) + ) + + actual_replication_method = stream_properties[0].get( + "metadata", {self.REPLICATION_METHOD: None}).get(self.REPLICATION_METHOD) + + actual_automatic_fields = set( + item.get("breadcrumb", ["properties", None])[1] for item in metadata + if item.get("metadata").get("inclusion") == "automatic" + ) + + ########################################################################## + ### metadata assertions + ########################################################################## + + actual_fields = [] + for md_entry in metadata: + if md_entry['breadcrumb'] != []: + actual_fields.append(md_entry['breadcrumb'][1]) + + # verify there are no duplicate metadata entries + self.assertEqual(len(actual_fields), len(set(actual_fields)), msg = "duplicates in the metadata entries retrieved") + + # verify there is only 1 top level breadcrumb in metadata + self.assertTrue(len(stream_properties) == 1, + msg="There is NOT only one top level breadcrumb for {}".format(stream) + \ + "\nstream_properties | {}".format(stream_properties)) + + # verify replication key(s) match expectations + self.assertSetEqual( + expected_replication_keys, actual_replication_keys + ) + + # verify primary key(s) match expectations + self.assertSetEqual( + expected_primary_keys, actual_primary_keys, + ) + + # verify that if there is a replication key we are doing INCREMENTAL otherwise FULL + if actual_replication_keys: + self.assertEqual(self.INCREMENTAL, actual_replication_method) + else: + self.assertEqual(self.FULL_TABLE, actual_replication_method) + + # verify the replication method matches our expectations + self.assertEqual( + expected_replication_method, actual_replication_method + ) + + # verify that primary keys and replication keys + # are given the inclusion of automatic in metadata. + self.assertSetEqual(expected_automatic_fields, actual_automatic_fields) + + # verify that all other fields have inclusion of available + # This assumes there are no unsupported fields for SaaS sources + self.assertTrue( + all({item.get("metadata").get("inclusion") == "available" + for item in metadata + if item.get("breadcrumb", []) != [] + and item.get("breadcrumb", ["properties", None])[1] + not in actual_automatic_fields}), + msg="Not all non key properties are set to available in metadata") + + def test_run(self): + + # Discovery test for Product Catalog version 1 + # self.is_product_catalog_v1 = True + # self.discovery_test_run() + + # Discovery test for Product Catalog version 2 + self.is_product_catalog_v1 = False + self.discovery_test_run() diff --git a/tests/test_chargebee_include_delete.py b/tests/test_chargebee_include_delete.py new file mode 100644 index 0000000..50e0c7c --- /dev/null +++ b/tests/test_chargebee_include_delete.py @@ -0,0 +1,95 @@ +"""Test tap sync mode and metadata.""" +import re + +import tap_tester.menagerie as menagerie +import tap_tester.runner as runner +import tap_tester.connections as connections + +from base import ChargebeeBaseTest + + +class ChargebeeIncludeDeletedTest(ChargebeeBaseTest): + """Test tap sync mode and metadata conforms to standards.""" + + @staticmethod + def name(): + return "tap_tester_chargebee_include_deleted_test" + + def setUp(self): + self.include_deleted = None + super().setUp() + + def get_properties(self): + properties = super().get_properties() + + # include_deleted is an optional property for configuration + if self.include_deleted is False: + properties["include_deleted"] = 'false' + + return properties + + def run_sync(self, expected_streams): + conn_id = connections.ensure_connection(self) + + found_catalogs = self.run_and_verify_check_mode(conn_id) + + # table and field selection + test_catalogs = [catalog for catalog in found_catalogs + if catalog.get('stream_name') in expected_streams] + + self.perform_and_verify_table_and_field_selection( + conn_id, test_catalogs) + + sync_job_name = runner.run_sync_mode(self, conn_id) + + # Verify tap and target exit codes + exit_status = menagerie.get_exit_status(conn_id, sync_job_name) + menagerie.verify_sync_exit_status(self, exit_status, sync_job_name) + return runner.get_upserts_from_target_output() + + def run_include_deleted_test(self): + """ + Testing that 2 sync have difference in data for stream invoices + """ + # Expected stream is only customers + expected_streams = ["invoices"] + + # default value + self.include_deleted = True + + # For include_delete true or not set + synced_records_with_include_deleted_true = self.run_sync( + expected_streams) + + deleted_status_for_include_deleted_true = [record["deleted"] for record in synced_records_with_include_deleted_true] + + # Verifying that deleted records are there before + self.assertEqual(True, True in deleted_status_for_include_deleted_true) + + # For include_delete false + + self.include_deleted = False + + synced_records_with_include_deleted_false = self.run_sync( + expected_streams) + + deleted_status_for_include_deleted_false = [record["deleted"] for record in synced_records_with_include_deleted_false] + + # Verifying that deleted records are not available + self.assertEqual(True, (True not in deleted_status_for_include_deleted_false)) + + # Compare with deleted records count with without deleted records count. With deleted records count must be higher. + self.assertGreater( + len(synced_records_with_include_deleted_true), + len(synced_records_with_include_deleted_false) + ) + + def test_run(self): + + #Sync test for Product Catalog version 1 + # self.is_product_catalog_v1 = True + # self.run_include_deleted_test() + + #Sync test for Product Catalog version 2 + self.is_product_catalog_v1 = False + self.run_include_deleted_test() \ No newline at end of file diff --git a/tests/test_chargebee_pagination.py b/tests/test_chargebee_pagination.py new file mode 100644 index 0000000..2c175a0 --- /dev/null +++ b/tests/test_chargebee_pagination.py @@ -0,0 +1,92 @@ +"""Test tap sync mode and metadata.""" +import re +import os +import requests + +from tap_tester import runner, menagerie, connections + +from base import ChargebeeBaseTest + + +class ChargebeePaginationTest(ChargebeeBaseTest): + """Test tap sync mode and metadata conforms to standards.""" + + @staticmethod + def name(): + return "tap_tester_chargebee_pagination_test" + + def generate_events(self): + # Generate events for product catalog v1 + url = 'https://{}.chargebee.com/api/v1/customers/cbdemo_dave'.format(os.getenv("TAP_CHARGEBEE_SITE")) + payload = 'first_name=Dave' + # Update customer 20 times which will generate 20 events + product_v1_api_key = os.getenv("TAP_CHARGEBEE_API_KEY") + for index in range(20): + requests.post(url=url, data=payload, auth=(product_v1_api_key,'')) + + # Generate events for product catalog v2 + url = 'https://{}.chargebee.com/api/v2/customers/cbdemo_carol'.format(os.getenv("TAP_CHARGEBEE_SITE_V2")) + payload = 'first_name=Carol' + # Update customer 20 times which will generate 20 events + product_v2_api_key = os.getenv("TAP_CHARGEBEE_API_KEY_V2") + for index in range(20): + requests.post(url=url, data=payload, auth=(product_v2_api_key,'')) + + + def pagination_test_run(self): + """ + Testing that sync creates the appropriate catalog with valid metadata. + • Verify that all fields and all streams have selected set to True in the metadata + """ + page_size = 10 # Page size for events + conn_id = connections.ensure_connection(self) + + # Expected stream is only events + expected_streams = ["events"] + found_catalogs = self.run_and_verify_check_mode(conn_id) + + # table and field selection + test_catalogs = [catalog for catalog in found_catalogs + if catalog.get('stream_name') in expected_streams] + + self.perform_and_verify_table_and_field_selection(conn_id,test_catalogs) + + record_count_by_stream = self.run_and_verify_sync(conn_id) + + synced_records = runner.get_records_from_target_output() + + for stream in expected_streams: + with self.subTest(stream=stream): + # expected values + expected_primary_keys = self.expected_primary_keys()[stream] + + # collect information for assertions from syncs 1 & 2 base on expected values + record_count_sync = record_count_by_stream.get(stream, 0) + primary_keys_list = [tuple(message.get('data').get(expected_pk) for expected_pk in expected_primary_keys) + for message in synced_records.get(stream).get('messages') + if message.get('action') == 'upsert'] + + # verify records are more than page size so multiple page is working + self.assertGreater(record_count_sync, page_size) + + if record_count_sync > page_size: + primary_keys_list_1 = primary_keys_list[:page_size] + primary_keys_list_2 = primary_keys_list[page_size:2*page_size] + + primary_keys_page_1 = set(primary_keys_list_1) + primary_keys_page_2 = set(primary_keys_list_2) + + #Verify by private keys that data is unique for page + self.assertTrue(primary_keys_page_1.isdisjoint(primary_keys_page_2)) + + def test_run(self): + # generate two events for both version so it will make more than 100 evenets in last 90 days + self.generate_events() + + #Pagination test for Product Catalog version 1 + # self.is_product_catalog_v1 = True + # self.pagination_test_run() + + #Pagintaion test for Product Catalog version 2 + self.is_product_catalog_v1 = False + self.pagination_test_run() diff --git a/tests/test_chargebee_start_date.py b/tests/test_chargebee_start_date.py new file mode 100644 index 0000000..5ca62bd --- /dev/null +++ b/tests/test_chargebee_start_date.py @@ -0,0 +1,139 @@ +import os + +from tap_tester import connections, runner + +from base import ChargebeeBaseTest +from tap_tester import LOGGER + + +class ChargebeeStartDateTest(ChargebeeBaseTest): + + start_date_1 = "" + start_date_2 = "" + + @staticmethod + def name(): + return "tap_tester_chargebee_start_date_test" + + def start_date_test_run(self): + """Instantiate start date according to the desired data set and run the test""" + + self.start_date_1 = self.get_properties().get('start_date') + if self.is_product_catalog_v1: + self.start_date_2 = '2021-03-03T00:00:00Z' + else: + self.start_date_2 = '2021-06-22T00:00:00Z' + + start_date_1_epoch = self.dt_to_ts(self.start_date_1, self.START_DATE_FORMAT) + start_date_2_epoch = self.dt_to_ts(self.start_date_2, self.START_DATE_FORMAT) + + self.start_date = self.start_date_1 + + # WE ARE NOT ABLE TO GENERATE TEST DATA SO SKIPPING THREE STREAMS(orders, gifts, virtual_bank_accounts) + expected_streams = self.expected_streams() - {'orders', 'gifts', 'virtual_bank_accounts', 'quotes'} + + ########################################################################## + ### First Sync + ########################################################################## + + # instantiate connection + conn_id_1 = connections.ensure_connection(self) + + # run check mode + found_catalogs_1 = self.run_and_verify_check_mode(conn_id_1) + + # table and field selection + test_catalogs_1_all_fields = [catalog for catalog in found_catalogs_1 + if catalog.get('stream_name') in expected_streams] + self.perform_and_verify_table_and_field_selection(conn_id_1, test_catalogs_1_all_fields, select_all_fields=True) + + # run initial sync + record_count_by_stream_1 = self.run_and_verify_sync(conn_id_1) + synced_records_1 = runner.get_records_from_target_output() + + ########################################################################## + ### Update START DATE Between Syncs + ########################################################################## + + LOGGER.info("REPLICATION START DATE CHANGE: {} ===>>> {} ".format(self.start_date, self.start_date_2)) + self.start_date = self.start_date_2 + + ########################################################################## + ### Second Sync + ########################################################################## + + # create a new connection with the new start_date + conn_id_2 = connections.ensure_connection(self, original_properties=False) + + # run check mode + found_catalogs_2 = self.run_and_verify_check_mode(conn_id_2) + + # table and field selection + test_catalogs_2_all_fields = [catalog for catalog in found_catalogs_2 + if catalog.get('stream_name') in expected_streams] + self.perform_and_verify_table_and_field_selection(conn_id_2, test_catalogs_2_all_fields, select_all_fields=True) + + # run sync + record_count_by_stream_2 = self.run_and_verify_sync(conn_id_2) + synced_records_2 = runner.get_records_from_target_output() + + # Verify the total number of records replicated in sync 1 is greater than the number + # of records replicated in sync 2 + self.assertGreaterEqual(sum(record_count_by_stream_1.values()), sum(record_count_by_stream_2.values())) + + for stream in expected_streams: + + with self.subTest(stream=stream): + + # expected values + expected_primary_keys = self.expected_primary_keys()[stream] + expected_bookmark_keys = self.expected_replication_keys()[stream] + + # collect information for assertions from syncs 1 & 2 base on expected values + record_count_sync_1 = record_count_by_stream_1.get(stream, 0) + record_count_sync_2 = record_count_by_stream_2.get(stream, 0) + primary_keys_list_1 = [tuple(message.get('data').get(expected_pk) for expected_pk in expected_primary_keys) + for message in synced_records_1.get(stream).get('messages') + if message.get('action') == 'upsert'] + primary_keys_list_2 = [tuple(message.get('data').get(expected_pk) for expected_pk in expected_primary_keys) + for message in synced_records_2.get(stream).get('messages') + if message.get('action') == 'upsert'] + + primary_keys_sync_1 = set(primary_keys_list_1) + primary_keys_sync_2 = set(primary_keys_list_2) + + # All streams are INCREMENTAL so no need of any condition + + # Expected bookmark key is one element in set so directly access it + bookmark_keys_list_1 = [message.get('data').get(next(iter(expected_bookmark_keys))) for message in synced_records_1.get(stream).get('messages') + if message.get('action') == 'upsert'] + bookmark_keys_list_2 = [message.get('data').get(next(iter(expected_bookmark_keys))) for message in synced_records_2.get(stream).get('messages') + if message.get('action') == 'upsert'] + + bookmark_key_sync_1 = set(bookmark_keys_list_1) + bookmark_key_sync_2 = set(bookmark_keys_list_2) + + # Verify bookmark key values are greater than or equal to start date of sync 1 + for bookmark_key_value in bookmark_key_sync_1: + self.assertGreaterEqual(self.dt_to_ts(bookmark_key_value, self.RECORD_REPLICATION_KEY_FORMAT), start_date_1_epoch) + + # Verify bookmark key values are greater than or equal to start date of sync 2 + for bookmark_key_value in bookmark_key_sync_2: + self.assertGreaterEqual(self.dt_to_ts(bookmark_key_value, self.RECORD_REPLICATION_KEY_FORMAT), start_date_2_epoch) + + # Verify the number of records replicated in sync 1 is greater than or equal to the number + # of records replicated in sync 2 for stream + self.assertGreaterEqual(record_count_sync_1, record_count_sync_2) + + # Verify the records replicated in sync 2 were also replicated in sync 1 + self.assertTrue(primary_keys_sync_2.issubset(primary_keys_sync_1)) + + def test_run(self): + + #Start date test Product Catalog version 1 + # self.is_product_catalog_v1 = True + # self.start_date_test_run() + + #Start date test Product Catalog version 1 + self.is_product_catalog_v1 = False + self.start_date_test_run() diff --git a/tests/test_chargebee_sync.py b/tests/test_chargebee_sync.py new file mode 100644 index 0000000..7ee8d17 --- /dev/null +++ b/tests/test_chargebee_sync.py @@ -0,0 +1,45 @@ +"""Test tap sync mode and metadata.""" +import re + +from tap_tester import runner, menagerie, connections + +from base import ChargebeeBaseTest + + +class ChargebeeSyncTest(ChargebeeBaseTest): + """Test tap sync mode and metadata conforms to standards.""" + + @staticmethod + def name(): + return "tap_tester_chargebee_sync_test" + + def sync_test_run(self): + """ + Testing that sync creates the appropriate catalog with valid metadata. + • Verify that all fields and all streams have selected set to True in the metadata + """ + conn_id = connections.ensure_connection(self) + + found_catalogs1 = self.run_and_verify_check_mode(conn_id) + + expected_streams = self.expected_streams() + + # table and field selection + found_catalogs = [catalog for catalog in found_catalogs1 + if catalog.get('stream_name') in expected_streams] + + self.perform_and_verify_table_and_field_selection(conn_id,found_catalogs) + + record_count_by_stream = self.run_and_verify_sync(conn_id) + + self.assertGreater(sum(record_count_by_stream.values()), 0) + + def test_run(self): + + #Sync test for Product Catalog version 1 + # self.is_product_catalog_v1 = True + # self.sync_test_run() + + #Sync test for Product Catalog version 2 + self.is_product_catalog_v1 = False + self.sync_test_run() diff --git a/tests/unittests/test_discover.py b/tests/unittests/test_discover.py new file mode 100644 index 0000000..851fa60 --- /dev/null +++ b/tests/unittests/test_discover.py @@ -0,0 +1,85 @@ +import unittest +from unittest.mock import patch, MagicMock, mock_open +from tap_chargebee.streams.comments import CommentsStream + + +class TestLoadSharedSchemaMethods(unittest.TestCase): + + def setUp(self): + self.config = {"start_date": "2017-01-01T00:00:00Z", "include_deleted": "false"} + self.base_instance = CommentsStream( + config=self.config, state={}, catalog=None, client=None + ) # Replace with actual class name + self.base_instance.config = {"item_model": False} + + @patch("tap_chargebee.streams.base.os") + @patch( + "tap_chargebee.streams.base.open", + new_callable=mock_open, + read_data='{"key": "value"}', + ) + def test_load_shared_schema_ref(self, mock_open, mock_os): + """ + Test load_shared_schema_ref method + """ + mock_os.listdir.return_value = ["quotes.json", "gifts.json", "orders.json"] + mock_os.path.isfile.side_effect = [True, True, True] + + result = self.base_instance.load_shared_schema_ref("common") + self.assertEqual(mock_open.call_count, 3) + self.assertEqual( + result, + { + "quotes.json": {"key": "value"}, + "gifts.json": {"key": "value"}, + "orders.json": {"key": "value"}, + }, + ) + + @patch("tap_chargebee.streams.base.os") + @patch( + "tap_chargebee.streams.base.open", + new_callable=mock_open, + read_data='{"key": "value"}', + ) + def test_load_shared_schema_refs_with_item_model(self, mock_open, mock_os): + """ + Test load_shared_schema_refs method with item_model set to True + """ + self.base_instance.config["item_model"] = True + self.base_instance.load_shared_schema_ref = MagicMock( + return_value={"schema1.json": {"key": "value"}} + ) + + result = self.base_instance.load_shared_schema_refs() + + self.assertEqual(self.base_instance.load_shared_schema_ref.call_count, 2) + self.base_instance.load_shared_schema_ref.assert_any_call("common") + self.base_instance.load_shared_schema_ref.assert_any_call("item_model") + self.assertEqual( + result, {"schema1.json": {"key": "value"}, "schema1.json": {"key": "value"}} + ) + + @patch("tap_chargebee.streams.base.os") + @patch( + "tap_chargebee.streams.base.open", + new_callable=mock_open, + read_data='{"key": "value"}', + ) + def test_load_shared_schema_refs_with_item_model(self, mock_open, mock_os): + """ + Test load_shared_schema_refs method with item_model set to False + """ + self.base_instance.config["item_model"] = False + self.base_instance.load_shared_schema_ref = MagicMock( + return_value={"schema1.json": {"key": "value"}} + ) + + result = self.base_instance.load_shared_schema_refs() + + self.assertEqual(self.base_instance.load_shared_schema_ref.call_count, 2) + self.base_instance.load_shared_schema_ref.assert_any_call("common") + self.base_instance.load_shared_schema_ref.assert_any_call("plan_model") + self.assertEqual( + result, {"schema1.json": {"key": "value"}, "schema1.json": {"key": "value"}} + ) diff --git a/tests/unittests/test_exception_handling.py b/tests/unittests/test_exception_handling.py new file mode 100644 index 0000000..4a00172 --- /dev/null +++ b/tests/unittests/test_exception_handling.py @@ -0,0 +1,404 @@ +from tap_chargebee import client +import unittest +import requests +from unittest import mock + + +def get_mock_http_response(status_code, contents): + """Returns mock rep""" + response = requests.Response() + response.status_code = status_code + response._content = contents.encode() + return response + + +@mock.patch("time.sleep") +@mock.patch("requests.request") +class TestErrorHandling(unittest.TestCase): + """ + Test cases to verify if the errors are handled as expected while communicating with Chargebee Environment + """ + + config = {"start_date": "2017-01-01T00:00:00Z"} + chargebee_client = client.ChargebeeClient(config) + + def test_400_Error_response_message(self, mocked_400_successful, mocked_sleep): + """ + Exception with response message should be raised if 400 status code returned from API + """ + resp_str = '{"message": "Sorry, Bad Request Error"}' + mocked_400_successful.return_value = get_mock_http_response(400, resp_str) + + expected_message = "HTTP-error-code: 400, Error: Sorry, Bad Request Error" + + with self.assertRaises(client.ChargebeeBadRequestError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), expected_message) + self.assertEqual(mocked_400_successful.call_count, 5) + + def test_401_Error_response_message(self, mocked_401_successful, mocked_sleep): + """ + Exception with response message should be raised if 401 status code returned from API + """ + resp_str = '{"message": "Sorry, authentication failed. Invalid api key"}' + mocked_401_successful.return_value = get_mock_http_response(401, resp_str) + + expected_message = ( + "HTTP-error-code: 401, Error: Sorry, authentication failed. Invalid api key" + ) + + with self.assertRaises(client.ChargebeeAuthenticationError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), expected_message) + self.assertEqual(mocked_401_successful.call_count, 5) + + def test_403_Error_response_message(self, mocked_403_successful, mocked_sleep): + """ + Exception with response message should be raised if 403 status code returned from API + """ + resp_str = '{"message": "Sorry, Operation not permitted"}' + mocked_403_successful.return_value = get_mock_http_response(403, resp_str) + + expected_message = "HTTP-error-code: 403, Error: Sorry, Operation not permitted" + + with self.assertRaises(client.ChargebeeForbiddenError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), expected_message) + self.assertEqual(mocked_403_successful.call_count, 5) + + def test_404_Error_response_message(self, mocked_404_successful, mocked_sleep): + """ + Exception with response message should be raised if 404 status code returned from API + """ + resp_str = '{"message": "Sorry, Resource not found"}' + mocked_404_successful.return_value = get_mock_http_response(404, resp_str) + + expected_message = "HTTP-error-code: 404, Error: Sorry, Resource not found" + + with self.assertRaises(client.ChargebeeNotFoundError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), expected_message) + self.assertEqual(mocked_404_successful.call_count, 5) + + def test_405_Error_response_message(self, mocked_405_successful, mocked_sleep): + """ + Exception with response message should be raised if 405 status code returned from API + """ + resp_str = '{"message": "Sorry, HTTP action not allowed for the API"}' + mocked_405_successful.return_value = get_mock_http_response(405, resp_str) + + expected_message = ( + "HTTP-error-code: 405, Error: Sorry, HTTP action not allowed for the API" + ) + + with self.assertRaises(client.ChargebeeMethodNotAllowedError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), expected_message) + self.assertEqual(mocked_405_successful.call_count, 5) + + def test_409_Error_response_message(self, mocked_409_successful, mocked_sleep): + """ + Exception with response message should be raised if 409 status code returned from API + """ + resp_str = '{"message": "Sorry, The request could not be processed"}' + mocked_409_successful.return_value = get_mock_http_response(409, resp_str) + + expected_message = ( + "HTTP-error-code: 409, Error: Sorry, The request could not be processed" + ) + + with self.assertRaises(client.ChargebeeNotProcessedError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), expected_message) + self.assertEqual(mocked_409_successful.call_count, 5) + + def test_429_Error_response_message(self, mocked_429_successful, mocked_sleep): + """ + Exception with response message should be raised if 429 status code returned from API + """ + resp_str = '{"message": "Sorry, Requesting too many requests"}' + mocked_429_successful.return_value = get_mock_http_response(429, resp_str) + + expected_message = ( + "HTTP-error-code: 429, Error: Sorry, Requesting too many requests" + ) + + with self.assertRaises(client.ChargebeeRateLimitError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), expected_message) + self.assertEqual(mocked_429_successful.call_count, 5) + + def test_500_Error_response_message(self, mocked_500_successful, mocked_sleep): + """ + Exception with response message should be raised if 500 status code returned from API + """ + resp_str = '{"message": "Sorry, Internal server error."}' + mocked_500_successful.return_value = get_mock_http_response(500, resp_str) + + expected_message = "HTTP-error-code: 500, Error: Sorry, Internal server error." + + with self.assertRaises(client.ChargebeeInternalServiceError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), expected_message) + self.assertEqual(mocked_500_successful.call_count, 5) + + def test_503_Error_response_message(self, mocked_503_successful, mocked_sleep): + """ + Exception with response message should be raised if 503 status code returned from API + """ + resp_str = '{"message": "Sorry, Temporary internal server error "}' + mocked_503_successful.return_value = get_mock_http_response(503, resp_str) + + expected_message = ( + "HTTP-error-code: 503, Error: Sorry, Temporary internal server error " + ) + + with self.assertRaises(client.ChargebeeServiceUnavailableError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), expected_message) + self.assertEqual(mocked_503_successful.call_count, 5) + + def test_400_error_custom_message(self, mocked_400_successful, mocked_sleep): + """ + Exception with custom message should be raised if 400 status code returned from API and 'message' not present in response + """ + mocked_400_successful.return_value = get_mock_http_response(400, "{}") + + expected_message = "HTTP-error-code: 400, Error: The request URI does not match the APIs in the system." + + with self.assertRaises(client.ChargebeeBadRequestError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_400_successful.call_count, 5) + + def test_401_error_custom_message(self, mocked_401_successful, mocked_sleep): + """ + Exception with custom message should be raised if 401 status code returned from API and 'message' not present in response + """ + mocked_401_successful.return_value = get_mock_http_response(401, "{}") + + expected_message = ( + "HTTP-error-code: 401, Error: The user is not authenticated to use the API." + ) + + with self.assertRaises(client.ChargebeeAuthenticationError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_401_successful.call_count, 5) + + def test_403_error_custom_message(self, mocked_403_successful, mocked_sleep): + """ + Exception with custom message should be raised if 403 status code returned from API and 'message' not present in response + """ + mocked_403_successful.return_value = get_mock_http_response(403, "{}") + + expected_message = "HTTP-error-code: 403, Error: The requested operation is not permitted for the user." + + with self.assertRaises(client.ChargebeeForbiddenError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_403_successful.call_count, 5) + + def test_404_error_custom_message(self, mocked_404_successful, mocked_sleep): + """ + Exception with custom message should be raised if 404 status code returned from API and 'message' not present in response + """ + mocked_404_successful.return_value = get_mock_http_response(404, "{}") + + expected_message = ( + "HTTP-error-code: 404, Error: The requested resource was not found." + ) + + with self.assertRaises(client.ChargebeeNotFoundError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_404_successful.call_count, 5) + + def test_405_error_custom_message(self, mocked_405_successful, mocked_sleep): + """ + Exception with custom message should be raised if 405 status code returned from API and 'message' not present in response + """ + mocked_405_successful.return_value = get_mock_http_response(405, "{}") + + expected_message = "HTTP-error-code: 405, Error: The HTTP action is not allowed for the requested REST API." + + with self.assertRaises(client.ChargebeeMethodNotAllowedError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_405_successful.call_count, 5) + + def test_409_error_custom_message(self, mocked_409_successful, mocked_sleep): + """ + Exception with custom message should be raised if 409 status code returned from API and 'message' not present in response + """ + mocked_409_successful.return_value = get_mock_http_response(409, "{}") + + expected_message = "HTTP-error-code: 409, Error: The request could not be processed because of conflict in the request." + + with self.assertRaises(client.ChargebeeNotProcessedError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_409_successful.call_count, 5) + + def test_429_error_custom_message(self, mocked_429_successful, mocked_sleep): + """ + Exception with custom message should be raised if 429 status code returned from API and 'message' not present in response + """ + mocked_429_successful.return_value = get_mock_http_response(429, "{}") + + expected_message = ( + "HTTP-error-code: 429, Error: You are requesting to many requests." + ) + + with self.assertRaises(client.ChargebeeRateLimitError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_429_successful.call_count, 5) + + def test_500_error_custom_message(self, mocked_500_successful, mocked_sleep): + """ + Exception with custom message should be raised if 500 status code returned from API and 'message' not present in response + """ + mocked_500_successful.return_value = get_mock_http_response(500, "{}") + + expected_message = "HTTP-error-code: 500, Error: The request could not be processed due to internal server error." + + with self.assertRaises(client.ChargebeeInternalServiceError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_500_successful.call_count, 5) + + def test_503_error_custom_message(self, mocked_503_successful, mocked_sleep): + """ + Exception with custom message should be raised if 503 status code returned from API and 'message' not present in response + """ + mocked_503_successful.return_value = get_mock_http_response(503, "{}") + + expected_message = "HTTP-error-code: 503, Error: The request could not be processed due to temporary internal server error." + + with self.assertRaises(client.ChargebeeServiceUnavailableError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_503_successful.call_count, 5) + + def test_5XX_error_custom_message(self, mocked_5xx_successful, mocked_sleep): + """ + Exception with custom message should be raised if 5XX status code returned from API and 'message' not present in response + """ + mocked_5xx_successful.return_value = get_mock_http_response(502, "{}") + + expected_message = "HTTP-error-code: 502, Error: Unknown Error" + + with self.assertRaises(client.Server5xxError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_5xx_successful.call_count, 5) + + def test_4XX_error_custom_message(self, mocked_4xx_successful, mocked_sleep): + """ + Exception with custom message should be raised if 4XX status code returned from API and 'message' not present in response + """ + mocked_4xx_successful.return_value = get_mock_http_response(450, "{}") + + expected_message = "HTTP-error-code: 450, Error: Unknown Error" + + with self.assertRaises(client.Server4xxError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_4xx_successful.call_count, 5) + + def test_unknown_error_custom_message( + self, mocked_unknown_successful, mocked_sleep + ): + """ + Exception with custom message should be raised if other than 4xx/5xx status code returned from API and 'message' not present in response + """ + mocked_unknown_successful.return_value = get_mock_http_response(350, "{}") + + expected_message = "HTTP-error-code: 350, Error: Unknown Error" + + with self.assertRaises(client.ChargebeeError) as e: + self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(str(e.exception), str(expected_message)) + self.assertEqual(mocked_unknown_successful.call_count, 1) + + @mock.patch("tap_chargebee.client.LOGGER", side_effect=requests.exceptions.Timeout) + def test_200_json_error_message( + self, mocked_warning, mocked_json_error, mocked_sleep + ): + """ + Verify that if response is not in JSON format then warning message is logged and empty dictionary is returned + """ + mocked_json_error.return_value = get_mock_http_response(200, "ssad") + + response = self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(response, {}) + mocked_warning.warning.assert_called_once_with("Response is not in JSON format") + + def test_200_success_response(self, mocked_json_error, mocked_sleep): + """ + Verify that if response is not in JSON format then warning message is logged and empty dictionary is returned + """ + mocked_json_error.return_value = get_mock_http_response(200, '{"data": []}') + + response = self.chargebee_client.make_request("/abc", "GET") + + self.assertEqual(response, {"data": []}) + + +@mock.patch("time.sleep") +class TestRequestTimeoutBackoff(unittest.TestCase): + + @mock.patch("requests.request", side_effect=requests.exceptions.Timeout) + def test_request_timeout_backoff(self, mocked_request, mocked_sleep): + """ + Verify make_request function is backoff for 5 times on Timeout exception + """ + config = {"start_date": "2017-01-01T00:00:00Z"} + + chargebee_client = client.ChargebeeClient(config) + with self.assertRaises(requests.exceptions.Timeout) as e: + chargebee_client.make_request("/abc", "GET") + + # Verify that requests.request is called 5 times + self.assertEqual(mocked_request.call_count, 5) + + +@mock.patch("time.sleep") +class TestConnectionErrorBackoff(unittest.TestCase): + + @mock.patch("requests.request", side_effect=requests.exceptions.ConnectionError) + def test_request_timeout_backoff(self, mocked_request, mocked_sleep): + """ + Verify make_request function is backoff for 5 times on ConnectionError exception + """ + config = {"start_date": "2017-01-01T00:00:00Z"} + chargebee_client = client.ChargebeeClient(config) + + with self.assertRaises(requests.exceptions.ConnectionError) as e: + chargebee_client.make_request("/abc", "GET") + + # Verify that requests.request is called 5 times + self.assertEqual(mocked_request.call_count, 5) diff --git a/tests/unittests/test_pagination.py b/tests/unittests/test_pagination.py new file mode 100644 index 0000000..b6c328b --- /dev/null +++ b/tests/unittests/test_pagination.py @@ -0,0 +1,76 @@ +import unittest +from unittest.mock import patch +from tap_chargebee.client import ChargebeeClient + + +class TestClientPagination(unittest.TestCase): + + config = {"start_date": "2017-01-01T00:00:00Z", "include_deleted": "false"} + chargebee_client = ChargebeeClient(config) + + @patch("tap_chargebee.client.ChargebeeClient.make_request") + def test_get_offset_based_pages_no_pages(self, mock_make_request): + """ + Test get_offset_based_pages method with no pages + """ + # Mock the response to simulate no pages + mock_make_request.return_value = {"list": [], "next_offset": None} + + pages = list( + self.chargebee_client.get_offset_based_pages( + "http://example.com", "GET", None + ) + ) + + self.assertEqual(pages, [[]]) + mock_make_request.assert_called_once() + + @patch("tap_chargebee.client.ChargebeeClient.make_request") + def test_get_offset_based_pages_multiple_pages(self, mock_make_request): + """ + Test get_offset_based_pages method with multiple pages + """ + # Mock the response to simulate multiple pages + mock_make_request.side_effect = [ + {"list": [1, 2, 3], "next_offset": "offset_1"}, + {"list": [4, 5, 6], "next_offset": None}, + ] + + pages = list( + self.chargebee_client.get_offset_based_pages( + "http://example.com", "GET", None + ) + ) + + self.assertEqual(pages, [[1, 2, 3], [4, 5, 6]]) + self.assertEqual(mock_make_request.call_count, 2) + + @patch("tap_chargebee.client.ChargebeeClient.make_request") + def test_get_offset_based_pages_with_sort_by(self, mock_make_request): + """ + Test get_offset_based_pages method with sort_by parameter + """ + # Mock the response to simulate sorted pages + mock_make_request.return_value = {"list": [1, 2, 3], "next_offset": None} + + pages = list( + self.chargebee_client.get_offset_based_pages( + "http://example.com", + "GET", + "created_at", + params={"updated_at[after]": 24234234}, + ) + ) + + self.assertEqual(pages, [[1, 2, 3]]) + mock_make_request.assert_called_once_with( + "http://example.com", + "GET", + { + "limit": 100, + "include_deleted": False, + "sort_by[asc]": "created_at", + "updated_at[after]": 24234234, + }, + None, + ) diff --git a/tests/unittests/test_request_timeout.py b/tests/unittests/test_request_timeout.py new file mode 100644 index 0000000..78bb54b --- /dev/null +++ b/tests/unittests/test_request_timeout.py @@ -0,0 +1,105 @@ +import tap_chargebee.client as _client +import unittest +import requests +from unittest import mock + + +# Mock response object +def get_mock_http_response(*args, **kwargs): + contents = '{"access_token": "test", "expires_in":100}' + response = requests.Response() + response.status_code = 200 + response._content = contents.encode() + return response + + +@mock.patch("requests.request", side_effect=get_mock_http_response) +class TestRequestTimeoutValue(unittest.TestCase): + + def test_no_request_timeout_in_config(self, mocked_request): + """ + Verify that if request_timeout is not provided in config then default value is used + """ + config = {"start_date": "2017-01-01T00:00:00Z"} # No request_timeout in config + chargebee_client = _client.ChargebeeClient(config) + + # Verify requests.request is called with expected timeout + self.assertEqual(chargebee_client.request_timeout, 300) + + def test_integer_request_timeout_in_config(self, mocked_request): + """ + Verify that if request_timeout is provided in config(integer value) then it should be use + """ + config = { + "start_date": "2017-01-01T00:00:00Z", + "request_timeout": 100, + } # integer timeout in config + chargebee_client = _client.ChargebeeClient(config) + + # Verify requests.request is called with expected timeout + self.assertEqual(chargebee_client.request_timeout, 100.0) + + def test_float_request_timeout_in_config(self, mocked_request): + """ + Verify that if request_timeout is provided in config(float value) then it should be use + """ + config = { + "start_date": "2017-01-01T00:00:00Z", + "request_timeout": 100.5, + } # float timeout in config + chargebee_client = _client.ChargebeeClient(config) + + # Verify requests.request is called with expected timeout + self.assertEqual(chargebee_client.request_timeout, 100.5) + + def test_string_request_timeout_in_config(self, mocked_request): + """ + Verify that if request_timeout is provided in config(string value) then it should be use + """ + config = { + "start_date": "2017-01-01T00:00:00Z", + "request_timeout": "100", + } # string format timeout in config + chargebee_client = _client.ChargebeeClient(config) + + # Verify requests.request is called with expected timeout + self.assertEqual(chargebee_client.request_timeout, 100.0) + + def test_empty_string_request_timeout_in_config(self, mocked_request): + """ + Verify that if request_timeout is provided in config with empty string then default value is used + """ + config = { + "start_date": "2017-01-01T00:00:00Z", + "request_timeout": "", + } # empty string in config + chargebee_client = _client.ChargebeeClient(config) + + # Verify requests.request is called with expected timeout + self.assertEqual(chargebee_client.request_timeout, 300) + + def test_zero_request_timeout_in_config(self, mocked_request): + """ + Verify that if request_timeout is provided in config with zero value then default value is used + """ + config = { + "start_date": "2017-01-01T00:00:00Z", + "request_timeout": 0.0, + } # zero value in config + chargebee_client = _client.ChargebeeClient(config) + + # Verify requests.request is called with expected timeout + self.assertEqual(chargebee_client.request_timeout, 300) + + def test_zero_string_request_timeout_in_config(self, mocked_request): + """ + Verify that if request_timeout is provided in config with zero in string format then default value is used + """ + config = { + "start_date": "2017-01-01T00:00:00Z", + "request_timeout": "0.0", + } # zero value in config + chargebee_client = _client.ChargebeeClient(config) + + # Verify requests.request is called with expected timeout + self.assertEqual(chargebee_client.request_timeout, 300) diff --git a/tests/unittests/test_sync.py b/tests/unittests/test_sync.py new file mode 100644 index 0000000..cf1f195 --- /dev/null +++ b/tests/unittests/test_sync.py @@ -0,0 +1,175 @@ +import unittest +from unittest.mock import patch, MagicMock +from tap_chargebee.streams.base import BaseChargebeeStream +from tap_chargebee.streams.comments import CommentsStream +from tap_chargebee.streams.events import EventsStream +from tap_chargebee.client import ChargebeeClient +import json + + +class TestSyncMethods(unittest.TestCase): + def setUp(self) -> None: + self.config = {"start_date": "2017-01-01T00:00:00Z", "include_deleted": "false"} + self.chargebee_client = ChargebeeClient(self.config) + + @patch("tap_chargebee.client.ChargebeeClient.get_offset_based_pages") + def test_sync_no_pages(self, mock_get_pages): + """ + Test sync method with no pages + """ + base_instance = CommentsStream( + config=self.config, state={}, catalog=None, client=self.chargebee_client + ) + counter = base_instance.sync() + base_instance.update_bookmark = MagicMock() + + base_instance.client.get_offset_based_pages.assert_called_once_with( + "https://None.chargebee.com/api/v2/comments", + "GET", + "created_at", + {"created_at[after]": 1483228798}, + ) + # Assert that update_bookmark is not called + base_instance.update_bookmark.assert_not_called() + + self.assertEqual(counter, 0) + + @patch("tap_chargebee.streams.base.Transformer.transform", return_value={}) + @patch("tap_chargebee.client.ChargebeeClient.get_offset_based_pages") + def test_sync_multiple_pages(self, mock_get_pages, mock_transform): + """ + Test sync method with multiple pages + """ + base_instance = CommentsStream( + config=self.config, state={}, catalog=None, client=self.chargebee_client + ) + base_instance.catalog = MagicMock() + mock_get_pages.return_value = [ + [{"comment": {"created_at": 1700042444}}], + [{"comment": {"created_at": 1700045644}}], + ] + + counter = base_instance.sync() + + base_instance.client.get_offset_based_pages.assert_called_once_with( + "https://None.chargebee.com/api/v2/comments", + "GET", + "created_at", + {"created_at[after]": 1483228798}, + ) + # Assert that update_bookmark is called with the latest created_at value + self.assertEqual( + base_instance.state, + {"bookmarks": {"comments": {"created_at": "2023-11-15T10:54:04Z"}}}, + ) + # Assert that transform is called twice + self.assertEqual(counter, 2) + + +class TestAppendCustomFields(unittest.TestCase): + def setUp(self) -> None: + self.config = { + "start_date": "2017-01-01T00:00:00Z", + "include_deleted": "false", + "item_model": True, + } + + def test_append_custom_fields_event_entity(self): + """ + Test appendCustomFields method for event entity + """ + base_instance = EventsStream( + config=self.config, state={}, catalog=None, client=None + ) + record = { + "event_type": "subscription_created", + "content": { + "subscription": { + "cf_custom_field1": "value1", + "cf_custom_field2": "value2", + } + }, + } + expected_record = { + "event_type": "subscription_created", + "content": { + "subscription": { + "cf_custom_field1": "value1", + "cf_custom_field2": "value2", + "custom_fields": json.dumps( + {"cf_custom_field1": "value1", "cf_custom_field2": "value2"} + ), + } + }, + "custom_fields": "{}", + } + result = base_instance.appendCustomFields(record) + self.assertEqual(result, expected_record) + + def test_append_custom_fields_non_event_entity(self): + """ + Test appendCustomFields method for non-event entity + """ + base_instance = CommentsStream( + config=self.config, state={}, catalog=None, client=None + ) + record = {"cf_custom_field1": "value1", "cf_custom_field2": "value2"} + expected_record = { + "cf_custom_field1": "value1", + "cf_custom_field2": "value2", + "custom_fields": json.dumps( + {"cf_custom_field1": "value1", "cf_custom_field2": "value2"} + ), + } + result = base_instance.appendCustomFields(record) + self.assertEqual(result, expected_record) + + +class TestBookmakrMethods(unittest.TestCase): + + def setUp(self) -> None: + self.config = {"start_date": "2017-01-01T00:00:00Z", "include_deleted": "false"} + + def test_update_bookmark_greater_value(self): + """ + Test update_bookmark method with greater value + """ + state = {"bookmarks": {"comments": {"created_at": "2019-01-01T00:00:00Z"}}} + base_instance = CommentsStream( + config=self.config, state=state, catalog=None, client=None + ) # Replace with actual class name + base_instance.update_bookmark("2021-02-01T00:00:00Z") + # Assert that the bookmark is updated + self.assertEqual( + base_instance.state, + {"bookmarks": {"comments": {"created_at": "2021-02-01T00:00:00Z"}}}, + ) + + def test_update_bookmark_lower_value(self): + """ + Test update_bookmark method with lower value + """ + state = {"bookmarks": {"comments": {"created_at": "2026-01-01T00:00:00Z"}}} + base_instance = CommentsStream( + config=self.config, state=state, catalog=None, client=None + ) # Replace with actual class name + base_instance.update_bookmark("2011-02-01T00:00:00Z") + # Assert that the bookmark is not updated + self.assertEqual( + base_instance.state, + {"bookmarks": {"comments": {"created_at": "2026-01-01T00:00:00Z"}}}, + ) + + def test_evaluate_bookmark_based_on_lookback(self): + """ + Test evaluate_bookmark_based_on_lookback method + """ + base_instance = CommentsStream( + config=self.config, state={}, catalog=None, client=None + ) + evaluate_bookmark = base_instance.evaluate_bookmark_based_on_lookback( + "2021-02-01T00:00:00Z", 10 + ) + # Assert that the bookmark is evaluated based on lookback + # 2021-02-01T00:00:00Z -> 1612137600 - 10 = 1612137590 + self.assertEqual(evaluate_bookmark, 1612137590)