mirror of
https://github.com/langgenius/dify.git
synced 2026-09-08 02:43:49 +08:00
refactor(api): migrate dataset rag pipeline endpoints to BaseModel (#37958)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
This commit is contained in:
parent
c3b1508712
commit
77ae583b44
@ -6,7 +6,7 @@ from pydantic import BaseModel, Field
|
|||||||
from werkzeug.exceptions import Forbidden, NotFound
|
from werkzeug.exceptions import Forbidden, NotFound
|
||||||
|
|
||||||
from configs import dify_config
|
from configs import dify_config
|
||||||
from controllers.common.fields import RedirectResponse, SimpleResultResponse
|
from controllers.common.fields import SimpleResultResponse
|
||||||
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
|
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
|
||||||
from controllers.console import console_ns
|
from controllers.console import console_ns
|
||||||
from controllers.console.wraps import (
|
from controllers.console.wraps import (
|
||||||
@ -19,11 +19,13 @@ from controllers.console.wraps import (
|
|||||||
with_current_tenant_id,
|
with_current_tenant_id,
|
||||||
with_current_user,
|
with_current_user,
|
||||||
)
|
)
|
||||||
|
from core.entities.provider_entities import ProviderConfig
|
||||||
from core.plugin.entities.plugin_daemon import PluginOAuthAuthorizationUrlResponse
|
from core.plugin.entities.plugin_daemon import PluginOAuthAuthorizationUrlResponse
|
||||||
from core.plugin.impl.oauth import OAuthHandler
|
from core.plugin.impl.oauth import OAuthHandler
|
||||||
|
from core.tools.entities.common_entities import I18nObject
|
||||||
from fields.base import ResponseModel
|
from fields.base import ResponseModel
|
||||||
from graphon.model_runtime.errors.validate import CredentialsValidateFailedError
|
from graphon.model_runtime.errors.validate import CredentialsValidateFailedError
|
||||||
from graphon.model_runtime.utils.encoders import jsonable_encoder
|
from libs.helper import dump_response
|
||||||
from libs.login import login_required
|
from libs.login import login_required
|
||||||
from models import Account
|
from models import Account
|
||||||
from models.provider_ids import DatasourceProviderID
|
from models.provider_ids import DatasourceProviderID
|
||||||
@ -33,7 +35,9 @@ from services.plugin.oauth_service import OAuthProxyService
|
|||||||
|
|
||||||
class DatasourceCredentialPayload(BaseModel):
|
class DatasourceCredentialPayload(BaseModel):
|
||||||
name: str | None = Field(default=None, max_length=100)
|
name: str | None = Field(default=None, max_length=100)
|
||||||
credentials: dict[str, Any]
|
credentials: dict[str, Any] = Field(
|
||||||
|
description="Plugin-defined credential parameters. The schema is declared by the datasource provider."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DatasourceCredentialDeletePayload(BaseModel):
|
class DatasourceCredentialDeletePayload(BaseModel):
|
||||||
@ -43,11 +47,17 @@ class DatasourceCredentialDeletePayload(BaseModel):
|
|||||||
class DatasourceCredentialUpdatePayload(BaseModel):
|
class DatasourceCredentialUpdatePayload(BaseModel):
|
||||||
credential_id: str
|
credential_id: str
|
||||||
name: str | None = Field(default=None, max_length=100)
|
name: str | None = Field(default=None, max_length=100)
|
||||||
credentials: dict[str, Any] | None = Field(default=None)
|
credentials: dict[str, Any] | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="Plugin-defined credential parameters. The schema is declared by the datasource provider.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DatasourceCustomClientPayload(BaseModel):
|
class DatasourceCustomClientPayload(BaseModel):
|
||||||
client_params: dict[str, Any] | None = Field(default=None)
|
client_params: dict[str, Any] | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="Plugin-defined OAuth client parameters. The schema is declared by the datasource provider.",
|
||||||
|
)
|
||||||
enable_oauth_custom_client: bool | None = None
|
enable_oauth_custom_client: bool | None = None
|
||||||
|
|
||||||
|
|
||||||
@ -71,8 +81,48 @@ class DatasourceOAuthCallbackQuery(BaseModel):
|
|||||||
context_id: str | None = Field(default=None, description="OAuth proxy context ID")
|
context_id: str | None = Field(default=None, description="OAuth proxy context ID")
|
||||||
|
|
||||||
|
|
||||||
class DatasourceCredentialsResponse(ResponseModel):
|
class DatasourceCredentialResponse(ResponseModel):
|
||||||
result: Any
|
credential: dict[str, Any] = Field(
|
||||||
|
description="Obfuscated plugin-defined credential parameters from the datasource provider."
|
||||||
|
)
|
||||||
|
type: str
|
||||||
|
name: str
|
||||||
|
avatar_url: str | None
|
||||||
|
id: str
|
||||||
|
is_default: bool
|
||||||
|
|
||||||
|
|
||||||
|
class DatasourceCredentialListResponse(ResponseModel):
|
||||||
|
result: list[DatasourceCredentialResponse]
|
||||||
|
|
||||||
|
|
||||||
|
class DatasourceOAuthSchemaResponse(ResponseModel):
|
||||||
|
client_schema: list[ProviderConfig]
|
||||||
|
credentials_schema: list[ProviderConfig]
|
||||||
|
oauth_custom_client_params: dict[str, Any] | None = Field(
|
||||||
|
description="Masked plugin-defined OAuth client parameters, when configured for the tenant."
|
||||||
|
)
|
||||||
|
is_oauth_custom_client_enabled: bool
|
||||||
|
is_system_oauth_params_exists: bool
|
||||||
|
redirect_uri: str
|
||||||
|
|
||||||
|
|
||||||
|
class DatasourceProviderAuthResponse(ResponseModel):
|
||||||
|
author: str
|
||||||
|
provider: str
|
||||||
|
plugin_id: str
|
||||||
|
plugin_unique_identifier: str
|
||||||
|
icon: str
|
||||||
|
name: str
|
||||||
|
label: I18nObject
|
||||||
|
description: I18nObject
|
||||||
|
credential_schema: list[ProviderConfig]
|
||||||
|
oauth_schema: DatasourceOAuthSchemaResponse | None
|
||||||
|
credentials_list: list[DatasourceCredentialResponse]
|
||||||
|
|
||||||
|
|
||||||
|
class DatasourceProviderAuthListResponse(ResponseModel):
|
||||||
|
result: list[DatasourceProviderAuthResponse]
|
||||||
|
|
||||||
|
|
||||||
register_schema_models(
|
register_schema_models(
|
||||||
@ -88,9 +138,9 @@ register_schema_models(
|
|||||||
)
|
)
|
||||||
register_response_schema_models(
|
register_response_schema_models(
|
||||||
console_ns,
|
console_ns,
|
||||||
DatasourceCredentialsResponse,
|
DatasourceCredentialListResponse,
|
||||||
|
DatasourceProviderAuthListResponse,
|
||||||
PluginOAuthAuthorizationUrlResponse,
|
PluginOAuthAuthorizationUrlResponse,
|
||||||
RedirectResponse,
|
|
||||||
SimpleResultResponse,
|
SimpleResultResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -100,7 +150,7 @@ class DatasourcePluginOAuthAuthorizationUrl(Resource):
|
|||||||
@console_ns.doc(params=query_params_from_model(DatasourceOAuthAuthorizationQuery))
|
@console_ns.doc(params=query_params_from_model(DatasourceOAuthAuthorizationQuery))
|
||||||
@console_ns.response(
|
@console_ns.response(
|
||||||
200,
|
200,
|
||||||
"Authorization URL retrieved successfully",
|
"Datasource OAuth authorization URL generated successfully",
|
||||||
console_ns.models[PluginOAuthAuthorizationUrlResponse.__name__],
|
console_ns.models[PluginOAuthAuthorizationUrlResponse.__name__],
|
||||||
)
|
)
|
||||||
@setup_required
|
@setup_required
|
||||||
@ -140,7 +190,8 @@ class DatasourcePluginOAuthAuthorizationUrl(Resource):
|
|||||||
redirect_uri=redirect_uri,
|
redirect_uri=redirect_uri,
|
||||||
system_credentials=oauth_config,
|
system_credentials=oauth_config,
|
||||||
)
|
)
|
||||||
response = make_response(jsonable_encoder(authorization_url_response))
|
# response-contract:ignore cookie-bearing Flask response
|
||||||
|
response = make_response(dump_response(PluginOAuthAuthorizationUrlResponse, authorization_url_response))
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
"context_id",
|
"context_id",
|
||||||
context_id,
|
context_id,
|
||||||
@ -154,11 +205,8 @@ class DatasourcePluginOAuthAuthorizationUrl(Resource):
|
|||||||
@console_ns.route("/oauth/plugin/<path:provider_id>/datasource/callback")
|
@console_ns.route("/oauth/plugin/<path:provider_id>/datasource/callback")
|
||||||
class DatasourceOAuthCallback(Resource):
|
class DatasourceOAuthCallback(Resource):
|
||||||
@console_ns.doc(params=query_params_from_model(DatasourceOAuthCallbackQuery))
|
@console_ns.doc(params=query_params_from_model(DatasourceOAuthCallbackQuery))
|
||||||
@console_ns.response(
|
# response-contract:ignore redirect response
|
||||||
302,
|
@console_ns.response(302, "Redirect to OAuth callback page")
|
||||||
"Redirect to console OAuth callback page",
|
|
||||||
console_ns.models[RedirectResponse.__name__],
|
|
||||||
)
|
|
||||||
@setup_required
|
@setup_required
|
||||||
def get(self, provider_id: str):
|
def get(self, provider_id: str):
|
||||||
context_id = request.cookies.get("context_id") or request.args.get("context_id")
|
context_id = request.cookies.get("context_id") or request.args.get("context_id")
|
||||||
@ -217,7 +265,9 @@ class DatasourceOAuthCallback(Resource):
|
|||||||
@console_ns.route("/auth/plugin/datasource/<path:provider_id>")
|
@console_ns.route("/auth/plugin/datasource/<path:provider_id>")
|
||||||
class DatasourceAuth(Resource):
|
class DatasourceAuth(Resource):
|
||||||
@console_ns.expect(console_ns.models[DatasourceCredentialPayload.__name__])
|
@console_ns.expect(console_ns.models[DatasourceCredentialPayload.__name__])
|
||||||
@console_ns.response(200, "Success", console_ns.models[SimpleResultResponse.__name__])
|
@console_ns.response(
|
||||||
|
200, "Datasource credential created successfully", console_ns.models[SimpleResultResponse.__name__]
|
||||||
|
)
|
||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@ -238,12 +288,16 @@ class DatasourceAuth(Resource):
|
|||||||
)
|
)
|
||||||
except CredentialsValidateFailedError as ex:
|
except CredentialsValidateFailedError as ex:
|
||||||
raise ValueError(str(ex))
|
raise ValueError(str(ex))
|
||||||
return {"result": "success"}, 200
|
return SimpleResultResponse(result="success").model_dump(mode="json"), 200
|
||||||
|
|
||||||
|
@console_ns.response(
|
||||||
|
200,
|
||||||
|
"Datasource credentials retrieved successfully",
|
||||||
|
console_ns.models[DatasourceCredentialListResponse.__name__],
|
||||||
|
)
|
||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@console_ns.response(200, "Success", console_ns.models[DatasourceCredentialsResponse.__name__])
|
|
||||||
@with_current_user
|
@with_current_user
|
||||||
@with_current_tenant_id
|
@with_current_tenant_id
|
||||||
def get(self, current_tenant_id: str, user: Account, provider_id: str):
|
def get(self, current_tenant_id: str, user: Account, provider_id: str):
|
||||||
@ -256,7 +310,7 @@ class DatasourceAuth(Resource):
|
|||||||
plugin_id=datasource_provider_id.plugin_id,
|
plugin_id=datasource_provider_id.plugin_id,
|
||||||
user=user,
|
user=user,
|
||||||
)
|
)
|
||||||
return {"result": datasources}, 200
|
return dump_response(DatasourceCredentialListResponse, {"result": datasources}), 200
|
||||||
|
|
||||||
|
|
||||||
@console_ns.route("/auth/plugin/datasource/<path:provider_id>/delete")
|
@console_ns.route("/auth/plugin/datasource/<path:provider_id>/delete")
|
||||||
@ -282,13 +336,15 @@ class DatasourceAuthDeleteApi(Resource):
|
|||||||
provider=provider_name,
|
provider=provider_name,
|
||||||
plugin_id=plugin_id,
|
plugin_id=plugin_id,
|
||||||
)
|
)
|
||||||
return {"result": "success"}, 200
|
return SimpleResultResponse(result="success").model_dump(mode="json"), 200
|
||||||
|
|
||||||
|
|
||||||
@console_ns.route("/auth/plugin/datasource/<path:provider_id>/update")
|
@console_ns.route("/auth/plugin/datasource/<path:provider_id>/update")
|
||||||
class DatasourceAuthUpdateApi(Resource):
|
class DatasourceAuthUpdateApi(Resource):
|
||||||
@console_ns.expect(console_ns.models[DatasourceCredentialUpdatePayload.__name__])
|
@console_ns.expect(console_ns.models[DatasourceCredentialUpdatePayload.__name__])
|
||||||
@console_ns.response(201, "Success", console_ns.models[SimpleResultResponse.__name__])
|
@console_ns.response(
|
||||||
|
201, "Datasource credential updated successfully", console_ns.models[SimpleResultResponse.__name__]
|
||||||
|
)
|
||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@ -308,12 +364,16 @@ class DatasourceAuthUpdateApi(Resource):
|
|||||||
credentials=payload.credentials or {},
|
credentials=payload.credentials or {},
|
||||||
name=payload.name,
|
name=payload.name,
|
||||||
)
|
)
|
||||||
return {"result": "success"}, 201
|
return SimpleResultResponse(result="success").model_dump(mode="json"), 201
|
||||||
|
|
||||||
|
|
||||||
@console_ns.route("/auth/plugin/datasource/list")
|
@console_ns.route("/auth/plugin/datasource/list")
|
||||||
class DatasourceAuthListApi(Resource):
|
class DatasourceAuthListApi(Resource):
|
||||||
@console_ns.response(200, "Success", console_ns.models[DatasourceCredentialsResponse.__name__])
|
@console_ns.response(
|
||||||
|
200,
|
||||||
|
"Datasource credentials retrieved successfully",
|
||||||
|
console_ns.models[DatasourceProviderAuthListResponse.__name__],
|
||||||
|
)
|
||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@ -321,12 +381,16 @@ class DatasourceAuthListApi(Resource):
|
|||||||
def get(self, current_tenant_id: str):
|
def get(self, current_tenant_id: str):
|
||||||
datasource_provider_service = DatasourceProviderService()
|
datasource_provider_service = DatasourceProviderService()
|
||||||
datasources = datasource_provider_service.get_all_datasource_credentials(tenant_id=current_tenant_id)
|
datasources = datasource_provider_service.get_all_datasource_credentials(tenant_id=current_tenant_id)
|
||||||
return {"result": jsonable_encoder(datasources)}, 200
|
return dump_response(DatasourceProviderAuthListResponse, {"result": datasources}), 200
|
||||||
|
|
||||||
|
|
||||||
@console_ns.route("/auth/plugin/datasource/default-list")
|
@console_ns.route("/auth/plugin/datasource/default-list")
|
||||||
class DatasourceHardCodeAuthListApi(Resource):
|
class DatasourceHardCodeAuthListApi(Resource):
|
||||||
@console_ns.response(200, "Success", console_ns.models[DatasourceCredentialsResponse.__name__])
|
@console_ns.response(
|
||||||
|
200,
|
||||||
|
"Default datasource credentials retrieved successfully",
|
||||||
|
console_ns.models[DatasourceProviderAuthListResponse.__name__],
|
||||||
|
)
|
||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@ -334,13 +398,15 @@ class DatasourceHardCodeAuthListApi(Resource):
|
|||||||
def get(self, current_tenant_id: str):
|
def get(self, current_tenant_id: str):
|
||||||
datasource_provider_service = DatasourceProviderService()
|
datasource_provider_service = DatasourceProviderService()
|
||||||
datasources = datasource_provider_service.get_hard_code_datasource_credentials(tenant_id=current_tenant_id)
|
datasources = datasource_provider_service.get_hard_code_datasource_credentials(tenant_id=current_tenant_id)
|
||||||
return {"result": jsonable_encoder(datasources)}, 200
|
return dump_response(DatasourceProviderAuthListResponse, {"result": datasources}), 200
|
||||||
|
|
||||||
|
|
||||||
@console_ns.route("/auth/plugin/datasource/<path:provider_id>/custom-client")
|
@console_ns.route("/auth/plugin/datasource/<path:provider_id>/custom-client")
|
||||||
class DatasourceAuthOauthCustomClient(Resource):
|
class DatasourceAuthOauthCustomClient(Resource):
|
||||||
@console_ns.expect(console_ns.models[DatasourceCustomClientPayload.__name__])
|
@console_ns.expect(console_ns.models[DatasourceCustomClientPayload.__name__])
|
||||||
@console_ns.response(200, "Success", console_ns.models[SimpleResultResponse.__name__])
|
@console_ns.response(
|
||||||
|
200, "Datasource OAuth custom client saved successfully", console_ns.models[SimpleResultResponse.__name__]
|
||||||
|
)
|
||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@ -357,7 +423,7 @@ class DatasourceAuthOauthCustomClient(Resource):
|
|||||||
client_params=payload.client_params or {},
|
client_params=payload.client_params or {},
|
||||||
enabled=payload.enable_oauth_custom_client or False,
|
enabled=payload.enable_oauth_custom_client or False,
|
||||||
)
|
)
|
||||||
return {"result": "success"}, 200
|
return SimpleResultResponse(result="success").model_dump(mode="json"), 200
|
||||||
|
|
||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@ -371,7 +437,7 @@ class DatasourceAuthOauthCustomClient(Resource):
|
|||||||
tenant_id=current_tenant_id,
|
tenant_id=current_tenant_id,
|
||||||
datasource_provider_id=datasource_provider_id,
|
datasource_provider_id=datasource_provider_id,
|
||||||
)
|
)
|
||||||
return {"result": "success"}, 200
|
return SimpleResultResponse(result="success").model_dump(mode="json"), 200
|
||||||
|
|
||||||
|
|
||||||
@console_ns.route("/auth/plugin/datasource/<path:provider_id>/default")
|
@console_ns.route("/auth/plugin/datasource/<path:provider_id>/default")
|
||||||
@ -393,7 +459,7 @@ class DatasourceAuthDefaultApi(Resource):
|
|||||||
datasource_provider_id=datasource_provider_id,
|
datasource_provider_id=datasource_provider_id,
|
||||||
credential_id=payload.id,
|
credential_id=payload.id,
|
||||||
)
|
)
|
||||||
return {"result": "success"}, 200
|
return SimpleResultResponse(result="success").model_dump(mode="json"), 200
|
||||||
|
|
||||||
|
|
||||||
@console_ns.route("/auth/plugin/datasource/<path:provider_id>/update-name")
|
@console_ns.route("/auth/plugin/datasource/<path:provider_id>/update-name")
|
||||||
@ -416,4 +482,4 @@ class DatasourceUpdateProviderNameApi(Resource):
|
|||||||
name=payload.name,
|
name=payload.name,
|
||||||
credential_id=payload.credential_id,
|
credential_id=payload.credential_id,
|
||||||
)
|
)
|
||||||
return {"result": "success"}, 200
|
return SimpleResultResponse(result="success").model_dump(mode="json"), 200
|
||||||
|
|||||||
@ -3,9 +3,9 @@ from typing import Any
|
|||||||
from flask_restx import ( # type: ignore
|
from flask_restx import ( # type: ignore
|
||||||
Resource, # type: ignore
|
Resource, # type: ignore
|
||||||
)
|
)
|
||||||
from pydantic import BaseModel, RootModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from controllers.common.schema import register_response_schema_models, register_schema_models
|
from controllers.common.schema import register_schema_models
|
||||||
from controllers.console import console_ns
|
from controllers.console import console_ns
|
||||||
from controllers.console.datasets.wraps import get_rag_pipeline
|
from controllers.console.datasets.wraps import get_rag_pipeline
|
||||||
from controllers.console.wraps import account_initialization_required, setup_required, with_current_user
|
from controllers.console.wraps import account_initialization_required, setup_required, with_current_user
|
||||||
@ -21,18 +21,13 @@ class Parser(BaseModel):
|
|||||||
credential_id: str | None = None
|
credential_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class DataSourceContentPreviewResponse(RootModel[Any]):
|
|
||||||
root: Any
|
|
||||||
|
|
||||||
|
|
||||||
register_schema_models(console_ns, Parser)
|
register_schema_models(console_ns, Parser)
|
||||||
register_response_schema_models(console_ns, DataSourceContentPreviewResponse)
|
|
||||||
|
|
||||||
|
|
||||||
@console_ns.route("/rag/pipelines/<uuid:pipeline_id>/workflows/published/datasource/nodes/<string:node_id>/preview")
|
@console_ns.route("/rag/pipelines/<uuid:pipeline_id>/workflows/published/datasource/nodes/<string:node_id>/preview")
|
||||||
class DataSourceContentPreviewApi(Resource):
|
class DataSourceContentPreviewApi(Resource):
|
||||||
@console_ns.expect(console_ns.models[Parser.__name__])
|
@console_ns.expect(console_ns.models[Parser.__name__])
|
||||||
@console_ns.response(200, "Success", console_ns.models[DataSourceContentPreviewResponse.__name__])
|
@console_ns.response(200, "Success")
|
||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from flask import request
|
from flask import request
|
||||||
from pydantic import BaseModel, Field, RootModel
|
from pydantic import BaseModel, Field, RootModel, field_validator
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from werkzeug.exceptions import Forbidden, NotFound
|
from werkzeug.exceptions import Forbidden, NotFound
|
||||||
@ -13,24 +14,20 @@ from controllers.common.errors import FilenameNotExistsError, NoFileUploadedErro
|
|||||||
from controllers.common.fields import GeneratedAppResponse
|
from controllers.common.fields import GeneratedAppResponse
|
||||||
from controllers.common.schema import (
|
from controllers.common.schema import (
|
||||||
query_params_from_model,
|
query_params_from_model,
|
||||||
|
query_params_from_request,
|
||||||
register_response_schema_models,
|
register_response_schema_models,
|
||||||
register_schema_model,
|
register_schema_model,
|
||||||
register_schema_models,
|
|
||||||
)
|
)
|
||||||
from controllers.console.app.wraps import with_session
|
from controllers.console.app.wraps import with_session
|
||||||
from controllers.service_api import service_api_ns
|
from controllers.service_api import service_api_ns
|
||||||
from controllers.service_api.dataset.error import PipelineRunError
|
from controllers.service_api.dataset.error import PipelineRunError
|
||||||
from controllers.service_api.dataset.rag_pipeline.serializers import serialize_upload_file
|
from controllers.service_api.schema import event_stream_response, json_or_event_stream_response, multipart_file_params
|
||||||
from controllers.service_api.schema import (
|
|
||||||
event_stream_response,
|
|
||||||
json_or_event_stream_response,
|
|
||||||
multipart_file_params,
|
|
||||||
)
|
|
||||||
from controllers.service_api.wraps import DatasetApiResource
|
from controllers.service_api.wraps import DatasetApiResource
|
||||||
from core.app.apps.pipeline.pipeline_generator import PipelineGenerator
|
from core.app.apps.pipeline.pipeline_generator import PipelineGenerator
|
||||||
from core.app.entities.app_invoke_entities import InvokeFrom
|
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||||
from fields.base import ResponseModel
|
from fields.base import ResponseModel
|
||||||
from libs import helper
|
from libs import helper
|
||||||
|
from libs.helper import dump_response
|
||||||
from libs.login import current_user
|
from libs.login import current_user
|
||||||
from models import Account
|
from models import Account
|
||||||
from models.dataset import Dataset, Pipeline
|
from models.dataset import Dataset, Pipeline
|
||||||
@ -84,7 +81,7 @@ class DatasourcePluginResponse(ResponseModel):
|
|||||||
datasource_type: str | None = None
|
datasource_type: str | None = None
|
||||||
title: str | None = None
|
title: str | None = None
|
||||||
user_input_variables: list[dict[str, Any]] = Field(default_factory=list)
|
user_input_variables: list[dict[str, Any]] = Field(default_factory=list)
|
||||||
credentials: list[DatasourceCredentialInfoResponse]
|
credentials: list[DatasourceCredentialInfoResponse] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
class DatasourcePluginListResponse(RootModel[list[DatasourcePluginResponse]]):
|
class DatasourcePluginListResponse(RootModel[list[DatasourcePluginResponse]]):
|
||||||
@ -100,14 +97,22 @@ class PipelineUploadFileResponse(ResponseModel):
|
|||||||
created_by: str
|
created_by: str
|
||||||
created_at: str | None = None
|
created_at: str | None = None
|
||||||
|
|
||||||
|
@field_validator("created_at", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def _normalize_created_at(cls, value: datetime | str | None) -> str | None:
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
return value.isoformat()
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
register_schema_model(service_api_ns, DatasourceNodeRunPayload)
|
register_schema_model(service_api_ns, DatasourceNodeRunPayload)
|
||||||
|
register_schema_model(service_api_ns, DatasourcePluginsQuery)
|
||||||
register_schema_model(service_api_ns, PipelineRunApiEntity)
|
register_schema_model(service_api_ns, PipelineRunApiEntity)
|
||||||
register_schema_models(service_api_ns, DatasourcePluginsQuery)
|
|
||||||
register_response_schema_models(
|
register_response_schema_models(
|
||||||
service_api_ns,
|
service_api_ns,
|
||||||
|
DatasourceCredentialInfoResponse,
|
||||||
|
DatasourcePluginResponse,
|
||||||
DatasourcePluginListResponse,
|
DatasourcePluginListResponse,
|
||||||
GeneratedAppResponse,
|
|
||||||
PipelineUploadFileResponse,
|
PipelineUploadFileResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -119,8 +124,8 @@ class DatasourcePluginsApi(DatasetApiResource):
|
|||||||
@service_api_ns.doc(
|
@service_api_ns.doc(
|
||||||
summary="List Datasource Plugins",
|
summary="List Datasource Plugins",
|
||||||
description=(
|
description=(
|
||||||
"List the datasource nodes configured in the knowledge pipeline. Each node includes the "
|
"List the datasource nodes configured in the knowledge pipeline. Each node includes the plugin it uses "
|
||||||
"plugin it uses plus the metadata needed to run it."
|
"plus the metadata needed to run it."
|
||||||
),
|
),
|
||||||
tags=["Knowledge Pipeline"],
|
tags=["Knowledge Pipeline"],
|
||||||
responses={
|
responses={
|
||||||
@ -152,14 +157,13 @@ class DatasourcePluginsApi(DatasetApiResource):
|
|||||||
if not dataset:
|
if not dataset:
|
||||||
raise NotFound("Dataset not found.")
|
raise NotFound("Dataset not found.")
|
||||||
|
|
||||||
# Get query parameter to determine published or draft
|
query = query_params_from_request(DatasourcePluginsQuery)
|
||||||
is_published: bool = request.args.get("is_published", default=True, type=bool)
|
|
||||||
|
|
||||||
rag_pipeline_service: RagPipelineService = RagPipelineService()
|
rag_pipeline_service: RagPipelineService = RagPipelineService()
|
||||||
datasource_plugins: list[dict[Any, Any]] = rag_pipeline_service.get_datasource_plugins(
|
datasource_plugins: list[dict[Any, Any]] = rag_pipeline_service.get_datasource_plugins(
|
||||||
tenant_id=tenant_id, dataset_id=dataset_id_str, is_published=is_published
|
tenant_id=tenant_id, dataset_id=dataset_id_str, is_published=query.is_published
|
||||||
)
|
)
|
||||||
return datasource_plugins, 200
|
return dump_response(DatasourcePluginListResponse, datasource_plugins), 200
|
||||||
|
|
||||||
|
|
||||||
@service_api_ns.route("/datasets/<uuid:dataset_id>/pipeline/datasource/nodes/<string:node_id>/run")
|
@service_api_ns.route("/datasets/<uuid:dataset_id>/pipeline/datasource/nodes/<string:node_id>/run")
|
||||||
@ -169,8 +173,8 @@ class DatasourceNodeRunApi(DatasetApiResource):
|
|||||||
@service_api_ns.doc(
|
@service_api_ns.doc(
|
||||||
summary="Run Datasource Node",
|
summary="Run Datasource Node",
|
||||||
description=(
|
description=(
|
||||||
"Execute a single datasource node within the knowledge pipeline. Returns a streaming "
|
"Execute a single datasource node within the knowledge pipeline. Returns a streaming response with the "
|
||||||
"response with the node execution results."
|
"node execution results."
|
||||||
),
|
),
|
||||||
tags=["Knowledge Pipeline"],
|
tags=["Knowledge Pipeline"],
|
||||||
responses={
|
responses={
|
||||||
@ -189,11 +193,6 @@ class DatasourceNodeRunApi(DatasetApiResource):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
@service_api_ns.expect(service_api_ns.models[DatasourceNodeRunPayload.__name__])
|
@service_api_ns.expect(service_api_ns.models[DatasourceNodeRunPayload.__name__])
|
||||||
@service_api_ns.response(
|
|
||||||
200,
|
|
||||||
"Datasource node run successfully",
|
|
||||||
service_api_ns.models[GeneratedAppResponse.__name__],
|
|
||||||
)
|
|
||||||
def post(self, tenant_id: str, dataset_id: UUID, node_id: str):
|
def post(self, tenant_id: str, dataset_id: UUID, node_id: str):
|
||||||
"""Resource for getting datasource plugins."""
|
"""Resource for getting datasource plugins."""
|
||||||
dataset_id_str = str(dataset_id)
|
dataset_id_str = str(dataset_id)
|
||||||
@ -210,10 +209,11 @@ class DatasourceNodeRunApi(DatasetApiResource):
|
|||||||
datasource_node_run_api_entity = DatasourceNodeRunApiEntity.model_validate(
|
datasource_node_run_api_entity = DatasourceNodeRunApiEntity.model_validate(
|
||||||
{
|
{
|
||||||
**payload.model_dump(exclude_none=True),
|
**payload.model_dump(exclude_none=True),
|
||||||
"pipeline_id": str(pipeline.id),
|
"pipeline_id": pipeline.id,
|
||||||
"node_id": node_id,
|
"node_id": node_id,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
# response-contract:ignore compact_generate_response
|
||||||
return helper.compact_generate_response(
|
return helper.compact_generate_response(
|
||||||
PipelineGenerator.convert_to_event_stream(
|
PipelineGenerator.convert_to_event_stream(
|
||||||
rag_pipeline_service.run_datasource_workflow_node(
|
rag_pipeline_service.run_datasource_workflow_node(
|
||||||
@ -236,8 +236,8 @@ class PipelineRunApi(DatasetApiResource):
|
|||||||
@service_api_ns.doc(
|
@service_api_ns.doc(
|
||||||
summary="Run Pipeline",
|
summary="Run Pipeline",
|
||||||
description=(
|
description=(
|
||||||
"Execute the full knowledge pipeline for a knowledge base. Supports both streaming and "
|
"Execute the full knowledge pipeline for a knowledge base. Supports both streaming and blocking response "
|
||||||
"blocking response modes."
|
"modes."
|
||||||
),
|
),
|
||||||
tags=["Knowledge Pipeline"],
|
tags=["Knowledge Pipeline"],
|
||||||
responses={
|
responses={
|
||||||
@ -293,6 +293,7 @@ class PipelineRunApi(DatasetApiResource):
|
|||||||
streaming=payload.response_mode == "streaming",
|
streaming=payload.response_mode == "streaming",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# response-contract:ignore compact_generate_response
|
||||||
return helper.compact_generate_response(response)
|
return helper.compact_generate_response(response)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
raise PipelineRunError(description=str(ex))
|
raise PipelineRunError(description=str(ex))
|
||||||
@ -368,4 +369,4 @@ class KnowledgebasePipelineFileUploadApi(DatasetApiResource):
|
|||||||
except services.errors.file.UnsupportedFileTypeError:
|
except services.errors.file.UnsupportedFileTypeError:
|
||||||
raise UnsupportedFileTypeError()
|
raise UnsupportedFileTypeError()
|
||||||
|
|
||||||
return serialize_upload_file(upload_file), 201
|
return dump_response(PipelineUploadFileResponse, upload_file), 201
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
"""
|
|
||||||
Serialization helpers for Service API knowledge pipeline endpoints.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, TypedDict
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from models.model import UploadFile
|
|
||||||
|
|
||||||
|
|
||||||
class UploadFileDict(TypedDict):
|
|
||||||
id: str
|
|
||||||
name: str
|
|
||||||
size: int
|
|
||||||
extension: str
|
|
||||||
mime_type: str | None
|
|
||||||
created_by: str
|
|
||||||
created_at: str | None
|
|
||||||
|
|
||||||
|
|
||||||
def serialize_upload_file(upload_file: UploadFile) -> UploadFileDict:
|
|
||||||
return {
|
|
||||||
"id": upload_file.id,
|
|
||||||
"name": upload_file.name,
|
|
||||||
"size": upload_file.size,
|
|
||||||
"extension": upload_file.extension,
|
|
||||||
"mime_type": upload_file.mime_type,
|
|
||||||
"created_by": upload_file.created_by,
|
|
||||||
"created_at": upload_file.created_at.isoformat() if upload_file.created_at else None,
|
|
||||||
}
|
|
||||||
@ -5106,14 +5106,14 @@ Refresh MCP server configuration and regenerate server code
|
|||||||
|
|
||||||
| Code | Description | Schema |
|
| Code | Description | Schema |
|
||||||
| ---- | ----------- | ------ |
|
| ---- | ----------- | ------ |
|
||||||
| 200 | Success | **application/json**: [DatasourceCredentialsResponse](#datasourcecredentialsresponse)<br> |
|
| 200 | Default datasource credentials retrieved successfully | **application/json**: [DatasourceProviderAuthListResponse](#datasourceproviderauthlistresponse)<br> |
|
||||||
|
|
||||||
### [GET] /auth/plugin/datasource/list
|
### [GET] /auth/plugin/datasource/list
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Code | Description | Schema |
|
| Code | Description | Schema |
|
||||||
| ---- | ----------- | ------ |
|
| ---- | ----------- | ------ |
|
||||||
| 200 | Success | **application/json**: [DatasourceCredentialsResponse](#datasourcecredentialsresponse)<br> |
|
| 200 | Datasource credentials retrieved successfully | **application/json**: [DatasourceProviderAuthListResponse](#datasourceproviderauthlistresponse)<br> |
|
||||||
|
|
||||||
### [GET] /auth/plugin/datasource/{provider_id}
|
### [GET] /auth/plugin/datasource/{provider_id}
|
||||||
#### Parameters
|
#### Parameters
|
||||||
@ -5126,7 +5126,7 @@ Refresh MCP server configuration and regenerate server code
|
|||||||
|
|
||||||
| Code | Description | Schema |
|
| Code | Description | Schema |
|
||||||
| ---- | ----------- | ------ |
|
| ---- | ----------- | ------ |
|
||||||
| 200 | Success | **application/json**: [DatasourceCredentialsResponse](#datasourcecredentialsresponse)<br> |
|
| 200 | Datasource credentials retrieved successfully | **application/json**: [DatasourceCredentialListResponse](#datasourcecredentiallistresponse)<br> |
|
||||||
|
|
||||||
### [POST] /auth/plugin/datasource/{provider_id}
|
### [POST] /auth/plugin/datasource/{provider_id}
|
||||||
#### Parameters
|
#### Parameters
|
||||||
@ -5145,7 +5145,7 @@ Refresh MCP server configuration and regenerate server code
|
|||||||
|
|
||||||
| Code | Description | Schema |
|
| Code | Description | Schema |
|
||||||
| ---- | ----------- | ------ |
|
| ---- | ----------- | ------ |
|
||||||
| 200 | Success | **application/json**: [SimpleResultResponse](#simpleresultresponse)<br> |
|
| 200 | Datasource credential created successfully | **application/json**: [SimpleResultResponse](#simpleresultresponse)<br> |
|
||||||
|
|
||||||
### [DELETE] /auth/plugin/datasource/{provider_id}/custom-client
|
### [DELETE] /auth/plugin/datasource/{provider_id}/custom-client
|
||||||
#### Parameters
|
#### Parameters
|
||||||
@ -5177,7 +5177,7 @@ Refresh MCP server configuration and regenerate server code
|
|||||||
|
|
||||||
| Code | Description | Schema |
|
| Code | Description | Schema |
|
||||||
| ---- | ----------- | ------ |
|
| ---- | ----------- | ------ |
|
||||||
| 200 | Success | **application/json**: [SimpleResultResponse](#simpleresultresponse)<br> |
|
| 200 | Datasource OAuth custom client saved successfully | **application/json**: [SimpleResultResponse](#simpleresultresponse)<br> |
|
||||||
|
|
||||||
### [POST] /auth/plugin/datasource/{provider_id}/default
|
### [POST] /auth/plugin/datasource/{provider_id}/default
|
||||||
#### Parameters
|
#### Parameters
|
||||||
@ -5234,7 +5234,7 @@ Refresh MCP server configuration and regenerate server code
|
|||||||
|
|
||||||
| Code | Description | Schema |
|
| Code | Description | Schema |
|
||||||
| ---- | ----------- | ------ |
|
| ---- | ----------- | ------ |
|
||||||
| 201 | Success | **application/json**: [SimpleResultResponse](#simpleresultresponse)<br> |
|
| 201 | Datasource credential updated successfully | **application/json**: [SimpleResultResponse](#simpleresultresponse)<br> |
|
||||||
|
|
||||||
### [POST] /auth/plugin/datasource/{provider_id}/update-name
|
### [POST] /auth/plugin/datasource/{provider_id}/update-name
|
||||||
#### Parameters
|
#### Parameters
|
||||||
@ -7638,9 +7638,9 @@ Initiate OAuth login process
|
|||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Code | Description | Schema |
|
| Code | Description |
|
||||||
| ---- | ----------- | ------ |
|
| ---- | ----------- |
|
||||||
| 302 | Redirect to console OAuth callback page | **application/json**: [RedirectResponse](#redirectresponse)<br> |
|
| 302 | Redirect to OAuth callback page |
|
||||||
|
|
||||||
### [GET] /oauth/plugin/{provider_id}/datasource/get-authorization-url
|
### [GET] /oauth/plugin/{provider_id}/datasource/get-authorization-url
|
||||||
#### Parameters
|
#### Parameters
|
||||||
@ -7654,7 +7654,7 @@ Initiate OAuth login process
|
|||||||
|
|
||||||
| Code | Description | Schema |
|
| Code | Description | Schema |
|
||||||
| ---- | ----------- | ------ |
|
| ---- | ----------- | ------ |
|
||||||
| 200 | Authorization URL retrieved successfully | **application/json**: [PluginOAuthAuthorizationUrlResponse](#pluginoauthauthorizationurlresponse)<br> |
|
| 200 | Datasource OAuth authorization URL generated successfully | **application/json**: [PluginOAuthAuthorizationUrlResponse](#pluginoauthauthorizationurlresponse)<br> |
|
||||||
|
|
||||||
### [GET] /oauth/plugin/{provider}/tool/authorization-url
|
### [GET] /oauth/plugin/{provider}/tool/authorization-url
|
||||||
#### Parameters
|
#### Parameters
|
||||||
@ -8477,9 +8477,9 @@ Initiate OAuth login process
|
|||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Code | Description | Schema |
|
| Code | Description |
|
||||||
| ---- | ----------- | ------ |
|
| ---- | ----------- |
|
||||||
| 200 | Success | **application/json**: [DataSourceContentPreviewResponse](#datasourcecontentpreviewresponse)<br> |
|
| 200 | Success |
|
||||||
|
|
||||||
### [POST] /rag/pipelines/{pipeline_id}/workflows/published/datasource/nodes/{node_id}/run
|
### [POST] /rag/pipelines/{pipeline_id}/workflows/published/datasource/nodes/{node_id}/run
|
||||||
**Run rag pipeline datasource**
|
**Run rag pipeline datasource**
|
||||||
@ -16109,12 +16109,6 @@ Model class for provider custom model configuration.
|
|||||||
| ---- | ---- | ----------- | -------- |
|
| ---- | ---- | ----------- | -------- |
|
||||||
| info_list | [InfoList](#infolist) | | Yes |
|
| info_list | [InfoList](#infolist) | | Yes |
|
||||||
|
|
||||||
#### DataSourceContentPreviewResponse
|
|
||||||
|
|
||||||
| Name | Type | Description | Required |
|
|
||||||
| ---- | ---- | ----------- | -------- |
|
|
||||||
| DataSourceContentPreviewResponse | | | |
|
|
||||||
|
|
||||||
#### DataSourceIntegrateIconResponse
|
#### DataSourceIntegrateIconResponse
|
||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
@ -16635,32 +16629,43 @@ Model class for provider custom model configuration.
|
|||||||
| ---- | ---- | ----------- | -------- |
|
| ---- | ---- | ----------- | -------- |
|
||||||
| credential_id | string | | Yes |
|
| credential_id | string | | Yes |
|
||||||
|
|
||||||
|
#### DatasourceCredentialListResponse
|
||||||
|
|
||||||
|
| Name | Type | Description | Required |
|
||||||
|
| ---- | ---- | ----------- | -------- |
|
||||||
|
| result | [ [DatasourceCredentialResponse](#datasourcecredentialresponse) ] | | Yes |
|
||||||
|
|
||||||
#### DatasourceCredentialPayload
|
#### DatasourceCredentialPayload
|
||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
| ---- | ---- | ----------- | -------- |
|
| ---- | ---- | ----------- | -------- |
|
||||||
| credentials | object | | Yes |
|
| credentials | object | Plugin-defined credential parameters. The schema is declared by the datasource provider. | Yes |
|
||||||
| name | string | | No |
|
| name | string | | No |
|
||||||
|
|
||||||
|
#### DatasourceCredentialResponse
|
||||||
|
|
||||||
|
| Name | Type | Description | Required |
|
||||||
|
| ---- | ---- | ----------- | -------- |
|
||||||
|
| avatar_url | string | | Yes |
|
||||||
|
| credential | object | Obfuscated plugin-defined credential parameters from the datasource provider. | Yes |
|
||||||
|
| id | string | | Yes |
|
||||||
|
| is_default | boolean | | Yes |
|
||||||
|
| name | string | | Yes |
|
||||||
|
| type | string | | Yes |
|
||||||
|
|
||||||
#### DatasourceCredentialUpdatePayload
|
#### DatasourceCredentialUpdatePayload
|
||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
| ---- | ---- | ----------- | -------- |
|
| ---- | ---- | ----------- | -------- |
|
||||||
| credential_id | string | | Yes |
|
| credential_id | string | | Yes |
|
||||||
| credentials | object | | No |
|
| credentials | object | Plugin-defined credential parameters. The schema is declared by the datasource provider. | No |
|
||||||
| name | string | | No |
|
| name | string | | No |
|
||||||
|
|
||||||
#### DatasourceCredentialsResponse
|
|
||||||
|
|
||||||
| Name | Type | Description | Required |
|
|
||||||
| ---- | ---- | ----------- | -------- |
|
|
||||||
| result | | | Yes |
|
|
||||||
|
|
||||||
#### DatasourceCustomClientPayload
|
#### DatasourceCustomClientPayload
|
||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
| ---- | ---- | ----------- | -------- |
|
| ---- | ---- | ----------- | -------- |
|
||||||
| client_params | object | | No |
|
| client_params | object | Plugin-defined OAuth client parameters. The schema is declared by the datasource provider. | No |
|
||||||
| enable_oauth_custom_client | boolean | | No |
|
| enable_oauth_custom_client | boolean | | No |
|
||||||
|
|
||||||
#### DatasourceDefaultPayload
|
#### DatasourceDefaultPayload
|
||||||
@ -16692,6 +16697,39 @@ Model class for provider custom model configuration.
|
|||||||
| error | string | Error message from OAuth provider | No |
|
| error | string | Error message from OAuth provider | No |
|
||||||
| state | string | OAuth state parameter | No |
|
| state | string | OAuth state parameter | No |
|
||||||
|
|
||||||
|
#### DatasourceOAuthSchemaResponse
|
||||||
|
|
||||||
|
| Name | Type | Description | Required |
|
||||||
|
| ---- | ---- | ----------- | -------- |
|
||||||
|
| client_schema | [ [ProviderConfig](#providerconfig) ] | | Yes |
|
||||||
|
| credentials_schema | [ [ProviderConfig](#providerconfig) ] | | Yes |
|
||||||
|
| is_oauth_custom_client_enabled | boolean | | Yes |
|
||||||
|
| is_system_oauth_params_exists | boolean | | Yes |
|
||||||
|
| oauth_custom_client_params | object | Masked plugin-defined OAuth client parameters, when configured for the tenant. | Yes |
|
||||||
|
| redirect_uri | string | | Yes |
|
||||||
|
|
||||||
|
#### DatasourceProviderAuthListResponse
|
||||||
|
|
||||||
|
| Name | Type | Description | Required |
|
||||||
|
| ---- | ---- | ----------- | -------- |
|
||||||
|
| result | [ [DatasourceProviderAuthResponse](#datasourceproviderauthresponse) ] | | Yes |
|
||||||
|
|
||||||
|
#### DatasourceProviderAuthResponse
|
||||||
|
|
||||||
|
| Name | Type | Description | Required |
|
||||||
|
| ---- | ---- | ----------- | -------- |
|
||||||
|
| author | string | | Yes |
|
||||||
|
| credential_schema | [ [ProviderConfig](#providerconfig) ] | | Yes |
|
||||||
|
| credentials_list | [ [DatasourceCredentialResponse](#datasourcecredentialresponse) ] | | Yes |
|
||||||
|
| description | [I18nObject](#i18nobject) | | Yes |
|
||||||
|
| icon | string | | Yes |
|
||||||
|
| label | [I18nObject](#i18nobject) | | Yes |
|
||||||
|
| name | string | | Yes |
|
||||||
|
| oauth_schema | [DatasourceOAuthSchemaResponse](#datasourceoauthschemaresponse) | | Yes |
|
||||||
|
| plugin_id | string | | Yes |
|
||||||
|
| plugin_unique_identifier | string | | Yes |
|
||||||
|
| provider | string | | Yes |
|
||||||
|
|
||||||
#### DatasourceUpdateNamePayload
|
#### DatasourceUpdateNamePayload
|
||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|
|||||||
@ -1046,12 +1046,12 @@ Execute a single datasource node within the knowledge pipeline. Returns a stream
|
|||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Code | Description | Schema |
|
| Code | Description |
|
||||||
| ---- | ----------- | ------ |
|
| ---- | ----------- |
|
||||||
| 200 | Streaming response with node execution events. | **text/event-stream**: [GeneratedAppResponse](#generatedappresponse)<br> |
|
| 200 | Streaming response with node execution events. |
|
||||||
| 401 | Unauthorized - invalid API token | |
|
| 401 | Unauthorized - invalid API token |
|
||||||
| 403 | Forbidden - dataset API access or workspace access denied | |
|
| 403 | Forbidden - dataset API access or workspace access denied |
|
||||||
| 404 | `not_found` : Dataset not found. | |
|
| 404 | `not_found` : Dataset not found. |
|
||||||
|
|
||||||
### [POST] /datasets/{dataset_id}/pipeline/run
|
### [POST] /datasets/{dataset_id}/pipeline/run
|
||||||
**Run Pipeline**
|
**Run Pipeline**
|
||||||
@ -2960,7 +2960,7 @@ Enum class for custom configuration status.
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
| ---- | ---- | ----------- | -------- |
|
| ---- | ---- | ----------- | -------- |
|
||||||
| credentials | [ [DatasourceCredentialInfoResponse](#datasourcecredentialinforesponse) ] | | Yes |
|
| credentials | [ [DatasourceCredentialInfoResponse](#datasourcecredentialinforesponse) ] | | No |
|
||||||
| datasource_type | string | | No |
|
| datasource_type | string | | No |
|
||||||
| node_id | string | | No |
|
| node_id | string | | No |
|
||||||
| plugin_id | string | | No |
|
| plugin_id | string | | No |
|
||||||
|
|||||||
@ -607,7 +607,11 @@ class TestMiscApis:
|
|||||||
method = unwrap(api.get)
|
method = unwrap(api.get)
|
||||||
|
|
||||||
service = MagicMock()
|
service = MagicMock()
|
||||||
service.get_recommended_plugins.return_value = [{"id": "p1"}]
|
recommended_plugins = {
|
||||||
|
"installed_recommended_plugins": [{"id": "p1"}],
|
||||||
|
"uninstalled_recommended_plugins": [{"id": "p2"}],
|
||||||
|
}
|
||||||
|
service.get_recommended_plugins.return_value = recommended_plugins
|
||||||
user = make_account()
|
user = make_account()
|
||||||
tenant_id = "tenant-1"
|
tenant_id = "tenant-1"
|
||||||
|
|
||||||
@ -619,7 +623,7 @@ class TestMiscApis:
|
|||||||
),
|
),
|
||||||
):
|
):
|
||||||
result = method(api, tenant_id, user)
|
result = method(api, tenant_id, user)
|
||||||
assert result == [{"id": "p1"}]
|
assert result == recommended_plugins
|
||||||
service.get_recommended_plugins.assert_called_once_with("all", user, tenant_id)
|
service.get_recommended_plugins.assert_called_once_with("all", user, tenant_id)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import inspect
|
import inspect
|
||||||
|
from datetime import UTC, datetime
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -23,6 +24,76 @@ from graphon.model_runtime.errors.validate import CredentialsValidateFailedError
|
|||||||
from services.datasource_provider_service import DatasourceProviderService
|
from services.datasource_provider_service import DatasourceProviderService
|
||||||
from services.plugin.oauth_service import OAuthProxyService
|
from services.plugin.oauth_service import OAuthProxyService
|
||||||
|
|
||||||
|
_PROVIDER_ID = "langgenius/notion_datasource/notion"
|
||||||
|
|
||||||
|
|
||||||
|
def _i18n(text: str) -> dict[str, str]:
|
||||||
|
return {"en_US": text, "zh_Hans": text, "pt_BR": text, "ja_JP": text}
|
||||||
|
|
||||||
|
|
||||||
|
def _provider_config(name: str, type_: str, label: str, *, required: bool = True) -> dict:
|
||||||
|
return {
|
||||||
|
"type": type_,
|
||||||
|
"name": name,
|
||||||
|
"scope": None,
|
||||||
|
"required": required,
|
||||||
|
"default": None,
|
||||||
|
"options": None,
|
||||||
|
"multiple": False,
|
||||||
|
"label": _i18n(label),
|
||||||
|
"help": None,
|
||||||
|
"url": None,
|
||||||
|
"placeholder": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _datasource_credential(credential_id: str = "cred-1", *, is_default: bool = True) -> dict:
|
||||||
|
return {
|
||||||
|
"credential": {
|
||||||
|
"api_key": "******",
|
||||||
|
"workspace": "engineering",
|
||||||
|
"database_id": "db-123",
|
||||||
|
},
|
||||||
|
"type": "api-key",
|
||||||
|
"name": "API Key",
|
||||||
|
"avatar_url": "https://cdn.example.com/notion.png",
|
||||||
|
"id": credential_id,
|
||||||
|
"is_default": is_default,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _datasource_auth() -> dict:
|
||||||
|
return {
|
||||||
|
"author": "Dify",
|
||||||
|
"provider": "notion",
|
||||||
|
"plugin_id": "langgenius/notion_datasource",
|
||||||
|
"plugin_unique_identifier": "langgenius/notion_datasource:0.0.1",
|
||||||
|
"icon": "icon.svg",
|
||||||
|
"name": "notion",
|
||||||
|
"label": _i18n("Notion"),
|
||||||
|
"description": _i18n("Notion datasource"),
|
||||||
|
"credential_schema": [
|
||||||
|
_provider_config("api_key", "secret-input", "API key"),
|
||||||
|
],
|
||||||
|
"oauth_schema": {
|
||||||
|
"client_schema": [
|
||||||
|
_provider_config("client_id", "text-input", "Client ID"),
|
||||||
|
],
|
||||||
|
"credentials_schema": [
|
||||||
|
_provider_config("access_token", "secret-input", "Access token"),
|
||||||
|
],
|
||||||
|
"oauth_custom_client_params": {"client_id": "masked-client", "client_secret": "********"},
|
||||||
|
"is_oauth_custom_client_enabled": True,
|
||||||
|
"is_system_oauth_params_exists": True,
|
||||||
|
"redirect_uri": "https://api.example.com/oauth/callback",
|
||||||
|
},
|
||||||
|
"credentials_list": [_datasource_credential(), _datasource_credential("cred-2", is_default=False)],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _success_response() -> dict[str, str]:
|
||||||
|
return {"result": "success"}
|
||||||
|
|
||||||
|
|
||||||
class TestDatasourcePluginOAuthAuthorizationUrl:
|
class TestDatasourcePluginOAuthAuthorizationUrl:
|
||||||
def test_get_success(self, app: Flask):
|
def test_get_success(self, app: Flask):
|
||||||
@ -30,28 +101,50 @@ class TestDatasourcePluginOAuthAuthorizationUrl:
|
|||||||
method = inspect.unwrap(api.get)
|
method = inspect.unwrap(api.get)
|
||||||
|
|
||||||
user = MagicMock(id="user-1")
|
user = MagicMock(id="user-1")
|
||||||
|
oauth_client = {"client_id": "abc", "client_secret": "shh", "scopes": ["read", "write"]}
|
||||||
|
auth_url_payload = {
|
||||||
|
"authorization_url": "https://auth.example.com/oauth?client_id=abc&state=xyz",
|
||||||
|
}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
app.test_request_context("/?credential_id=cred-1"),
|
app.test_request_context("/?credential_id=cred-1"),
|
||||||
patch.object(
|
patch.object(
|
||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"get_oauth_client",
|
"get_oauth_client",
|
||||||
return_value={"client_id": "abc"},
|
return_value=oauth_client,
|
||||||
),
|
) as get_oauth_client,
|
||||||
patch.object(
|
patch.object(
|
||||||
OAuthProxyService,
|
OAuthProxyService,
|
||||||
"create_proxy_context",
|
"create_proxy_context",
|
||||||
return_value="ctx-1",
|
return_value="ctx-1",
|
||||||
),
|
) as create_proxy_context,
|
||||||
patch.object(
|
patch.object(
|
||||||
OAuthHandler,
|
OAuthHandler,
|
||||||
"get_authorization_url",
|
"get_authorization_url",
|
||||||
return_value={"url": "http://auth"},
|
return_value=auth_url_payload,
|
||||||
),
|
) as get_authorization_url,
|
||||||
):
|
):
|
||||||
response = method(api, "tenant-1", user, "notion")
|
response = method(api, "tenant-1", user, _PROVIDER_ID)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
assert response.get_json() == auth_url_payload
|
||||||
|
assert "context_id=ctx-1" in response.headers.get("Set-Cookie")
|
||||||
|
provider_id = get_oauth_client.call_args.kwargs["datasource_provider_id"]
|
||||||
|
assert str(provider_id) == _PROVIDER_ID
|
||||||
|
get_oauth_client.assert_called_once()
|
||||||
|
create_proxy_context.assert_called_once_with(
|
||||||
|
user_id="user-1",
|
||||||
|
tenant_id="tenant-1",
|
||||||
|
plugin_id="langgenius/notion_datasource",
|
||||||
|
provider="notion",
|
||||||
|
credential_id="cred-1",
|
||||||
|
)
|
||||||
|
get_authorization_url.assert_called_once()
|
||||||
|
assert get_authorization_url.call_args.kwargs["tenant_id"] == "tenant-1"
|
||||||
|
assert get_authorization_url.call_args.kwargs["user_id"] == "user-1"
|
||||||
|
assert get_authorization_url.call_args.kwargs["plugin_id"] == "langgenius/notion_datasource"
|
||||||
|
assert get_authorization_url.call_args.kwargs["provider"] == "notion"
|
||||||
|
assert get_authorization_url.call_args.kwargs["system_credentials"] == oauth_client
|
||||||
|
|
||||||
def test_get_no_oauth_config(self, app: Flask):
|
def test_get_no_oauth_config(self, app: Flask):
|
||||||
api = DatasourcePluginOAuthAuthorizationUrl()
|
api = DatasourcePluginOAuthAuthorizationUrl()
|
||||||
@ -90,10 +183,10 @@ class TestDatasourcePluginOAuthAuthorizationUrl:
|
|||||||
patch.object(
|
patch.object(
|
||||||
OAuthHandler,
|
OAuthHandler,
|
||||||
"get_authorization_url",
|
"get_authorization_url",
|
||||||
return_value={"url": "http://auth"},
|
return_value={"authorization_url": "http://auth"},
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
response = method(api, "tenant-1", user, "notion")
|
response = method(api, "tenant-1", user, _PROVIDER_ID)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert "context_id" in response.headers.get("Set-Cookie")
|
assert "context_id" in response.headers.get("Set-Cookie")
|
||||||
@ -106,8 +199,9 @@ class TestDatasourceOAuthCallback:
|
|||||||
|
|
||||||
oauth_response = MagicMock()
|
oauth_response = MagicMock()
|
||||||
oauth_response.credentials = {"token": "abc"}
|
oauth_response.credentials = {"token": "abc"}
|
||||||
oauth_response.expires_at = None
|
expires_at = datetime(2024, 1, 2, 3, 4, 5, tzinfo=UTC)
|
||||||
oauth_response.metadata = {"name": "test"}
|
oauth_response.expires_at = expires_at
|
||||||
|
oauth_response.metadata = {"name": "Workspace Bot", "avatar_url": "https://avatar.example.com/bot.png"}
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"user_id": "user-1",
|
"user_id": "user-1",
|
||||||
@ -125,7 +219,7 @@ class TestDatasourceOAuthCallback:
|
|||||||
patch.object(
|
patch.object(
|
||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"get_oauth_client",
|
"get_oauth_client",
|
||||||
return_value={"client_id": "abc"},
|
return_value={"client_id": "abc", "client_secret": "secret"},
|
||||||
),
|
),
|
||||||
patch.object(
|
patch.object(
|
||||||
OAuthHandler,
|
OAuthHandler,
|
||||||
@ -136,11 +230,22 @@ class TestDatasourceOAuthCallback:
|
|||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"add_datasource_oauth_provider",
|
"add_datasource_oauth_provider",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
) as add_oauth_provider,
|
||||||
):
|
):
|
||||||
response = method(api, "notion")
|
response = method(api, _PROVIDER_ID)
|
||||||
|
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
|
assert "/oauth-callback" in response.location
|
||||||
|
add_oauth_provider.assert_called_once()
|
||||||
|
assert add_oauth_provider.call_args.kwargs == {
|
||||||
|
"tenant_id": "tenant-1",
|
||||||
|
"provider_id": add_oauth_provider.call_args.kwargs["provider_id"],
|
||||||
|
"avatar_url": "https://avatar.example.com/bot.png",
|
||||||
|
"name": "Workspace Bot",
|
||||||
|
"expire_at": expires_at,
|
||||||
|
"credentials": {"token": "abc"},
|
||||||
|
}
|
||||||
|
assert str(add_oauth_provider.call_args.kwargs["provider_id"]) == _PROVIDER_ID
|
||||||
|
|
||||||
def test_callback_missing_context(self, app: Flask):
|
def test_callback_missing_context(self, app: Flask):
|
||||||
api = DatasourceOAuthCallback()
|
api = DatasourceOAuthCallback()
|
||||||
@ -223,12 +328,16 @@ class TestDatasourceOAuthCallback:
|
|||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"reauthorize_datasource_oauth_provider",
|
"reauthorize_datasource_oauth_provider",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
) as reauthorize_provider,
|
||||||
):
|
):
|
||||||
response = method(api, "notion")
|
response = method(api, _PROVIDER_ID)
|
||||||
|
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
assert "/oauth-callback" in response.location
|
assert "/oauth-callback" in response.location
|
||||||
|
reauthorize_provider.assert_called_once()
|
||||||
|
assert str(reauthorize_provider.call_args.kwargs["provider_id"]) == _PROVIDER_ID
|
||||||
|
assert reauthorize_provider.call_args.kwargs["credential_id"] == "cred-1"
|
||||||
|
assert reauthorize_provider.call_args.kwargs["credentials"] == {"token": "abc"}
|
||||||
|
|
||||||
def test_callback_context_id_from_cookie(self, app: Flask):
|
def test_callback_context_id_from_cookie(self, app: Flask):
|
||||||
api = DatasourceOAuthCallback()
|
api = DatasourceOAuthCallback()
|
||||||
@ -278,7 +387,14 @@ class TestDatasourceAuth:
|
|||||||
api = DatasourceAuth()
|
api = DatasourceAuth()
|
||||||
method = inspect.unwrap(api.post)
|
method = inspect.unwrap(api.post)
|
||||||
|
|
||||||
payload = {"credentials": {"key": "val"}}
|
payload = {
|
||||||
|
"name": "Engineering Notion",
|
||||||
|
"credentials": {
|
||||||
|
"api_key": "secret-token",
|
||||||
|
"workspace": "engineering",
|
||||||
|
"database_id": "db-123",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
app.test_request_context("/", json=payload),
|
app.test_request_context("/", json=payload),
|
||||||
@ -287,11 +403,17 @@ class TestDatasourceAuth:
|
|||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"add_datasource_api_key_provider",
|
"add_datasource_api_key_provider",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
) as add_api_key_provider,
|
||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", _PROVIDER_ID)
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
assert status == 200
|
assert status == 200
|
||||||
|
add_api_key_provider.assert_called_once()
|
||||||
|
assert add_api_key_provider.call_args.kwargs["tenant_id"] == "tenant-1"
|
||||||
|
assert str(add_api_key_provider.call_args.kwargs["provider_id"]) == _PROVIDER_ID
|
||||||
|
assert add_api_key_provider.call_args.kwargs["credentials"] == payload["credentials"]
|
||||||
|
assert add_api_key_provider.call_args.kwargs["name"] == "Engineering Notion"
|
||||||
|
|
||||||
def test_post_invalid_credentials(self, app: Flask):
|
def test_post_invalid_credentials(self, app: Flask):
|
||||||
api = DatasourceAuth()
|
api = DatasourceAuth()
|
||||||
@ -321,19 +443,19 @@ class TestDatasourceAuth:
|
|||||||
patch.object(
|
patch.object(
|
||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"list_datasource_credentials",
|
"list_datasource_credentials",
|
||||||
return_value=[{"id": "1"}],
|
return_value=[_datasource_credential()],
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1", user, "notion")
|
response, status = method(api, "tenant-1", user, _PROVIDER_ID)
|
||||||
|
|
||||||
assert status == 200
|
assert status == 200
|
||||||
assert response["result"]
|
assert response == {"result": [_datasource_credential()]}
|
||||||
|
|
||||||
def test_post_missing_credentials(self, app: Flask):
|
def test_post_missing_credentials(self, app: Flask):
|
||||||
api = DatasourceAuth()
|
api = DatasourceAuth()
|
||||||
method = inspect.unwrap(api.post)
|
method = inspect.unwrap(api.post)
|
||||||
|
|
||||||
payload = {}
|
payload: dict[str, object] = {}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
app.test_request_context("/", json=payload),
|
app.test_request_context("/", json=payload),
|
||||||
@ -375,17 +497,24 @@ class TestDatasourceAuthDeleteApi:
|
|||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"remove_datasource_credentials",
|
"remove_datasource_credentials",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
) as remove_datasource_credentials,
|
||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", _PROVIDER_ID)
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
assert status == 200
|
assert status == 200
|
||||||
|
remove_datasource_credentials.assert_called_once_with(
|
||||||
|
tenant_id="tenant-1",
|
||||||
|
auth_id="cred-1",
|
||||||
|
provider="notion",
|
||||||
|
plugin_id="langgenius/notion_datasource",
|
||||||
|
)
|
||||||
|
|
||||||
def test_delete_missing_credential_id(self, app: Flask):
|
def test_delete_missing_credential_id(self, app: Flask):
|
||||||
api = DatasourceAuthDeleteApi()
|
api = DatasourceAuthDeleteApi()
|
||||||
method = inspect.unwrap(api.post)
|
method = inspect.unwrap(api.post)
|
||||||
|
|
||||||
payload = {}
|
payload: dict[str, object] = {}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
app.test_request_context("/", json=payload),
|
app.test_request_context("/", json=payload),
|
||||||
@ -400,7 +529,11 @@ class TestDatasourceAuthUpdateApi:
|
|||||||
api = DatasourceAuthUpdateApi()
|
api = DatasourceAuthUpdateApi()
|
||||||
method = inspect.unwrap(api.post)
|
method = inspect.unwrap(api.post)
|
||||||
|
|
||||||
payload = {"credential_id": "id", "credentials": {"k": "v"}}
|
payload = {
|
||||||
|
"credential_id": "cred-1",
|
||||||
|
"name": "Updated Notion",
|
||||||
|
"credentials": {"api_key": "new-secret", "database_id": "db-456"},
|
||||||
|
}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
app.test_request_context("/", json=payload),
|
app.test_request_context("/", json=payload),
|
||||||
@ -409,11 +542,20 @@ class TestDatasourceAuthUpdateApi:
|
|||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"update_datasource_credentials",
|
"update_datasource_credentials",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
) as update_datasource_credentials,
|
||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", _PROVIDER_ID)
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
assert status == 201
|
assert status == 201
|
||||||
|
update_datasource_credentials.assert_called_once_with(
|
||||||
|
tenant_id="tenant-1",
|
||||||
|
auth_id="cred-1",
|
||||||
|
provider="notion",
|
||||||
|
plugin_id="langgenius/notion_datasource",
|
||||||
|
credentials=payload["credentials"],
|
||||||
|
name="Updated Notion",
|
||||||
|
)
|
||||||
|
|
||||||
def test_update_with_credentials_none(self, app: Flask):
|
def test_update_with_credentials_none(self, app: Flask):
|
||||||
api = DatasourceAuthUpdateApi()
|
api = DatasourceAuthUpdateApi()
|
||||||
@ -432,7 +574,9 @@ class TestDatasourceAuthUpdateApi:
|
|||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", "notion")
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
update_mock.assert_called_once()
|
update_mock.assert_called_once()
|
||||||
|
assert update_mock.call_args.kwargs["credentials"] == {}
|
||||||
assert status == 201
|
assert status == 201
|
||||||
|
|
||||||
def test_update_name_only(self, app: Flask):
|
def test_update_name_only(self, app: Flask):
|
||||||
@ -450,8 +594,9 @@ class TestDatasourceAuthUpdateApi:
|
|||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
_, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", "notion")
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
assert status == 201
|
assert status == 201
|
||||||
|
|
||||||
def test_update_with_empty_credentials_dict(self, app: Flask):
|
def test_update_with_empty_credentials_dict(self, app: Flask):
|
||||||
@ -469,8 +614,9 @@ class TestDatasourceAuthUpdateApi:
|
|||||||
return_value=None,
|
return_value=None,
|
||||||
) as update_mock,
|
) as update_mock,
|
||||||
):
|
):
|
||||||
_, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", "notion")
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
update_mock.assert_called_once()
|
update_mock.assert_called_once()
|
||||||
assert status == 201
|
assert status == 201
|
||||||
|
|
||||||
@ -485,12 +631,13 @@ class TestDatasourceAuthListApi:
|
|||||||
patch.object(
|
patch.object(
|
||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"get_all_datasource_credentials",
|
"get_all_datasource_credentials",
|
||||||
return_value=[{"id": "1"}],
|
return_value=[_datasource_auth()],
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1")
|
response, status = method(api, "tenant-1")
|
||||||
|
|
||||||
assert status == 200
|
assert status == 200
|
||||||
|
assert response == {"result": [_datasource_auth()]}
|
||||||
|
|
||||||
def test_auth_list_empty(self, app: Flask):
|
def test_auth_list_empty(self, app: Flask):
|
||||||
api = DatasourceAuthListApi()
|
api = DatasourceAuthListApi()
|
||||||
@ -537,7 +684,7 @@ class TestDatasourceHardCodeAuthListApi:
|
|||||||
patch.object(
|
patch.object(
|
||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"get_hard_code_datasource_credentials",
|
"get_hard_code_datasource_credentials",
|
||||||
return_value=[{"id": "1"}],
|
return_value=[_datasource_auth()],
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1")
|
response, status = method(api, "tenant-1")
|
||||||
@ -550,7 +697,14 @@ class TestDatasourceAuthOauthCustomClient:
|
|||||||
api = DatasourceAuthOauthCustomClient()
|
api = DatasourceAuthOauthCustomClient()
|
||||||
method = inspect.unwrap(api.post)
|
method = inspect.unwrap(api.post)
|
||||||
|
|
||||||
payload = {"client_params": {}, "enable_oauth_custom_client": True}
|
payload = {
|
||||||
|
"client_params": {
|
||||||
|
"client_id": "custom-client",
|
||||||
|
"client_secret": "custom-secret",
|
||||||
|
"authorize_url": "https://auth.example.com/authorize",
|
||||||
|
},
|
||||||
|
"enable_oauth_custom_client": True,
|
||||||
|
}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
app.test_request_context("/", json=payload),
|
app.test_request_context("/", json=payload),
|
||||||
@ -559,11 +713,17 @@ class TestDatasourceAuthOauthCustomClient:
|
|||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"setup_oauth_custom_client_params",
|
"setup_oauth_custom_client_params",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
) as setup_custom_client,
|
||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", _PROVIDER_ID)
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
assert status == 200
|
assert status == 200
|
||||||
|
setup_custom_client.assert_called_once()
|
||||||
|
assert setup_custom_client.call_args.kwargs["tenant_id"] == "tenant-1"
|
||||||
|
assert str(setup_custom_client.call_args.kwargs["datasource_provider_id"]) == _PROVIDER_ID
|
||||||
|
assert setup_custom_client.call_args.kwargs["client_params"] == payload["client_params"]
|
||||||
|
assert setup_custom_client.call_args.kwargs["enabled"] is True
|
||||||
|
|
||||||
def test_delete_success(self, app: Flask):
|
def test_delete_success(self, app: Flask):
|
||||||
api = DatasourceAuthOauthCustomClient()
|
api = DatasourceAuthOauthCustomClient()
|
||||||
@ -575,17 +735,20 @@ class TestDatasourceAuthOauthCustomClient:
|
|||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"remove_oauth_custom_client_params",
|
"remove_oauth_custom_client_params",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
) as remove_custom_client,
|
||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", _PROVIDER_ID)
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
assert status == 200
|
assert status == 200
|
||||||
|
remove_custom_client.assert_called_once()
|
||||||
|
assert str(remove_custom_client.call_args.kwargs["datasource_provider_id"]) == _PROVIDER_ID
|
||||||
|
|
||||||
def test_post_empty_payload(self, app: Flask):
|
def test_post_empty_payload(self, app: Flask):
|
||||||
api = DatasourceAuthOauthCustomClient()
|
api = DatasourceAuthOauthCustomClient()
|
||||||
method = inspect.unwrap(api.post)
|
method = inspect.unwrap(api.post)
|
||||||
|
|
||||||
payload = {}
|
payload: dict[str, object] = {}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
app.test_request_context("/", json=payload),
|
app.test_request_context("/", json=payload),
|
||||||
@ -596,8 +759,9 @@ class TestDatasourceAuthOauthCustomClient:
|
|||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
_, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", "notion")
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
assert status == 200
|
assert status == 200
|
||||||
|
|
||||||
def test_post_disabled_flag(self, app: Flask):
|
def test_post_disabled_flag(self, app: Flask):
|
||||||
@ -618,9 +782,12 @@ class TestDatasourceAuthOauthCustomClient:
|
|||||||
return_value=None,
|
return_value=None,
|
||||||
) as setup_mock,
|
) as setup_mock,
|
||||||
):
|
):
|
||||||
_, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", "notion")
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
setup_mock.assert_called_once()
|
setup_mock.assert_called_once()
|
||||||
|
assert setup_mock.call_args.kwargs["client_params"] == {"a": 1}
|
||||||
|
assert setup_mock.call_args.kwargs["enabled"] is False
|
||||||
assert status == 200
|
assert status == 200
|
||||||
|
|
||||||
|
|
||||||
@ -638,17 +805,22 @@ class TestDatasourceAuthDefaultApi:
|
|||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"set_default_datasource_provider",
|
"set_default_datasource_provider",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
) as set_default_datasource_provider,
|
||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", _PROVIDER_ID)
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
assert status == 200
|
assert status == 200
|
||||||
|
set_default_datasource_provider.assert_called_once()
|
||||||
|
assert set_default_datasource_provider.call_args.kwargs["tenant_id"] == "tenant-1"
|
||||||
|
assert str(set_default_datasource_provider.call_args.kwargs["datasource_provider_id"]) == _PROVIDER_ID
|
||||||
|
assert set_default_datasource_provider.call_args.kwargs["credential_id"] == "cred-1"
|
||||||
|
|
||||||
def test_default_missing_id(self, app: Flask):
|
def test_default_missing_id(self, app: Flask):
|
||||||
api = DatasourceAuthDefaultApi()
|
api = DatasourceAuthDefaultApi()
|
||||||
method = inspect.unwrap(api.post)
|
method = inspect.unwrap(api.post)
|
||||||
|
|
||||||
payload = {}
|
payload: dict[str, object] = {}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
app.test_request_context("/", json=payload),
|
app.test_request_context("/", json=payload),
|
||||||
@ -663,7 +835,7 @@ class TestDatasourceUpdateProviderNameApi:
|
|||||||
api = DatasourceUpdateProviderNameApi()
|
api = DatasourceUpdateProviderNameApi()
|
||||||
method = inspect.unwrap(api.post)
|
method = inspect.unwrap(api.post)
|
||||||
|
|
||||||
payload = {"credential_id": "id", "name": "New Name"}
|
payload = {"credential_id": "cred-1", "name": "New Name"}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
app.test_request_context("/", json=payload),
|
app.test_request_context("/", json=payload),
|
||||||
@ -672,11 +844,17 @@ class TestDatasourceUpdateProviderNameApi:
|
|||||||
DatasourceProviderService,
|
DatasourceProviderService,
|
||||||
"update_datasource_provider_name",
|
"update_datasource_provider_name",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
),
|
) as update_datasource_provider_name,
|
||||||
):
|
):
|
||||||
response, status = method(api, "tenant-1", "notion")
|
response, status = method(api, "tenant-1", _PROVIDER_ID)
|
||||||
|
|
||||||
|
assert response == _success_response()
|
||||||
assert status == 200
|
assert status == 200
|
||||||
|
update_datasource_provider_name.assert_called_once()
|
||||||
|
assert update_datasource_provider_name.call_args.kwargs["tenant_id"] == "tenant-1"
|
||||||
|
assert str(update_datasource_provider_name.call_args.kwargs["datasource_provider_id"]) == _PROVIDER_ID
|
||||||
|
assert update_datasource_provider_name.call_args.kwargs["name"] == "New Name"
|
||||||
|
assert update_datasource_provider_name.call_args.kwargs["credential_id"] == "cred-1"
|
||||||
|
|
||||||
def test_update_name_too_long(self, app: Flask):
|
def test_update_name_too_long(self, app: Flask):
|
||||||
api = DatasourceUpdateProviderNameApi()
|
api = DatasourceUpdateProviderNameApi()
|
||||||
|
|||||||
@ -158,3 +158,65 @@ def test_rag_pipeline_workflow_patch_serializes_response_model(app: Flask, monke
|
|||||||
assert response["id"] == "workflow-1"
|
assert response["id"] == "workflow-1"
|
||||||
assert response["marked_name"] == "Updated release"
|
assert response["marked_name"] == "Updated release"
|
||||||
assert response["hash"] == "hash-1"
|
assert response["hash"] == "hash-1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_rag_pipeline_block_configs_serializes_root_response(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
block_configs = [{"type": "start", "config": {"title": "Start"}}]
|
||||||
|
monkeypatch.setattr(
|
||||||
|
module,
|
||||||
|
"RagPipelineService",
|
||||||
|
lambda: SimpleNamespace(get_default_block_configs=lambda: block_configs),
|
||||||
|
)
|
||||||
|
|
||||||
|
api = module.DefaultRagPipelineBlockConfigsApi()
|
||||||
|
handler = unwrap_all(api.get)
|
||||||
|
|
||||||
|
response = handler(api, _pipeline())
|
||||||
|
|
||||||
|
assert response == block_configs
|
||||||
|
|
||||||
|
|
||||||
|
def test_draft_rag_pipeline_second_step_parameters_serializes_variables(app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
variables = [
|
||||||
|
{
|
||||||
|
"belong_to_node_id": "shared",
|
||||||
|
"type": "number",
|
||||||
|
"label": "Chunk size",
|
||||||
|
"variable": "chunk_size",
|
||||||
|
"default_value": 1024,
|
||||||
|
"required": True,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
monkeypatch.setattr(
|
||||||
|
module,
|
||||||
|
"RagPipelineService",
|
||||||
|
lambda: SimpleNamespace(get_second_step_parameters=lambda **_kwargs: variables),
|
||||||
|
)
|
||||||
|
|
||||||
|
api = module.DraftRagPipelineSecondStepApi()
|
||||||
|
handler = unwrap_all(api.get)
|
||||||
|
|
||||||
|
with app.test_request_context("/?node_id=node-1"):
|
||||||
|
response = handler(api, _pipeline())
|
||||||
|
|
||||||
|
assert response["variables"] == variables
|
||||||
|
|
||||||
|
|
||||||
|
def test_rag_pipeline_recommended_plugins_serializes_known_envelope(app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
recommended_plugins = {
|
||||||
|
"installed_recommended_plugins": [{"name": "Dify Extractor", "meta": {"version": "1.0.0"}}],
|
||||||
|
"uninstalled_recommended_plugins": [{"plugin_id": "langgenius/notion_datasource"}],
|
||||||
|
}
|
||||||
|
monkeypatch.setattr(
|
||||||
|
module,
|
||||||
|
"RagPipelineService",
|
||||||
|
lambda: SimpleNamespace(get_recommended_plugins=lambda *_args: recommended_plugins),
|
||||||
|
)
|
||||||
|
|
||||||
|
api = module.RagPipelineRecommendedPluginApi()
|
||||||
|
handler = unwrap_all(api.get)
|
||||||
|
|
||||||
|
with app.test_request_context("/?type=tool"):
|
||||||
|
response = handler(api, "tenant-1", _account())
|
||||||
|
|
||||||
|
assert response == recommended_plugins
|
||||||
|
|||||||
@ -325,10 +325,12 @@ class TestPipelineRunApiEntity:
|
|||||||
def test_entity_missing_required_field(self):
|
def test_entity_missing_required_field(self):
|
||||||
"""Test entity raises on missing required field."""
|
"""Test entity raises on missing required field."""
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
PipelineRunApiEntity(
|
PipelineRunApiEntity.model_validate(
|
||||||
inputs={},
|
{
|
||||||
datasource_type="online_document",
|
"inputs": {},
|
||||||
# missing datasource_info_list, start_node_id, etc.
|
"datasource_type": "online_document",
|
||||||
|
# missing datasource_info_list, start_node_id, etc.
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -382,8 +384,19 @@ class TestDatasourcePluginsApiGet:
|
|||||||
mock_dataset = Mock()
|
mock_dataset = Mock()
|
||||||
mock_db.session.scalar.return_value = mock_dataset
|
mock_db.session.scalar.return_value = mock_dataset
|
||||||
|
|
||||||
|
datasource_plugins = [
|
||||||
|
{
|
||||||
|
"node_id": "node-datasource-1",
|
||||||
|
"plugin_id": "plugin-a",
|
||||||
|
"provider_name": "provider-a",
|
||||||
|
"datasource_type": "online_document",
|
||||||
|
"title": "Online Docs",
|
||||||
|
"user_input_variables": [{"variable": "url", "label": "URL", "type": "text-input", "required": True}],
|
||||||
|
"credentials": [{"id": "cred-1", "name": "Default credential", "type": "oauth2", "is_default": True}],
|
||||||
|
}
|
||||||
|
]
|
||||||
mock_svc_instance = Mock()
|
mock_svc_instance = Mock()
|
||||||
mock_svc_instance.get_datasource_plugins.return_value = [{"name": "plugin_a"}]
|
mock_svc_instance.get_datasource_plugins.return_value = datasource_plugins
|
||||||
mock_svc_cls.return_value = mock_svc_instance
|
mock_svc_cls.return_value = mock_svc_instance
|
||||||
|
|
||||||
with app.test_request_context("/datasets/test/pipeline/datasource-plugins?is_published=true"):
|
with app.test_request_context("/datasets/test/pipeline/datasource-plugins?is_published=true"):
|
||||||
@ -391,11 +404,33 @@ class TestDatasourcePluginsApiGet:
|
|||||||
response, status = api.get(tenant_id=tenant_id, dataset_id=dataset_id)
|
response, status = api.get(tenant_id=tenant_id, dataset_id=dataset_id)
|
||||||
|
|
||||||
assert status == 200
|
assert status == 200
|
||||||
assert response == [{"name": "plugin_a"}]
|
assert response == datasource_plugins
|
||||||
mock_svc_instance.get_datasource_plugins.assert_called_once_with(
|
mock_svc_instance.get_datasource_plugins.assert_called_once_with(
|
||||||
tenant_id=tenant_id, dataset_id=dataset_id, is_published=True
|
tenant_id=tenant_id, dataset_id=dataset_id, is_published=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@patch("controllers.service_api.dataset.rag_pipeline.rag_pipeline_workflow.db")
|
||||||
|
@patch("controllers.service_api.dataset.rag_pipeline.rag_pipeline_workflow.RagPipelineService")
|
||||||
|
def test_get_plugins_parses_false_is_published_query(self, mock_svc_cls, mock_db, app: Flask):
|
||||||
|
"""Test false query string is parsed as boolean False."""
|
||||||
|
tenant_id = str(uuid.uuid4())
|
||||||
|
dataset_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
mock_db.session.scalar.return_value = Mock()
|
||||||
|
mock_svc_instance = Mock()
|
||||||
|
mock_svc_instance.get_datasource_plugins.return_value = []
|
||||||
|
mock_svc_cls.return_value = mock_svc_instance
|
||||||
|
|
||||||
|
with app.test_request_context("/datasets/test/pipeline/datasource-plugins?is_published=false"):
|
||||||
|
api = DatasourcePluginsApi()
|
||||||
|
response, status = api.get(tenant_id=tenant_id, dataset_id=dataset_id)
|
||||||
|
|
||||||
|
assert status == 200
|
||||||
|
assert response == []
|
||||||
|
mock_svc_instance.get_datasource_plugins.assert_called_once_with(
|
||||||
|
tenant_id=tenant_id, dataset_id=dataset_id, is_published=False
|
||||||
|
)
|
||||||
|
|
||||||
@patch("controllers.service_api.dataset.rag_pipeline.rag_pipeline_workflow.db")
|
@patch("controllers.service_api.dataset.rag_pipeline.rag_pipeline_workflow.db")
|
||||||
def test_get_plugins_not_found(self, mock_db, app: Flask):
|
def test_get_plugins_not_found(self, mock_db, app: Flask):
|
||||||
"""Test NotFound when dataset check fails."""
|
"""Test NotFound when dataset check fails."""
|
||||||
|
|||||||
@ -2,9 +2,10 @@
|
|||||||
Unit tests for Service API knowledge pipeline file-upload serialization.
|
Unit tests for Service API knowledge pipeline file-upload serialization.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import importlib.util
|
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from pathlib import Path
|
|
||||||
|
from controllers.service_api.dataset.rag_pipeline.rag_pipeline_workflow import PipelineUploadFileResponse
|
||||||
|
from libs.helper import dump_response
|
||||||
|
|
||||||
|
|
||||||
class FakeUploadFile:
|
class FakeUploadFile:
|
||||||
@ -17,21 +18,7 @@ class FakeUploadFile:
|
|||||||
created_at: datetime | None
|
created_at: datetime | None
|
||||||
|
|
||||||
|
|
||||||
def _load_serialize_upload_file():
|
|
||||||
api_dir = Path(__file__).resolve().parents[5]
|
|
||||||
serializers_path = api_dir / "controllers" / "service_api" / "dataset" / "rag_pipeline" / "serializers.py"
|
|
||||||
|
|
||||||
spec = importlib.util.spec_from_file_location("rag_pipeline_serializers", serializers_path)
|
|
||||||
assert spec
|
|
||||||
assert spec.loader
|
|
||||||
module = importlib.util.module_from_spec(spec)
|
|
||||||
spec.loader.exec_module(module) # type: ignore[attr-defined]
|
|
||||||
return module.serialize_upload_file
|
|
||||||
|
|
||||||
|
|
||||||
def test_file_upload_created_at_is_isoformat_string():
|
def test_file_upload_created_at_is_isoformat_string():
|
||||||
serialize_upload_file = _load_serialize_upload_file()
|
|
||||||
|
|
||||||
created_at = datetime(2026, 2, 8, 12, 0, 0, tzinfo=UTC)
|
created_at = datetime(2026, 2, 8, 12, 0, 0, tzinfo=UTC)
|
||||||
upload_file = FakeUploadFile()
|
upload_file = FakeUploadFile()
|
||||||
upload_file.id = "file-1"
|
upload_file.id = "file-1"
|
||||||
@ -42,13 +29,11 @@ def test_file_upload_created_at_is_isoformat_string():
|
|||||||
upload_file.created_by = "account-1"
|
upload_file.created_by = "account-1"
|
||||||
upload_file.created_at = created_at
|
upload_file.created_at = created_at
|
||||||
|
|
||||||
result = serialize_upload_file(upload_file)
|
result = dump_response(PipelineUploadFileResponse, upload_file)
|
||||||
assert result["created_at"] == created_at.isoformat()
|
assert result["created_at"] == created_at.isoformat()
|
||||||
|
|
||||||
|
|
||||||
def test_file_upload_created_at_none_serializes_to_null():
|
def test_file_upload_created_at_none_serializes_to_null():
|
||||||
serialize_upload_file = _load_serialize_upload_file()
|
|
||||||
|
|
||||||
upload_file = FakeUploadFile()
|
upload_file = FakeUploadFile()
|
||||||
upload_file.id = "file-1"
|
upload_file.id = "file-1"
|
||||||
upload_file.name = "test.pdf"
|
upload_file.name = "test.pdf"
|
||||||
@ -58,5 +43,5 @@ def test_file_upload_created_at_none_serializes_to_null():
|
|||||||
upload_file.created_by = "account-1"
|
upload_file.created_by = "account-1"
|
||||||
upload_file.created_at = None
|
upload_file.created_at = None
|
||||||
|
|
||||||
result = serialize_upload_file(upload_file)
|
result = dump_response(PipelineUploadFileResponse, upload_file)
|
||||||
assert result["created_at"] is None
|
assert result["created_at"] is None
|
||||||
|
|||||||
@ -4,8 +4,12 @@ export type ClientOptions = {
|
|||||||
baseUrl: `${string}://${string}/console/api` | (string & {})
|
baseUrl: `${string}://${string}/console/api` | (string & {})
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DatasourceCredentialsResponse = {
|
export type DatasourceProviderAuthListResponse = {
|
||||||
result: unknown
|
result: Array<DatasourceProviderAuthResponse>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DatasourceCredentialListResponse = {
|
||||||
|
result: Array<DatasourceCredentialResponse>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DatasourceCredentialPayload = {
|
export type DatasourceCredentialPayload = {
|
||||||
@ -47,6 +51,90 @@ export type DatasourceUpdateNamePayload = {
|
|||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DatasourceProviderAuthResponse = {
|
||||||
|
author: string
|
||||||
|
credential_schema: Array<ProviderConfig>
|
||||||
|
credentials_list: Array<DatasourceCredentialResponse>
|
||||||
|
description: I18nObject
|
||||||
|
icon: string
|
||||||
|
label: I18nObject
|
||||||
|
name: string
|
||||||
|
oauth_schema: DatasourceOAuthSchemaResponse | null
|
||||||
|
plugin_id: string
|
||||||
|
plugin_unique_identifier: string
|
||||||
|
provider: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DatasourceCredentialResponse = {
|
||||||
|
avatar_url: string | null
|
||||||
|
credential: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
id: string
|
||||||
|
is_default: boolean
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProviderConfig = {
|
||||||
|
default?: number | string | number | boolean | null
|
||||||
|
help?: I18nObject | null
|
||||||
|
label?: I18nObject | null
|
||||||
|
multiple?: boolean
|
||||||
|
name: string
|
||||||
|
options?: Array<Option> | null
|
||||||
|
placeholder?: I18nObject | null
|
||||||
|
required?: boolean
|
||||||
|
scope?: AppSelectorScope | ModelSelectorScope | ToolSelectorScope | null
|
||||||
|
type: CoreEntitiesProviderEntitiesBasicProviderConfigType
|
||||||
|
url?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type I18nObject = {
|
||||||
|
en_US: string
|
||||||
|
ja_JP?: string | null
|
||||||
|
pt_BR?: string | null
|
||||||
|
zh_Hans?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DatasourceOAuthSchemaResponse = {
|
||||||
|
client_schema: Array<ProviderConfig>
|
||||||
|
credentials_schema: Array<ProviderConfig>
|
||||||
|
is_oauth_custom_client_enabled: boolean
|
||||||
|
is_system_oauth_params_exists: boolean
|
||||||
|
oauth_custom_client_params: {
|
||||||
|
[key: string]: unknown
|
||||||
|
} | null
|
||||||
|
redirect_uri: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Option = {
|
||||||
|
label: I18nObject
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppSelectorScope = 'all' | 'chat' | 'completion' | 'workflow'
|
||||||
|
|
||||||
|
export type ModelSelectorScope
|
||||||
|
= | 'llm'
|
||||||
|
| 'moderation'
|
||||||
|
| 'rerank'
|
||||||
|
| 'speech2text'
|
||||||
|
| 'text-embedding'
|
||||||
|
| 'tts'
|
||||||
|
| 'vision'
|
||||||
|
|
||||||
|
export type ToolSelectorScope = 'all' | 'builtin' | 'custom' | 'workflow'
|
||||||
|
|
||||||
|
export type CoreEntitiesProviderEntitiesBasicProviderConfigType
|
||||||
|
= | 'app-selector'
|
||||||
|
| 'array[tools]'
|
||||||
|
| 'boolean'
|
||||||
|
| 'model-selector'
|
||||||
|
| 'secret-input'
|
||||||
|
| 'select'
|
||||||
|
| 'text-input'
|
||||||
|
|
||||||
export type GetAuthPluginDatasourceDefaultListData = {
|
export type GetAuthPluginDatasourceDefaultListData = {
|
||||||
body?: never
|
body?: never
|
||||||
path?: never
|
path?: never
|
||||||
@ -55,7 +143,7 @@ export type GetAuthPluginDatasourceDefaultListData = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type GetAuthPluginDatasourceDefaultListResponses = {
|
export type GetAuthPluginDatasourceDefaultListResponses = {
|
||||||
200: DatasourceCredentialsResponse
|
200: DatasourceProviderAuthListResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetAuthPluginDatasourceDefaultListResponse
|
export type GetAuthPluginDatasourceDefaultListResponse
|
||||||
@ -69,7 +157,7 @@ export type GetAuthPluginDatasourceListData = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type GetAuthPluginDatasourceListResponses = {
|
export type GetAuthPluginDatasourceListResponses = {
|
||||||
200: DatasourceCredentialsResponse
|
200: DatasourceProviderAuthListResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetAuthPluginDatasourceListResponse
|
export type GetAuthPluginDatasourceListResponse
|
||||||
@ -85,7 +173,7 @@ export type GetAuthPluginDatasourceByProviderIdData = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type GetAuthPluginDatasourceByProviderIdResponses = {
|
export type GetAuthPluginDatasourceByProviderIdResponses = {
|
||||||
200: DatasourceCredentialsResponse
|
200: DatasourceCredentialListResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetAuthPluginDatasourceByProviderIdResponse
|
export type GetAuthPluginDatasourceByProviderIdResponse
|
||||||
|
|||||||
@ -2,13 +2,6 @@
|
|||||||
|
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
|
|
||||||
/**
|
|
||||||
* DatasourceCredentialsResponse
|
|
||||||
*/
|
|
||||||
export const zDatasourceCredentialsResponse = z.object({
|
|
||||||
result: z.unknown(),
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DatasourceCredentialPayload
|
* DatasourceCredentialPayload
|
||||||
*/
|
*/
|
||||||
@ -64,23 +57,153 @@ export const zDatasourceUpdateNamePayload = z.object({
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Success
|
* DatasourceCredentialResponse
|
||||||
*/
|
*/
|
||||||
export const zGetAuthPluginDatasourceDefaultListResponse = zDatasourceCredentialsResponse
|
export const zDatasourceCredentialResponse = z.object({
|
||||||
|
avatar_url: z.string().nullable(),
|
||||||
|
credential: z.record(z.string(), z.unknown()),
|
||||||
|
id: z.string(),
|
||||||
|
is_default: z.boolean(),
|
||||||
|
name: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Success
|
* DatasourceCredentialListResponse
|
||||||
*/
|
*/
|
||||||
export const zGetAuthPluginDatasourceListResponse = zDatasourceCredentialsResponse
|
export const zDatasourceCredentialListResponse = z.object({
|
||||||
|
result: z.array(zDatasourceCredentialResponse),
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I18nObject
|
||||||
|
*
|
||||||
|
* Model class for i18n object.
|
||||||
|
*/
|
||||||
|
export const zI18nObject = z.object({
|
||||||
|
en_US: z.string(),
|
||||||
|
ja_JP: z.string().nullish(),
|
||||||
|
pt_BR: z.string().nullish(),
|
||||||
|
zh_Hans: z.string().nullish(),
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option
|
||||||
|
*/
|
||||||
|
export const zOption = z.object({
|
||||||
|
label: zI18nObject,
|
||||||
|
value: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AppSelectorScope
|
||||||
|
*/
|
||||||
|
export const zAppSelectorScope = z.enum(['all', 'chat', 'completion', 'workflow'])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ModelSelectorScope
|
||||||
|
*/
|
||||||
|
export const zModelSelectorScope = z.enum([
|
||||||
|
'llm',
|
||||||
|
'moderation',
|
||||||
|
'rerank',
|
||||||
|
'speech2text',
|
||||||
|
'text-embedding',
|
||||||
|
'tts',
|
||||||
|
'vision',
|
||||||
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ToolSelectorScope
|
||||||
|
*/
|
||||||
|
export const zToolSelectorScope = z.enum(['all', 'builtin', 'custom', 'workflow'])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type
|
||||||
|
*/
|
||||||
|
export const zCoreEntitiesProviderEntitiesBasicProviderConfigType = z.enum([
|
||||||
|
'app-selector',
|
||||||
|
'array[tools]',
|
||||||
|
'boolean',
|
||||||
|
'model-selector',
|
||||||
|
'secret-input',
|
||||||
|
'select',
|
||||||
|
'text-input',
|
||||||
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProviderConfig
|
||||||
|
*
|
||||||
|
* Model class for common provider settings like credentials
|
||||||
|
*/
|
||||||
|
export const zProviderConfig = z.object({
|
||||||
|
default: z.union([z.int(), z.string(), z.number(), z.boolean()]).nullish(),
|
||||||
|
help: zI18nObject.nullish(),
|
||||||
|
label: zI18nObject.nullish(),
|
||||||
|
multiple: z.boolean().optional().default(false),
|
||||||
|
name: z.string(),
|
||||||
|
options: z.array(zOption).nullish(),
|
||||||
|
placeholder: zI18nObject.nullish(),
|
||||||
|
required: z.boolean().optional().default(false),
|
||||||
|
scope: z.union([zAppSelectorScope, zModelSelectorScope, zToolSelectorScope]).nullish(),
|
||||||
|
type: zCoreEntitiesProviderEntitiesBasicProviderConfigType,
|
||||||
|
url: z.string().nullish(),
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DatasourceOAuthSchemaResponse
|
||||||
|
*/
|
||||||
|
export const zDatasourceOAuthSchemaResponse = z.object({
|
||||||
|
client_schema: z.array(zProviderConfig),
|
||||||
|
credentials_schema: z.array(zProviderConfig),
|
||||||
|
is_oauth_custom_client_enabled: z.boolean(),
|
||||||
|
is_system_oauth_params_exists: z.boolean(),
|
||||||
|
oauth_custom_client_params: z.record(z.string(), z.unknown()).nullable(),
|
||||||
|
redirect_uri: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DatasourceProviderAuthResponse
|
||||||
|
*/
|
||||||
|
export const zDatasourceProviderAuthResponse = z.object({
|
||||||
|
author: z.string(),
|
||||||
|
credential_schema: z.array(zProviderConfig),
|
||||||
|
credentials_list: z.array(zDatasourceCredentialResponse),
|
||||||
|
description: zI18nObject,
|
||||||
|
icon: z.string(),
|
||||||
|
label: zI18nObject,
|
||||||
|
name: z.string(),
|
||||||
|
oauth_schema: zDatasourceOAuthSchemaResponse.nullable(),
|
||||||
|
plugin_id: z.string(),
|
||||||
|
plugin_unique_identifier: z.string(),
|
||||||
|
provider: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DatasourceProviderAuthListResponse
|
||||||
|
*/
|
||||||
|
export const zDatasourceProviderAuthListResponse = z.object({
|
||||||
|
result: z.array(zDatasourceProviderAuthResponse),
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default datasource credentials retrieved successfully
|
||||||
|
*/
|
||||||
|
export const zGetAuthPluginDatasourceDefaultListResponse = zDatasourceProviderAuthListResponse
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Datasource credentials retrieved successfully
|
||||||
|
*/
|
||||||
|
export const zGetAuthPluginDatasourceListResponse = zDatasourceProviderAuthListResponse
|
||||||
|
|
||||||
export const zGetAuthPluginDatasourceByProviderIdPath = z.object({
|
export const zGetAuthPluginDatasourceByProviderIdPath = z.object({
|
||||||
provider_id: z.string(),
|
provider_id: z.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Success
|
* Datasource credentials retrieved successfully
|
||||||
*/
|
*/
|
||||||
export const zGetAuthPluginDatasourceByProviderIdResponse = zDatasourceCredentialsResponse
|
export const zGetAuthPluginDatasourceByProviderIdResponse = zDatasourceCredentialListResponse
|
||||||
|
|
||||||
export const zPostAuthPluginDatasourceByProviderIdBody = zDatasourceCredentialPayload
|
export const zPostAuthPluginDatasourceByProviderIdBody = zDatasourceCredentialPayload
|
||||||
|
|
||||||
@ -89,7 +212,7 @@ export const zPostAuthPluginDatasourceByProviderIdPath = z.object({
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Success
|
* Datasource credential created successfully
|
||||||
*/
|
*/
|
||||||
export const zPostAuthPluginDatasourceByProviderIdResponse = zSimpleResultResponse
|
export const zPostAuthPluginDatasourceByProviderIdResponse = zSimpleResultResponse
|
||||||
|
|
||||||
@ -109,7 +232,7 @@ export const zPostAuthPluginDatasourceByProviderIdCustomClientPath = z.object({
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Success
|
* Datasource OAuth custom client saved successfully
|
||||||
*/
|
*/
|
||||||
export const zPostAuthPluginDatasourceByProviderIdCustomClientResponse = zSimpleResultResponse
|
export const zPostAuthPluginDatasourceByProviderIdCustomClientResponse = zSimpleResultResponse
|
||||||
|
|
||||||
@ -142,7 +265,7 @@ export const zPostAuthPluginDatasourceByProviderIdUpdatePath = z.object({
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Success
|
* Datasource credential updated successfully
|
||||||
*/
|
*/
|
||||||
export const zPostAuthPluginDatasourceByProviderIdUpdateResponse = zSimpleResultResponse
|
export const zPostAuthPluginDatasourceByProviderIdUpdateResponse = zSimpleResultResponse
|
||||||
|
|
||||||
|
|||||||
@ -135,7 +135,7 @@ export const zGetOauthPluginByProviderIdDatasourceGetAuthorizationUrlQuery = z.o
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authorization URL retrieved successfully
|
* Datasource OAuth authorization URL generated successfully
|
||||||
*/
|
*/
|
||||||
export const zGetOauthPluginByProviderIdDatasourceGetAuthorizationUrlResponse
|
export const zGetOauthPluginByProviderIdDatasourceGetAuthorizationUrlResponse
|
||||||
= zPluginOAuthAuthorizationUrlResponse
|
= zPluginOAuthAuthorizationUrlResponse
|
||||||
|
|||||||
@ -327,8 +327,6 @@ export type Parser = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DataSourceContentPreviewResponse = unknown
|
|
||||||
|
|
||||||
export type PublishedWorkflowRunPayload = {
|
export type PublishedWorkflowRunPayload = {
|
||||||
datasource_info_list: Array<{
|
datasource_info_list: Array<{
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
@ -1329,7 +1327,9 @@ export type PostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeI
|
|||||||
|
|
||||||
export type PostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdPreviewResponses
|
export type PostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdPreviewResponses
|
||||||
= {
|
= {
|
||||||
200: DataSourceContentPreviewResponse
|
200: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdPreviewResponse
|
export type PostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdPreviewResponse
|
||||||
|
|||||||
@ -199,11 +199,6 @@ export const zParser = z.object({
|
|||||||
inputs: z.record(z.string(), z.unknown()),
|
inputs: z.record(z.string(), z.unknown()),
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* DataSourceContentPreviewResponse
|
|
||||||
*/
|
|
||||||
export const zDataSourceContentPreviewResponse = z.unknown()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PublishedWorkflowRunPayload
|
* PublishedWorkflowRunPayload
|
||||||
*/
|
*/
|
||||||
@ -1179,7 +1174,7 @@ export const zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNod
|
|||||||
* Success
|
* Success
|
||||||
*/
|
*/
|
||||||
export const zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdPreviewResponse
|
export const zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdPreviewResponse
|
||||||
= zDataSourceContentPreviewResponse
|
= z.record(z.string(), z.unknown())
|
||||||
|
|
||||||
export const zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdRunBody
|
export const zPostRagPipelinesByPipelineIdWorkflowsPublishedDatasourceNodesByNodeIdRunBody
|
||||||
= zDatasourceNodeRunPayload
|
= zDatasourceNodeRunPayload
|
||||||
|
|||||||
@ -567,7 +567,7 @@ export type DatasourceNodeRunPayload = {
|
|||||||
export type DatasourcePluginListResponse = Array<DatasourcePluginResponse>
|
export type DatasourcePluginListResponse = Array<DatasourcePluginResponse>
|
||||||
|
|
||||||
export type DatasourcePluginResponse = {
|
export type DatasourcePluginResponse = {
|
||||||
credentials: Array<DatasourceCredentialInfoResponse>
|
credentials?: Array<DatasourceCredentialInfoResponse>
|
||||||
datasource_type?: string | null
|
datasource_type?: string | null
|
||||||
node_id?: string | null
|
node_id?: string | null
|
||||||
plugin_id?: string | null
|
plugin_id?: string | null
|
||||||
@ -3196,7 +3196,9 @@ export type PostDatasetsByDatasetIdPipelineDatasourceNodesByNodeIdRunErrors = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type PostDatasetsByDatasetIdPipelineDatasourceNodesByNodeIdRunResponses = {
|
export type PostDatasetsByDatasetIdPipelineDatasourceNodesByNodeIdRunResponses = {
|
||||||
200: GeneratedAppResponse
|
200: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PostDatasetsByDatasetIdPipelineDatasourceNodesByNodeIdRunResponse
|
export type PostDatasetsByDatasetIdPipelineDatasourceNodesByNodeIdRunResponse
|
||||||
|
|||||||
@ -698,7 +698,7 @@ export const zDatasourceNodeRunPayload = z.object({
|
|||||||
* DatasourcePluginResponse
|
* DatasourcePluginResponse
|
||||||
*/
|
*/
|
||||||
export const zDatasourcePluginResponse = z.object({
|
export const zDatasourcePluginResponse = z.object({
|
||||||
credentials: z.array(zDatasourceCredentialInfoResponse),
|
credentials: z.array(zDatasourceCredentialInfoResponse).optional(),
|
||||||
datasource_type: z.string().nullish(),
|
datasource_type: z.string().nullish(),
|
||||||
node_id: z.string().nullish(),
|
node_id: z.string().nullish(),
|
||||||
plugin_id: z.string().nullish(),
|
plugin_id: z.string().nullish(),
|
||||||
@ -3063,8 +3063,10 @@ export const zPostDatasetsByDatasetIdPipelineDatasourceNodesByNodeIdRunPath = z.
|
|||||||
/**
|
/**
|
||||||
* Streaming response with node execution events.
|
* Streaming response with node execution events.
|
||||||
*/
|
*/
|
||||||
export const zPostDatasetsByDatasetIdPipelineDatasourceNodesByNodeIdRunResponse
|
export const zPostDatasetsByDatasetIdPipelineDatasourceNodesByNodeIdRunResponse = z.record(
|
||||||
= zGeneratedAppResponse
|
z.string(),
|
||||||
|
z.unknown(),
|
||||||
|
)
|
||||||
|
|
||||||
export const zPostDatasetsByDatasetIdPipelineRunBody = zPipelineRunApiEntity
|
export const zPostDatasetsByDatasetIdPipelineRunBody = zPipelineRunApiEntity
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user