reconify-python 1.0.0__tar.gz → 2.0.0__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.
Files changed (37) hide show
  1. reconify_python-2.0.0/.openapi-contract.json +4 -0
  2. {reconify_python-1.0.0 → reconify_python-2.0.0}/PKG-INFO +8 -8
  3. {reconify_python-1.0.0 → reconify_python-2.0.0}/README.md +7 -7
  4. {reconify_python-1.0.0 → reconify_python-2.0.0}/UPGRADING.md +6 -6
  5. {reconify_python-1.0.0 → reconify_python-2.0.0}/pyproject.toml +1 -1
  6. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/client.py +2 -2
  7. reconify_python-2.0.0/src/reconify/resources/__init__.py +71 -0
  8. {reconify_python-1.0.0 → reconify_python-2.0.0}/tests/test_client.py +5 -5
  9. {reconify_python-1.0.0 → reconify_python-2.0.0}/tests/test_openapi_coverage.py +5 -5
  10. reconify_python-1.0.0/.openapi-contract.json +0 -4
  11. reconify_python-1.0.0/src/reconify/resources/__init__.py +0 -54
  12. {reconify_python-1.0.0 → reconify_python-2.0.0}/.bumpversion.cfg +0 -0
  13. {reconify_python-1.0.0 → reconify_python-2.0.0}/.conductor/settings.toml +0 -0
  14. {reconify_python-1.0.0 → reconify_python-2.0.0}/.github/workflows/ci.yml +0 -0
  15. {reconify_python-1.0.0 → reconify_python-2.0.0}/.github/workflows/release.yml +0 -0
  16. {reconify_python-1.0.0 → reconify_python-2.0.0}/.github/workflows/sync-contract.yml +0 -0
  17. {reconify_python-1.0.0 → reconify_python-2.0.0}/.gitignore +0 -0
  18. {reconify_python-1.0.0 → reconify_python-2.0.0}/AGENTS.md +0 -0
  19. {reconify_python-1.0.0 → reconify_python-2.0.0}/CHANGELOG.md +0 -0
  20. {reconify_python-1.0.0 → reconify_python-2.0.0}/LICENSE +0 -0
  21. {reconify_python-1.0.0 → reconify_python-2.0.0}/context7.json +0 -0
  22. {reconify_python-1.0.0 → reconify_python-2.0.0}/reports/api-error-data-report.md +0 -0
  23. {reconify_python-1.0.0 → reconify_python-2.0.0}/scripts/fetch_contract.py +0 -0
  24. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/__init__.py +0 -0
  25. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/errors.py +0 -0
  26. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/models.py +0 -0
  27. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/pagination.py +0 -0
  28. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/py.typed +0 -0
  29. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/resources/base.py +0 -0
  30. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/resources/events.py +0 -0
  31. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/resources/ingestion.py +0 -0
  32. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/resources/issues.py +0 -0
  33. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/resources/metadata.py +0 -0
  34. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/resources/organization.py +0 -0
  35. {reconify_python-1.0.0 → reconify_python-2.0.0}/src/reconify/transport.py +0 -0
  36. {reconify_python-1.0.0 → reconify_python-2.0.0}/tests/test_pagination.py +0 -0
  37. {reconify_python-1.0.0 → reconify_python-2.0.0}/uv.lock +0 -0
@@ -0,0 +1,4 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "sha256": "5cccdf920a62fb325c7fcf9410728889858d156831a78760e41e3f98774db7f2"
4
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: reconify-python
3
- Version: 1.0.0
3
+ Version: 2.0.0
4
4
  Summary: Typed Python client for the Reconify Public API
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -18,7 +18,7 @@ Description-Content-Type: text/markdown
18
18
 
19
19
  # Reconify Python SDK
20
20
 
21
- Typed synchronous and asynchronous clients for the public Reconify v1 API.
21
+ Typed synchronous and asynchronous clients for the public Reconify v2 API.
22
22
 
23
23
  ## Installation and quickstart
24
24
 
@@ -36,8 +36,8 @@ with Reconify(api_key="rk_...") as client:
36
36
  ```
37
37
 
38
38
  The API key may also come from `RECONIFY_API_KEY`. The default endpoint is
39
- `https://api.reconifyhq.com/v1`. `RECONIFY_API_URL` or `base_url` can select a
40
- staging or self-hosted endpoint, and URLs with or without `/v1` are accepted.
39
+ `https://api.reconifyhq.com/v2`. `RECONIFY_API_URL` or `base_url` can select a
40
+ staging or self-hosted endpoint, and URLs with or without `/v2` are accepted.
41
41
 
42
42
  ## Public resources
43
43
 
@@ -74,11 +74,11 @@ The default source is the public manifest at
74
74
  set `RECONIFY_OPENAPI_SPEC` to an explicit OpenAPI JSON file. The SDK never
75
75
  depends on another checkout or an absolute workspace path.
76
76
 
77
- ## Migration to 1.0.0
77
+ ## Migration to 2.0.0
78
78
 
79
- Version `1.0.0` targets the current monitoring and issue-investigation API. The
80
- former ledger, wallet, setup, search, alert, and reconciliation methods are
81
- removed because they are not part of the public contract. See
79
+ Version `2.0.0` targets the v2 monitoring and issue-investigation API at `/v2`.
80
+ Generated operation IDs use stable `resource_action` identifiers while the
81
+ Python resource methods retain their snake_case names. See
82
82
  [UPGRADING.md](UPGRADING.md).
83
83
 
84
84
  ## Build and release
@@ -1,6 +1,6 @@
1
1
  # Reconify Python SDK
2
2
 
3
- Typed synchronous and asynchronous clients for the public Reconify v1 API.
3
+ Typed synchronous and asynchronous clients for the public Reconify v2 API.
4
4
 
5
5
  ## Installation and quickstart
6
6
 
@@ -18,8 +18,8 @@ with Reconify(api_key="rk_...") as client:
18
18
  ```
19
19
 
20
20
  The API key may also come from `RECONIFY_API_KEY`. The default endpoint is
21
- `https://api.reconifyhq.com/v1`. `RECONIFY_API_URL` or `base_url` can select a
22
- staging or self-hosted endpoint, and URLs with or without `/v1` are accepted.
21
+ `https://api.reconifyhq.com/v2`. `RECONIFY_API_URL` or `base_url` can select a
22
+ staging or self-hosted endpoint, and URLs with or without `/v2` are accepted.
23
23
 
24
24
  ## Public resources
25
25
 
@@ -56,11 +56,11 @@ The default source is the public manifest at
56
56
  set `RECONIFY_OPENAPI_SPEC` to an explicit OpenAPI JSON file. The SDK never
57
57
  depends on another checkout or an absolute workspace path.
58
58
 
59
- ## Migration to 1.0.0
59
+ ## Migration to 2.0.0
60
60
 
61
- Version `1.0.0` targets the current monitoring and issue-investigation API. The
62
- former ledger, wallet, setup, search, alert, and reconciliation methods are
63
- removed because they are not part of the public contract. See
61
+ Version `2.0.0` targets the v2 monitoring and issue-investigation API at `/v2`.
62
+ Generated operation IDs use stable `resource_action` identifiers while the
63
+ Python resource methods retain their snake_case names. See
64
64
  [UPGRADING.md](UPGRADING.md).
65
65
 
66
66
  ## Build and release
@@ -1,8 +1,8 @@
1
- # Upgrading to 1.0.0
1
+ # Upgrading to 2.0.0
2
2
 
3
- The 1.0.0 client is rebuilt against the current public Reconify v1 contract.
4
- It removes methods that described private or retired ledger, wallet, setup,
5
- search, alert, and reconciliation routes.
3
+ The 2.0.0 client targets the public Reconify v2 contract at `/v2`.
4
+ Generated operation IDs now use stable `resource_action` identifiers. The
5
+ Python resource methods keep their existing snake_case names.
6
6
 
