Skip to content

Conversation

@BrandtKruger
Copy link
Contributor

Explain your changes

Implement conditional pytest dependencies to maintain backward compatibility
with Python 3.9 while allowing Python 3.10+ to use pytest v9.

Changes:

Add environment markers in pyproject.toml [project.optional-dependencies.dev]
Python 3.9: pytest>=7.4.0,<9.0.0
Python 3.10+: pytest>=9.0.0
Update requirements.txt with conditional pytest dependencies using markers
Update pytest-asyncio to ^0.26.0 and black to ^24.3.0 (from PR #134)
Keep Poetry dev-dependencies with Python 3.9 compatible version as fallback
This resolves the Python 3.9 compatibility issue in PR #134 where pytest v9
requires Python 3.10+. The conditional dependencies ensure:

Python 3.9 CI/CD continues to work with pytest <9.0.0
Python 3.10+ developers can use pytest v9 features
No breaking changes for existing Python 3.9 users

Checklist

🛟 If you need help, consider asking for advice over in the Kinde community.

lexthink and others added 7 commits October 19, 2025 03:02
- Update response types for various endpoints
- Add new endpoints for user identities management
Fix the singularization bug that was generating incorrect method names:
- create_user_identitie → create_user_identity
- get_identitie → get_identity
- update_identitie → update_identity
- delete_identitie → delete_identity

Changes:
- Add _singularize_resource() helper method to centralize singularization logic
- Handle special cases: user_identities → user_identity, identities → identity
- Update both _generate_methods() and _create_api_method() to use the helper
- Maintains existing behavior for other resources (users, organizations, etc.)

This fixes a pre-existing bug that was exposed by PR kinde-oss#128's addition of
user_identities and identities endpoints.
Update test assertions to reflect removed endpoints:
- Remove get_permission assertion (permissions no longer has get endpoint)
- Remove get_feature_flags and get_feature_flag assertions (removed in PR kinde-oss#128)
- Remove update_api_application assertion (removed in PR kinde-oss#128)
- Update test_feature_flags_api_calls to test create_feature_flag instead

These changes align tests with the updated Management API endpoints
introduced in PR kinde-oss#128.
Add the missing 'list' endpoint to feature_flags resource to enable
get_feature_flags() method generation.

Changes:
- Add 'list': ('GET', '/api/v1/feature_flags') to feature_flags API_ENDPOINTS
- Add corresponding response type mapping: 'list': {'200': 'GetFeatureFlagsResponse', ...}
- Update test to assert get_feature_flags method exists

This addresses the CodeRabbit review comment on PR kinde-oss#143 and enables
the SDK to generate the get_feature_flags() method as documented in
the Kinde Management API.
…nse types

- Corrected endpoint paths for organizations and API applications to use plural forms.
- Updated response types for create endpoints to reflect correct HTTP status codes (201).
- Adjusted test assertions to ensure alignment with the updated API structure.

These changes enhance consistency with the Kinde Management API specifications.
Implement conditional pytest dependencies to maintain backward compatibility
with Python 3.9 while allowing Python 3.10+ to use pytest v9.

Changes:
- Add environment markers in pyproject.toml [project.optional-dependencies.dev]
  - Python 3.9: pytest>=7.4.0,<9.0.0
  - Python 3.10+: pytest>=9.0.0
- Update requirements.txt with conditional pytest dependencies using markers
- Update pytest-asyncio to ^0.26.0 and black to ^24.3.0 (from PR kinde-oss#134)
- Keep Poetry dev-dependencies with Python 3.9 compatible version as fallback

This resolves the Python 3.9 compatibility issue in PR kinde-oss#134 where pytest v9
requires Python 3.10+. The conditional dependencies ensure:
- Python 3.9 CI/CD continues to work with pytest <9.0.0
- Python 3.10+ developers can use pytest v9 features
- No breaking changes for existing Python 3.9 users

Fixes: kinde-oss#134
- Fix singularization for words ending in 'ies' (e.g., 'properties' -> 'property')
  This fixes the critical issue where 'properties' was incorrectly singularized
  to 'propertie' instead of 'property', causing incorrect method names.

- Fix HTTP status codes to follow REST conventions:
  - Users create: 200 -> 201 (POST should return 201 Created)
  - Roles update: 201 -> 200 (PATCH should return 200 OK)

These changes address the CodeRabbit review comments on PR kinde-oss#144.
@BrandtKruger BrandtKruger requested review from a team as code owners December 2, 2025 16:57
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 2, 2025

Warning

Rate limit exceeded

@BrandtKruger has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 2 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 6160041 and 29c526a.

⛔ Files ignored due to path filters (1)
  • pyproject.toml is excluded by !**/*.toml
📒 Files selected for processing (1)
  • requirements.txt (1 hunks)

Walkthrough

The management client API endpoints are updated from singular to plural resource names for consistency. New identity-related resources (user_identities, identities) are introduced with corresponding CRUD endpoints. A dedicated singularization utility is added to derive singular method names. Response type mappings are expanded to reflect the new and renamed resources. Development dependencies are bumped, and tests are updated to reflect the API changes.

Changes

Cohort / File(s) Summary
Management Client Core
kinde_sdk/management/management_client.py
API endpoints pluralized (e.g., organization → organizations, role → roles). New resources added: user_identities (list/create endpoints) and identities (get/update/delete endpoints). New private method _singularize_resource() introduced to handle resource name singularization with special cases. Response type mappings (RESPONSE_TYPES) expanded and adjusted for all resources. Dynamic method generation refactored to use unified singularization flow.
Dependencies
requirements.txt
pytest version updated with Python-version conditionals (≥7.4.0,<9.0.0 for Python <3.10; ≥9.0.0 for Python ≥3.10). pytest-asyncio bumped from 0.21.1 to 0.26.0. black bumped from 23.7.0 to 24.3.0.
Test Updates
testv2/testv2_management/test_management_client.py
Removed existence checks for get_permission and get_feature_flag. Removed check for update_api_application. Feature flags API tests refactored to use create_feature_flag flow with POST method. Updated test assertions and response structures to match creation endpoint behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • kinde_sdk/management/management_client.py: Review the singularization logic carefully, particularly the special cases (business, user_identities, identities) and edge cases in the helper function. Verify that endpoint paths are correctly pluralized and that the refactored dynamic method generation maintains backward compatibility.
  • Response type mappings: Ensure all response types correctly map to the updated endpoint structure and that the mappings are consistent across all resources.
  • Test coverage: Verify that test updates fully exercise the new identity resources and that the removed test checks do not leave gaps in coverage.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'pytest conditional deps' is vague and uses abbreviated terminology that doesn't clearly convey the main change. It lacks specificity about what the conditional dependencies are for or why they matter. Revise the title to be more descriptive, such as 'Add conditional pytest dependencies for Python 3.9/3.10+ compatibility' to clearly communicate the intent and scope of the change.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description is comprehensive, well-structured, and clearly explains the changes made, their rationale, and the problem they solve. It directly relates to the changeset shown in the raw_summary.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
kinde_sdk/management/management_client.py (1)

480-511: Redundant query string construction.

final_path is constructed with query parameters (lines 480-485) but then formatted_path (without query params) is used in param_serialize (line 489-494), and query_params is passed separately. The final_path variable appears unused after construction.

The final_path construction (lines 480-485) seems to be dead code. If param_serialize handles query parameters, remove the redundant logic:

-            # FIXED: Use param_serialize to properly construct the full URL with host
-            # Handle query parameters by appending them to the path
-            final_path = formatted_path
-            if query_params and http_method in ('GET', 'DELETE'):
-                query_string = '&'.join([f"{k}={v}" for k, v in query_params.items() if v is not None])
-                if query_string:
-                    separator = '&' if '?' in final_path else '?'
-                    final_path = f"{final_path}{separator}{query_string}"
-            
             # Use param_serialize to get the proper URL with host
🧹 Nitpick comments (5)
requirements.txt (1)

26-27: Consider adding upper bounds to dev dependencies for reproducibility.

While the conditional constraints for pytest are well-scoped, the lack of upper bounds on pytest>=9.0.0 (line 27), pytest-asyncio>=0.26.0 (line 29), and black>=24.3.0 (line 32) could allow breaking changes in future versions to silently introduce instability.

This is especially relevant given the PR description mentions Poetry uses stricter caret-based versions (e.g., pytest-asyncio ^0.26.0). Consider aligning requirements.txt with those stricter constraints to ensure reproducible dev environments.

If you want reproducible builds, consider tightening the upper bounds. For example:

-pytest>=9.0.0; python_version >= "3.10"
-pytest-asyncio>=0.26.0
+pytest>=9.0.0,<10.0.0; python_version >= "3.10"
+pytest-asyncio>=0.26.0,<0.27.0

(and similar for black if desired)

Also applies to: 29-29, 32-32

kinde_sdk/management/management_client.py (3)

389-413: Singularization logic has incomplete coverage for compound resources.

The _singularize_resource method handles user_identities and identities explicitly, but doesn't handle other compound resources ending in _identities or similar patterns. Also, resources like industries would incorrectly become industrie (removing trailing s after the iesy check fails).

Wait, let me re-check: industries ends with ies, so it would become industry via line 407-408. That's correct.

However, the method doesn't handle timezonestimezone correctly since it just removes trailing s. Actually, that would work: timezones[:-1] = timezone. ✓

One edge case: business is handled, but what about potential future resources like addresses? addresses ends with es not ies, so it would become addresse (incorrect - should be address).

Consider handling words ending in es (like addressesaddress) if such resources might be added:

         elif resource.endswith('ies'):
             return resource[:-3] + 'y'
+        # Handle words ending in 'es' (e.g., 'addresses' -> 'address')
+        elif resource.endswith('ses') or resource.endswith('xes') or resource.endswith('zes'):
+            return resource[:-2]
         # Default: remove trailing 's' if present
         elif resource.endswith('s'):
             return resource[:-1]

Alternatively, consider using an inflect library for robust pluralization handling if this becomes more complex.


482-485: Query string construction doesn't URL-encode values.

For GET/DELETE requests, query parameters are concatenated without URL encoding. Special characters in values could break the URL.

Consider using urllib.parse.urlencode:

+from urllib.parse import urlencode
+
 # Handle query parameters by appending them to the path
 final_path = formatted_path
 if query_params and http_method in ('GET', 'DELETE'):
-    query_string = '&'.join([f"{k}={v}" for k, v in query_params.items() if v is not None])
-    if query_string:
-        separator = '&' if '?' in final_path else '?'
-        final_path = f"{final_path}{separator}{query_string}"
+    separator = '&' if '?' in final_path else '?'
+    final_path = f"{final_path}{separator}{urlencode(query_params)}"

Note: The param_serialize call at line 491 may already handle encoding, in which case this manual query string construction (lines 481-485) might be redundant. Verify if final_path is actually used downstream or if url from param_serialize is the one used in the request.


600-623: Backward compatibility loop has no effect.

The loop at lines 600-622 iterates over method name pairs but only executes pass, making it a no-op. The comment suggests these methods "will be created dynamically," but the dynamic generation already happens in _generate_methods().

Either remove the dead code or implement actual deprecation warnings if backward compatibility aliases are needed:

-# Add backwards compatibility methods for common operations
-# These will be deprecated in future versions
-for method_name, new_name in [
-    ('get_users', 'get_users'),
-    # ... all pairs ...
-]:
-    pass  # These methods will be created dynamically
+# Note: Backwards compatibility methods are created dynamically via _generate_methods()
testv2/testv2_management/test_management_client.py (1)

104-108: Consider adding test coverage for new identity methods.

The dynamic method generation test verifies feature flag methods but doesn't include assertions for the new user_identities and identities endpoints.

Add assertions for the new identity-related methods:

# Test user identity methods
assert hasattr(client, 'get_user_identities')
assert hasattr(client, 'create_user_identity')

# Test identity methods
assert hasattr(client, 'get_identity')
assert hasattr(client, 'update_identity')
assert hasattr(client, 'delete_identity')
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7deb113 and 6160041.

⛔ Files ignored due to path filters (1)
  • pyproject.toml is excluded by !**/*.toml
📒 Files selected for processing (3)
  • kinde_sdk/management/management_client.py (12 hunks)
  • requirements.txt (1 hunks)
  • testv2/testv2_management/test_management_client.py (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-08T14:07:43.901Z
Learnt from: brettchaldecott
Repo: kinde-oss/kinde-python-sdk PR: 87
File: test_organization_users.py:70-70
Timestamp: 2025-07-08T14:07:43.901Z
Learning: The method names in test_organization_users.py have been tested and confirmed to be correct by the developer, including get_user_feature_flag and refresh_user_refresh_claim mappings.

Applied to files:

  • testv2/testv2_management/test_management_client.py
🔇 Additional comments (7)
requirements.txt (1)

23-27: Conditional pytest dependencies correctly implemented.

The PEP 508 environment markers are properly formatted and the version constraints correctly balance backward compatibility (Python 3.9 with pytest <9.0.0) and forward compatibility (Python 3.10+ with pytest 9+).

kinde_sdk/management/management_client.py (5)

208-216: New identity endpoints look correctly structured.

The user_identities and identities resource definitions follow the established pattern with appropriate HTTP methods and path parameters.


350-358: Response types for identities endpoints are properly defined.

The response type mappings for user_identities and identities follow the established pattern and include appropriate error responses.


88-89: Verify PUT vs PATCH semantics for feature_flags update.

The feature_flags update endpoint uses PUT (line 89), while most other update endpoints use PATCH. PUT typically implies full resource replacement, whereas PATCH is for partial updates.

Confirm this is intentional and aligns with the Kinde API specification. If partial updates are expected, consider:

-            'update': ('PUT', '/api/v1/feature_flags/{feature_flag_key}'),
+            'update': ('PATCH', '/api/v1/feature_flags/{feature_flag_key}'),

131-131: Verify PUT vs PATCH for properties update.

Similar to feature_flags, the properties update endpoint uses PUT. Ensure this matches the API specification.


468-476: Potential issue: kwargs.pop() mutates the dictionary during iteration.

The code iterates over list(kwargs) and uses kwargs.pop(k) which is safe due to the list copy. However, line 476 then iterates over kwargs.items() after modification. This works but could be clearer.

The logic correctly separates query parameters from body parameters for POST/PUT/PATCH requests.

testv2/testv2_management/test_management_client.py (1)

489-531: Test correctly updated for feature flag creation flow.

The test properly exercises create_feature_flag with the expected payload structure (key, type, value) and verifies the mocked response handling.

…atibility

Address CodeRabbit review comment: pytest-asyncio 0.26.0 does not support
pytest 9.0+. According to pytest-asyncio changelog, explicit support for
pytest 9 was added in version 1.3.0+.

Changes:
- Add conditional pytest-asyncio dependencies matching pytest versions:
  - Python <3.10: pytest-asyncio>=0.26.0 (compatible with pytest 7.4.x)
  - Python >=3.10: pytest-asyncio>=1.3.0 (required for pytest 9.0+)
- Update both requirements.txt and pyproject.toml
- Add comments explaining the version requirements

This ensures Python 3.10+ environments with pytest 9.0+ use a compatible
pytest-asyncio version, preventing runtime failures.

Fixes: CodeRabbit review comment on PR kinde-oss#144
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants