mirror of
https://github.com/langgenius/dify.git
synced 2026-05-20 08:46:57 +08:00
Read-side surface for difyctl describe / get / list:
- GET /openapi/v1/apps paginated list (workspace_id required)
- GET /openapi/v1/apps/<id> single app summary
- GET /openapi/v1/apps/<id>/parameters port of service_api parameters
- GET /openapi/v1/apps/<id>/describe merged { info, parameters }
All gated by validate_bearer(ACCEPT_USER_ANY) + require_scope(APPS_READ) +
require_workspace_member(ctx, tenant_id). SSO subjects 404 (account-only
helper account_or_404 deduplicates the guard across the four endpoints).
PaginationEnvelope[T] (page, limit, total, has_more, data) is the canonical
shape for every /openapi/v1/* list endpoint. has_more is computed by the
server from page * limit < total. /account/sessions migrates from the
legacy { sessions: [...] } shape to the envelope; integration tests assert
the legacy key is gone.
58 lines
1.5 KiB
Python
58 lines
1.5 KiB
Python
import builtins
|
|
|
|
from flask import Flask
|
|
from flask.views import MethodView
|
|
|
|
from controllers.openapi import bp as openapi_bp
|
|
from controllers.openapi.app_info import AppInfoApi
|
|
|
|
if not hasattr(builtins, "MethodView"):
|
|
builtins.MethodView = MethodView # type: ignore[attr-defined]
|
|
|
|
|
|
def _openapi_app() -> Flask:
|
|
app = Flask(__name__)
|
|
app.config["TESTING"] = True
|
|
app.register_blueprint(openapi_bp)
|
|
return app
|
|
|
|
|
|
def _rule(app: Flask, path: str):
|
|
return next(r for r in app.url_map.iter_rules() if r.rule == path)
|
|
|
|
|
|
def test_app_info_route_registered():
|
|
rules = {r.rule for r in _openapi_app().url_map.iter_rules()}
|
|
assert "/openapi/v1/apps/<string:app_id>/info" in rules
|
|
|
|
|
|
def test_app_info_dispatches_to_class():
|
|
app = _openapi_app()
|
|
rule = _rule(app, "/openapi/v1/apps/<string:app_id>/info")
|
|
assert app.view_functions[rule.endpoint].view_class is AppInfoApi
|
|
assert "GET" in rule.methods
|
|
|
|
|
|
def test_app_info_payload_shape():
|
|
from types import SimpleNamespace
|
|
|
|
from controllers.openapi.apps import app_info_payload
|
|
|
|
app_obj = SimpleNamespace(
|
|
id="app1",
|
|
name="X",
|
|
description="d",
|
|
mode="chat",
|
|
author_name="alice",
|
|
tags=[SimpleNamespace(name="prod")],
|
|
)
|
|
payload = app_info_payload(app_obj)
|
|
assert payload == {
|
|
"id": "app1",
|
|
"name": "X",
|
|
"description": "d",
|
|
"mode": "chat",
|
|
"author": "alice",
|
|
"tags": [{"name": "prod"}],
|
|
}
|