refactor(openapi): adopt resource-oriented paths and colon custom methods

Align the /openapi/v1 surface with the API Design Guide: replace verb path
suffixes with standard resource GETs and AIP-136 `:action` custom methods.

  GET  /apps/{id}/describe                          -> GET  /apps/{id}
  GET  /permitted-external-apps/{id}/describe        -> GET  /permitted-external-apps/{id}
  POST /apps/{id}/run                                -> POST /apps/{id}:run
  POST /apps/{id}/tasks/{tid}/stop                   -> POST /apps/{id}/tasks/{tid}:stop
  POST /apps/{id}/files/upload                       -> POST /apps/{id}/files
  GET  /apps/{id}/export                             -> GET  /apps/{id}/dsl
  GET  /apps/{id}/check-dependencies                 -> GET  /apps/{id}/dependencies
  POST /workspaces/{ws}/apps/imports/{iid}/confirm   -> POST /workspaces/{ws}/apps/imports/{iid}:confirm
  POST /workspaces/{ws}/switch                       -> POST /workspaces/{ws}:switch
  GET|POST /apps/{id}/form/human_input/{tok}         -> /apps/{id}/human-input-forms/{tok} (+ :submit for POST)
  PUT  /workspaces/{ws}/members/{mid}/role           -> PATCH /workspaces/{ws}/members/{mid}

difyctl is the sole consumer and ships in lockstep, so this is a hard cutover
(no /v2, no dual-mount). RFC 8628 device-flow routes and the `limit` pagination
param are intentionally left unchanged.
This commit is contained in:
L1nSn0w
2026-07-03 13:35:53 +08:00
parent 5b4ceacbe7
commit 6ca8b7bd6e
18 changed files with 96 additions and 105 deletions

View File

@ -113,7 +113,7 @@ class WorkspaceByIdApi(Resource):
return _workspace_detail(tenant, membership)
@openapi_ns.route("/workspaces/<string:workspace_id>/switch")
@openapi_ns.route("/workspaces/<string:workspace_id>:switch")
class WorkspaceSwitchApi(Resource):
"""Server-side switch — equivalent to the console's POST /workspaces/switch.
@ -212,11 +212,12 @@ class WorkspaceMembersApi(Resource):
@openapi_ns.route("/workspaces/<string:workspace_id>/members/<string:member_id>")
class WorkspaceMemberApi(Resource):
"""Remove a member.
"""Remove a member (DELETE) or change a member's role (PATCH).
Self-removal and owner-removal are explicitly rejected by the service
layer (CannotOperateSelfError, NoPermissionError) — both surface as
400 per the spec, with the service's message preserved.
400 per the spec, with the service's message preserved. Owner can never be
assigned via PATCH (closed enum); admin cannot demote the standing owner.
"""
@auth_router.guard_workspace(
@ -243,15 +244,6 @@ class WorkspaceMemberApi(Resource):
return MemberActionResponse()
@openapi_ns.route("/workspaces/<string:workspace_id>/members/<string:member_id>/role")
class WorkspaceMemberRoleApi(Resource):
"""Change a member's role.
Owner cannot be assigned here (closed enum). Admin cannot demote the
standing owner (service NoPermissionError → 400, per spec).
"""
@auth_router.guard_workspace(
scope=Scope.WORKSPACE_WRITE,
allowed_token_types=frozenset({TokenType.OAUTH_ACCOUNT}),
@ -259,7 +251,7 @@ class WorkspaceMemberRoleApi(Resource):
)
@returns(200, MemberActionResponse, description="Role updated")
@accepts(body=MemberRoleUpdatePayload)
def put(self, workspace_id: str, member_id: str, *, auth_data: AuthData, body: MemberRoleUpdatePayload):
def patch(self, workspace_id: str, member_id: str, *, auth_data: AuthData, body: MemberRoleUpdatePayload):
operator = _load_account(auth_data.account_id)
tenant = _load_tenant(workspace_id)
member = AccountService.get_account_by_id(db.session, member_id)