feat(cli,api): difyctl use workspace + member management#36360
Open
lin-snow wants to merge 4 commits into
Open
feat(cli,api): difyctl use workspace + member management#36360lin-snow wants to merge 4 commits into
lin-snow wants to merge 4 commits into
Conversation
Contributor
Pyrefly Diffbase → PR--- /tmp/pyrefly_base.txt 2026-05-19 11:02:39.922857722 +0000
+++ /tmp/pyrefly_pr.txt 2026-05-19 11:02:30.357853380 +0000
@@ -2182,6 +2182,26 @@
--> tests/unit_tests/controllers/openapi/test_workspaces.py:49:12
ERROR `in` is not supported between `Literal['GET']` and `None` [not-iterable]
--> tests/unit_tests/controllers/openapi/test_workspaces.py:50:12
+ERROR Object of class `FunctionType` has no attribute `view_class` [missing-attribute]
+ --> tests/unit_tests/controllers/openapi/test_workspaces_members.py:114:12
+ERROR `in` is not supported between `Literal['POST']` and `None` [not-iterable]
+ --> tests/unit_tests/controllers/openapi/test_workspaces_members.py:115:12
+ERROR Object of class `FunctionType` has no attribute `view_class` [missing-attribute]
+ --> tests/unit_tests/controllers/openapi/test_workspaces_members.py:120:12
+ERROR `in` is not supported between `Literal['GET']` and `None` [not-iterable]
+ --> tests/unit_tests/controllers/openapi/test_workspaces_members.py:121:12
+ERROR `in` is not supported between `Literal['POST']` and `None` [not-iterable]
+ --> tests/unit_tests/controllers/openapi/test_workspaces_members.py:122:12
+ERROR Object of class `FunctionType` has no attribute `view_class` [missing-attribute]
+ --> tests/unit_tests/controllers/openapi/test_workspaces_members.py:127:12
+ERROR `in` is not supported between `Literal['DELETE']` and `None` [not-iterable]
+ --> tests/unit_tests/controllers/openapi/test_workspaces_members.py:128:12
+ERROR Object of class `FunctionType` has no attribute `view_class` [missing-attribute]
+ --> tests/unit_tests/controllers/openapi/test_workspaces_members.py:133:12
+ERROR `in` is not supported between `Literal['PUT']` and `None` [not-iterable]
+ --> tests/unit_tests/controllers/openapi/test_workspaces_members.py:134:12
+ERROR Object of class `NoneType` has no attribute `json`
+ERROR Object of class `NoneType` has no attribute `json`
ERROR Cannot index into `Iterable[bytes]` [bad-index]
--> tests/unit_tests/controllers/service_api/app/test_audio.py:190:16
ERROR Cannot index into `Response` [bad-index]
|
Contributor
Pyrefly Type Coverage
|
…ted openapi Adds five bearer-authed endpoints under /openapi/v1/workspaces/<id>/ (switch, members CRUD, role update) gated by a new @require_workspace_role decorator that returns 404 for non-members (matching the existing GET /workspaces/<id> convention so workspace IDs don't leak across tenants) and 403 for insufficient role. TenantService / RegisterService domain logic is reused as-is — invites still go through invite_new_member so the Celery activation email fires for newly-invited addresses. Owner is intentionally not assignable through invite or role-update; ownership transfer remains console-only. CLI gains five commands: difyctl use workspace <id> difyctl get member [-w <id>] [-o ...] difyctl create member --email <e> --role <r> [-w <id>] difyctl delete member <member-id> [-w <id>] difyctl set member <member-id> --role <r> [-w <id>] use workspace strictly orders POST /switch -> GET /workspaces -> saveHosts; any failure aborts with no local mutation so hosts.yml never diverges from the server. get member marks the calling account row with '*' (matched via hosts.yml bundle.account.id). --role is client-enum-validated to normal|admin before any HTTP call. The old `difyctl auth use` (a pure-local workspace picker) is removed — its semantics conflict with server-side switch and keeping it would only confuse. The "no workspace selected" hint now points at `difyctl use workspace <id>`.
Inline checks on POST /openapi/v1/workspaces/<id>/members for:
- SaaS subscription members.limit (members.limit_exceeded)
- EE license workspace_members cap (workspace_members.license_exceeded)
Envelope {code, message, hint} on the wire body so CLI error-mapper
can surface structured remediation guidance without edition awareness.
EE per-workspace allow_member_invite policy continues via service-layer
check_workspace_member_invite_permission inside invite_new_member.
Reruns pnpm gen-api-contract and pnpm tree:gen after rebasing onto upstream/feat/cli (which migrated CLI types to @dify/contracts). Adds the Member* types to the shared contract package and registers the new CLI commands (use workspace, create/delete/get/set member) in the build-time command tree.
508e575 to
0a425bc
Compare
…Workspace + simplify _member_response - invite_url is always set server-side (always-non-null URL build path); drop the misleading Optional so generated CLI/SDK types stop forcing callers through pointless null checks. - use/workspace: pickWorkspace was used in one of two adjacent shape conversions; inline both for symmetry. - _member_response: TenantAccountRole and AccountStatus are StrEnums — the getattr + `if role else ""` defenses are unreachable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
Fixes #<issue number>.Summary
Adds server-side workspace switching and full member management to
difyctl, on parity with the console's/workspaces/current/membersendpoints but bearer-authed and gated on the path'sworkspace_id.Backend — five new
/openapi/v1/endpoints, all double-gated by@accept_subjects(ACCOUNT)+@require_workspace_role(...):/workspaces/<id>/switch/workspaces/<id>/members/workspaces/<id>/members/workspaces/<id>/members/<member_id>/workspaces/<id>/members/<member_id>/roleThe new
require_workspace_roledecorator returns 404 for non-members (matching the existingGET /workspaces/<id>convention to avoid tenant-id existence leaks) and 403 for members with insufficient role. Domain logic is reused as-is fromTenantService/RegisterService; invites still go throughinvite_new_memberso the Celery activation email fires for newly-invited addresses. Owner is intentionally not assignable through invite or role-update — ownership transfer remains console-only.CLI — five new commands:
use workspacestrictly ordersPOST /switch → GET /workspaces → saveHosts; any failure aborts with no local mutation sohosts.ymlnever diverges from the server.get memberflags the calling account row with*(matched viahosts.yml.bundle.account.id).--roleis client-enum-validated tonormal | adminbefore any HTTP call.The old
difyctl auth use(a pure-local workspace picker) is deleted — its semantics are incompatible with the new server-side switch, so keeping it would only confuse. The "no workspace selected" hint now points atdifyctl use workspace <id>.Tests: 11 cases for the role-gate decorator, 26 for the new endpoints + payload validation, 12 for
MembersClient+WorkspacesClient.switch, 21 across the five CLI command runners (including the no-fallback-on-switch-failure invariant). Local: 229/229 backend openapi tests, 681/681 CLI tests,pnpm type-checkandpnpm buildclean.Screenshots
N/A — CLI-only.
Checklist
make lint && make type-check(backend) andcd web && pnpm exec vp staged(frontend) to appease the lint gods