7
7
  Use these resources:
8
8
 
@@ -13,5 +13,5 @@ Use these resources:
13
13
  - organization: organization and member reads
14
14
 
15
15
  Python models use snake_case fields and preserve unknown enum values through
16
- tolerant string enums. Regenerate or refresh the models after downloading a
17
- new public contract version.
16
+ tolerant string enums. Existing v1 clients can continue using `/v1`; v2
17
+ clients must use the v2 artifact and endpoint.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "reconify-python"
7
- version = "1.0.0"
7
+ version = "2.0.0"
8
8
  description = "Typed Python client for the Reconify Public API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -26,12 +26,12 @@ from .resources import (
26
26
  )
27
27
  from .transport import AsyncTransport, RetryConfig, SyncTransport
28
28
 
29
- DEFAULT_BASE_URL = "https://api.reconifyhq.com/v1"
29
+ DEFAULT_BASE_URL = "https://api.reconifyhq.com/v2"
30
30
 
31
31
 
32
32
  def _normalize_base_url(base_url: str | None) -> str:
33
33
  value = (base_url or os.getenv("RECONIFY_API_URL") or DEFAULT_BASE_URL).rstrip("/")
34
- return value if value.endswith("/v1") else f"{value}/v1"
34
+ return value if value.endswith("/v2") else f"{value}/v2"
35
35
 
36
36
 
37
37
  def _api_key(api_key: str | None) -> str:
@@ -0,0 +1,71 @@
1
+ """Resource clients grouped by the current public API surface."""
2
+
3
+ from .events import AsyncEvents, Events
4
+ from .ingestion import AsyncIngestion, Ingestion
5
+ from .issues import AsyncIssues, Issues
6
+ from .metadata import AsyncMetadata, Metadata
7
+ from .organization import AsyncOrganization, Organization
8
+
9
+ SYNC_RESOURCE_CLASSES = {
10
+ "metadata": Metadata,
11
+ "events": Events,
12
+ "ingestion": Ingestion,
13
+ "issues": Issues,
14
+ "organization": Organization,
15
+ }
16
+ ASYNC_RESOURCE_CLASSES = {
17
+ "metadata": AsyncMetadata,
18
+ "events": AsyncEvents,
19
+ "ingestion": AsyncIngestion,
20
+ "issues": AsyncIssues,
21
+ "organization": AsyncOrganization,
22
+ }
23
+
24
+ OPERATION_SPECS = {
25
+ "api_info_get": ("metadata", "GET", "/"),
26
+ "events_list": ("events", "GET", "/events"),
27
+ "events_ingest": ("ingestion", "POST", "/events"),
28
+ "events_get": ("events", "GET", "/events/{event_id}"),
29
+ "health_get": ("metadata", "GET", "/health"),
30
+ "issues_list": ("issues", "GET", "/issues"),
31
+ "issues_get": ("issues", "GET", "/issues/{issue_id}"),
32
+ "issues_assign": ("issues", "PATCH", "/issues/{issue_id}"),
33
+ "issues_list_events": ("events", "GET", "/issues/{issue_id}/events"),
34
+ "issues_list_notes": ("issues", "GET", "/issues/{issue_id}/notes"),
35
+ "issues_add_note": ("issues", "POST", "/issues/{issue_id}/notes"),
36
+ "organization_get": ("organization", "GET", "/organization"),
37
+ "organization_list_members": ("organization", "GET", "/organization/members"),
38
+ }
39
+
40
+ OPERATION_METHODS = {
41
+ "api_info_get": "get_api_info",
42
+ "events_list": "list_events",
43
+ "events_ingest": "ingest_monitoring_events",
44
+ "events_get": "get_event",
45
+ "health_get": "get_health",
46
+ "issues_list": "list_issues",
47
+ "issues_get": "get_issue",
48
+ "issues_assign": "update_issue",
49
+ "issues_list_events": "list_issue_events",
50
+ "issues_list_notes": "list_issue_notes",
51
+ "issues_add_note": "add_issue_note",
52
+ "organization_get": "get_organization",
53
+ "organization_list_members": "list_organization_members",
54
+ }
55
+
56
+ __all__ = [
57
+ "AsyncEvents",
58
+ "AsyncIngestion",
59
+ "AsyncIssues",
60
+ "AsyncMetadata",
61
+ "AsyncOrganization",
62
+ "Events",
63
+ "Ingestion",
64
+ "Issues",
65
+ "Metadata",
66
+ "Organization",
67
+ "ASYNC_RESOURCE_CLASSES",
68
+ "OPERATION_SPECS",
69
+ "OPERATION_METHODS",
70
+ "SYNC_RESOURCE_CLASSES",
71
+ ]
@@ -49,20 +49,20 @@ def test_base_url_and_request_contract() -> None:
49
49
 
