Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b96062be0 | ||
|
|
391007d02e | ||
|
|
e41965061c | ||
|
|
2b9eb06555 | ||
|
|
31f7752ba9 |
@@ -213,6 +213,7 @@ api/.vscode
|
||||
# pnpm
|
||||
/.pnpm-store
|
||||
/node_modules
|
||||
.vite-hooks/_
|
||||
|
||||
# plugin migrate
|
||||
plugins.jsonl
|
||||
|
||||
Regular → Executable
+1
-1
@@ -77,7 +77,7 @@ if $web_modified; then
|
||||
fi
|
||||
|
||||
cd ./web || exit 1
|
||||
lint-staged
|
||||
vp staged
|
||||
|
||||
if $web_ts_modified; then
|
||||
echo "Running TypeScript type-check:tsgo"
|
||||
@@ -593,17 +593,15 @@ class PublishedRagPipelineApi(Resource):
|
||||
# The role of the current user in the ta table must be admin, owner, or editor
|
||||
current_user, _ = current_account_with_tenant()
|
||||
rag_pipeline_service = RagPipelineService()
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
pipeline = session.merge(pipeline)
|
||||
workflow = rag_pipeline_service.publish_workflow(
|
||||
session=session,
|
||||
pipeline=pipeline,
|
||||
account=current_user,
|
||||
)
|
||||
pipeline.is_published = True
|
||||
pipeline.workflow_id = workflow.id
|
||||
session.add(pipeline)
|
||||
workflow_created_at = TimestampField().format(workflow.created_at)
|
||||
workflow = rag_pipeline_service.publish_workflow(
|
||||
session=db.session, # type: ignore[reportArgumentType,arg-type]
|
||||
pipeline=pipeline,
|
||||
account=current_user,
|
||||
)
|
||||
pipeline.is_published = True
|
||||
pipeline.workflow_id = workflow.id
|
||||
db.session.commit()
|
||||
workflow_created_at = TimestampField().format(workflow.created_at)
|
||||
|
||||
return {
|
||||
"result": "success",
|
||||
|
||||
@@ -6,7 +6,7 @@ from flask import current_app, request
|
||||
from flask_login import user_logged_in
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from extensions.ext_database import db
|
||||
from libs.login import current_user
|
||||
@@ -33,7 +33,7 @@ def get_user(tenant_id: str, user_id: str | None) -> EndUser:
|
||||
user_id = DefaultEndUserSessionID.DEFAULT_SESSION_ID
|
||||
is_anonymous = user_id == DefaultEndUserSessionID.DEFAULT_SESSION_ID
|
||||
try:
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
user_model = None
|
||||
|
||||
if is_anonymous:
|
||||
@@ -56,7 +56,7 @@ def get_user(tenant_id: str, user_id: str | None) -> EndUser:
|
||||
session_id=user_id,
|
||||
)
|
||||
session.add(user_model)
|
||||
session.commit()
|
||||
session.flush()
|
||||
session.refresh(user_model)
|
||||
|
||||
except Exception:
|
||||
|
||||
@@ -3,7 +3,7 @@ from typing import Any, Literal
|
||||
from flask import request
|
||||
from flask_restx import Resource
|
||||
from pydantic import BaseModel, Field, TypeAdapter, field_validator, model_validator
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import BadRequest, NotFound
|
||||
|
||||
import services
|
||||
@@ -116,7 +116,7 @@ class ConversationApi(Resource):
|
||||
last_id = str(query_args.last_id) if query_args.last_id else None
|
||||
|
||||
try:
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
pagination = ConversationService.pagination_by_last_id(
|
||||
session=session,
|
||||
app_model=app_model,
|
||||
|
||||
@@ -8,7 +8,7 @@ from graphon.enums import WorkflowExecutionStatus
|
||||
from graphon.graph_engine.manager import GraphEngineManager
|
||||
from graphon.model_runtime.errors.invoke import InvokeError
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import BadRequest, InternalServerError, NotFound
|
||||
|
||||
from controllers.common.schema import register_schema_models
|
||||
@@ -314,7 +314,7 @@ class WorkflowAppLogApi(Resource):
|
||||
|
||||
# get paginate workflow app logs
|
||||
workflow_app_service = WorkflowAppService()
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
workflow_app_log_pagination = workflow_app_service.get_paginate_workflow_app_logs(
|
||||
session=session,
|
||||
app_model=app_model,
|
||||
|
||||
@@ -2,6 +2,7 @@ import threading
|
||||
from typing import Any
|
||||
|
||||
import pytz
|
||||
from sqlalchemy import select
|
||||
|
||||
import contexts
|
||||
from core.app.app_config.easy_ui_based_app.agent.manager import AgentConfigManager
|
||||
@@ -23,25 +24,25 @@ class AgentService:
|
||||
contexts.plugin_tool_providers.set({})
|
||||
contexts.plugin_tool_providers_lock.set(threading.Lock())
|
||||
|
||||
conversation: Conversation | None = (
|
||||
db.session.query(Conversation)
|
||||
conversation: Conversation | None = db.session.scalar(
|
||||
select(Conversation)
|
||||
.where(
|
||||
Conversation.id == conversation_id,
|
||||
Conversation.app_id == app_model.id,
|
||||
)
|
||||
.first()
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
if not conversation:
|
||||
raise ValueError(f"Conversation not found: {conversation_id}")
|
||||
|
||||
message: Message | None = (
|
||||
db.session.query(Message)
|
||||
message: Message | None = db.session.scalar(
|
||||
select(Message)
|
||||
.where(
|
||||
Message.id == message_id,
|
||||
Message.conversation_id == conversation_id,
|
||||
)
|
||||
.first()
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
if not message:
|
||||
@@ -51,16 +52,11 @@ class AgentService:
|
||||
|
||||
if conversation.from_end_user_id:
|
||||
# only select name field
|
||||
executor = (
|
||||
db.session.query(EndUser, EndUser.name).where(EndUser.id == conversation.from_end_user_id).first()
|
||||
)
|
||||
executor_name = db.session.scalar(select(EndUser.name).where(EndUser.id == conversation.from_end_user_id))
|
||||
else:
|
||||
executor = db.session.query(Account, Account.name).where(Account.id == conversation.from_account_id).first()
|
||||
executor_name = db.session.scalar(select(Account.name).where(Account.id == conversation.from_account_id))
|
||||
|
||||
if executor:
|
||||
executor = executor.name
|
||||
else:
|
||||
executor = "Unknown"
|
||||
executor = executor_name or "Unknown"
|
||||
assert isinstance(current_user, Account)
|
||||
assert current_user.timezone is not None
|
||||
timezone = pytz.timezone(current_user.timezone)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from sqlalchemy import select
|
||||
|
||||
from core.extension.api_based_extension_requestor import APIBasedExtensionRequestor
|
||||
from core.helper.encrypter import decrypt_token, encrypt_token
|
||||
from extensions.ext_database import db
|
||||
@@ -7,11 +9,12 @@ from models.api_based_extension import APIBasedExtension, APIBasedExtensionPoint
|
||||
class APIBasedExtensionService:
|
||||
@staticmethod
|
||||
def get_all_by_tenant_id(tenant_id: str) -> list[APIBasedExtension]:
|
||||
extension_list = (
|
||||
db.session.query(APIBasedExtension)
|
||||
.filter_by(tenant_id=tenant_id)
|
||||
.order_by(APIBasedExtension.created_at.desc())
|
||||
.all()
|
||||
extension_list = list(
|
||||
db.session.scalars(
|
||||
select(APIBasedExtension)
|
||||
.where(APIBasedExtension.tenant_id == tenant_id)
|
||||
.order_by(APIBasedExtension.created_at.desc())
|
||||
).all()
|
||||
)
|
||||
|
||||
for extension in extension_list:
|
||||
@@ -36,11 +39,10 @@ class APIBasedExtensionService:
|
||||
|
||||
@staticmethod
|
||||
def get_with_tenant_id(tenant_id: str, api_based_extension_id: str) -> APIBasedExtension:
|
||||
extension = (
|
||||
db.session.query(APIBasedExtension)
|
||||
.filter_by(tenant_id=tenant_id)
|
||||
.filter_by(id=api_based_extension_id)
|
||||
.first()
|
||||
extension = db.session.scalar(
|
||||
select(APIBasedExtension)
|
||||
.where(APIBasedExtension.tenant_id == tenant_id, APIBasedExtension.id == api_based_extension_id)
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
if not extension:
|
||||
@@ -58,23 +60,27 @@ class APIBasedExtensionService:
|
||||
|
||||
if not extension_data.id:
|
||||
# case one: check new data, name must be unique
|
||||
is_name_existed = (
|
||||
db.session.query(APIBasedExtension)
|
||||
.filter_by(tenant_id=extension_data.tenant_id)
|
||||
.filter_by(name=extension_data.name)
|
||||
.first()
|
||||
is_name_existed = db.session.scalar(
|
||||
select(APIBasedExtension)
|
||||
.where(
|
||||
APIBasedExtension.tenant_id == extension_data.tenant_id,
|
||||
APIBasedExtension.name == extension_data.name,
|
||||
)
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
if is_name_existed:
|
||||
raise ValueError("name must be unique, it is already existed")
|
||||
else:
|
||||
# case two: check existing data, name must be unique
|
||||
is_name_existed = (
|
||||
db.session.query(APIBasedExtension)
|
||||
.filter_by(tenant_id=extension_data.tenant_id)
|
||||
.filter_by(name=extension_data.name)
|
||||
.where(APIBasedExtension.id != extension_data.id)
|
||||
.first()
|
||||
is_name_existed = db.session.scalar(
|
||||
select(APIBasedExtension)
|
||||
.where(
|
||||
APIBasedExtension.tenant_id == extension_data.tenant_id,
|
||||
APIBasedExtension.name == extension_data.name,
|
||||
APIBasedExtension.id != extension_data.id,
|
||||
)
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
if is_name_existed:
|
||||
|
||||
@@ -6,6 +6,7 @@ import sqlalchemy as sa
|
||||
from flask_sqlalchemy.pagination import Pagination
|
||||
from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType
|
||||
from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
|
||||
from sqlalchemy import select
|
||||
|
||||
from configs import dify_config
|
||||
from constants.model_template import default_app_templates
|
||||
@@ -433,9 +434,7 @@ class AppService:
|
||||
meta["tool_icons"][tool_name] = url_prefix + provider_id + "/icon"
|
||||
elif provider_type == "api":
|
||||
try:
|
||||
provider: ApiToolProvider | None = (
|
||||
db.session.query(ApiToolProvider).where(ApiToolProvider.id == provider_id).first()
|
||||
)
|
||||
provider: ApiToolProvider | None = db.session.get(ApiToolProvider, provider_id)
|
||||
if provider is None:
|
||||
raise ValueError(f"provider not found for tool {tool_name}")
|
||||
meta["tool_icons"][tool_name] = json.loads(provider.icon)
|
||||
@@ -451,7 +450,7 @@ class AppService:
|
||||
:param app_id: app id
|
||||
:return: app code
|
||||
"""
|
||||
site = db.session.query(Site).where(Site.app_id == app_id).first()
|
||||
site = db.session.scalar(select(Site).where(Site.app_id == app_id).limit(1))
|
||||
if not site:
|
||||
raise ValueError(f"App with id {app_id} not found")
|
||||
return str(site.code)
|
||||
@@ -463,7 +462,7 @@ class AppService:
|
||||
:param app_code: app code
|
||||
:return: app id
|
||||
"""
|
||||
site = db.session.query(Site).where(Site.code == app_code).first()
|
||||
site = db.session.scalar(select(Site).where(Site.code == app_code).limit(1))
|
||||
if not site:
|
||||
raise ValueError(f"App with code {app_code} not found")
|
||||
return str(site.app_id)
|
||||
|
||||
@@ -4,7 +4,7 @@ import json
|
||||
from datetime import datetime
|
||||
|
||||
from flask import Response
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy import or_, select
|
||||
|
||||
from extensions.ext_database import db
|
||||
from models.enums import FeedbackRating
|
||||
@@ -41,8 +41,8 @@ class FeedbackService:
|
||||
raise ValueError(f"Unsupported format: {format_type}")
|
||||
|
||||
# Build base query
|
||||
query = (
|
||||
db.session.query(MessageFeedback, Message, Conversation, App, Account)
|
||||
stmt = (
|
||||
select(MessageFeedback, Message, Conversation, App, Account)
|
||||
.join(Message, MessageFeedback.message_id == Message.id)
|
||||
.join(Conversation, MessageFeedback.conversation_id == Conversation.id)
|
||||
.join(App, MessageFeedback.app_id == App.id)
|
||||
@@ -52,36 +52,36 @@ class FeedbackService:
|
||||
|
||||
# Apply filters
|
||||
if from_source:
|
||||
query = query.filter(MessageFeedback.from_source == from_source)
|
||||
stmt = stmt.where(MessageFeedback.from_source == from_source)
|
||||
|
||||
if rating:
|
||||
query = query.filter(MessageFeedback.rating == rating)
|
||||
stmt = stmt.where(MessageFeedback.rating == rating)
|
||||
|
||||
if has_comment is not None:
|
||||
if has_comment:
|
||||
query = query.filter(MessageFeedback.content.isnot(None), MessageFeedback.content != "")
|
||||
stmt = stmt.where(MessageFeedback.content.isnot(None), MessageFeedback.content != "")
|
||||
else:
|
||||
query = query.filter(or_(MessageFeedback.content.is_(None), MessageFeedback.content == ""))
|
||||
stmt = stmt.where(or_(MessageFeedback.content.is_(None), MessageFeedback.content == ""))
|
||||
|
||||
if start_date:
|
||||
try:
|
||||
start_dt = datetime.strptime(start_date, "%Y-%m-%d")
|
||||
query = query.filter(MessageFeedback.created_at >= start_dt)
|
||||
stmt = stmt.where(MessageFeedback.created_at >= start_dt)
|
||||
except ValueError:
|
||||
raise ValueError(f"Invalid start_date format: {start_date}. Use YYYY-MM-DD")
|
||||
|
||||
if end_date:
|
||||
try:
|
||||
end_dt = datetime.strptime(end_date, "%Y-%m-%d")
|
||||
query = query.filter(MessageFeedback.created_at <= end_dt)
|
||||
stmt = stmt.where(MessageFeedback.created_at <= end_dt)
|
||||
except ValueError:
|
||||
raise ValueError(f"Invalid end_date format: {end_date}. Use YYYY-MM-DD")
|
||||
|
||||
# Order by creation date (newest first)
|
||||
query = query.order_by(MessageFeedback.created_at.desc())
|
||||
stmt = stmt.order_by(MessageFeedback.created_at.desc())
|
||||
|
||||
# Execute query
|
||||
results = query.all()
|
||||
results = db.session.execute(stmt).all()
|
||||
|
||||
# Prepare data for export
|
||||
export_data = []
|
||||
|
||||
@@ -6,6 +6,7 @@ from uuid import uuid4
|
||||
|
||||
import yaml
|
||||
from flask_login import current_user
|
||||
from sqlalchemy import select
|
||||
|
||||
from constants import DOCUMENT_EXTENSIONS
|
||||
from core.plugin.impl.plugin import PluginInstaller
|
||||
@@ -26,7 +27,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class RagPipelineTransformService:
|
||||
def transform_dataset(self, dataset_id: str):
|
||||
dataset = db.session.query(Dataset).where(Dataset.id == dataset_id).first()
|
||||
dataset = db.session.get(Dataset, dataset_id)
|
||||
if not dataset:
|
||||
raise ValueError("Dataset not found")
|
||||
if dataset.pipeline_id and dataset.runtime_mode == DatasetRuntimeMode.RAG_PIPELINE:
|
||||
@@ -306,7 +307,7 @@ class RagPipelineTransformService:
|
||||
jina_node_id = "1752491761974"
|
||||
firecrawl_node_id = "1752565402678"
|
||||
|
||||
documents = db.session.query(Document).where(Document.dataset_id == dataset.id).all()
|
||||
documents = db.session.scalars(select(Document).where(Document.dataset_id == dataset.id)).all()
|
||||
|
||||
for document in documents:
|
||||
data_source_info_dict = document.data_source_info_dict
|
||||
@@ -316,7 +317,7 @@ class RagPipelineTransformService:
|
||||
document.data_source_type = DataSourceType.LOCAL_FILE
|
||||
file_id = data_source_info_dict.get("upload_file_id")
|
||||
if file_id:
|
||||
file = db.session.query(UploadFile).where(UploadFile.id == file_id).first()
|
||||
file = db.session.get(UploadFile, file_id)
|
||||
if file:
|
||||
data_source_info = json.dumps(
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from sqlalchemy import select
|
||||
|
||||
from configs import dify_config
|
||||
from extensions.ext_database import db
|
||||
from models.model import AccountTrialAppRecord, TrialApp
|
||||
@@ -27,7 +29,7 @@ class RecommendedAppService:
|
||||
apps = result["recommended_apps"]
|
||||
for app in apps:
|
||||
app_id = app["app_id"]
|
||||
trial_app_model = db.session.query(TrialApp).where(TrialApp.app_id == app_id).first()
|
||||
trial_app_model = db.session.scalar(select(TrialApp).where(TrialApp.app_id == app_id).limit(1))
|
||||
if trial_app_model:
|
||||
app["can_trial"] = True
|
||||
else:
|
||||
@@ -46,7 +48,7 @@ class RecommendedAppService:
|
||||
result: dict = retrieval_instance.get_recommend_app_detail(app_id)
|
||||
if FeatureService.get_system_features().enable_trial_app:
|
||||
app_id = result["id"]
|
||||
trial_app_model = db.session.query(TrialApp).where(TrialApp.app_id == app_id).first()
|
||||
trial_app_model = db.session.scalar(select(TrialApp).where(TrialApp.app_id == app_id).limit(1))
|
||||
if trial_app_model:
|
||||
result["can_trial"] = True
|
||||
else:
|
||||
@@ -60,10 +62,10 @@ class RecommendedAppService:
|
||||
:param app_id: app id
|
||||
:return:
|
||||
"""
|
||||
account_trial_app_record = (
|
||||
db.session.query(AccountTrialAppRecord)
|
||||
account_trial_app_record = db.session.scalar(
|
||||
select(AccountTrialAppRecord)
|
||||
.where(AccountTrialAppRecord.app_id == app_id, AccountTrialAppRecord.account_id == account_id)
|
||||
.first()
|
||||
.limit(1)
|
||||
)
|
||||
if account_trial_app_record:
|
||||
account_trial_app_record.count += 1
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from typing import Union
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from extensions.ext_database import db
|
||||
from libs.infinite_scroll_pagination import InfiniteScrollPagination
|
||||
from models import Account
|
||||
@@ -16,16 +18,15 @@ class SavedMessageService:
|
||||
) -> InfiniteScrollPagination:
|
||||
if not user:
|
||||
raise ValueError("User is required")
|
||||
saved_messages = (
|
||||
db.session.query(SavedMessage)
|
||||
saved_messages = db.session.scalars(
|
||||
select(SavedMessage)
|
||||
.where(
|
||||
SavedMessage.app_id == app_model.id,
|
||||
SavedMessage.created_by_role == ("account" if isinstance(user, Account) else "end_user"),
|
||||
SavedMessage.created_by == user.id,
|
||||
)
|
||||
.order_by(SavedMessage.created_at.desc())
|
||||
.all()
|
||||
)
|
||||
).all()
|
||||
message_ids = [sm.message_id for sm in saved_messages]
|
||||
|
||||
return MessageService.pagination_by_last_id(
|
||||
@@ -36,15 +37,15 @@ class SavedMessageService:
|
||||
def save(cls, app_model: App, user: Union[Account, EndUser] | None, message_id: str):
|
||||
if not user:
|
||||
return
|
||||
saved_message = (
|
||||
db.session.query(SavedMessage)
|
||||
saved_message = db.session.scalar(
|
||||
select(SavedMessage)
|
||||
.where(
|
||||
SavedMessage.app_id == app_model.id,
|
||||
SavedMessage.message_id == message_id,
|
||||
SavedMessage.created_by_role == ("account" if isinstance(user, Account) else "end_user"),
|
||||
SavedMessage.created_by == user.id,
|
||||
)
|
||||
.first()
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
if saved_message:
|
||||
@@ -66,15 +67,15 @@ class SavedMessageService:
|
||||
def delete(cls, app_model: App, user: Union[Account, EndUser] | None, message_id: str):
|
||||
if not user:
|
||||
return
|
||||
saved_message = (
|
||||
db.session.query(SavedMessage)
|
||||
saved_message = db.session.scalar(
|
||||
select(SavedMessage)
|
||||
.where(
|
||||
SavedMessage.app_id == app_model.id,
|
||||
SavedMessage.message_id == message_id,
|
||||
SavedMessage.created_by_role == ("account" if isinstance(user, Account) else "end_user"),
|
||||
SavedMessage.created_by == user.id,
|
||||
)
|
||||
.first()
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
if not saved_message:
|
||||
|
||||
@@ -332,12 +332,11 @@ class BuiltinToolManageService:
|
||||
get builtin tool provider credentials
|
||||
"""
|
||||
with db.session.no_autoflush:
|
||||
providers = (
|
||||
db.session.query(BuiltinToolProvider)
|
||||
.filter_by(tenant_id=tenant_id, provider=provider_name)
|
||||
providers = db.session.scalars(
|
||||
select(BuiltinToolProvider)
|
||||
.where(BuiltinToolProvider.tenant_id == tenant_id, BuiltinToolProvider.provider == provider_name)
|
||||
.order_by(BuiltinToolProvider.is_default.desc(), BuiltinToolProvider.created_at.asc())
|
||||
.all()
|
||||
)
|
||||
).all()
|
||||
|
||||
if len(providers) == 0:
|
||||
return []
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import logging
|
||||
|
||||
from graphon.model_runtime.entities.model_entities import ModelType
|
||||
from sqlalchemy import delete, select
|
||||
|
||||
from core.model_manager import ModelInstance, ModelManager
|
||||
from core.rag.datasource.keyword.keyword_factory import Keyword
|
||||
@@ -29,7 +30,7 @@ class VectorService:
|
||||
|
||||
for segment in segments:
|
||||
if doc_form == IndexStructureType.PARENT_CHILD_INDEX:
|
||||
dataset_document = db.session.query(DatasetDocument).filter_by(id=segment.document_id).first()
|
||||
dataset_document = db.session.get(DatasetDocument, segment.document_id)
|
||||
if not dataset_document:
|
||||
logger.warning(
|
||||
"Expected DatasetDocument record to exist, but none was found, document_id=%s, segment_id=%s",
|
||||
@@ -38,11 +39,7 @@ class VectorService:
|
||||
)
|
||||
continue
|
||||
# get the process rule
|
||||
processing_rule = (
|
||||
db.session.query(DatasetProcessRule)
|
||||
.where(DatasetProcessRule.id == dataset_document.dataset_process_rule_id)
|
||||
.first()
|
||||
)
|
||||
processing_rule = db.session.get(DatasetProcessRule, dataset_document.dataset_process_rule_id)
|
||||
if not processing_rule:
|
||||
raise ValueError("No processing rule found.")
|
||||
# get embedding model instance
|
||||
@@ -271,8 +268,8 @@ class VectorService:
|
||||
vector.delete_by_ids(old_attachment_ids)
|
||||
|
||||
# Delete existing segment attachment bindings in one operation
|
||||
db.session.query(SegmentAttachmentBinding).where(SegmentAttachmentBinding.segment_id == segment.id).delete(
|
||||
synchronize_session=False
|
||||
db.session.execute(
|
||||
delete(SegmentAttachmentBinding).where(SegmentAttachmentBinding.segment_id == segment.id)
|
||||
)
|
||||
|
||||
if not attachment_ids:
|
||||
@@ -280,7 +277,7 @@ class VectorService:
|
||||
return
|
||||
|
||||
# Bulk fetch upload files - only fetch needed fields
|
||||
upload_file_list = db.session.query(UploadFile).where(UploadFile.id.in_(attachment_ids)).all()
|
||||
upload_file_list = db.session.scalars(select(UploadFile).where(UploadFile.id.in_(attachment_ids))).all()
|
||||
|
||||
if not upload_file_list:
|
||||
db.session.commit()
|
||||
|
||||
@@ -138,14 +138,14 @@ class WorkflowService:
|
||||
if workflow_id:
|
||||
return self.get_published_workflow_by_id(app_model, workflow_id)
|
||||
# fetch draft workflow by app_model
|
||||
workflow = (
|
||||
db.session.query(Workflow)
|
||||
workflow = db.session.scalar(
|
||||
select(Workflow)
|
||||
.where(
|
||||
Workflow.tenant_id == app_model.tenant_id,
|
||||
Workflow.app_id == app_model.id,
|
||||
Workflow.version == Workflow.VERSION_DRAFT,
|
||||
)
|
||||
.first()
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
# return draft workflow
|
||||
@@ -155,14 +155,14 @@ class WorkflowService:
|
||||
"""
|
||||
fetch published workflow by workflow_id
|
||||
"""
|
||||
workflow = (
|
||||
db.session.query(Workflow)
|
||||
workflow = db.session.scalar(
|
||||
select(Workflow)
|
||||
.where(
|
||||
Workflow.tenant_id == app_model.tenant_id,
|
||||
Workflow.app_id == app_model.id,
|
||||
Workflow.id == workflow_id,
|
||||
)
|
||||
.first()
|
||||
.limit(1)
|
||||
)
|
||||
if not workflow:
|
||||
return None
|
||||
@@ -182,14 +182,14 @@ class WorkflowService:
|
||||
return None
|
||||
|
||||
# fetch published workflow by workflow_id
|
||||
workflow = (
|
||||
db.session.query(Workflow)
|
||||
workflow = db.session.scalar(
|
||||
select(Workflow)
|
||||
.where(
|
||||
Workflow.tenant_id == app_model.tenant_id,
|
||||
Workflow.app_id == app_model.id,
|
||||
Workflow.id == app_model.workflow_id,
|
||||
)
|
||||
.first()
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
return workflow
|
||||
@@ -544,14 +544,14 @@ class WorkflowService:
|
||||
|
||||
# Use the same fallback logic as runtime: get the first available credential
|
||||
# ordered by is_default DESC, created_at ASC (same as tool_manager.py)
|
||||
default_provider = (
|
||||
db.session.query(BuiltinToolProvider)
|
||||
default_provider = db.session.scalar(
|
||||
select(BuiltinToolProvider)
|
||||
.where(
|
||||
BuiltinToolProvider.tenant_id == tenant_id,
|
||||
BuiltinToolProvider.provider == provider,
|
||||
)
|
||||
.order_by(BuiltinToolProvider.is_default.desc(), BuiltinToolProvider.created_at.asc())
|
||||
.first()
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
if not default_provider:
|
||||
|
||||
@@ -99,7 +99,7 @@ class TestFeedbackService:
|
||||
)
|
||||
]
|
||||
|
||||
mock_db_session.query.return_value = mock_query
|
||||
mock_db_session.execute.return_value = mock_query
|
||||
|
||||
# Test CSV export
|
||||
result = FeedbackService.export_feedbacks(app_id=sample_data["app"].id, format_type="csv")
|
||||
@@ -138,7 +138,7 @@ class TestFeedbackService:
|
||||
)
|
||||
]
|
||||
|
||||
mock_db_session.query.return_value = mock_query
|
||||
mock_db_session.execute.return_value = mock_query
|
||||
|
||||
# Test JSON export
|
||||
result = FeedbackService.export_feedbacks(app_id=sample_data["app"].id, format_type="json")
|
||||
@@ -175,7 +175,7 @@ class TestFeedbackService:
|
||||
)
|
||||
]
|
||||
|
||||
mock_db_session.query.return_value = mock_query
|
||||
mock_db_session.execute.return_value = mock_query
|
||||
|
||||
# Test with filters
|
||||
result = FeedbackService.export_feedbacks(
|
||||
@@ -188,11 +188,8 @@ class TestFeedbackService:
|
||||
format_type="csv",
|
||||
)
|
||||
|
||||
# Verify filters were applied
|
||||
assert mock_query.filter.called
|
||||
filter_calls = mock_query.filter.call_args_list
|
||||
# At least three filter invocations are expected (source, rating, comment)
|
||||
assert len(filter_calls) >= 3
|
||||
# Verify query was executed (filters are baked into the select statement)
|
||||
assert mock_db_session.execute.called
|
||||
|
||||
def test_export_feedbacks_no_data(self, mock_db_session, sample_data):
|
||||
"""Test exporting feedback when no data exists."""
|
||||
@@ -206,7 +203,7 @@ class TestFeedbackService:
|
||||
mock_query.order_by.return_value = mock_query
|
||||
mock_query.all.return_value = []
|
||||
|
||||
mock_db_session.query.return_value = mock_query
|
||||
mock_db_session.execute.return_value = mock_query
|
||||
|
||||
result = FeedbackService.export_feedbacks(app_id=sample_data["app"].id, format_type="csv")
|
||||
|
||||
@@ -271,7 +268,7 @@ class TestFeedbackService:
|
||||
)
|
||||
]
|
||||
|
||||
mock_db_session.query.return_value = mock_query
|
||||
mock_db_session.execute.return_value = mock_query
|
||||
|
||||
# Test export
|
||||
result = FeedbackService.export_feedbacks(app_id=sample_data["app"].id, format_type="json")
|
||||
@@ -329,7 +326,7 @@ class TestFeedbackService:
|
||||
)
|
||||
]
|
||||
|
||||
mock_db_session.query.return_value = mock_query
|
||||
mock_db_session.execute.return_value = mock_query
|
||||
|
||||
# Test export
|
||||
result = FeedbackService.export_feedbacks(app_id=sample_data["app"].id, format_type="csv")
|
||||
@@ -367,7 +364,7 @@ class TestFeedbackService:
|
||||
),
|
||||
]
|
||||
|
||||
mock_db_session.query.return_value = mock_query
|
||||
mock_db_session.execute.return_value = mock_query
|
||||
|
||||
# Test export
|
||||
result = FeedbackService.export_feedbacks(app_id=sample_data["app"].id, format_type="json")
|
||||
|
||||
@@ -41,15 +41,15 @@ class TestGetUser:
|
||||
"""Test get_user function"""
|
||||
|
||||
@patch("controllers.inner_api.plugin.wraps.EndUser")
|
||||
@patch("controllers.inner_api.plugin.wraps.Session")
|
||||
@patch("controllers.inner_api.plugin.wraps.sessionmaker")
|
||||
@patch("controllers.inner_api.plugin.wraps.db")
|
||||
def test_should_return_existing_user_by_id(self, mock_db, mock_session_class, mock_enduser_class, app: Flask):
|
||||
def test_should_return_existing_user_by_id(self, mock_db, mock_sessionmaker, mock_enduser_class, app: Flask):
|
||||
"""Test returning existing user when found by ID"""
|
||||
# Arrange
|
||||
mock_user = MagicMock()
|
||||
mock_user.id = "user123"
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_sessionmaker.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
mock_session.get.return_value = mock_user
|
||||
|
||||
# Act
|
||||
@@ -61,17 +61,17 @@ class TestGetUser:
|
||||
mock_session.get.assert_called_once()
|
||||
|
||||
@patch("controllers.inner_api.plugin.wraps.EndUser")
|
||||
@patch("controllers.inner_api.plugin.wraps.Session")
|
||||
@patch("controllers.inner_api.plugin.wraps.sessionmaker")
|
||||
@patch("controllers.inner_api.plugin.wraps.db")
|
||||
def test_should_return_existing_anonymous_user_by_session_id(
|
||||
self, mock_db, mock_session_class, mock_enduser_class, app: Flask
|
||||
self, mock_db, mock_sessionmaker, mock_enduser_class, app: Flask
|
||||
):
|
||||
"""Test returning existing anonymous user by session_id"""
|
||||
# Arrange
|
||||
mock_user = MagicMock()
|
||||
mock_user.session_id = "anonymous_session"
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_sessionmaker.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
# non-anonymous path uses session.get(); anonymous uses session.scalar()
|
||||
mock_session.get.return_value = mock_user
|
||||
|
||||
@@ -83,13 +83,13 @@ class TestGetUser:
|
||||
assert result == mock_user
|
||||
|
||||
@patch("controllers.inner_api.plugin.wraps.EndUser")
|
||||
@patch("controllers.inner_api.plugin.wraps.Session")
|
||||
@patch("controllers.inner_api.plugin.wraps.sessionmaker")
|
||||
@patch("controllers.inner_api.plugin.wraps.db")
|
||||
def test_should_create_new_user_when_not_found(self, mock_db, mock_session_class, mock_enduser_class, app: Flask):
|
||||
def test_should_create_new_user_when_not_found(self, mock_db, mock_sessionmaker, mock_enduser_class, app: Flask):
|
||||
"""Test creating new user when not found in database"""
|
||||
# Arrange
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_sessionmaker.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
mock_session.get.return_value = None
|
||||
mock_new_user = MagicMock()
|
||||
mock_enduser_class.return_value = mock_new_user
|
||||
@@ -101,21 +101,20 @@ class TestGetUser:
|
||||
# Assert
|
||||
assert result == mock_new_user
|
||||
mock_session.add.assert_called_once()
|
||||
mock_session.commit.assert_called_once()
|
||||
mock_session.refresh.assert_called_once()
|
||||
|
||||
@patch("controllers.inner_api.plugin.wraps.select")
|
||||
@patch("controllers.inner_api.plugin.wraps.EndUser")
|
||||
@patch("controllers.inner_api.plugin.wraps.Session")
|
||||
@patch("controllers.inner_api.plugin.wraps.sessionmaker")
|
||||
@patch("controllers.inner_api.plugin.wraps.db")
|
||||
def test_should_use_default_session_id_when_user_id_none(
|
||||
self, mock_db, mock_session_class, mock_enduser_class, mock_select, app: Flask
|
||||
self, mock_db, mock_sessionmaker, mock_enduser_class, mock_select, app: Flask
|
||||
):
|
||||
"""Test using default session ID when user_id is None"""
|
||||
# Arrange
|
||||
mock_user = MagicMock()
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_sessionmaker.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
# When user_id is None, is_anonymous=True, so session.scalar() is used
|
||||
mock_session.scalar.return_value = mock_user
|
||||
|
||||
@@ -127,15 +126,13 @@ class TestGetUser:
|
||||
assert result == mock_user
|
||||
|
||||
@patch("controllers.inner_api.plugin.wraps.EndUser")
|
||||
@patch("controllers.inner_api.plugin.wraps.Session")
|
||||
@patch("controllers.inner_api.plugin.wraps.sessionmaker")
|
||||
@patch("controllers.inner_api.plugin.wraps.db")
|
||||
def test_should_raise_error_on_database_exception(
|
||||
self, mock_db, mock_session_class, mock_enduser_class, app: Flask
|
||||
):
|
||||
def test_should_raise_error_on_database_exception(self, mock_db, mock_sessionmaker, mock_enduser_class, app: Flask):
|
||||
"""Test raising ValueError when database operation fails"""
|
||||
# Arrange
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_sessionmaker.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
mock_session.get.side_effect = Exception("Database error")
|
||||
|
||||
# Act & Assert
|
||||
|
||||
@@ -433,13 +433,20 @@ class TestConversationApiController:
|
||||
handler(api, app_model=app_model, end_user=end_user)
|
||||
|
||||
def test_list_last_not_found(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
class _SessionStub:
|
||||
class _BeginStub:
|
||||
def __enter__(self):
|
||||
return SimpleNamespace()
|
||||
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
class _SessionMakerStub:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def begin(self):
|
||||
return _BeginStub()
|
||||
|
||||
monkeypatch.setattr(
|
||||
ConversationService,
|
||||
"pagination_by_last_id",
|
||||
@@ -447,7 +454,7 @@ class TestConversationApiController:
|
||||
)
|
||||
conversation_module = sys.modules["controllers.service_api.app.conversation"]
|
||||
monkeypatch.setattr(conversation_module, "db", SimpleNamespace(engine=object()))
|
||||
monkeypatch.setattr(conversation_module, "Session", lambda *_args, **_kwargs: _SessionStub())
|
||||
monkeypatch.setattr(conversation_module, "sessionmaker", _SessionMakerStub)
|
||||
|
||||
api = ConversationApi()
|
||||
handler = _unwrap(api.get)
|
||||
|
||||
@@ -470,16 +470,23 @@ class TestWorkflowTaskStopApi:
|
||||
|
||||
class TestWorkflowAppLogApi:
|
||||
def test_success(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
class _SessionStub:
|
||||
class _BeginStub:
|
||||
def __enter__(self):
|
||||
return SimpleNamespace()
|
||||
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
class _SessionMakerStub:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def begin(self):
|
||||
return _BeginStub()
|
||||
|
||||
workflow_module = sys.modules["controllers.service_api.app.workflow"]
|
||||
monkeypatch.setattr(workflow_module, "db", SimpleNamespace(engine=object()))
|
||||
monkeypatch.setattr(workflow_module, "Session", lambda *_args, **_kwargs: _SessionStub())
|
||||
monkeypatch.setattr(workflow_module, "sessionmaker", _SessionMakerStub)
|
||||
monkeypatch.setattr(
|
||||
WorkflowAppService,
|
||||
"get_paginate_workflow_app_logs",
|
||||
@@ -635,11 +642,14 @@ class TestWorkflowAppLogApiGet:
|
||||
mock_svc_instance.get_paginate_workflow_app_logs.return_value = mock_pagination
|
||||
mock_wf_svc_cls.return_value = mock_svc_instance
|
||||
|
||||
# Mock Session context manager
|
||||
# Mock sessionmaker(...).begin() context manager
|
||||
mock_session = Mock()
|
||||
mock_db.engine = Mock()
|
||||
mock_session.__enter__ = Mock(return_value=mock_session)
|
||||
mock_session.__exit__ = Mock(return_value=False)
|
||||
mock_begin = Mock()
|
||||
mock_begin.__enter__ = Mock(return_value=mock_session)
|
||||
mock_begin.__exit__ = Mock(return_value=False)
|
||||
mock_session_factory = Mock()
|
||||
mock_session_factory.begin.return_value = mock_begin
|
||||
|
||||
from controllers.service_api.app.workflow import WorkflowAppLogApi
|
||||
|
||||
@@ -647,7 +657,7 @@ class TestWorkflowAppLogApiGet:
|
||||
"/workflows/logs?page=1&limit=20",
|
||||
method="GET",
|
||||
):
|
||||
with patch("controllers.service_api.app.workflow.Session", return_value=mock_session):
|
||||
with patch("controllers.service_api.app.workflow.sessionmaker", return_value=mock_session_factory):
|
||||
api = WorkflowAppLogApi()
|
||||
result = _unwrap(api.get)(api, app_model=mock_workflow_app)
|
||||
|
||||
|
||||
@@ -77,22 +77,12 @@ def _make_segment(
|
||||
def _mock_db_session_for_update_multimodel(*, upload_files: list[_UploadFileStub] | None) -> MagicMock:
|
||||
session = MagicMock(name="session")
|
||||
|
||||
binding_query = MagicMock(name="binding_query")
|
||||
binding_query.where.return_value = binding_query
|
||||
binding_query.delete.return_value = 1
|
||||
# db.session.execute() is used for delete(SegmentAttachmentBinding).where(...)
|
||||
session.execute = MagicMock(name="execute")
|
||||
|
||||
upload_query = MagicMock(name="upload_query")
|
||||
upload_query.where.return_value = upload_query
|
||||
upload_query.all.return_value = upload_files or []
|
||||
# db.session.scalars(select(UploadFile).where(...)).all() returns upload files
|
||||
session.scalars.return_value.all.return_value = upload_files or []
|
||||
|
||||
def query_side_effect(model: object) -> MagicMock:
|
||||
if model is vector_service_module.SegmentAttachmentBinding:
|
||||
return binding_query
|
||||
if model is vector_service_module.UploadFile:
|
||||
return upload_query
|
||||
return MagicMock(name=f"query({model})")
|
||||
|
||||
session.query.side_effect = query_side_effect
|
||||
db_mock = MagicMock(name="db")
|
||||
db_mock.session = session
|
||||
return db_mock
|
||||
@@ -165,22 +155,15 @@ def _mock_parent_child_queries(
|
||||
) -> MagicMock:
|
||||
session = MagicMock(name="session")
|
||||
|
||||
doc_query = MagicMock(name="doc_query")
|
||||
doc_query.filter_by.return_value = doc_query
|
||||
doc_query.first.return_value = dataset_document
|
||||
get_dispatch: dict[object, object | None] = {
|
||||
vector_service_module.DatasetDocument: dataset_document,
|
||||
vector_service_module.DatasetProcessRule: processing_rule,
|
||||
}
|
||||
|
||||
rule_query = MagicMock(name="rule_query")
|
||||
rule_query.where.return_value = rule_query
|
||||
rule_query.first.return_value = processing_rule
|
||||
def get_side_effect(model: object, pk: object) -> object | None:
|
||||
return get_dispatch.get(model)
|
||||
|
||||
def query_side_effect(model: object) -> MagicMock:
|
||||
if model is vector_service_module.DatasetDocument:
|
||||
return doc_query
|
||||
if model is vector_service_module.DatasetProcessRule:
|
||||
return rule_query
|
||||
return MagicMock(name=f"query({model})")
|
||||
|
||||
session.query.side_effect = query_side_effect
|
||||
session.get.side_effect = get_side_effect
|
||||
db_mock = MagicMock(name="db")
|
||||
db_mock.session = session
|
||||
return db_mock
|
||||
@@ -609,7 +592,7 @@ def test_update_multimodel_vector_deletes_bindings_and_commits_on_empty_new_ids(
|
||||
|
||||
vector_cls.assert_called_once_with(dataset=dataset)
|
||||
vector_instance.delete_by_ids.assert_called_once_with(["old-1", "old-2"])
|
||||
db_mock.session.query.assert_called_once_with(vector_service_module.SegmentAttachmentBinding)
|
||||
db_mock.session.execute.assert_called_once()
|
||||
db_mock.session.commit.assert_called_once()
|
||||
db_mock.session.add_all.assert_not_called()
|
||||
vector_instance.add_texts.assert_not_called()
|
||||
@@ -644,6 +627,8 @@ def test_update_multimodel_vector_adds_bindings_and_vectors_and_skips_missing_up
|
||||
|
||||
binding_ctor = MagicMock(side_effect=lambda **kwargs: kwargs)
|
||||
monkeypatch.setattr(vector_service_module, "SegmentAttachmentBinding", binding_ctor)
|
||||
monkeypatch.setattr(vector_service_module, "delete", MagicMock())
|
||||
monkeypatch.setattr(vector_service_module, "select", MagicMock())
|
||||
|
||||
logger_mock = MagicMock()
|
||||
monkeypatch.setattr(vector_service_module, "logger", logger_mock)
|
||||
@@ -677,6 +662,8 @@ def test_update_multimodel_vector_updates_bindings_without_multimodal_vector_ops
|
||||
monkeypatch.setattr(
|
||||
vector_service_module, "SegmentAttachmentBinding", MagicMock(side_effect=lambda **kwargs: kwargs)
|
||||
)
|
||||
monkeypatch.setattr(vector_service_module, "delete", MagicMock())
|
||||
monkeypatch.setattr(vector_service_module, "select", MagicMock())
|
||||
|
||||
VectorService.update_multimodel_vector(segment=segment, attachment_ids=["file-1"], dataset=dataset)
|
||||
|
||||
@@ -698,6 +685,8 @@ def test_update_multimodel_vector_rolls_back_and_reraises_on_error(monkeypatch:
|
||||
monkeypatch.setattr(
|
||||
vector_service_module, "SegmentAttachmentBinding", MagicMock(side_effect=lambda **kwargs: kwargs)
|
||||
)
|
||||
monkeypatch.setattr(vector_service_module, "delete", MagicMock())
|
||||
monkeypatch.setattr(vector_service_module, "select", MagicMock())
|
||||
|
||||
logger_mock = MagicMock()
|
||||
monkeypatch.setattr(vector_service_module, "logger", logger_mock)
|
||||
|
||||
@@ -268,7 +268,7 @@ class TestWorkflowService:
|
||||
Provides mock implementations of:
|
||||
- session.add(): Adding new records
|
||||
- session.commit(): Committing transactions
|
||||
- session.query(): Querying database
|
||||
- session.scalar(): Scalar queries
|
||||
- session.execute(): Executing SQL statements
|
||||
"""
|
||||
with patch("services.workflow_service.db") as mock_db:
|
||||
@@ -276,7 +276,7 @@ class TestWorkflowService:
|
||||
mock_db.session = mock_session
|
||||
mock_session.add = MagicMock()
|
||||
mock_session.commit = MagicMock()
|
||||
mock_session.query = MagicMock()
|
||||
mock_session.scalar = MagicMock()
|
||||
mock_session.execute = MagicMock()
|
||||
yield mock_db
|
||||
|
||||
@@ -338,10 +338,8 @@ class TestWorkflowService:
|
||||
app = TestWorkflowAssociatedDataFactory.create_app_mock()
|
||||
mock_workflow = TestWorkflowAssociatedDataFactory.create_workflow_mock()
|
||||
|
||||
# Mock database query
|
||||
mock_query = MagicMock()
|
||||
mock_db_session.session.query.return_value = mock_query
|
||||
mock_query.where.return_value.first.return_value = mock_workflow
|
||||
# Mock db.session.scalar() used by get_draft_workflow
|
||||
mock_db_session.session.scalar.return_value = mock_workflow
|
||||
|
||||
result = workflow_service.get_draft_workflow(app)
|
||||
|
||||
@@ -351,10 +349,8 @@ class TestWorkflowService:
|
||||
"""Test get_draft_workflow returns None when no draft exists."""
|
||||
app = TestWorkflowAssociatedDataFactory.create_app_mock()
|
||||
|
||||
# Mock database query to return None
|
||||
mock_query = MagicMock()
|
||||
mock_db_session.session.query.return_value = mock_query
|
||||
mock_query.where.return_value.first.return_value = None
|
||||
# Mock db.session.scalar() to return None
|
||||
mock_db_session.session.scalar.return_value = None
|
||||
|
||||
result = workflow_service.get_draft_workflow(app)
|
||||
|
||||
@@ -366,10 +362,8 @@ class TestWorkflowService:
|
||||
workflow_id = "workflow-123"
|
||||
mock_workflow = TestWorkflowAssociatedDataFactory.create_workflow_mock(version="v1")
|
||||
|
||||
# Mock database query
|
||||
mock_query = MagicMock()
|
||||
mock_db_session.session.query.return_value = mock_query
|
||||
mock_query.where.return_value.first.return_value = mock_workflow
|
||||
# Mock db.session.scalar() used by get_published_workflow_by_id
|
||||
mock_db_session.session.scalar.return_value = mock_workflow
|
||||
|
||||
result = workflow_service.get_draft_workflow(app, workflow_id=workflow_id)
|
||||
|
||||
@@ -384,10 +378,8 @@ class TestWorkflowService:
|
||||
workflow_id = "workflow-123"
|
||||
mock_workflow = TestWorkflowAssociatedDataFactory.create_workflow_mock(workflow_id=workflow_id, version="v1")
|
||||
|
||||
# Mock database query
|
||||
mock_query = MagicMock()
|
||||
mock_db_session.session.query.return_value = mock_query
|
||||
mock_query.where.return_value.first.return_value = mock_workflow
|
||||
# Mock db.session.scalar() used by get_published_workflow_by_id
|
||||
mock_db_session.session.scalar.return_value = mock_workflow
|
||||
|
||||
result = workflow_service.get_published_workflow_by_id(app, workflow_id)
|
||||
|
||||
@@ -406,10 +398,8 @@ class TestWorkflowService:
|
||||
workflow_id=workflow_id, version=Workflow.VERSION_DRAFT
|
||||
)
|
||||
|
||||
# Mock database query
|
||||
mock_query = MagicMock()
|
||||
mock_db_session.session.query.return_value = mock_query
|
||||
mock_query.where.return_value.first.return_value = mock_workflow
|
||||
# Mock db.session.scalar() used by get_published_workflow_by_id
|
||||
mock_db_session.session.scalar.return_value = mock_workflow
|
||||
|
||||
with pytest.raises(IsDraftWorkflowError):
|
||||
workflow_service.get_published_workflow_by_id(app, workflow_id)
|
||||
@@ -419,10 +409,8 @@ class TestWorkflowService:
|
||||
app = TestWorkflowAssociatedDataFactory.create_app_mock()
|
||||
workflow_id = "nonexistent-workflow"
|
||||
|
||||
# Mock database query to return None
|
||||
mock_query = MagicMock()
|
||||
mock_db_session.session.query.return_value = mock_query
|
||||
mock_query.where.return_value.first.return_value = None
|
||||
# Mock db.session.scalar() to return None
|
||||
mock_db_session.session.scalar.return_value = None
|
||||
|
||||
result = workflow_service.get_published_workflow_by_id(app, workflow_id)
|
||||
|
||||
@@ -434,10 +422,8 @@ class TestWorkflowService:
|
||||
app = TestWorkflowAssociatedDataFactory.create_app_mock(workflow_id=workflow_id)
|
||||
mock_workflow = TestWorkflowAssociatedDataFactory.create_workflow_mock(workflow_id=workflow_id, version="v1")
|
||||
|
||||
# Mock database query
|
||||
mock_query = MagicMock()
|
||||
mock_db_session.session.query.return_value = mock_query
|
||||
mock_query.where.return_value.first.return_value = mock_workflow
|
||||
# Mock db.session.scalar() used by get_published_workflow
|
||||
mock_db_session.session.scalar.return_value = mock_workflow
|
||||
|
||||
result = workflow_service.get_published_workflow(app)
|
||||
|
||||
@@ -466,11 +452,9 @@ class TestWorkflowService:
|
||||
graph = TestWorkflowAssociatedDataFactory.create_valid_workflow_graph()
|
||||
features = {"file_upload": {"enabled": False}}
|
||||
|
||||
# Mock get_draft_workflow to return None (no existing draft)
|
||||
# Mock db.session.scalar() to return None (no existing draft)
|
||||
# This simulates the first time a workflow is created for an app
|
||||
mock_query = MagicMock()
|
||||
mock_db_session.session.query.return_value = mock_query
|
||||
mock_query.where.return_value.first.return_value = None
|
||||
mock_db_session.session.scalar.return_value = None
|
||||
|
||||
with (
|
||||
patch.object(workflow_service, "validate_features_structure"),
|
||||
@@ -504,12 +488,10 @@ class TestWorkflowService:
|
||||
features = {"file_upload": {"enabled": False}}
|
||||
unique_hash = "test-hash-123"
|
||||
|
||||
# Mock existing draft workflow
|
||||
# Mock existing draft workflow via db.session.scalar()
|
||||
mock_workflow = TestWorkflowAssociatedDataFactory.create_workflow_mock(unique_hash=unique_hash)
|
||||
|
||||
mock_query = MagicMock()
|
||||
mock_db_session.session.query.return_value = mock_query
|
||||
mock_query.where.return_value.first.return_value = mock_workflow
|
||||
mock_db_session.session.scalar.return_value = mock_workflow
|
||||
|
||||
with (
|
||||
patch.object(workflow_service, "validate_features_structure"),
|
||||
@@ -545,12 +527,10 @@ class TestWorkflowService:
|
||||
graph = TestWorkflowAssociatedDataFactory.create_valid_workflow_graph()
|
||||
features = {}
|
||||
|
||||
# Mock existing draft workflow with different hash
|
||||
# Mock existing draft workflow with different hash via db.session.scalar()
|
||||
mock_workflow = TestWorkflowAssociatedDataFactory.create_workflow_mock(unique_hash="old-hash")
|
||||
|
||||
mock_query = MagicMock()
|
||||
mock_db_session.session.query.return_value = mock_query
|
||||
mock_query.where.return_value.first.return_value = mock_workflow
|
||||
mock_db_session.session.scalar.return_value = mock_workflow
|
||||
|
||||
with pytest.raises(WorkflowHashNotEqualError):
|
||||
workflow_service.sync_draft_workflow(
|
||||
|
||||
@@ -347,7 +347,7 @@ class TestGetBuiltinToolProviderCredentials:
|
||||
def test_returns_empty_when_no_providers(self, mock_db):
|
||||
mock_db.session.no_autoflush.__enter__ = MagicMock(return_value=None)
|
||||
mock_db.session.no_autoflush.__exit__ = MagicMock(return_value=False)
|
||||
mock_db.session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = []
|
||||
mock_db.session.scalars.return_value.all.return_value = []
|
||||
|
||||
result = BuiltinToolManageService.get_builtin_tool_provider_credentials("t", "google")
|
||||
|
||||
@@ -362,7 +362,7 @@ class TestGetBuiltinToolProviderCredentials:
|
||||
mock_db.session.no_autoflush.__exit__ = MagicMock(return_value=False)
|
||||
|
||||
provider = MagicMock(provider="google", is_default=False)
|
||||
mock_db.session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = [provider]
|
||||
mock_db.session.scalars.return_value.all.return_value = [provider]
|
||||
|
||||
mock_encrypter = MagicMock()
|
||||
mock_encrypter.decrypt.return_value = {"key": "decrypted"}
|
||||
|
||||
+8
-4
@@ -1,11 +1,15 @@
|
||||
{
|
||||
"name": "dify",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"prepare": "vp config"
|
||||
},
|
||||
"devDependencies": {
|
||||
"taze": "catalog:",
|
||||
"vite-plus": "catalog:"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^22.22.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
"devDependencies": {
|
||||
"taze": "catalog:"
|
||||
}
|
||||
"packageManager": "pnpm@10.33.0"
|
||||
}
|
||||
|
||||
Generated
+3
-152
@@ -345,9 +345,6 @@ catalogs:
|
||||
html-to-image:
|
||||
specifier: 1.11.13
|
||||
version: 1.11.13
|
||||
husky:
|
||||
specifier: 9.1.7
|
||||
version: 9.1.7
|
||||
i18next:
|
||||
specifier: 25.10.10
|
||||
version: 25.10.10
|
||||
@@ -390,9 +387,6 @@ catalogs:
|
||||
lexical:
|
||||
specifier: 0.42.0
|
||||
version: 0.42.0
|
||||
lint-staged:
|
||||
specifier: 16.4.0
|
||||
version: 16.4.0
|
||||
mermaid:
|
||||
specifier: 11.13.0
|
||||
version: 11.13.0
|
||||
@@ -624,6 +618,9 @@ importers:
|
||||
taze:
|
||||
specifier: 'catalog:'
|
||||
version: 19.10.0
|
||||
vite-plus:
|
||||
specifier: 'catalog:'
|
||||
version: 0.1.14(@types/node@25.5.0)(esbuild@0.27.2)(happy-dom@20.8.9)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(yaml@2.8.3)
|
||||
|
||||
e2e:
|
||||
devDependencies:
|
||||
@@ -1165,18 +1162,12 @@ importers:
|
||||
hono:
|
||||
specifier: 'catalog:'
|
||||
version: 4.12.9
|
||||
husky:
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.7
|
||||
iconify-import-svg:
|
||||
specifier: 'catalog:'
|
||||
version: 0.1.2
|
||||
knip:
|
||||
specifier: 'catalog:'
|
||||
version: 6.1.0(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)
|
||||
lint-staged:
|
||||
specifier: 'catalog:'
|
||||
version: 16.4.0
|
||||
postcss:
|
||||
specifier: 'catalog:'
|
||||
version: 8.5.8
|
||||
@@ -4751,10 +4742,6 @@ packages:
|
||||
ajv@8.18.0:
|
||||
resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
|
||||
|
||||
ansi-escapes@7.3.0:
|
||||
resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
ansi-regex@4.1.1:
|
||||
resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -4775,10 +4762,6 @@ packages:
|
||||
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
ansi-styles@6.2.3:
|
||||
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ansis@4.2.0:
|
||||
resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -5066,18 +5049,10 @@ packages:
|
||||
resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
cli-cursor@5.0.0:
|
||||
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
cli-table3@0.6.5:
|
||||
resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==}
|
||||
engines: {node: 10.* || >= 12.*}
|
||||
|
||||
cli-truncate@5.2.0:
|
||||
resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
client-only@0.0.1:
|
||||
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
||||
|
||||
@@ -5104,9 +5079,6 @@ packages:
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
colorette@2.0.20:
|
||||
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||
|
||||
comma-separated-tokens@1.0.8:
|
||||
resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==}
|
||||
|
||||
@@ -5572,10 +5544,6 @@ packages:
|
||||
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
environment@1.1.0:
|
||||
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
error-stack-parser-es@1.0.5:
|
||||
resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
|
||||
|
||||
@@ -5965,9 +5933,6 @@ packages:
|
||||
event-target-bus@1.0.0:
|
||||
resolution: {integrity: sha512-uPcWKbj/BJU3Tbw9XqhHqET4/LBOhvv3/SJWr7NksxA6TC5YqBpaZgawE9R+WpYFCBFSAE4Vun+xQS6w4ABdlA==}
|
||||
|
||||
eventemitter3@5.0.4:
|
||||
resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
|
||||
|
||||
events@3.3.0:
|
||||
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||
engines: {node: '>=0.8.x'}
|
||||
@@ -6289,11 +6254,6 @@ packages:
|
||||
htmlparser2@10.1.0:
|
||||
resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==}
|
||||
|
||||
husky@9.1.7:
|
||||
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
i18next-resources-to-backend@1.2.1:
|
||||
resolution: {integrity: sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==}
|
||||
|
||||
@@ -6419,10 +6379,6 @@ packages:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-fullwidth-code-point@5.1.0:
|
||||
resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -6730,15 +6686,6 @@ packages:
|
||||
lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
|
||||
lint-staged@16.4.0:
|
||||
resolution: {integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==}
|
||||
engines: {node: '>=20.17'}
|
||||
hasBin: true
|
||||
|
||||
listr2@9.0.5:
|
||||
resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
load-tsconfig@0.2.5:
|
||||
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
@@ -6770,10 +6717,6 @@ packages:
|
||||
lodash@4.17.23:
|
||||
resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
|
||||
|
||||
log-update@6.1.0:
|
||||
resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
longest-streak@3.1.0:
|
||||
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
|
||||
|
||||
@@ -7920,9 +7863,6 @@ packages:
|
||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
rfdc@1.4.1:
|
||||
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
|
||||
|
||||
robust-predicates@3.0.3:
|
||||
resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==}
|
||||
|
||||
@@ -8043,14 +7983,6 @@ packages:
|
||||
size-sensor@1.0.3:
|
||||
resolution: {integrity: sha512-+k9mJ2/rQMiRmQUcjn+qznch260leIXY8r4FyYKKyRBO/s5UoeMAHGkCJyE1R/4wrIhTJONfyloY55SkE7ve3A==}
|
||||
|
||||
slice-ansi@7.1.2:
|
||||
resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
slice-ansi@8.0.0:
|
||||
resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
smol-toml@1.6.1:
|
||||
resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==}
|
||||
engines: {node: '>= 18'}
|
||||
@@ -8134,10 +8066,6 @@ packages:
|
||||
resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==}
|
||||
engines: {node: '>=0.6.19'}
|
||||
|
||||
string-argv@0.3.2:
|
||||
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
|
||||
engines: {node: '>=0.6.19'}
|
||||
|
||||
string-ts@2.3.1:
|
||||
resolution: {integrity: sha512-xSJq+BS52SaFFAVxuStmx6n5aYZU571uYUnUrPXkPFCfdHyZMMlbP2v2Wx5sNBnAVzq/2+0+mcBLBa3Xa5ubYw==}
|
||||
|
||||
@@ -8874,10 +8802,6 @@ packages:
|
||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
wrap-ansi@9.0.2:
|
||||
resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
@@ -12658,10 +12582,6 @@ snapshots:
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
ansi-escapes@7.3.0:
|
||||
dependencies:
|
||||
environment: 1.1.0
|
||||
|
||||
ansi-regex@4.1.1: {}
|
||||
|
||||
ansi-regex@5.0.1: {}
|
||||
@@ -12674,8 +12594,6 @@ snapshots:
|
||||
|
||||
ansi-styles@5.2.0: {}
|
||||
|
||||
ansi-styles@6.2.3: {}
|
||||
|
||||
ansis@4.2.0: {}
|
||||
|
||||
any-promise@1.3.0: {}
|
||||
@@ -12947,21 +12865,12 @@ snapshots:
|
||||
dependencies:
|
||||
escape-string-regexp: 1.0.5
|
||||
|
||||
cli-cursor@5.0.0:
|
||||
dependencies:
|
||||
restore-cursor: 5.1.0
|
||||
|
||||
cli-table3@0.6.5:
|
||||
dependencies:
|
||||
string-width: 8.2.0
|
||||
optionalDependencies:
|
||||
'@colors/colors': 1.5.0
|
||||
|
||||
cli-truncate@5.2.0:
|
||||
dependencies:
|
||||
slice-ansi: 8.0.0
|
||||
string-width: 8.2.0
|
||||
|
||||
client-only@0.0.1: {}
|
||||
|
||||
clsx@2.1.1: {}
|
||||
@@ -12998,8 +12907,6 @@ snapshots:
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
colorette@2.0.20: {}
|
||||
|
||||
comma-separated-tokens@1.0.8: {}
|
||||
|
||||
comma-separated-tokens@2.0.3: {}
|
||||
@@ -13443,8 +13350,6 @@ snapshots:
|
||||
|
||||
entities@7.0.1: {}
|
||||
|
||||
environment@1.1.0: {}
|
||||
|
||||
error-stack-parser-es@1.0.5: {}
|
||||
|
||||
error-stack-parser@2.1.4:
|
||||
@@ -14106,8 +14011,6 @@ snapshots:
|
||||
|
||||
event-target-bus@1.0.0: {}
|
||||
|
||||
eventemitter3@5.0.4: {}
|
||||
|
||||
events@3.3.0: {}
|
||||
|
||||
expand-template@2.0.3:
|
||||
@@ -14496,8 +14399,6 @@ snapshots:
|
||||
domutils: 3.2.2
|
||||
entities: 7.0.1
|
||||
|
||||
husky@9.1.7: {}
|
||||
|
||||
i18next-resources-to-backend@1.2.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.2
|
||||
@@ -14596,10 +14497,6 @@ snapshots:
|
||||
|
||||
is-extglob@2.1.1: {}
|
||||
|
||||
is-fullwidth-code-point@5.1.0:
|
||||
dependencies:
|
||||
get-east-asian-width: 1.5.0
|
||||
|
||||
is-glob@4.0.3:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
@@ -14853,24 +14750,6 @@ snapshots:
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
lint-staged@16.4.0:
|
||||
dependencies:
|
||||
commander: 14.0.3
|
||||
listr2: 9.0.5
|
||||
picomatch: 4.0.4
|
||||
string-argv: 0.3.2
|
||||
tinyexec: 1.0.4
|
||||
yaml: 2.8.3
|
||||
|
||||
listr2@9.0.5:
|
||||
dependencies:
|
||||
cli-truncate: 5.2.0
|
||||
colorette: 2.0.20
|
||||
eventemitter3: 5.0.4
|
||||
log-update: 6.1.0
|
||||
rfdc: 1.4.1
|
||||
wrap-ansi: 9.0.2
|
||||
|
||||
load-tsconfig@0.2.5: {}
|
||||
|
||||
loader-runner@4.3.1: {}
|
||||
@@ -14895,14 +14774,6 @@ snapshots:
|
||||
|
||||
lodash@4.17.23: {}
|
||||
|
||||
log-update@6.1.0:
|
||||
dependencies:
|
||||
ansi-escapes: 7.3.0
|
||||
cli-cursor: 5.0.0
|
||||
slice-ansi: 7.1.2
|
||||
strip-ansi: 7.2.0
|
||||
wrap-ansi: 9.0.2
|
||||
|
||||
longest-streak@3.1.0: {}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
@@ -16539,8 +16410,6 @@ snapshots:
|
||||
|
||||
reusify@1.1.0: {}
|
||||
|
||||
rfdc@1.4.1: {}
|
||||
|
||||
robust-predicates@3.0.3: {}
|
||||
|
||||
rolldown@1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1):
|
||||
@@ -16734,16 +16603,6 @@ snapshots:
|
||||
|
||||
size-sensor@1.0.3: {}
|
||||
|
||||
slice-ansi@7.1.2:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.3
|
||||
is-fullwidth-code-point: 5.1.0
|
||||
|
||||
slice-ansi@8.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.3
|
||||
is-fullwidth-code-point: 5.1.0
|
||||
|
||||
smol-toml@1.6.1: {}
|
||||
|
||||
solid-js@1.9.11:
|
||||
@@ -16844,8 +16703,6 @@ snapshots:
|
||||
|
||||
string-argv@0.3.1: {}
|
||||
|
||||
string-argv@0.3.2: {}
|
||||
|
||||
string-ts@2.3.1: {}
|
||||
|
||||
string-width@8.2.0:
|
||||
@@ -17690,12 +17547,6 @@ snapshots:
|
||||
|
||||
word-wrap@1.2.5: {}
|
||||
|
||||
wrap-ansi@9.0.2:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.3
|
||||
string-width: 8.2.0
|
||||
strip-ansi: 7.2.0
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
|
||||
ws@8.20.0: {}
|
||||
|
||||
@@ -183,7 +183,6 @@ catalog:
|
||||
hono: 4.12.9
|
||||
html-entities: 2.6.0
|
||||
html-to-image: 1.11.13
|
||||
husky: 9.1.7
|
||||
i18next: 25.10.10
|
||||
i18next-resources-to-backend: 1.2.1
|
||||
iconify-import-svg: 0.1.2
|
||||
@@ -198,7 +197,6 @@ catalog:
|
||||
ky: 1.14.3
|
||||
lamejs: 1.2.1
|
||||
lexical: 0.42.0
|
||||
lint-staged: 16.4.0
|
||||
mermaid: 11.13.0
|
||||
mime: 4.1.0
|
||||
mitt: 3.0.1
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { defineConfig } from 'vite-plus'
|
||||
|
||||
export default defineConfig({
|
||||
staged: {},
|
||||
})
|
||||
+1
-1
@@ -31,7 +31,7 @@ RUN corepack install
|
||||
|
||||
# Install only the web workspace to keep image builds from pulling in
|
||||
# unrelated workspace dependencies such as e2e tooling.
|
||||
RUN pnpm install --filter ./web... --frozen-lockfile
|
||||
RUN VITE_GIT_HOOKS=0 pnpm install --filter ./web... --frozen-lockfile
|
||||
|
||||
# build resources
|
||||
FROM base AS builder
|
||||
|
||||
@@ -22,7 +22,6 @@ web/node_modules
|
||||
web/dist
|
||||
web/build
|
||||
web/coverage
|
||||
web/.husky
|
||||
web/.next
|
||||
web/.pnpm-store
|
||||
web/.vscode
|
||||
|
||||
@@ -11,7 +11,7 @@ const PartnerStackCookieRecorder = () => {
|
||||
if (!IS_CLOUD_EDITION)
|
||||
return
|
||||
saveOrUpdate()
|
||||
}, [])
|
||||
}, [saveOrUpdate])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ const PartnerStack: FC = () => {
|
||||
saveOrUpdate()
|
||||
// bind PartnerStack info after user logged in
|
||||
bind()
|
||||
}, [])
|
||||
}, [saveOrUpdate, bind])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
"lint:quiet": "vp run lint --quiet",
|
||||
"lint:tss": "tsslint --project tsconfig.json",
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"prepare": "cd ../ && node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky ./web/.husky",
|
||||
"refactor-component": "node ./scripts/refactor-component.js",
|
||||
"start": "node ./scripts/copy-and-start.mjs",
|
||||
"start:vinext": "vinext start",
|
||||
@@ -218,10 +217,8 @@
|
||||
"eslint-plugin-storybook": "catalog:",
|
||||
"happy-dom": "catalog:",
|
||||
"hono": "catalog:",
|
||||
"husky": "catalog:",
|
||||
"iconify-import-svg": "catalog:",
|
||||
"knip": "catalog:",
|
||||
"lint-staged": "catalog:",
|
||||
"postcss": "catalog:",
|
||||
"postcss-js": "catalog:",
|
||||
"react-server-dom-webpack": "catalog:",
|
||||
@@ -237,8 +234,5 @@
|
||||
"vite-plus": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-canvas-mock": "catalog:"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*": "eslint --fix --pass-on-unpruned-suppressions"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ export default defineConfig(({ mode }) => {
|
||||
|| process.argv.some(arg => arg.toLowerCase().includes('storybook'))
|
||||
|
||||
return {
|
||||
staged: {
|
||||
'*': 'eslint --fix --pass-on-unpruned-suppressions',
|
||||
},
|
||||
plugins: isTest
|
||||
? [
|
||||
nextStaticImageTestPlugin({ projectRoot }),
|
||||
|
||||
Reference in New Issue
Block a user