diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index 95bfec6..88e1845 100644 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,19 +1,19 @@ lockVersion: 2.0.0 id: 0c8d1667-2710-4860-aee3-7d221dbc0a60 management: - docChecksum: 05d24e44cc963ad134c02540195ca422 + docChecksum: 9b05a214555fdbadc6d870c22f2384d3 docVersion: 1.0.0 - speakeasyVersion: 1.677.0 - generationVersion: 2.785.0 - releaseVersion: 0.2.0 - configChecksum: 6b79cbf841b85a547fe9795fcb2c326b + speakeasyVersion: 1.680.0 + generationVersion: 2.788.4 + releaseVersion: 0.2.1 + configChecksum: 78fee749a5967b9f164577499699114b repoURL: https://github.com/kombohq/python-sdk.git installationURL: https://github.com/kombohq/python-sdk.git published: true persistentEdits: - generation_id: ae5562fa-3dcc-4489-bf9b-010afeb0cf49 - pristine_commit_hash: fb531bd6e199ddb5a0f532d0f4cfa4262ff7b39c - pristine_tree_hash: 6817e3f141f0cf13af05bd85abd48d1051e9b7c2 + generation_id: ca3d4da3-b6bd-484c-aeb7-292b9ee639c5 + pristine_commit_hash: e182dd3a6b9652b65c44405f0d4bb1f6f614863f + pristine_tree_hash: dd4ebc809a2cdb75f2ca43fa6327421c4c52d67c features: python: additionalDependencies: 1.0.0 @@ -2444,8 +2444,8 @@ trackedFiles: pristine_git_object: 19ef9d65384acaaefd7c043bbaf5e3ed9eb883fb docs/models/postatscandidatesrequestbodylocation.md: id: 20384c803736 - last_write_checksum: sha1:aed3b9084a913dae570fa686b75d520a1f676e13 - pristine_git_object: a328638c60f967868f2cfcb3c873721c2007d702 + last_write_checksum: sha1:6977ae056f9e44032fdcae1301cafd571b066574 + pristine_git_object: e338221402ac66492c3ed21c9ce932b7dcd9b141 docs/models/postatscandidatesrequestbodylocationdata.md: id: 43ae3df4983f last_write_checksum: sha1:877d1837fb851c30cad36daa8257813a92e763e5 @@ -3004,8 +3004,8 @@ trackedFiles: pristine_git_object: 55d28b36da3ac30df1b94c85d1b9051b28584371 docs/models/postatsjobsjobidapplicationsrequestbodylocation.md: id: 5b76dca1e5cb - last_write_checksum: sha1:dc7937f4f9ff294f59d599e3a2b1e33cc5cfc888 - pristine_git_object: 5f8302b1316c5350ee72840a218055e4e435dd9e + last_write_checksum: sha1:4e408c1867eb58a15a285627d607b267c05a5530 + pristine_git_object: 2707848357c4507ab792c77fc1882cd241f614a4 docs/models/postatsjobsjobidapplicationsrequestbodylocationdata.md: id: 1bd36ca9a246 last_write_checksum: sha1:6be2eb25d2156f01df267d47fbed8290d152da54 @@ -3924,8 +3924,8 @@ trackedFiles: pristine_git_object: 813f107e12643bbfadeffac84a7deee6b43b656a docs/sdks/general/README.md: id: ce0ada6fa2e1 - last_write_checksum: sha1:9db8d1882e2486079dc9b6b56c616f02595732f6 - pristine_git_object: 906ec0c0d9d1ed445e40a01d53d5b9043ac9c120 + last_write_checksum: sha1:46a674c5a3ce3b8895731c5b1e5a563725f23224 + pristine_git_object: 23bc8f11e8084f7bc233d8d9eec964169b71bf4d docs/sdks/hris/README.md: id: 0baa868a038c last_write_checksum: sha1:694a328b1d895e2964ad2c367ec6af0e23382461 @@ -3940,8 +3940,8 @@ trackedFiles: pristine_git_object: 000d9930f874a1b01cda74f245fc00e1b6eae49e pyproject.toml: id: 5d07e7d72637 - last_write_checksum: sha1:6a217832ff3a8cd76bd991eb9e6c35bfd631975f - pristine_git_object: 348fd3894c8facd9dd43a7817d94aa641522fb17 + last_write_checksum: sha1:01fe6b1c0d1ff666d9af3cc36599807ab74e951c + pristine_git_object: 55a6fa6caa7b78d044c2c7726b2f63c700018c95 scripts/prepare_readme.py: id: e0c5957a6035 last_write_checksum: sha1:b6ad6f0515a952d7cb052befddb6a117d4f627d8 @@ -3968,16 +3968,16 @@ trackedFiles: pristine_git_object: e589e541e5fc70d336de44780721f301d754ed72 src/kombo/_version.py: id: 45cf440ad1df - last_write_checksum: sha1:3f141bbffcdc5498141628c7668a3d0dccf8f206 - pristine_git_object: 2b7d525096a6845a6920c5790a7a68503e38af7c + last_write_checksum: sha1:d11e33855824ad0b03aa6a12ced2edced79d7edf + pristine_git_object: d94a43cc9a5f4a37b0438f119a199c306e56bd5d src/kombo/assessment.py: id: aec6e8a7589f - last_write_checksum: sha1:3a68d6284f98e1b553804b93ec8a7db760d8ae41 - pristine_git_object: 54d4009649d16b45840fa4c7b0f208812b4eea9b + last_write_checksum: sha1:1406e437a55541ba21ab8a76696b092238678670 + pristine_git_object: cefc8da0d5425357182f67a16b6d2a3a1e1e32cf src/kombo/ats.py: id: a9839762f5c3 - last_write_checksum: sha1:96473c0bb9d016467675a04ce576f25b3c356152 - pristine_git_object: 82ddd864a0d85385e06ab61d99cb98717822b1f2 + last_write_checksum: sha1:b656378e665e7406a5e9663351677afaea5ed344 + pristine_git_object: d0789a0e1d08aa09e10f1bdbd961a227ee6733d8 src/kombo/basesdk.py: id: 8f30a0bd36ff last_write_checksum: sha1:0161e74290bb9dcc311a8481865ab2b2e5908244 @@ -4020,12 +4020,12 @@ trackedFiles: pristine_git_object: 16ac2977e80b6eabe4020aa8a21dd28e83032f9a src/kombo/general.py: id: 107ecb9f3651 - last_write_checksum: sha1:9f6daf9cab3bc1ce314723d790211e936b70a1a4 - pristine_git_object: 317ba19598b436afc6c54cdc00d93e0292fe5e2a + last_write_checksum: sha1:28f48e3c8ce393afe7adb10d94b6860b1988a93c + pristine_git_object: f0c37dd48dbd2ec15dcc43310b5e4a914132aeea src/kombo/hris.py: id: 9ba1b37ab10a - last_write_checksum: sha1:3b4ddc578003b28b622dc60f024867351373e045 - pristine_git_object: 0492d44b79e0911316f87a635c187e95f9b71e7b + last_write_checksum: sha1:80635a7d13e9629da01ad74820c92611acf41e31 + pristine_git_object: 01114f2250c5493f9a0077e055f51383b7079594 src/kombo/httpclient.py: id: 4e74c7821304 last_write_checksum: sha1:5e55338d6ee9f01ab648cad4380201a8a3da7dd7 @@ -4480,8 +4480,8 @@ trackedFiles: pristine_git_object: 429ec6552c1b09ae271e71aa779dfbb4c067d0dc src/kombo/models/postatscandidatesrequestbody.py: id: ac6e98f043e9 - last_write_checksum: sha1:4ad5ea6da4edc8f931441f072e10ac5aecaf09c0 - pristine_git_object: 17411292e5b37e90c1677b8c105d47f28b9d2273 + last_write_checksum: sha1:67ce682473edd36345ffae99bc9b48027b4fdff0 + pristine_git_object: 0f0a79a3b665544bc09253f78380f0432d0e0daf src/kombo/models/postatsimporttrackedapplicationop.py: id: d36d9960022a last_write_checksum: sha1:02cfd2ebff78e9c688c49b67ff2ab1b657356e78 @@ -4504,8 +4504,8 @@ trackedFiles: pristine_git_object: 10df4096c92c64540e275dd5ad77ea7bf47dfd69 src/kombo/models/postatsjobsjobidapplicationsrequestbody.py: id: a451a5e6b515 - last_write_checksum: sha1:ee388a2fcd85cf6e8afef3d1b0069b1fef004cd4 - pristine_git_object: c184be17c7f104e81026ac9ca2739ae545bb12d5 + last_write_checksum: sha1:01eca49c2c7f15b4ed107d79220d52b0723a49a8 + pristine_git_object: 1f137a163496c52029c62f2f5dcf134a636ae110 src/kombo/models/postconnectcreatelinkpositiveresponse.py: id: a3271239a82f last_write_checksum: sha1:db7ae512920d425bba4b82473a1c1a17c448f7ce @@ -7088,7 +7088,7 @@ examples: default: application/json: {"status": "error", "error": {"code": null, "title": null, "message": "The message is always in the response.", "log_url": null}} examplesVersion: 1.0.2 -releaseNotes: "## Python SDK Changes:\n* `kombo.hris.create_employee_with_form()`: `response.data` **Changed** **Breaking** :warning:\n* `kombo.general.get_integration_details()`: `response.data` **Changed**\n* `kombo.general.get_tools()`: \n * `request.category` **Changed**\n * `response.data.tools.[].coverage.write_actions.[].fields` **Added**\n* `kombo.ats.create_candidate()`: \n * `request.remote_fields.pinpoint` **Added**\n* `kombo.ats.create_application()`: \n * `request.remote_fields.pinpoint` **Added**\n" +releaseNotes: "## Python SDK Changes:\n* `kombo.ats.create_candidate()`: \n * `request.candidate.location.street_1` **Added**\n* `kombo.ats.create_application()`: \n * `request.candidate.location.street_1` **Added**\n" generatedFiles: - .gitattributes - .vscode/settings.json diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index 9791cea..9314997 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -28,7 +28,7 @@ generation: generateNewTests: true skipResponseBodyAssertions: false python: - version: 0.2.0 + version: 0.2.1 additionalDependencies: dev: pytest: ">=8.0.0" diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index ed987d3..c00b3a3 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -1,21 +1,21 @@ -speakeasyVersion: 1.677.0 +speakeasyVersion: 1.680.0 sources: kombo-prepared-spec: sourceNamespace: kombo-api - sourceRevisionDigest: sha256:0daaa815fa1603748afee40ea8af72ce4f7636cd041a2f4ba49b498af0133383 - sourceBlobDigest: sha256:1dd50ff7d0ffb08a8ef96a19b414d775574983cf16109b151fa997dca37f95b2 + sourceRevisionDigest: sha256:fcf67d7c843299a5593a23052142af93f881f9e7cbe671fbd2ced6b3cd1ec248 + sourceBlobDigest: sha256:2eca6082deff96b56739183cd7c1ae8132b75b0e89de922a52af6bbc37202c38 tags: - latest - - speakeasy-sdk-regen-1764030105 + - speakeasy-sdk-regen-1765904393 - 1.0.0 targets: kombo-python: source: kombo-prepared-spec sourceNamespace: kombo-api - sourceRevisionDigest: sha256:0daaa815fa1603748afee40ea8af72ce4f7636cd041a2f4ba49b498af0133383 - sourceBlobDigest: sha256:1dd50ff7d0ffb08a8ef96a19b414d775574983cf16109b151fa997dca37f95b2 + sourceRevisionDigest: sha256:fcf67d7c843299a5593a23052142af93f881f9e7cbe671fbd2ced6b3cd1ec248 + sourceBlobDigest: sha256:2eca6082deff96b56739183cd7c1ae8132b75b0e89de922a52af6bbc37202c38 codeSamplesNamespace: kombo-api-python-code-samples - codeSamplesRevisionDigest: sha256:63ec7ec7f20895c5a8085668be08ea17370b4e8cd48befc2a8244349b32759e3 + codeSamplesRevisionDigest: sha256:8ab52863c5c298fb2da431959c9d9206d459e6528d7e2f937b13b3ffd1f99b5e workflow: workflowVersion: 1.0.0 speakeasyVersion: latest diff --git a/RELEASES.md b/RELEASES.md index 7596adf..952f570 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -68,4 +68,14 @@ Based on: ### Generated - [python v0.2.0] . ### Releases -- [PyPI v0.2.0] https://pypi.org/project/kombo/0.2.0 - . \ No newline at end of file +- [PyPI v0.2.0] https://pypi.org/project/kombo/0.2.0 - . + +## 2025-12-18 00:20:23 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.680.0 (2.788.4) https://github.com/speakeasy-api/speakeasy +### Generated +- [python v0.2.1] . +### Releases +- [PyPI v0.2.1] https://pypi.org/project/kombo/0.2.1 - . \ No newline at end of file diff --git a/docs/models/postatscandidatesrequestbodylocation.md b/docs/models/postatscandidatesrequestbodylocation.md index a328638..e338221 100644 --- a/docs/models/postatscandidatesrequestbodylocation.md +++ b/docs/models/postatscandidatesrequestbodylocation.md @@ -10,4 +10,5 @@ The location of the candidate. | `city` | *Optional[str]* | :heavy_minus_sign: | N/A | | `country` | *str* | :heavy_check_mark: | The uppercase two-letter ISO country (e.g., `DE`). For systems that use codes in formats other than `ISO 3166-1 alpha-2`, Kombo transforms the ISO Codes to the appropriate value. | | `state` | *Optional[str]* | :heavy_minus_sign: | N/A | +| `street_1` | *Optional[str]* | :heavy_minus_sign: | N/A | | `zip_code` | *Optional[str]* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/models/postatsjobsjobidapplicationsrequestbodylocation.md b/docs/models/postatsjobsjobidapplicationsrequestbodylocation.md index 5f8302b..2707848 100644 --- a/docs/models/postatsjobsjobidapplicationsrequestbodylocation.md +++ b/docs/models/postatsjobsjobidapplicationsrequestbodylocation.md @@ -10,4 +10,5 @@ The location of the candidate. | `city` | *Optional[str]* | :heavy_minus_sign: | N/A | | `country` | *str* | :heavy_check_mark: | The uppercase two-letter ISO country (e.g., `DE`). For systems that use codes in formats other than `ISO 3166-1 alpha-2`, Kombo transforms the ISO Codes to the appropriate value. | | `state` | *Optional[str]* | :heavy_minus_sign: | N/A | +| `street_1` | *Optional[str]* | :heavy_minus_sign: | N/A | | `zip_code` | *Optional[str]* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/sdks/general/README.md b/docs/sdks/general/README.md index 906ec0c..23bc8f1 100644 --- a/docs/sdks/general/README.md +++ b/docs/sdks/general/README.md @@ -157,6 +157,7 @@ To get started, please pick the relevant API (some tools provide multiple to due |HeavenHR|`heavenhr/v2`|[HeavenHR API](https://api.heavenhr.com/). We automatically authenticate all requests using the provided credentials and use `https://api.heavenhr.com/api/v2` as the base URL.| |HiBob|`hibob/v1`|[HibBob's v1 API](https://apidocs.hibob.com/reference/get_people). We automatically authenticate all requests using the service user credentials (or, for old integrations, the API key) and use `https://api.hibob.com/v1` as the base URL.| |HiBob|`hibob/hire`|[HiBob's Hire API](https://apidocs.hibob.com/docs/how-to-integrate-with-ats-hire-api). We automatically authenticate all requests using the hire service user credentials. The base URL is configured during the integration setup.| +|HiBob|`hibob/docs`|This passthrough is only used for fetching employee documents in Hibob. It is present as a workaround while we are working on a new endpoint for fetching documents in HRIS. It should not be used for any other purpose. We automatically authenticate all requests using the service user credentials and use `https://app.hibob.com/api/docs/employees/` as the base URL.| |Softgarden|`softgarden/frontend-v3`|[Softgarden's Frontend API v3](https://dev.softgarden.de/frontend-v3/. We automatically authenticate all requests and use 'https://api.softgarden.io/api/rest' as base URL.| |Softgarden|`softgarden/apply-api`|[Softgarden's Apply API](https://dev.softgarden.de/apply-api/). We automatically authenticate all requests and use 'https://jobdb.softgarden.de/jobdb/public' as base URL.| |Cezanne HR|`cezannehr/dataservice`|[CezanneHR's v7 dataservice API](https://api.cezannehr.com/).We automatically authenticate all requests and use the base URL `https://subdomain.cezanneondemand.com/cezanneondemand/v7/dataservice.svc`| @@ -228,7 +229,8 @@ To get started, please pick the relevant API (some tools provide multiple to due |InRecruiting by Zucchetti|`inrecruiting/default`|[inRecruiting's v3 API](https://inrecruiting.intervieweb.it/api-docs/). We automatically authenticate all requests and use the customers domain as base URL| |Connexys By Bullhorn|`connexys/api`|[Connexy's API](https://api.conexsys.com/client/v2/docs/#section/Overview). We automatically authenticate all requests and use `https://\{connexys_domain\}/` as the base URL.| |HR4YOU|`hr4you/v2`|[HR4YOU's v2 API](https://apiprodemo.hr4you.org/api2/docs). We automatically authenticate all requests and use the customers provided base URL (e.g., https://`\{base_url\}`/ or https://`\{subdomain\}.hr4you.org`/).| -|Cornerstone OnDemand|`cornerstoneondemand/recruiting`|Cornerstone's [Recruiting API](https://csod.dev/reference/recruiting/). We automatically authenticate all requests using the client ID and secret and use `https://your_domain.csod.com/services/api` as the base URL.| +|Cornerstone OnDemand|`cornerstoneondemand/recruiting`|Cornerstone's [Recruiting API](https://csod.dev/reference/recruiting/). We automatically authenticate all requests using the client ID and secret and use `https://\{your_domain\}.csod.com/services/api` as the base URL.| +|Cornerstone OnDemand|`cornerstoneondemand/learning`|Cornerstone's [Learning API](https://csod.dev/reference/learning/). We automatically authenticate all requests using the client ID and secret and use `https://\{your_domain\}.csod.com/services/api` as the base URL.| |Zvoove Recruit|`zvooverecruit/jobs`|[Zvoove Recruit's Jobs API](https://api.zvoove.com/docs/). We automatically authenticate all requests using the jobs API key and use 'https://\{domain\}/api/public' as base URL.| |Zvoove Recruit|`zvooverecruit/applicants`|[Zvoove Recruit's Applicants API](https://api.zvoove.com/docs/). We automatically authenticate all requests using the applicants API key and use 'https://\{domain\}/api/public' as base URL.| |Spark Hire Recruit|`comeet/api`|[Spark Hire Recruit's API.](https://developers.comeet.com/reference/recruiting-api-overview)We automatically authenticate all requests and use `https://api.comeet.com` as the base URL.| diff --git a/pyproject.toml b/pyproject.toml index c15a633..587d5fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kombo" -version = "0.2.0" +version = "0.2.1" description = "The official Python SDK for the Kombo Unified API" authors = [{ name = "Kombo Technologies GmbH" },] readme = "README-PYPI.md" diff --git a/src/kombo/_version.py b/src/kombo/_version.py index 2b7d525..d94a43c 100644 --- a/src/kombo/_version.py +++ b/src/kombo/_version.py @@ -3,10 +3,10 @@ import importlib.metadata __title__: str = "kombo" -__version__: str = "0.2.0" +__version__: str = "0.2.1" __openapi_doc_version__: str = "1.0.0" -__gen_version__: str = "2.785.0" -__user_agent__: str = "speakeasy-sdk/python 0.2.0 2.785.0 1.0.0 kombo" +__gen_version__: str = "2.788.4" +__user_agent__: str = "speakeasy-sdk/python 0.2.1 2.788.4 1.0.0 kombo" try: if __package__ is not None: diff --git a/src/kombo/assessment.py b/src/kombo/assessment.py index 54d4009..cefc8da 100644 --- a/src/kombo/assessment.py +++ b/src/kombo/assessment.py @@ -507,7 +507,7 @@ def get_open_orders( def next_func() -> Optional[models.GetAssessmentOrdersOpenResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -619,7 +619,7 @@ def next_func() -> Awaitable[Optional[models.GetAssessmentOrdersOpenResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() diff --git a/src/kombo/ats.py b/src/kombo/ats.py index 82ddd86..d0789a0 100644 --- a/src/kombo/ats.py +++ b/src/kombo/ats.py @@ -137,7 +137,7 @@ def get_applications( def next_func() -> Optional[models.GetAtsApplicationsResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -304,7 +304,7 @@ def next_func() -> Awaitable[Optional[models.GetAtsApplicationsResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -1994,7 +1994,7 @@ def get_candidates( def next_func() -> Optional[models.GetAtsCandidatesResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -2144,7 +2144,7 @@ def next_func() -> Awaitable[Optional[models.GetAtsCandidatesResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -4083,7 +4083,7 @@ def get_tags( def next_func() -> Optional[models.GetAtsTagsResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -4217,7 +4217,7 @@ def next_func() -> Awaitable[Optional[models.GetAtsTagsResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -4358,7 +4358,7 @@ def get_application_stages( def next_func() -> Optional[models.GetAtsApplicationStagesResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -4502,7 +4502,7 @@ def next_func() -> Awaitable[Optional[models.GetAtsApplicationStagesResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -4668,7 +4668,7 @@ def get_jobs( def next_func() -> Optional[models.GetAtsJobsResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -4844,7 +4844,7 @@ def next_func() -> Awaitable[Optional[models.GetAtsJobsResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -5489,7 +5489,7 @@ def get_users( def next_func() -> Optional[models.GetAtsUsersResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -5627,7 +5627,7 @@ def next_func() -> Awaitable[Optional[models.GetAtsUsersResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -5759,7 +5759,7 @@ def get_offers( def next_func() -> Optional[models.GetAtsOffersResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -5893,7 +5893,7 @@ def next_func() -> Awaitable[Optional[models.GetAtsOffersResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -6026,7 +6026,7 @@ def get_rejection_reasons( def next_func() -> Optional[models.GetAtsRejectionReasonsResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -6162,7 +6162,7 @@ def next_func() -> Awaitable[Optional[models.GetAtsRejectionReasonsResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -6296,7 +6296,7 @@ def get_interviews( def next_func() -> Optional[models.GetAtsInterviewsResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -6434,7 +6434,7 @@ def next_func() -> Awaitable[Optional[models.GetAtsInterviewsResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() diff --git a/src/kombo/general.py b/src/kombo/general.py index 317ba19..f0c37dd 100644 --- a/src/kombo/general.py +++ b/src/kombo/general.py @@ -430,6 +430,7 @@ def send_passthrough_request( |HeavenHR|`heavenhr/v2`|[HeavenHR API](https://api.heavenhr.com/). We automatically authenticate all requests using the provided credentials and use `https://api.heavenhr.com/api/v2` as the base URL.| |HiBob|`hibob/v1`|[HibBob's v1 API](https://apidocs.hibob.com/reference/get_people). We automatically authenticate all requests using the service user credentials (or, for old integrations, the API key) and use `https://api.hibob.com/v1` as the base URL.| |HiBob|`hibob/hire`|[HiBob's Hire API](https://apidocs.hibob.com/docs/how-to-integrate-with-ats-hire-api). We automatically authenticate all requests using the hire service user credentials. The base URL is configured during the integration setup.| + |HiBob|`hibob/docs`|This passthrough is only used for fetching employee documents in Hibob. It is present as a workaround while we are working on a new endpoint for fetching documents in HRIS. It should not be used for any other purpose. We automatically authenticate all requests using the service user credentials and use `https://app.hibob.com/api/docs/employees/` as the base URL.| |Softgarden|`softgarden/frontend-v3`|[Softgarden's Frontend API v3](https://dev.softgarden.de/frontend-v3/. We automatically authenticate all requests and use 'https://api.softgarden.io/api/rest' as base URL.| |Softgarden|`softgarden/apply-api`|[Softgarden's Apply API](https://dev.softgarden.de/apply-api/). We automatically authenticate all requests and use 'https://jobdb.softgarden.de/jobdb/public' as base URL.| |Cezanne HR|`cezannehr/dataservice`|[CezanneHR's v7 dataservice API](https://api.cezannehr.com/).We automatically authenticate all requests and use the base URL `https://subdomain.cezanneondemand.com/cezanneondemand/v7/dataservice.svc`| @@ -501,7 +502,8 @@ def send_passthrough_request( |InRecruiting by Zucchetti|`inrecruiting/default`|[inRecruiting's v3 API](https://inrecruiting.intervieweb.it/api-docs/). We automatically authenticate all requests and use the customers domain as base URL| |Connexys By Bullhorn|`connexys/api`|[Connexy's API](https://api.conexsys.com/client/v2/docs/#section/Overview). We automatically authenticate all requests and use `https://\{connexys_domain\}/` as the base URL.| |HR4YOU|`hr4you/v2`|[HR4YOU's v2 API](https://apiprodemo.hr4you.org/api2/docs). We automatically authenticate all requests and use the customers provided base URL (e.g., https://`\{base_url\}`/ or https://`\{subdomain\}.hr4you.org`/).| - |Cornerstone OnDemand|`cornerstoneondemand/recruiting`|Cornerstone's [Recruiting API](https://csod.dev/reference/recruiting/). We automatically authenticate all requests using the client ID and secret and use `https://your_domain.csod.com/services/api` as the base URL.| + |Cornerstone OnDemand|`cornerstoneondemand/recruiting`|Cornerstone's [Recruiting API](https://csod.dev/reference/recruiting/). We automatically authenticate all requests using the client ID and secret and use `https://\{your_domain\}.csod.com/services/api` as the base URL.| + |Cornerstone OnDemand|`cornerstoneondemand/learning`|Cornerstone's [Learning API](https://csod.dev/reference/learning/). We automatically authenticate all requests using the client ID and secret and use `https://\{your_domain\}.csod.com/services/api` as the base URL.| |Zvoove Recruit|`zvooverecruit/jobs`|[Zvoove Recruit's Jobs API](https://api.zvoove.com/docs/). We automatically authenticate all requests using the jobs API key and use 'https://\{domain\}/api/public' as base URL.| |Zvoove Recruit|`zvooverecruit/applicants`|[Zvoove Recruit's Applicants API](https://api.zvoove.com/docs/). We automatically authenticate all requests using the applicants API key and use 'https://\{domain\}/api/public' as base URL.| |Spark Hire Recruit|`comeet/api`|[Spark Hire Recruit's API.](https://developers.comeet.com/reference/recruiting-api-overview)We automatically authenticate all requests and use `https://api.comeet.com` as the base URL.| @@ -720,6 +722,7 @@ async def send_passthrough_request_async( |HeavenHR|`heavenhr/v2`|[HeavenHR API](https://api.heavenhr.com/). We automatically authenticate all requests using the provided credentials and use `https://api.heavenhr.com/api/v2` as the base URL.| |HiBob|`hibob/v1`|[HibBob's v1 API](https://apidocs.hibob.com/reference/get_people). We automatically authenticate all requests using the service user credentials (or, for old integrations, the API key) and use `https://api.hibob.com/v1` as the base URL.| |HiBob|`hibob/hire`|[HiBob's Hire API](https://apidocs.hibob.com/docs/how-to-integrate-with-ats-hire-api). We automatically authenticate all requests using the hire service user credentials. The base URL is configured during the integration setup.| + |HiBob|`hibob/docs`|This passthrough is only used for fetching employee documents in Hibob. It is present as a workaround while we are working on a new endpoint for fetching documents in HRIS. It should not be used for any other purpose. We automatically authenticate all requests using the service user credentials and use `https://app.hibob.com/api/docs/employees/` as the base URL.| |Softgarden|`softgarden/frontend-v3`|[Softgarden's Frontend API v3](https://dev.softgarden.de/frontend-v3/. We automatically authenticate all requests and use 'https://api.softgarden.io/api/rest' as base URL.| |Softgarden|`softgarden/apply-api`|[Softgarden's Apply API](https://dev.softgarden.de/apply-api/). We automatically authenticate all requests and use 'https://jobdb.softgarden.de/jobdb/public' as base URL.| |Cezanne HR|`cezannehr/dataservice`|[CezanneHR's v7 dataservice API](https://api.cezannehr.com/).We automatically authenticate all requests and use the base URL `https://subdomain.cezanneondemand.com/cezanneondemand/v7/dataservice.svc`| @@ -791,7 +794,8 @@ async def send_passthrough_request_async( |InRecruiting by Zucchetti|`inrecruiting/default`|[inRecruiting's v3 API](https://inrecruiting.intervieweb.it/api-docs/). We automatically authenticate all requests and use the customers domain as base URL| |Connexys By Bullhorn|`connexys/api`|[Connexy's API](https://api.conexsys.com/client/v2/docs/#section/Overview). We automatically authenticate all requests and use `https://\{connexys_domain\}/` as the base URL.| |HR4YOU|`hr4you/v2`|[HR4YOU's v2 API](https://apiprodemo.hr4you.org/api2/docs). We automatically authenticate all requests and use the customers provided base URL (e.g., https://`\{base_url\}`/ or https://`\{subdomain\}.hr4you.org`/).| - |Cornerstone OnDemand|`cornerstoneondemand/recruiting`|Cornerstone's [Recruiting API](https://csod.dev/reference/recruiting/). We automatically authenticate all requests using the client ID and secret and use `https://your_domain.csod.com/services/api` as the base URL.| + |Cornerstone OnDemand|`cornerstoneondemand/recruiting`|Cornerstone's [Recruiting API](https://csod.dev/reference/recruiting/). We automatically authenticate all requests using the client ID and secret and use `https://\{your_domain\}.csod.com/services/api` as the base URL.| + |Cornerstone OnDemand|`cornerstoneondemand/learning`|Cornerstone's [Learning API](https://csod.dev/reference/learning/). We automatically authenticate all requests using the client ID and secret and use `https://\{your_domain\}.csod.com/services/api` as the base URL.| |Zvoove Recruit|`zvooverecruit/jobs`|[Zvoove Recruit's Jobs API](https://api.zvoove.com/docs/). We automatically authenticate all requests using the jobs API key and use 'https://\{domain\}/api/public' as base URL.| |Zvoove Recruit|`zvooverecruit/applicants`|[Zvoove Recruit's Applicants API](https://api.zvoove.com/docs/). We automatically authenticate all requests using the applicants API key and use 'https://\{domain\}/api/public' as base URL.| |Spark Hire Recruit|`comeet/api`|[Spark Hire Recruit's API.](https://developers.comeet.com/reference/recruiting-api-overview)We automatically authenticate all requests and use `https://api.comeet.com` as the base URL.| @@ -1623,7 +1627,7 @@ def next_func() -> ( ): body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -1744,7 +1748,7 @@ def next_func() -> ( async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -2060,7 +2064,7 @@ def next_func() -> ( ): body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -2179,7 +2183,7 @@ def next_func() -> ( async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() diff --git a/src/kombo/hris.py b/src/kombo/hris.py index 0492d44..01114f2 100644 --- a/src/kombo/hris.py +++ b/src/kombo/hris.py @@ -139,7 +139,7 @@ def get_employees( def next_func() -> Optional[models.GetHrisEmployeesResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -310,7 +310,7 @@ def next_func() -> Awaitable[Optional[models.GetHrisEmployeesResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -1238,7 +1238,7 @@ def get_employee_document_categories( def next_func() -> Optional[models.GetHrisEmployeeDocumentCategoriesResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -1374,7 +1374,7 @@ def next_func() -> ( async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -1513,7 +1513,7 @@ def get_groups( def next_func() -> Optional[models.GetHrisGroupsResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -1657,7 +1657,7 @@ def next_func() -> Awaitable[Optional[models.GetHrisGroupsResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -1790,7 +1790,7 @@ def get_employments( def next_func() -> Optional[models.GetHrisEmploymentsResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -1924,7 +1924,7 @@ def next_func() -> Awaitable[Optional[models.GetHrisEmploymentsResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -2058,7 +2058,7 @@ def get_locations( def next_func() -> Optional[models.GetHrisLocationsResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -2196,7 +2196,7 @@ def next_func() -> Awaitable[Optional[models.GetHrisLocationsResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -2328,7 +2328,7 @@ def get_absence_types( def next_func() -> Optional[models.GetHrisAbsenceTypesResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -2462,7 +2462,7 @@ def next_func() -> Awaitable[Optional[models.GetHrisAbsenceTypesResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -2596,7 +2596,7 @@ def get_time_off_balances( def next_func() -> Optional[models.GetHrisTimeOffBalancesResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -2734,7 +2734,7 @@ def next_func() -> Awaitable[Optional[models.GetHrisTimeOffBalancesResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -2878,7 +2878,7 @@ def get_absences( def next_func() -> Optional[models.GetHrisAbsencesResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -3028,7 +3028,7 @@ def next_func() -> Awaitable[Optional[models.GetHrisAbsencesResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -3706,7 +3706,7 @@ def get_legal_entities( def next_func() -> Optional[models.GetHrisLegalEntitiesResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -3844,7 +3844,7 @@ def next_func() -> Awaitable[Optional[models.GetHrisLegalEntitiesResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -3997,7 +3997,7 @@ def get_timesheets( def next_func() -> Optional[models.GetHrisTimesheetsResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -4157,7 +4157,7 @@ def next_func() -> Awaitable[Optional[models.GetHrisTimesheetsResponse]]: async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -4299,7 +4299,7 @@ def get_performance_review_cycles( def next_func() -> Optional[models.GetHrisPerformanceReviewCyclesResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -4441,7 +4441,7 @@ def next_func() -> ( async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() @@ -4589,7 +4589,7 @@ def get_performance_reviews( def next_func() -> Optional[models.GetHrisPerformanceReviewsResponse]: body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return None @@ -4745,7 +4745,7 @@ def next_func() -> ( async def empty_result(): return None - next_cursor = JSONPath("$.next").parse(body) + next_cursor = JSONPath("$.data.next").parse(body) if len(next_cursor) == 0: return empty_result() diff --git a/src/kombo/models/postatscandidatesrequestbody.py b/src/kombo/models/postatscandidatesrequestbody.py index 1741129..0f0a79a 100644 --- a/src/kombo/models/postatscandidatesrequestbody.py +++ b/src/kombo/models/postatscandidatesrequestbody.py @@ -58,6 +58,7 @@ class PostAtsCandidatesRequestBodyLocationTypedDict(TypedDict): r"""The uppercase two-letter ISO country (e.g., `DE`). For systems that use codes in formats other than `ISO 3166-1 alpha-2`, Kombo transforms the ISO Codes to the appropriate value.""" city: NotRequired[str] state: NotRequired[str] + street_1: NotRequired[str] zip_code: NotRequired[str] @@ -71,6 +72,8 @@ class PostAtsCandidatesRequestBodyLocation(BaseModel): state: Optional[str] = None + street_1: Optional[str] = None + zip_code: Optional[str] = None diff --git a/src/kombo/models/postatsjobsjobidapplicationsrequestbody.py b/src/kombo/models/postatsjobsjobidapplicationsrequestbody.py index c184be1..1f137a1 100644 --- a/src/kombo/models/postatsjobsjobidapplicationsrequestbody.py +++ b/src/kombo/models/postatsjobsjobidapplicationsrequestbody.py @@ -58,6 +58,7 @@ class PostAtsJobsJobIDApplicationsRequestBodyLocationTypedDict(TypedDict): r"""The uppercase two-letter ISO country (e.g., `DE`). For systems that use codes in formats other than `ISO 3166-1 alpha-2`, Kombo transforms the ISO Codes to the appropriate value.""" city: NotRequired[str] state: NotRequired[str] + street_1: NotRequired[str] zip_code: NotRequired[str] @@ -71,6 +72,8 @@ class PostAtsJobsJobIDApplicationsRequestBodyLocation(BaseModel): state: Optional[str] = None + street_1: Optional[str] = None + zip_code: Optional[str] = None diff --git a/tests/conftest.py b/tests/conftest.py index 7547d2b..507aaeb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,7 @@ """Test fixtures and helpers for Kombo SDK tests.""" from typing import Optional, Dict, Any, List +from collections import defaultdict, deque import json import re import pytest @@ -45,6 +46,10 @@ def __init__(self, api_key: Optional[str] = None, integration_id: Any = _UNSET): id_to_use = integration_id self._captured_requests: List[CapturedRequest] = [] + # Queue of responses for each (method, path) combination + self._response_queues: Dict[tuple, deque] = defaultdict(deque) + # Track if we've already set up the route for a given (method, path_pattern) + self._registered_routes: set = set() # Initialize SDK if id_to_use is None: @@ -65,24 +70,48 @@ def mock_endpoint( :param path: URL path (e.g., "/v1/ats/jobs") :param response: Response dict with 'body', optional 'status_code', and optional 'headers' """ - status_code = response.get("status_code", 200) - body = response.get("body") - response_headers = response.get("headers", {}) - - # Set up Content-Type header for JSON responses if not provided - if isinstance(body, dict) and "Content-Type" not in response_headers: - response_headers = {**response_headers, "Content-Type": "application/json"} - - # Prepare response content - if isinstance(body, (dict, list)): - content = json.dumps(body).encode() - elif isinstance(body, str): - content = body.encode() - else: - content = b"" + base_url = f"https://api.kombo.dev{path}" + route_key = (method, base_url) + + # Add response to the queue for this endpoint + self._response_queues[route_key].append(response) + + # Only register the route once per (method, path) combination + if route_key in self._registered_routes: + return + + self._registered_routes.add(route_key) - # Create response function that captures request + # Create response function that uses the queue def create_response(request: httpx.Request) -> httpx.Response: + # Get the next response from the queue for this endpoint + response_queue = self._response_queues[route_key] + + if not response_queue: + raise RuntimeError( + f"No more mocked responses available for {method} {path}. " + f"Did you mock enough responses for all the requests?" + ) + + # Pop the first response from the queue (FIFO) + current_response = response_queue.popleft() + + status_code = current_response.get("status_code", 200) + body = current_response.get("body") + response_headers = current_response.get("headers", {}) + + # Set up Content-Type header for JSON responses if not provided + if isinstance(body, dict) and "Content-Type" not in response_headers: + response_headers = {**response_headers, "Content-Type": "application/json"} + + # Prepare response content + if isinstance(body, (dict, list)): + content = json.dumps(body).encode() + elif isinstance(body, str): + content = body.encode() + else: + content = b"" + # Capture request details query_string = request.url.query.decode() if request.url.query else "" full_path = request.url.path + (f"?{query_string}" if query_string else "") @@ -114,7 +143,6 @@ def create_response(request: httpx.Request) -> httpx.Response: # Create the mock route # For GET requests, match any query parameters using regex # For other methods, match exact path - base_url = f"https://api.kombo.dev{path}" if method == "GET": # Match the base path, allowing any query parameters route = respx.request( @@ -146,6 +174,8 @@ def clear(self) -> None: clear registered routes between calls within the same test. """ self._captured_requests.clear() + self._response_queues.clear() + self._registered_routes.clear() respx.clear() diff --git a/uv.lock b/uv.lock index 8d0c4ab..b072d33 100644 --- a/uv.lock +++ b/uv.lock @@ -210,7 +210,7 @@ wheels = [ [[package]] name = "kombo" -version = "0.2.0" +version = "0.2.1" source = { editable = "." } dependencies = [ { name = "httpcore" },