Conversation
…packaging process
- restructure tasks, add phases, and enhance documentation
* docs: record Phase 1 PR completion and update next steps * feat: implement JobResource with run/status/output and polling helpers * feat: add jobs resource property to sync client * chore: export job resources from package __init__.py * feat: add jobs resource property to async client * test: add unit tests for JobResource with respx mocking * docs: Phase 2 complete (9/9 tasks done) * docs: add Pull Requests section and update Phase 2 status * test: add quick test script for local testing
* Initial plan * fix: guard raise last_exception against NoneType; fix import errors; add retry unit tests Co-authored-by: Sivivatu <7496456+Sivivatu@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Sivivatu <7496456+Sivivatu@users.noreply.github.com>
## Summary This PR completes the migration from Poetry to UV with final fixes and configuration updates. ### Changes - ✅ Fix pyproject.toml schema violation (removed conflicting dynamic version) - ✅ Complete branch-updates workflow migration from Poetry to UV - ✅ Fix PRP prompt files (correct 'mode' to 'agent') ### Stack Structure This PR is stacked on top of #4 and should be merged after Phase 1 & 2 are complete. **Stack:** - master ← #4 (agentic_process_updates) ← this PR (ci-uv-final-fixes) ### Related - Builds on #7 (Migrate CI/CD pipelines from Poetry to UV) - Part of the v0.2.0 development cycle
… handling - Remove duplicate empty AlteryxClient class causing IndentationError - Change AsyncJobResource.cancel() to catch NotFoundError by type instead of fragile string matching on exception message
- Update imports from old alteryx_gallery_api to alteryx_server_py - Replace responses library mocks with respx/unittest.mock for httpx - Align test fixtures with new client constructor (client_id/client_secret) - Use resource-based API (client.workflows.list()) instead of removed methods - Fix test_exceptions.py infinite recursion from Windows path handling - Fix test_job_resource.py async/sync mismatch and respx mock patterns - Correct test data to use valid ExecutionMode enum values
- Add None guard for _client before request calls in AlteryxClient - Remove str from data parameter union (httpx doesn't accept str for data=) - Remove unused Union import from client.py - Type-narrow _client in JobResource and AsyncJobResource - Use JobPriority enum conversion for priority parameter - Use cast() for ambiguous return types in get_output methods - Remove invalid message kwarg from JobNotFoundError calls
This stack of pull requests is managed by Graphite. Learn more about stacking. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: df3a25e990
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| expiration_date: Optional[datetime] = Field(None, alias="expirationDate") | ||
| permissions: CollectionPermission |
There was a problem hiding this comment.
Split user permission updates from group permission updates
CollectionPermissionUpdateRequest is the only update-permissions contract added here, but it makes expiration_date optional. In Alteryx's Collection endpoint docs, PUT /v3/collections/{collectionId}/users/{userId}/permissions requires expirationDate, while the user-group variant allows it to be omitted. With the shared optional field, callers can build a locally valid request for the user-permissions endpoint that will still be rejected by the server once this model is wired up.
Useful? React with 👍 / 👎.
| """Flatten nested permissions to match form-encoded API contracts.""" | ||
| data = super().model_dump(*args, **kwargs) | ||
| permissions = data.pop("permissions", None) | ||
| if permissions: | ||
| data.update(permissions) |
There was a problem hiding this comment.
Serialize expirationDate before flattening collection form payloads
These model_dump() overrides are intended to match the form-encoded collection contracts, but they only flatten permissions and never convert expiration_date. Alteryx documents expirationDate as an ISO8601 value for these collection share/update endpoints, so any caller that sets an expiration and posts this dumped dict as form data will end up sending Python's raw datetime representation instead of the documented wire format. The same issue is duplicated in the other two model_dump() overrides below.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Adds new Pydantic model definitions for additional Alteryx Server API surfaces (“phase 4”), expanding the client’s typed request/response layer and re-exporting them from models.__init__ for easier consumption.
Changes:
- Added Collection models + request payload models (including permission-flattening
model_dump()helpers). - Added Credential models + request payload models.
- Added (currently schema-less) ServerInfo/ServerSettings response models and exported all new models in
models/__init__.py.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
src/alteryx_server_py/models/server.py |
Introduces ServerInfo/ServerSettings models (currently permissive “extra allow”). |
src/alteryx_server_py/models/credentials.py |
Adds credential response + create/update/share request models. |
src/alteryx_server_py/models/collections.py |
Adds collection response + create/update/share request models with permission flattening for form-encoded contracts. |
src/alteryx_server_py/models/__init__.py |
Re-exports the new models for public package access. |
| user_ids: list[UserId] = Field(default_factory=list, alias="userIds") | ||
| user_group_ids: list[UserGroupId] = Field(default_factory=list, alias="userGroupIds") | ||
| workflow_ids: list[WorkflowId] = Field(default_factory=list, alias="workflowIds") | ||
| schedule_ids: list[str] = Field(default_factory=list, alias="scheduleIds") |
| def model_dump(self, *args, **kwargs): | ||
| """Flatten nested permissions to match form-encoded API contracts.""" | ||
| data = super().model_dump(*args, **kwargs) | ||
| permissions = data.pop("permissions", None) | ||
| if permissions: | ||
| data.update(permissions) | ||
| return data |
| model_config = ConfigDict( | ||
| str_strip_whitespace=True, | ||
| validate_assignment=True, | ||
| extra="allow", | ||
| populate_by_name=True, | ||
| ) |
| class CredentialCreateRequest(BaseModel): | ||
| """Request model for creating a credential.""" | ||
|
|
||
| model_config = ConfigDict(populate_by_name=True) | ||
|
|
||
| username: str | ||
| password: str | ||
|
|
||
|
|
||
| class CredentialUpdateRequest(BaseModel): | ||
| """Request model for updating a credential password.""" | ||
|
|
||
| model_config = ConfigDict(populate_by_name=True) | ||
|
|
||
| new_password: str = Field(..., alias="NewPassword") | ||
|
|
| model_config = ConfigDict( | ||
| str_strip_whitespace=True, | ||
| validate_assignment=True, | ||
| extra="allow", |
| model_config = ConfigDict( | ||
| str_strip_whitespace=True, | ||
| validate_assignment=True, | ||
| extra="allow", |
Merge activity
|

No description provided.