50
50
  with Reconify(
51
51
  "rk_test",
52
- base_url="http://localhost:3002/v1/",
52
+ base_url="http://localhost:3002/v2/",
53
53
  request_id="caller-id",
54
54
  http_client=httpx.Client(transport=httpx.MockTransport(handler)),
55
55
  ) as client:
56
56
  result = client.events.get_event("event id")
57
57
 
58
58
  assert result.id == "evt_1"
59
- assert str(requests[0].url) == "http://localhost:3002/v1/events/event%20id"
59
+ assert str(requests[0].url) == "http://localhost:3002/v2/events/event%20id"
60
60
  assert requests[0].headers["Authorization"] == "Bearer rk_test"
61
61
  assert requests[0].headers["X-Request-ID"] == "caller-id"
62
62
 
63
63
 
64
64
  def test_base_url_and_key_can_come_from_environment(monkeypatch: pytest.MonkeyPatch) -> None:
65
- monkeypatch.setenv("RECONIFY_API_URL", "http://api.test/v1/")
65
+ monkeypatch.setenv("RECONIFY_API_URL", "http://api.test/v2/")
66
66
  monkeypatch.setenv("RECONIFY_API_KEY", "rk_environment")
67
67
  requests: list[httpx.Request] = []
68
68
 
@@ -73,7 +73,7 @@ def test_base_url_and_key_can_come_from_environment(monkeypatch: pytest.MonkeyPa
73
73
  with Reconify(http_client=httpx.Client(transport=httpx.MockTransport(handler))) as client:
74
74
  client.metadata.get_health()
75
75
 
76
- assert str(requests[0].url) == "http://api.test/v1/health"
76
+ assert str(requests[0].url) == "http://api.test/v2/health"
77
77
  assert requests[0].headers["Authorization"] == "Bearer rk_environment"
78
78
 
79
79
 
@@ -104,7 +104,7 @@ def test_ingestion_serializes_typed_body() -> None:
104
104
  client.ingestion.ingest_monitoring_events(body)
105
105
 
106
106
  assert json.loads(requests[0].content)["events"][0]["entity_id"] == "wallet-1"
107
- assert requests[0].url.path == "/v1/events"
107
+ assert requests[0].url.path == "/v2/events"
108
108
 
109
109
 
110
110
  def test_note_idempotency_header_and_issue_assignment() -> None:
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
 
7
7
  import pytest
8
8
 
9
- from reconify.resources import OPERATION_SPECS, SYNC_RESOURCE_CLASSES
9
+ from reconify.resources import OPERATION_METHODS, OPERATION_SPECS, SYNC_RESOURCE_CLASSES
10
10
 
11
11
 
12
12
  def _openapi_path() -> Path | None:
@@ -23,7 +23,7 @@ def _operations() -> list[tuple[str, str, str]]:
23
23
  pytest.skip("OpenAPI source is not available; run scripts/fetch_contract.py")
24
24
  document = json.loads(path.read_text())
25
25
  return [
26
- (operation["operationId"], method.upper(), route.removeprefix("/v1") or "/")
26
+ (operation["operationId"], method.upper(), route.removeprefix("/v2") or "/")
27
27
  for route, methods in document["paths"].items()
28
28
  for method, operation in methods.items()
29
29
  if method.lower() in {"get", "post", "put", "patch", "delete"}
@@ -44,9 +44,9 @@ def test_every_openapi_operation_has_a_public_method() -> None:
44
44
  assert len({operation_id for operation_id, _, _ in operations}) == len(operations)
45
45
  assert len(OPERATION_SPECS) == len(operations)
46
46
  for operation_id, verb, route in operations:
47
- method_name = operation_id.replace("-", "_")
48
- assert method_name in OPERATION_SPECS, f"Missing SDK contract for {operation_id}"
49
- group, registered_verb, registered_route = OPERATION_SPECS[method_name]
47
+ method_name = OPERATION_METHODS[operation_id]
48
+ assert operation_id in OPERATION_SPECS, f"Missing SDK contract for {operation_id}"
49
+ group, registered_verb, registered_route = OPERATION_SPECS[operation_id]
50
50
  assert registered_route == route
51
51
  assert registered_verb == verb
52
52
  assert hasattr(SYNC_RESOURCE_CLASSES[group], method_name), (
@@ -1,4 +0,0 @@
1
- {
2
- "version": "1.0.0",
3
- "sha256": "04170b7eda8a7bacef6591a7132b9a5cbdb62963af9ca6260e1948c1ee8233c1"
4
- }
@@ -1,54 +0,0 @@
1
- """Resource clients grouped by the current public API surface."""
2
-
3
- from .events import AsyncEvents, Events
4
- from .ingestion import AsyncIngestion, Ingestion
5
- from .issues import AsyncIssues, Issues
6
- from .metadata import AsyncMetadata, Metadata
7
- from .organization import AsyncOrganization, Organization
8
-
9
- SYNC_RESOURCE_CLASSES = {
10
- "metadata": Metadata,
11
- "events": Events,
12
- "ingestion": Ingestion,
13
- "issues": Issues,
14
- "organization": Organization,
15
- }
16
- ASYNC_RESOURCE_CLASSES = {
17
- "metadata": AsyncMetadata,
18
- "events": AsyncEvents,
19
- "ingestion": AsyncIngestion,
20
- "issues": AsyncIssues,
21
- "organization": AsyncOrganization,
22
- }
23
-
24
- OPERATION_SPECS = {
25
- "get_api_info": ("metadata", "GET", "/"),
26
- "list_events": ("events", "GET", "/events"),
27
- "ingest_monitoring_events": ("ingestion", "POST", "/events"),
28
- "get_event": ("events", "GET", "/events/{event_id}"),
29
- "get_health": ("metadata", "GET", "/health"),
30
- "list_issues": ("issues", "GET", "/issues"),
31
- "get_issue": ("issues", "GET", "/issues/{issue_id}"),
32
- "update_issue": ("issues", "PATCH", "/issues/{issue_id}"),
33
- "list_issue_events": ("events", "GET", "/issues/{issue_id}/events"),
34
- "list_issue_notes": ("issues", "GET", "/issues/{issue_id}/notes"),
35
- "add_issue_note": ("issues", "POST", "/issues/{issue_id}/notes"),
36
- "get_organization": ("organization", "GET", "/organization"),
37
- "list_organization_members": ("organization", "GET", "/organization/members"),
38
- }
39
-
40
- __all__ = [
41
- "AsyncEvents",
42
- "AsyncIngestion",
43
- "AsyncIssues",
44
- "AsyncMetadata",
45
- "AsyncOrganization",
46
- "Events",
47
- "Ingestion",
48
- "Issues",
49
- "Metadata",
50
- "Organization",
51
- "ASYNC_RESOURCE_CLASSES",
52
- "OPERATION_SPECS",
53
- "SYNC_RESOURCE_CLASSES",
54
- ]
File without changes
File without changes