tap-belvo 0.3.1__tar.gz → 0.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tap-belvo might be problematic. Click here for more details.

Files changed (47) hide show
  1. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/.github/workflows/dynamic-publish.yaml +1 -1
  2. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/.github/workflows/test.yml +6 -6
  3. tap_belvo-0.3.3/.github/workflows/update_catalog.yml +41 -0
  4. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/.pre-commit-config.yaml +3 -3
  5. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/PKG-INFO +3 -2
  6. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/pyproject.toml +23 -18
  7. tap_belvo-0.3.3/scripts/update_openapi.py +29 -0
  8. tap_belvo-0.3.3/tap_belvo/client.py +123 -0
  9. tap_belvo-0.3.1/tap_belvo/openapi/BelvoOpenFinanceApiSpec.json → tap_belvo-0.3.3/tap_belvo/openapi.json +27594 -18672
  10. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tap_belvo/streams/banking.py +10 -12
  11. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tap_belvo/streams/core.py +14 -16
  12. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tap_belvo/tap.py +11 -7
  13. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[banking_accounts].json +1226 -0
  14. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[banking_owners].json +309 -0
  15. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[banking_transactions].json +1226 -0
  16. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[enrichment_incomes].json +515 -0
  17. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[enrichment_recurring_expenses].json +1542 -0
  18. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[enrichment_risk_insights].json +4908 -0
  19. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[fiscal_invoices].json +1632 -0
  20. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[fiscal_tax_compliance_statuses].json +148 -0
  21. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[fiscal_tax_retentions].json +443 -0
  22. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[fiscal_tax_statuses].json +824 -0
  23. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[institutions].json +406 -0
  24. tap_belvo-0.3.3/tests/__snapshots__/test_schema_evolution/test_catalog_changes[links].json +242 -0
  25. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tests/conftest.py +2 -0
  26. tap_belvo-0.3.3/tests/test_schema_evolution.py +31 -0
  27. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/uv.lock +224 -114
  28. tap_belvo-0.3.1/tap_belvo/client.py +0 -185
  29. tap_belvo-0.3.1/tap_belvo/openapi/__init__.py +0 -24
  30. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/.copier-answers.yml +0 -0
  31. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/.github/CODEOWNERS +0 -0
  32. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/.github/renovate.json5 +0 -0
  33. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/.gitignore +0 -0
  34. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/.secrets/.gitignore +0 -0
  35. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/LICENSE +0 -0
  36. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/README.md +0 -0
  37. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/meltano.yml +0 -0
  38. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/output/.gitignore +0 -0
  39. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/plugins/loaders/target-duckdb--jwills.lock +0 -0
  40. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/plugins/loaders/target-jsonl--andyh1203.lock +0 -0
  41. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tap_belvo/__init__.py +0 -0
  42. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tap_belvo/__main__.py +0 -0
  43. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tap_belvo/streams/__init__.py +0 -0
  44. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tap_belvo/streams/enrichment.py +0 -0
  45. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tap_belvo/streams/fiscal.py +0 -0
  46. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tests/__init__.py +0 -0
  47. {tap_belvo-0.3.1 → tap_belvo-0.3.3}/tests/test_core.py +0 -0
@@ -13,7 +13,7 @@ jobs:
13
13
  - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
14
14
  with:
15
15
  fetch-depth: 0
16
- - uses: hynek/build-and-inspect-python-package@c52c3a4710070b50470d903818a7b25115dcd076 # v2.13.0
16
+ - uses: hynek/build-and-inspect-python-package@efb823f52190ad02594531168b7a2d5790e66516 # v2.14.0
17
17
  id: baipp
18
18
 
19
19
  publish:
@@ -30,13 +30,13 @@ concurrency:
30
30
  env:
31
31
  FORCE_COLOR: 1
32
32
  # renovate: datasource=pypi depName=prek
33
- PREK_VERSION: 0.2.1
33
+ PREK_VERSION: 0.2.10
34
34
  # renovate: datasource=pypi depName=uv
35
- UV_VERSION: 0.8.22
35
+ UV_VERSION: 0.9.4
36
36
  # renovate: datasource=pypi depName=tox
37
- TOX_VERSION: 4.30.2
37
+ TOX_VERSION: 4.31.0
38
38
  # renovate: datasource=pypi depName=tox-uv
39
- TOX_UV_VERSION: 1.28.0
39
+ TOX_UV_VERSION: 1.29.0
40
40
 
41
41
  jobs:
42
42
  tests:
@@ -60,7 +60,7 @@ jobs:
60
60
  with:
61
61
  python-version: ${{ matrix.python-version }}
62
62
  allow-prereleases: true
63
- - uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
63
+ - uses: astral-sh/setup-uv@2ddd2b9cb38ad8efd50337e8ab201519a34c9f24 # v7.1.1
64
64
  with:
65
65
  version: ${{ env.UV_VERSION }}
66
66
  - name: Run
@@ -82,7 +82,7 @@ jobs:
82
82
  uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
83
83
 
84
84
  - name: Install the latest version of uv
85
- uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
85
+ uses: astral-sh/setup-uv@2ddd2b9cb38ad8efd50337e8ab201519a34c9f24 # v7.1.1
86
86
  with:
87
87
  version: ${{ env.UV_VERSION }}
88
88
 
@@ -0,0 +1,41 @@
1
+ name: Update OpenAPI Specification for belvo.com
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ schedule:
6
+ # Daily at noon
7
+ - cron: '0 12 * * *'
8
+
9
+ env:
10
+ FORCE_COLOR: "1"
11
+ # renovate: datasource=pypi depName=uv
12
+ UV_VERSION: 0.9.4
13
+
14
+ jobs:
15
+ update_openapi_specification:
16
+ name: Update Catalog
17
+ runs-on: ubuntu-24.04
18
+ permissions:
19
+ contents: write
20
+ pull-requests: write
21
+ steps:
22
+ - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
23
+ id: generate-token
24
+ with:
25
+ app-id: ${{ vars.APP_ID }}
26
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
27
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
28
+ - uses: astral-sh/setup-uv@2ddd2b9cb38ad8efd50337e8ab201519a34c9f24 # v7.1.1
29
+ with:
30
+ version: ${{ env.UV_VERSION }}
31
+ - run: uv run --only-group=scripts scripts/update_openapi.py
32
+ - run: uv run pytest --snapshot-update tests/test_schema_evolution.py
33
+ - id: check_snapshot_changes
34
+ continue-on-error: true
35
+ run: git diff --quiet tests/__snapshots__
36
+ - uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
37
+ if: steps.check_snapshot_changes.outcome == 'failure'
38
+ with:
39
+ title: 'chore: Update catalog'
40
+ token: ${{ steps.generate-token.outputs.token }}
41
+ signoff: true
@@ -19,12 +19,12 @@ repos:
19
19
  - id: trailing-whitespace
20
20
 
21
21
  - repo: https://github.com/tox-dev/pyproject-fmt
22
- rev: "v2.6.0"
22
+ rev: "v2.11.0"
23
23
  hooks:
24
24
  - id: pyproject-fmt
25
25
 
26
26
  - repo: https://github.com/astral-sh/ruff-pre-commit
27
- rev: "v0.13.2"
27
+ rev: "v0.14.1"
28
28
  hooks:
29
29
  - id: ruff-check
30
30
  args: [--fix, --exit-non-zero-on-fix, --show-fixes]
@@ -36,7 +36,7 @@ repos:
36
36
  - id: validate_manifest
37
37
 
38
38
  - repo: https://github.com/astral-sh/uv-pre-commit
39
- rev: "0.8.22"
39
+ rev: "0.9.4"
40
40
  hooks:
41
41
  - id: uv-lock
42
42
  - id: uv-sync
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tap-belvo
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Singer tap for Belvo, built with the Meltano SDK for Singer Taps.
5
5
  Project-URL: Documentation, https://github.com/reservoir-data/tap-belvo#readme
6
6
  Project-URL: Homepage, https://github.com/reservoir-data/tap-belvo
@@ -18,7 +18,8 @@ Classifier: Programming Language :: Python :: 3.14
18
18
  Requires-Python: >=3.11
19
19
  Requires-Dist: requests-cache==1.*
20
20
  Requires-Dist: requests~=2.32.3
21
- Requires-Dist: singer-sdk~=0.49.1
21
+ Requires-Dist: singer-sdk~=0.51.0
22
+ Requires-Dist: typing-extensions>=4.15; python_full_version < '3.12'
22
23
  Description-Content-Type: text/markdown
23
24
 
24
25
  # tap-belvo
@@ -31,7 +31,8 @@ dynamic = [
31
31
  dependencies = [
32
32
  "requests~=2.32.3",
33
33
  "requests-cache==1.*",
34
- "singer-sdk~=0.49.1",
34
+ "singer-sdk~=0.51.0",
35
+ "typing-extensions>=4.15; python_full_version<'3.12'",
35
36
  ]
36
37
  urls.Documentation = "https://github.com/reservoir-data/tap-belvo#readme"
37
38
  urls.Homepage = "https://github.com/reservoir-data/tap-belvo"
@@ -40,22 +41,35 @@ scripts."tap-belvo" = "tap_belvo.tap:TapBelvo.cli"
40
41
 
41
42
  [dependency-groups]
42
43
  dev = [
44
+ { include-group = "scripts" },
43
45
  { include-group = "testing" },
44
46
  { include-group = "typing" },
45
47
  ]
48
+ scripts = [
49
+ "requests>=2.32.5",
50
+ ]
46
51
  testing = [
47
- "deptry>=0.12",
48
- "pytest>=7.4",
52
+ "deptry>=0.23",
53
+ "pytest>=8",
54
+ "pytest-subtests>=0.15",
49
55
  "singer-sdk[testing]",
56
+ "syrupy>=5",
50
57
  ]
51
58
  typing = [
52
- "mypy",
53
- "types-requests",
59
+ "mypy>=1.18",
60
+ "ty>=0.0.1a21",
61
+ "types-requests>=2.32.3",
62
+ { include-group = "testing" },
54
63
  ]
55
64
 
56
65
  [tool.hatch.version]
57
66
  source = "vcs"
58
67
 
68
+ [tool.uv]
69
+ prerelease = "allow"
70
+ preview = true
71
+ required-version = ">=0.8"
72
+
59
73
  [tool.ruff]
60
74
  line-length = 88
61
75
 
@@ -96,9 +110,6 @@ env_list = [
96
110
  "dependencies",
97
111
  "typing",
98
112
  "3.14",
99
- "3.13",
100
- "3.12",
101
- "3.11",
102
113
  ]
103
114
 
104
115
  [tool.tox.env_run_base]
@@ -113,7 +124,10 @@ commands = [ [ "deptry", "." ] ]
113
124
 
114
125
  [tool.tox.env.typing]
115
126
  dependency_groups = [ "testing", "typing" ]
116
- commands = [ [ "mypy", "--strict", { replace = "posargs", default = [ "tap_belvo", "tests" ], extend = true } ] ]
127
+ commands = [
128
+ [ "mypy", "--strict", { replace = "posargs", default = [ "tap_belvo", "tests" ], extend = true } ],
129
+ [ "ty", "check", { replace = "posargs", default = [ "tap_belvo", "tests" ], extend = true } ],
130
+ ]
117
131
 
118
132
  [tool.mypy]
119
133
  enable_error_code = [
@@ -127,12 +141,3 @@ warn_no_return = true
127
141
  warn_unreachable = true
128
142
  warn_unused_configs = true
129
143
  warn_unused_ignores = true
130
-
131
- [[tool.mypy.overrides]]
132
- ignore_missing_imports = true
133
- module = [ "backoff.*" ]
134
-
135
- [tool.uv]
136
- prerelease = "allow"
137
- preview = true
138
- required-version = ">=0.5.19"
@@ -0,0 +1,29 @@
1
+ """Update the OpenAPI schema from the Belvo API.
2
+
3
+ Copyright (c) 2025 Edgar Ramírez-Mondragón
4
+ """ # noqa: INP001
5
+
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ import pathlib
10
+
11
+ import requests
12
+
13
+ OPENAPI_URL = "https://developers.belvo.com/_spec/apis/BelvoOpenApiSpec.json"
14
+ PATH = "tap_belvo/openapi.json"
15
+
16
+
17
+ def main() -> None:
18
+ """Update the OpenAPI schema from the Belvo API."""
19
+ with pathlib.Path(PATH).open("w", encoding="utf-8") as file:
20
+ response = requests.get(OPENAPI_URL, timeout=5)
21
+ response.raise_for_status()
22
+ spec = response.json()
23
+
24
+ content = json.dumps(spec, indent=2) + "\n"
25
+ file.write(content)
26
+
27
+
28
+ if __name__ == "__main__":
29
+ main()
@@ -0,0 +1,123 @@
1
+ """REST client handling, including BelvoStream base class."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from abc import ABCMeta, abstractmethod
7
+ from copy import deepcopy
8
+ from importlib import resources
9
+ from typing import TYPE_CHECKING, Any
10
+ from urllib.parse import ParseResult, parse_qsl
11
+
12
+ from requests.auth import HTTPBasicAuth
13
+ from requests_cache import install_cache
14
+ from singer_sdk import OpenAPISchema, RESTStream
15
+ from singer_sdk.pagination import BaseHATEOASPaginator
16
+
17
+ if sys.version_info >= (3, 12):
18
+ from typing import override
19
+ else:
20
+ from typing_extensions import override
21
+
22
+ if TYPE_CHECKING:
23
+ from requests import Response
24
+ from singer_sdk.helpers.types import Context
25
+
26
+
27
+ PAGE_SIZE = 1000
28
+ OPENAPI = OpenAPISchema(resources.files("tap_belvo") / "openapi.json")
29
+
30
+ install_cache("tap_belvo_cache", backend="sqlite", expire_after=3600)
31
+
32
+
33
+ def _handle_schema_nullable(schema: dict[str, Any]) -> dict[str, Any]:
34
+ """Resolve x-nullable properties to standard JSON Schema nullable type.
35
+
36
+ Args:
37
+ schema: A JSON Schema dictionary.
38
+
39
+ Returns:
40
+ A new JSON Schema dictionary with 'x-nullable' resolved to [<type>, "null"].
41
+ """
42
+ result = deepcopy(schema)
43
+
44
+ if "object" in result["type"]:
45
+ for prop, prop_schema in result.get("properties", {}).items():
46
+ prop_type: str | list[str] = prop_schema.get("type", [])
47
+ types = [prop_type] if isinstance(prop_type, str) else prop_type
48
+ nullable: bool = prop_schema.get("nullable", False)
49
+
50
+ if nullable:
51
+ prop_schema["type"] = [*types, "null"]
52
+
53
+ result["properties"][prop] = _handle_schema_nullable(prop_schema)
54
+
55
+ elif "array" in result["type"]:
56
+ result["items"] = _handle_schema_nullable(result["items"])
57
+
58
+ if "enum" in result and None not in result["enum"]:
59
+ result["enum"].append(None)
60
+
61
+ return result
62
+
63
+
64
+ class BelvoPaginator(BaseHATEOASPaginator):
65
+ """Belvo API paginator class."""
66
+
67
+ @override
68
+ def get_next_url(self, response: Response) -> str | None:
69
+ """Get the next URL from the response."""
70
+ return response.json().get("next") # type: ignore[no-any-return]
71
+
72
+
73
+ class BelvoStream(RESTStream[ParseResult], metaclass=ABCMeta):
74
+ """Belvo stream class."""
75
+
76
+ records_jsonpath = "$.results[*]" # Or override `parse_response`.
77
+
78
+ @override
79
+ @property
80
+ def url_base(self) -> str:
81
+ return self.config["base_url"] # type: ignore[no-any-return]
82
+
83
+ @override
84
+ @property
85
+ def authenticator(self) -> HTTPBasicAuth:
86
+ return HTTPBasicAuth(self.config["secret_id"], self.config["password"])
87
+
88
+ @override
89
+ def get_new_paginator(self) -> BelvoPaginator:
90
+ return BelvoPaginator()
91
+
92
+ @override
93
+ def get_url_params(
94
+ self,
95
+ context: Context | None,
96
+ next_page_token: ParseResult | None,
97
+ ) -> dict[str, Any]:
98
+ """Get URL query parameters."""
99
+ params: dict[str, Any] = {
100
+ "page": 1,
101
+ "page_size": PAGE_SIZE,
102
+ }
103
+
104
+ if next_page_token:
105
+ params.update(parse_qsl(next_page_token.query))
106
+
107
+ if (
108
+ self.replication_key # Only if the stream is running incrementally
109
+ and (start_date := self.get_starting_timestamp(context))
110
+ ):
111
+ params[f"{self.replication_key}__gte"] = start_date.date().isoformat()
112
+
113
+ return params
114
+
115
+ @override
116
+ @property
117
+ def schema(self) -> dict[str, Any]:
118
+ return _handle_schema_nullable(OPENAPI.fetch_schema(self.openapi_ref))
119
+
120
+ @property
121
+ @abstractmethod
122
+ def openapi_ref(self) -> str:
123
+ """OpenAPI component name for this stream."""