mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 16:03:14 +08:00
Merge branch 'main' into jzh
This commit is contained in:
@@ -2,7 +2,7 @@ import flask_restx
|
||||
from flask_restx import Resource, fields, marshal_with
|
||||
from flask_restx._http import HTTPStatus
|
||||
from sqlalchemy import delete, func, select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from extensions.ext_database import db
|
||||
@@ -34,7 +34,7 @@ api_key_list_model = console_ns.model(
|
||||
|
||||
|
||||
def _get_resource(resource_id, tenant_id, resource_model):
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
resource = session.execute(
|
||||
select(resource_model).filter_by(id=resource_id, tenant_id=tenant_id)
|
||||
).scalar_one_or_none()
|
||||
|
||||
@@ -9,7 +9,7 @@ from graphon.enums import WorkflowExecutionStatus
|
||||
from graphon.file import helpers as file_helpers
|
||||
from pydantic import AliasChoices, BaseModel, ConfigDict, Field, computed_field, field_validator
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import BadRequest
|
||||
|
||||
from controllers.common.helpers import FileInfo
|
||||
@@ -642,7 +642,7 @@ class AppCopyApi(Resource):
|
||||
|
||||
args = CopyAppPayload.model_validate(console_ns.payload or {})
|
||||
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
import_service = AppDslService(session)
|
||||
yaml_content = import_service.export_dsl(app_model=app_model, include_secret=True)
|
||||
result = import_service.import_app(
|
||||
@@ -655,7 +655,6 @@ class AppCopyApi(Resource):
|
||||
icon=args.icon,
|
||||
icon_background=args.icon_background,
|
||||
)
|
||||
session.commit()
|
||||
|
||||
# Inherit web app permission from original app
|
||||
if result.app_id and FeatureService.get_system_features().webapp_auth.enabled:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from flask_restx import Resource, fields, marshal_with
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from controllers.console.app.wraps import get_app_model
|
||||
from controllers.console.wraps import (
|
||||
@@ -71,7 +71,7 @@ class AppImportApi(Resource):
|
||||
args = AppImportPayload.model_validate(console_ns.payload)
|
||||
|
||||
# Create service with session
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
import_service = AppDslService(session)
|
||||
# Import app
|
||||
account = current_user
|
||||
@@ -87,7 +87,6 @@ class AppImportApi(Resource):
|
||||
icon_background=args.icon_background,
|
||||
app_id=args.app_id,
|
||||
)
|
||||
session.commit()
|
||||
if result.app_id and FeatureService.get_system_features().webapp_auth.enabled:
|
||||
# update web app setting as private
|
||||
EnterpriseService.WebAppAuth.update_app_access_mode(result.app_id, "private")
|
||||
@@ -112,12 +111,11 @@ class AppImportConfirmApi(Resource):
|
||||
current_user, _ = current_account_with_tenant()
|
||||
|
||||
# Create service with session
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
import_service = AppDslService(session)
|
||||
# Confirm import
|
||||
account = current_user
|
||||
result = import_service.confirm_import(import_id=import_id, account=account)
|
||||
session.commit()
|
||||
|
||||
# Return appropriate status code based on result
|
||||
if result.status == ImportStatus.FAILED:
|
||||
@@ -134,7 +132,7 @@ class AppImportCheckDependenciesApi(Resource):
|
||||
@marshal_with(app_import_check_dependencies_model)
|
||||
@edit_permission_required
|
||||
def get(self, app_model: App):
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
import_service = AppDslService(session)
|
||||
result = import_service.check_dependencies(app_model=app_model)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from flask import request
|
||||
from flask_restx import Resource, fields, marshal_with
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app.wraps import get_app_model
|
||||
@@ -69,7 +69,7 @@ class ConversationVariablesApi(Resource):
|
||||
page_size = 100
|
||||
stmt = stmt.limit(page_size).offset((page - 1) * page_size)
|
||||
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
rows = session.scalars(stmt).all()
|
||||
|
||||
return {
|
||||
|
||||
@@ -10,7 +10,7 @@ from graphon.file import File
|
||||
from graphon.graph_engine.manager import GraphEngineManager
|
||||
from graphon.model_runtime.utils.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import BadRequest, Forbidden, InternalServerError, NotFound
|
||||
|
||||
import services
|
||||
@@ -840,7 +840,7 @@ class PublishedWorkflowApi(Resource):
|
||||
args = PublishWorkflowPayload.model_validate(console_ns.payload or {})
|
||||
|
||||
workflow_service = WorkflowService()
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
workflow = workflow_service.publish_workflow(
|
||||
session=session,
|
||||
app_model=app_model,
|
||||
@@ -858,8 +858,6 @@ class PublishedWorkflowApi(Resource):
|
||||
|
||||
workflow_created_at = TimestampField().format(workflow.created_at)
|
||||
|
||||
session.commit()
|
||||
|
||||
return {
|
||||
"result": "success",
|
||||
"created_at": workflow_created_at,
|
||||
@@ -982,7 +980,7 @@ class PublishedAllWorkflowApi(Resource):
|
||||
raise Forbidden()
|
||||
|
||||
workflow_service = WorkflowService()
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
workflows, has_more = workflow_service.get_all_published_workflow(
|
||||
session=session,
|
||||
app_model=app_model,
|
||||
@@ -1072,7 +1070,7 @@ class WorkflowByIdApi(Resource):
|
||||
workflow_service = WorkflowService()
|
||||
|
||||
# Create a session and manage the transaction
|
||||
with Session(db.engine, expire_on_commit=False) as session:
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
workflow = workflow_service.update_workflow(
|
||||
session=session,
|
||||
workflow_id=workflow_id,
|
||||
@@ -1084,9 +1082,6 @@ class WorkflowByIdApi(Resource):
|
||||
if not workflow:
|
||||
raise NotFound("Workflow not found")
|
||||
|
||||
# Commit the transaction in the controller
|
||||
session.commit()
|
||||
|
||||
return workflow
|
||||
|
||||
@setup_required
|
||||
@@ -1101,13 +1096,11 @@ class WorkflowByIdApi(Resource):
|
||||
workflow_service = WorkflowService()
|
||||
|
||||
# Create a session and manage the transaction
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
try:
|
||||
workflow_service.delete_workflow(
|
||||
session=session, workflow_id=workflow_id, tenant_id=app_model.tenant_id
|
||||
)
|
||||
# Commit the transaction in the controller
|
||||
session.commit()
|
||||
except WorkflowInUseError as e:
|
||||
abort(400, description=str(e))
|
||||
except DraftWorkflowDeletionError as e:
|
||||
|
||||
@@ -5,7 +5,7 @@ from flask import request
|
||||
from flask_restx import Resource, marshal_with
|
||||
from graphon.enums import WorkflowExecutionStatus
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app.wraps import get_app_model
|
||||
@@ -87,7 +87,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,
|
||||
@@ -124,7 +124,7 @@ class WorkflowArchivedLogApi(Resource):
|
||||
args = WorkflowAppLogQuery.model_validate(request.args.to_dict(flat=True)) # type: ignore
|
||||
|
||||
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_archive_logs(
|
||||
session=session,
|
||||
app_model=app_model,
|
||||
|
||||
@@ -10,7 +10,7 @@ from graphon.variables.segment_group import SegmentGroup
|
||||
from graphon.variables.segments import ArrayFileSegment, FileSegment, Segment
|
||||
from graphon.variables.types import SegmentType
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app.error import (
|
||||
@@ -244,7 +244,7 @@ class WorkflowVariableCollectionApi(Resource):
|
||||
raise DraftWorkflowNotExist()
|
||||
|
||||
# fetch draft workflow by app_model
|
||||
with Session(bind=db.engine, expire_on_commit=False) as session:
|
||||
with sessionmaker(bind=db.engine, expire_on_commit=False).begin() as session:
|
||||
draft_var_srv = WorkflowDraftVariableService(
|
||||
session=session,
|
||||
)
|
||||
@@ -298,7 +298,7 @@ class NodeVariableCollectionApi(Resource):
|
||||
@marshal_with(workflow_draft_variable_list_model)
|
||||
def get(self, app_model: App, node_id: str):
|
||||
validate_node_id(node_id)
|
||||
with Session(bind=db.engine, expire_on_commit=False) as session:
|
||||
with sessionmaker(bind=db.engine, expire_on_commit=False).begin() as session:
|
||||
draft_var_srv = WorkflowDraftVariableService(
|
||||
session=session,
|
||||
)
|
||||
@@ -465,7 +465,7 @@ class VariableResetApi(Resource):
|
||||
|
||||
|
||||
def _get_variable_list(app_model: App, node_id) -> WorkflowDraftVariableList:
|
||||
with Session(bind=db.engine, expire_on_commit=False) as session:
|
||||
with sessionmaker(bind=db.engine, expire_on_commit=False).begin() as session:
|
||||
draft_var_srv = WorkflowDraftVariableService(
|
||||
session=session,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ from flask import request
|
||||
from flask_restx import Resource, fields, marshal_with
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from configs import dify_config
|
||||
@@ -64,7 +64,7 @@ class WebhookTriggerApi(Resource):
|
||||
|
||||
node_id = args.node_id
|
||||
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
# Get webhook trigger for this app and node
|
||||
webhook_trigger = (
|
||||
session.query(WorkflowWebhookTrigger)
|
||||
@@ -95,7 +95,7 @@ class AppTriggersApi(Resource):
|
||||
assert isinstance(current_user, Account)
|
||||
assert current_user.current_tenant_id is not None
|
||||
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
# Get all triggers for this app using select API
|
||||
triggers = (
|
||||
session.execute(
|
||||
@@ -137,7 +137,7 @@ class AppTriggerEnableApi(Resource):
|
||||
assert current_user.current_tenant_id is not None
|
||||
|
||||
trigger_id = args.trigger_id
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
# Find the trigger using select
|
||||
trigger = session.execute(
|
||||
select(AppTrigger).where(
|
||||
@@ -153,9 +153,6 @@ class AppTriggerEnableApi(Resource):
|
||||
# Update status based on enable_trigger boolean
|
||||
trigger.status = AppTriggerStatus.ENABLED if args.enable_trigger else AppTriggerStatus.DISABLED
|
||||
|
||||
session.commit()
|
||||
session.refresh(trigger)
|
||||
|
||||
# Add computed icon field
|
||||
url_prefix = dify_config.CONSOLE_API_URL + "/console/api/workspaces/current/tool-provider/builtin/"
|
||||
if trigger.trigger_type == "trigger-plugin":
|
||||
|
||||
@@ -36,7 +36,7 @@ class Subscription(Resource):
|
||||
@only_edition_cloud
|
||||
def get(self):
|
||||
current_user, current_tenant_id = current_account_with_tenant()
|
||||
args = SubscriptionQuery.model_validate(request.args.to_dict(flat=True)) # type: ignore
|
||||
args = SubscriptionQuery.model_validate(request.args.to_dict(flat=True))
|
||||
BillingService.is_tenant_owner_or_admin(current_user)
|
||||
return BillingService.get_subscription(args.plan, args.interval, current_user.email, current_tenant_id)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class ComplianceApi(Resource):
|
||||
@only_edition_cloud
|
||||
def get(self):
|
||||
current_user, current_tenant_id = current_account_with_tenant()
|
||||
args = ComplianceDownloadQuery.model_validate(request.args.to_dict(flat=True)) # type: ignore
|
||||
args = ComplianceDownloadQuery.model_validate(request.args.to_dict(flat=True))
|
||||
|
||||
ip_address = extract_remote_ip(request)
|
||||
device_info = request.headers.get("User-Agent", "Unknown device")
|
||||
|
||||
@@ -6,7 +6,7 @@ from flask import request
|
||||
from flask_restx import Resource, fields, marshal_with
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from controllers.common.schema import get_or_create_model, register_schema_model
|
||||
@@ -159,7 +159,7 @@ class DataSourceApi(Resource):
|
||||
@account_initialization_required
|
||||
def patch(self, binding_id, action: Literal["enable", "disable"]):
|
||||
binding_id = str(binding_id)
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
data_source_binding = session.execute(
|
||||
select(DataSourceOauthBinding).filter_by(id=binding_id)
|
||||
).scalar_one_or_none()
|
||||
@@ -211,7 +211,7 @@ class DataSourceNotionListApi(Resource):
|
||||
if not credential:
|
||||
raise NotFound("Credential not found.")
|
||||
exist_page_ids = []
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
# import notion in the exist dataset
|
||||
if query.dataset_id:
|
||||
dataset = DatasetService.get_dataset(query.dataset_id)
|
||||
|
||||
@@ -3,7 +3,7 @@ import logging
|
||||
from flask import request
|
||||
from flask_restx import Resource
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from controllers.common.schema import register_schema_models
|
||||
from controllers.console import console_ns
|
||||
@@ -85,7 +85,7 @@ class CustomizedPipelineTemplateApi(Resource):
|
||||
@account_initialization_required
|
||||
@enterprise_license_required
|
||||
def post(self, template_id: str):
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
template = (
|
||||
session.query(PipelineCustomizedTemplate).where(PipelineCustomizedTemplate.id == template_id).first()
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from flask_restx import Resource, marshal
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
import services
|
||||
@@ -54,7 +54,7 @@ class CreateRagPipelineDatasetApi(Resource):
|
||||
yaml_content=payload.yaml_content,
|
||||
)
|
||||
try:
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
rag_pipeline_dsl_service = RagPipelineDslService(session)
|
||||
import_info = rag_pipeline_dsl_service.create_rag_pipeline_dataset(
|
||||
tenant_id=current_tenant_id,
|
||||
|
||||
@@ -5,7 +5,7 @@ from flask import Response, request
|
||||
from flask_restx import Resource, marshal, marshal_with
|
||||
from graphon.variables.types import SegmentType
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from controllers.common.schema import register_schema_models
|
||||
@@ -96,7 +96,7 @@ class RagPipelineVariableCollectionApi(Resource):
|
||||
raise DraftWorkflowNotExist()
|
||||
|
||||
# fetch draft workflow by app_model
|
||||
with Session(bind=db.engine, expire_on_commit=False) as session:
|
||||
with sessionmaker(bind=db.engine, expire_on_commit=False).begin() as session:
|
||||
draft_var_srv = WorkflowDraftVariableService(
|
||||
session=session,
|
||||
)
|
||||
@@ -143,7 +143,7 @@ class RagPipelineNodeVariableCollectionApi(Resource):
|
||||
@marshal_with(workflow_draft_variable_list_model)
|
||||
def get(self, pipeline: Pipeline, node_id: str):
|
||||
validate_node_id(node_id)
|
||||
with Session(bind=db.engine, expire_on_commit=False) as session:
|
||||
with sessionmaker(bind=db.engine, expire_on_commit=False).begin() as session:
|
||||
draft_var_srv = WorkflowDraftVariableService(
|
||||
session=session,
|
||||
)
|
||||
@@ -289,7 +289,7 @@ class RagPipelineVariableResetApi(Resource):
|
||||
|
||||
|
||||
def _get_variable_list(pipeline: Pipeline, node_id) -> WorkflowDraftVariableList:
|
||||
with Session(bind=db.engine, expire_on_commit=False) as session:
|
||||
with sessionmaker(bind=db.engine, expire_on_commit=False).begin() as session:
|
||||
draft_var_srv = WorkflowDraftVariableService(
|
||||
session=session,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from flask import request
|
||||
from flask_restx import Resource, fields, marshal_with # type: ignore
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from controllers.common.schema import get_or_create_model, register_schema_models
|
||||
from controllers.console import console_ns
|
||||
@@ -68,7 +68,7 @@ class RagPipelineImportApi(Resource):
|
||||
payload = RagPipelineImportPayload.model_validate(console_ns.payload or {})
|
||||
|
||||
# Create service with session
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
import_service = RagPipelineDslService(session)
|
||||
# Import app
|
||||
account = current_user
|
||||
@@ -80,7 +80,6 @@ class RagPipelineImportApi(Resource):
|
||||
pipeline_id=payload.pipeline_id,
|
||||
dataset_name=payload.name,
|
||||
)
|
||||
session.commit()
|
||||
|
||||
# Return appropriate status code based on result
|
||||
status = result.status
|
||||
@@ -102,12 +101,11 @@ class RagPipelineImportConfirmApi(Resource):
|
||||
current_user, _ = current_account_with_tenant()
|
||||
|
||||
# Create service with session
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
import_service = RagPipelineDslService(session)
|
||||
# Confirm import
|
||||
account = current_user
|
||||
result = import_service.confirm_import(import_id=import_id, account=account)
|
||||
session.commit()
|
||||
|
||||
# Return appropriate status code based on result
|
||||
if result.status == ImportStatus.FAILED:
|
||||
@@ -124,7 +122,7 @@ class RagPipelineImportCheckDependenciesApi(Resource):
|
||||
@edit_permission_required
|
||||
@marshal_with(pipeline_import_check_dependencies_model)
|
||||
def get(self, pipeline: Pipeline):
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
import_service = RagPipelineDslService(session)
|
||||
result = import_service.check_dependencies(pipeline=pipeline)
|
||||
|
||||
@@ -142,7 +140,7 @@ class RagPipelineExportApi(Resource):
|
||||
# Add include_secret params
|
||||
query = IncludeSecretQuery.model_validate(request.args.to_dict())
|
||||
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
export_service = RagPipelineDslService(session)
|
||||
result = export_service.export_rag_pipeline_dsl(
|
||||
pipeline=pipeline, include_secret=query.include_secret == "true"
|
||||
|
||||
@@ -6,7 +6,7 @@ from flask import abort, request
|
||||
from flask_restx import Resource, marshal_with # type: ignore
|
||||
from graphon.model_runtime.utils.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import BadRequest, Forbidden, InternalServerError, NotFound
|
||||
|
||||
import services
|
||||
@@ -608,7 +608,7 @@ 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 Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
pipeline = session.merge(pipeline)
|
||||
workflow = rag_pipeline_service.publish_workflow(
|
||||
session=session,
|
||||
@@ -620,8 +620,6 @@ class PublishedRagPipelineApi(Resource):
|
||||
session.add(pipeline)
|
||||
workflow_created_at = TimestampField().format(workflow.created_at)
|
||||
|
||||
session.commit()
|
||||
|
||||
return {
|
||||
"result": "success",
|
||||
"created_at": workflow_created_at,
|
||||
@@ -695,7 +693,7 @@ class PublishedAllRagPipelineApi(Resource):
|
||||
raise Forbidden()
|
||||
|
||||
rag_pipeline_service = RagPipelineService()
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
workflows, has_more = rag_pipeline_service.get_all_published_workflow(
|
||||
session=session,
|
||||
pipeline=pipeline,
|
||||
@@ -767,7 +765,7 @@ class RagPipelineByIdApi(Resource):
|
||||
rag_pipeline_service = RagPipelineService()
|
||||
|
||||
# Create a session and manage the transaction
|
||||
with Session(db.engine, expire_on_commit=False) as session:
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
workflow = rag_pipeline_service.update_workflow(
|
||||
session=session,
|
||||
workflow_id=workflow_id,
|
||||
@@ -779,9 +777,6 @@ class RagPipelineByIdApi(Resource):
|
||||
if not workflow:
|
||||
raise NotFound("Workflow not found")
|
||||
|
||||
# Commit the transaction in the controller
|
||||
session.commit()
|
||||
|
||||
return workflow
|
||||
|
||||
@setup_required
|
||||
@@ -798,14 +793,13 @@ class RagPipelineByIdApi(Resource):
|
||||
|
||||
workflow_service = WorkflowService()
|
||||
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
try:
|
||||
workflow_service.delete_workflow(
|
||||
session=session,
|
||||
workflow_id=workflow_id,
|
||||
tenant_id=pipeline.tenant_id,
|
||||
)
|
||||
session.commit()
|
||||
except WorkflowInUseError as e:
|
||||
abort(400, description=str(e))
|
||||
except DraftWorkflowDeletionError as e:
|
||||
|
||||
@@ -2,7 +2,7 @@ from typing import Any
|
||||
|
||||
from flask import request
|
||||
from pydantic import BaseModel, Field, TypeAdapter, model_validator
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from controllers.common.schema import register_schema_models
|
||||
@@ -74,7 +74,7 @@ class ConversationListApi(InstalledAppResource):
|
||||
try:
|
||||
if not isinstance(current_user, Account):
|
||||
raise ValueError("current_user must be an Account instance")
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
pagination = WebConversationService.pagination_by_last_id(
|
||||
session=session,
|
||||
app_model=app_model,
|
||||
|
||||
@@ -2,7 +2,7 @@ from collections.abc import Callable
|
||||
from functools import wraps
|
||||
from typing import ParamSpec, TypeVar
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from extensions.ext_database import db
|
||||
@@ -24,7 +24,7 @@ def plugin_permission_required(
|
||||
user = current_user
|
||||
tenant_id = current_tenant_id
|
||||
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
permission = (
|
||||
session.query(TenantPluginPermission)
|
||||
.where(
|
||||
|
||||
@@ -8,7 +8,7 @@ from flask import request
|
||||
from flask_restx import Resource, fields, marshal_with
|
||||
from pydantic import BaseModel, Field, field_validator, model_validator
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from configs import dify_config
|
||||
from constants.languages import supported_language
|
||||
@@ -519,7 +519,7 @@ class EducationAutoCompleteApi(Resource):
|
||||
@cloud_edition_billing_enabled
|
||||
@marshal_with(data_fields)
|
||||
def get(self):
|
||||
payload = request.args.to_dict(flat=True) # type: ignore
|
||||
payload = request.args.to_dict(flat=True)
|
||||
args = EducationAutocompleteQuery.model_validate(payload)
|
||||
|
||||
return BillingService.EducationIdentity.autocomplete(args.keywords, args.page, args.limit)
|
||||
@@ -562,7 +562,7 @@ class ChangeEmailSendEmailApi(Resource):
|
||||
|
||||
user_email = current_user.email
|
||||
else:
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
account = AccountService.get_account_by_email_with_case_fallback(args.email, session=session)
|
||||
if account is None:
|
||||
raise AccountNotFound()
|
||||
|
||||
@@ -99,7 +99,7 @@ class ModelProviderListApi(Resource):
|
||||
_, current_tenant_id = current_account_with_tenant()
|
||||
tenant_id = current_tenant_id
|
||||
|
||||
payload = request.args.to_dict(flat=True) # type: ignore
|
||||
payload = request.args.to_dict(flat=True)
|
||||
args = ParserModelList.model_validate(payload)
|
||||
|
||||
model_provider_service = ModelProviderService()
|
||||
@@ -118,7 +118,7 @@ class ModelProviderCredentialApi(Resource):
|
||||
_, current_tenant_id = current_account_with_tenant()
|
||||
tenant_id = current_tenant_id
|
||||
# if credential_id is not provided, return current used credential
|
||||
payload = request.args.to_dict(flat=True) # type: ignore
|
||||
payload = request.args.to_dict(flat=True)
|
||||
args = ParserCredentialId.model_validate(payload)
|
||||
|
||||
model_provider_service = ModelProviderService()
|
||||
|
||||
@@ -7,7 +7,7 @@ from flask import make_response, redirect, request, send_file
|
||||
from flask_restx import Resource
|
||||
from graphon.model_runtime.utils.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel, Field, HttpUrl, field_validator, model_validator
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from configs import dify_config
|
||||
@@ -1019,7 +1019,7 @@ class ToolProviderMCPApi(Resource):
|
||||
|
||||
# Step 1: Get provider data for URL validation (short-lived session, no network I/O)
|
||||
validation_data = None
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
validation_data = service.get_provider_for_url_validation(
|
||||
tenant_id=current_tenant_id, provider_id=payload.provider_id
|
||||
@@ -1034,7 +1034,7 @@ class ToolProviderMCPApi(Resource):
|
||||
)
|
||||
|
||||
# Step 3: Perform database update in a transaction
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
service.update_provider(
|
||||
tenant_id=current_tenant_id,
|
||||
@@ -1061,7 +1061,7 @@ class ToolProviderMCPApi(Resource):
|
||||
payload = MCPProviderDeletePayload.model_validate(console_ns.payload or {})
|
||||
_, current_tenant_id = current_account_with_tenant()
|
||||
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
service.delete_provider(tenant_id=current_tenant_id, provider_id=payload.provider_id)
|
||||
|
||||
@@ -1079,7 +1079,7 @@ class ToolMCPAuthApi(Resource):
|
||||
provider_id = payload.provider_id
|
||||
_, tenant_id = current_account_with_tenant()
|
||||
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
db_provider = service.get_provider(provider_id=provider_id, tenant_id=tenant_id)
|
||||
if not db_provider:
|
||||
@@ -1100,7 +1100,7 @@ class ToolMCPAuthApi(Resource):
|
||||
sse_read_timeout=provider_entity.sse_read_timeout,
|
||||
):
|
||||
# Update credentials in new transaction
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
service.update_provider_credentials(
|
||||
provider_id=provider_id,
|
||||
@@ -1118,17 +1118,17 @@ class ToolMCPAuthApi(Resource):
|
||||
resource_metadata_url=e.resource_metadata_url,
|
||||
scope_hint=e.scope_hint,
|
||||
)
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
response = service.execute_auth_actions(auth_result)
|
||||
return response
|
||||
except MCPRefreshTokenError as e:
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
service.clear_provider_credentials(provider_id=provider_id, tenant_id=tenant_id)
|
||||
raise ValueError(f"Failed to refresh token, please try to authorize again: {e}") from e
|
||||
except (MCPError, ValueError) as e:
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
service.clear_provider_credentials(provider_id=provider_id, tenant_id=tenant_id)
|
||||
raise ValueError(f"Failed to connect to MCP server: {e}") from e
|
||||
@@ -1141,7 +1141,7 @@ class ToolMCPDetailApi(Resource):
|
||||
@account_initialization_required
|
||||
def get(self, provider_id):
|
||||
_, tenant_id = current_account_with_tenant()
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
provider = service.get_provider(provider_id=provider_id, tenant_id=tenant_id)
|
||||
return jsonable_encoder(ToolTransformService.mcp_provider_to_user_provider(provider, for_list=True))
|
||||
@@ -1155,7 +1155,7 @@ class ToolMCPListAllApi(Resource):
|
||||
def get(self):
|
||||
_, tenant_id = current_account_with_tenant()
|
||||
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
# Skip sensitive data decryption for list view to improve performance
|
||||
tools = service.list_providers(tenant_id=tenant_id, include_sensitive=False)
|
||||
@@ -1170,7 +1170,7 @@ class ToolMCPUpdateApi(Resource):
|
||||
@account_initialization_required
|
||||
def get(self, provider_id):
|
||||
_, tenant_id = current_account_with_tenant()
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
service = MCPToolManageService(session=session)
|
||||
tools = service.list_provider_tools(
|
||||
tenant_id=tenant_id,
|
||||
@@ -1188,7 +1188,7 @@ class ToolMCPCallbackApi(Resource):
|
||||
authorization_code = query.code
|
||||
|
||||
# Create service instance for handle_callback
|
||||
with Session(db.engine) as session, session.begin():
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
mcp_service = MCPToolManageService(session=session)
|
||||
# handle_callback now returns state data and tokens
|
||||
state_data, tokens = handle_callback(state_key, authorization_code)
|
||||
|
||||
@@ -5,7 +5,7 @@ from flask import make_response, redirect, request
|
||||
from flask_restx import Resource
|
||||
from graphon.model_runtime.utils.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel, model_validator
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import BadRequest, Forbidden
|
||||
|
||||
from configs import dify_config
|
||||
@@ -375,7 +375,7 @@ class TriggerSubscriptionDeleteApi(Resource):
|
||||
assert user.current_tenant_id is not None
|
||||
|
||||
try:
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
# Delete trigger provider subscription
|
||||
TriggerProviderService.delete_trigger_provider(
|
||||
session=session,
|
||||
@@ -388,7 +388,6 @@ class TriggerSubscriptionDeleteApi(Resource):
|
||||
tenant_id=user.current_tenant_id,
|
||||
subscription_id=subscription_id,
|
||||
)
|
||||
session.commit()
|
||||
return {"result": "success"}
|
||||
except ValueError as e:
|
||||
raise BadRequest(str(e))
|
||||
|
||||
@@ -155,7 +155,7 @@ class WorkspaceListApi(Resource):
|
||||
@setup_required
|
||||
@admin_required
|
||||
def get(self):
|
||||
payload = request.args.to_dict(flat=True) # type: ignore
|
||||
payload = request.args.to_dict(flat=True)
|
||||
args = WorkspaceListQuery.model_validate(payload)
|
||||
|
||||
stmt = select(Tenant).order_by(Tenant.created_at.desc())
|
||||
|
||||
@@ -4,7 +4,7 @@ from flask import Response
|
||||
from flask_restx import Resource
|
||||
from graphon.variables.input_entities import VariableEntity
|
||||
from pydantic import BaseModel, Field, ValidationError
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
|
||||
from controllers.common.schema import register_schema_model
|
||||
from controllers.mcp import mcp_ns
|
||||
@@ -67,7 +67,7 @@ class MCPAppApi(Resource):
|
||||
request_id: Union[int, str] | None = args.id
|
||||
mcp_request = self._parse_mcp_request(args.model_dump(exclude_none=True))
|
||||
|
||||
with Session(db.engine, expire_on_commit=False) as session:
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
# Get MCP server and app
|
||||
mcp_server, app = self._get_mcp_server_and_app(server_code, session)
|
||||
self._validate_server_status(mcp_server)
|
||||
@@ -189,7 +189,7 @@ class MCPAppApi(Resource):
|
||||
|
||||
def _retrieve_end_user(self, tenant_id: str, mcp_server_id: str) -> EndUser | None:
|
||||
"""Get end user - manages its own database session"""
|
||||
with Session(db.engine, expire_on_commit=False) as session, session.begin():
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
return (
|
||||
session.query(EndUser)
|
||||
.where(EndUser.tenant_id == tenant_id)
|
||||
@@ -229,9 +229,7 @@ class MCPAppApi(Resource):
|
||||
if not end_user and isinstance(mcp_request.root, mcp_types.InitializeRequest):
|
||||
client_info = mcp_request.root.params.clientInfo
|
||||
client_name = f"{client_info.name}@{client_info.version}"
|
||||
# Commit the session before creating end user to avoid transaction conflicts
|
||||
session.commit()
|
||||
with Session(db.engine, expire_on_commit=False) as create_session, create_session.begin():
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as create_session:
|
||||
end_user = self._create_end_user(client_name, app.tenant_id, app.id, mcp_server.id, create_session)
|
||||
|
||||
return handle_mcp_request(app, mcp_request, user_input_form, mcp_server, end_user, request_id)
|
||||
|
||||
@@ -2,7 +2,7 @@ from typing import Literal
|
||||
|
||||
from flask import request
|
||||
from pydantic import BaseModel, Field, TypeAdapter, field_validator, model_validator
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from controllers.common.schema import register_schema_models
|
||||
@@ -99,7 +99,7 @@ class ConversationListApi(WebApiResource):
|
||||
query = ConversationListQuery.model_validate(raw_args)
|
||||
|
||||
try:
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
pagination = WebConversationService.pagination_by_last_id(
|
||||
session=session,
|
||||
app_model=app_model,
|
||||
|
||||
@@ -4,7 +4,7 @@ import secrets
|
||||
from flask import request
|
||||
from flask_restx import Resource
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from controllers.common.schema import register_schema_models
|
||||
from controllers.console.auth.error import (
|
||||
@@ -81,7 +81,7 @@ class ForgotPasswordSendEmailApi(Resource):
|
||||
else:
|
||||
language = "en-US"
|
||||
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
account = AccountService.get_account_by_email_with_case_fallback(request_email, session=session)
|
||||
token = None
|
||||
if account is None:
|
||||
@@ -180,18 +180,17 @@ class ForgotPasswordResetApi(Resource):
|
||||
|
||||
email = reset_data.get("email", "")
|
||||
|
||||
with Session(db.engine) as session:
|
||||
with sessionmaker(db.engine).begin() as session:
|
||||
account = AccountService.get_account_by_email_with_case_fallback(email, session=session)
|
||||
|
||||
if account:
|
||||
self._update_existing_account(account, password_hashed, salt, session)
|
||||
self._update_existing_account(account, password_hashed, salt)
|
||||
else:
|
||||
raise AuthenticationFailedError()
|
||||
|
||||
return {"result": "success"}
|
||||
|
||||
def _update_existing_account(self, account: Account, password_hashed, salt, session):
|
||||
def _update_existing_account(self, account: Account, password_hashed, salt):
|
||||
# Update existing account credentials
|
||||
account.password = base64.b64encode(password_hashed).decode()
|
||||
account.password_salt = base64.b64encode(salt).decode()
|
||||
session.commit()
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import Concatenate, ParamSpec, TypeVar
|
||||
from flask import request
|
||||
from flask_restx import Resource
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import BadRequest, NotFound, Unauthorized
|
||||
|
||||
from constants import HEADER_NAME_APP_CODE
|
||||
@@ -49,7 +49,7 @@ def decode_jwt_token(app_code: str | None = None, user_id: str | None = None):
|
||||
decoded = PassportService().verify(tk)
|
||||
app_code = decoded.get("app_code")
|
||||
app_id = decoded.get("app_id")
|
||||
with Session(db.engine, expire_on_commit=False) as session:
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
app_model = session.scalar(select(App).where(App.id == app_id))
|
||||
site = session.scalar(select(Site).where(Site.code == app_code))
|
||||
if not app_model:
|
||||
|
||||
@@ -383,14 +383,21 @@ class TestWorkflowAppLogEndpoints:
|
||||
|
||||
monkeypatch.setattr(workflow_app_log_module, "db", SimpleNamespace(engine=MagicMock()))
|
||||
|
||||
class DummySession:
|
||||
class DummySessionCtx:
|
||||
def __enter__(self):
|
||||
return "session"
|
||||
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
monkeypatch.setattr(workflow_app_log_module, "Session", lambda *args, **kwargs: DummySession())
|
||||
class DummySessionMaker:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def begin(self):
|
||||
return DummySessionCtx()
|
||||
|
||||
monkeypatch.setattr(workflow_app_log_module, "sessionmaker", DummySessionMaker)
|
||||
|
||||
def fake_get_paginate(self, **_kwargs):
|
||||
return {"items": [], "total": 0}
|
||||
@@ -423,13 +430,20 @@ class TestWorkflowDraftVariableEndpoints:
|
||||
monkeypatch.setattr(workflow_draft_variable_module, "db", SimpleNamespace(engine=MagicMock()))
|
||||
monkeypatch.setattr(workflow_draft_variable_module, "current_user", SimpleNamespace(id="user-1"))
|
||||
|
||||
class DummySession:
|
||||
class DummySessionCtx:
|
||||
def __enter__(self):
|
||||
return "session"
|
||||
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
class DummySessionMaker:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def begin(self):
|
||||
return DummySessionCtx()
|
||||
|
||||
class DummyDraftService:
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
@@ -437,7 +451,7 @@ class TestWorkflowDraftVariableEndpoints:
|
||||
def list_variables_without_values(self, **_kwargs):
|
||||
return {"items": [], "total": 0}
|
||||
|
||||
monkeypatch.setattr(workflow_draft_variable_module, "Session", lambda *args, **kwargs: DummySession())
|
||||
monkeypatch.setattr(workflow_draft_variable_module, "sessionmaker", DummySessionMaker)
|
||||
|
||||
class DummyWorkflowService:
|
||||
def is_workflow_exist(self, *args, **kwargs):
|
||||
@@ -543,14 +557,21 @@ class TestWorkflowTriggerEndpoints:
|
||||
session = MagicMock()
|
||||
session.query.return_value.where.return_value.first.return_value = trigger
|
||||
|
||||
class DummySession:
|
||||
class DummySessionCtx:
|
||||
def __enter__(self):
|
||||
return session
|
||||
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
monkeypatch.setattr(workflow_trigger_module, "Session", lambda *_args, **_kwargs: DummySession())
|
||||
class DummySessionMaker:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def begin(self):
|
||||
return DummySessionCtx()
|
||||
|
||||
monkeypatch.setattr(workflow_trigger_module, "sessionmaker", DummySessionMaker)
|
||||
|
||||
with app.test_request_context("/?node_id=node-1"):
|
||||
result = method(app_model=SimpleNamespace(id="app-1"))
|
||||
|
||||
@@ -102,12 +102,12 @@ class TestDataSourceApi:
|
||||
|
||||
with (
|
||||
app.test_request_context("/"),
|
||||
patch("controllers.console.datasets.data_source.Session") as mock_session_class,
|
||||
patch("controllers.console.datasets.data_source.sessionmaker") as mock_session_class,
|
||||
patch("controllers.console.datasets.data_source.db.session.add"),
|
||||
patch("controllers.console.datasets.data_source.db.session.commit"),
|
||||
):
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_session_class.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
mock_session.execute.return_value.scalar_one_or_none.return_value = binding
|
||||
|
||||
response, status = method(api, "b1", "enable")
|
||||
@@ -123,12 +123,12 @@ class TestDataSourceApi:
|
||||
|
||||
with (
|
||||
app.test_request_context("/"),
|
||||
patch("controllers.console.datasets.data_source.Session") as mock_session_class,
|
||||
patch("controllers.console.datasets.data_source.sessionmaker") as mock_session_class,
|
||||
patch("controllers.console.datasets.data_source.db.session.add"),
|
||||
patch("controllers.console.datasets.data_source.db.session.commit"),
|
||||
):
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_session_class.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
mock_session.execute.return_value.scalar_one_or_none.return_value = binding
|
||||
|
||||
response, status = method(api, "b1", "disable")
|
||||
@@ -142,10 +142,10 @@ class TestDataSourceApi:
|
||||
|
||||
with (
|
||||
app.test_request_context("/"),
|
||||
patch("controllers.console.datasets.data_source.Session") as mock_session_class,
|
||||
patch("controllers.console.datasets.data_source.sessionmaker") as mock_session_class,
|
||||
):
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_session_class.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
mock_session.execute.return_value.scalar_one_or_none.return_value = None
|
||||
|
||||
with pytest.raises(NotFound):
|
||||
@@ -159,10 +159,10 @@ class TestDataSourceApi:
|
||||
|
||||
with (
|
||||
app.test_request_context("/"),
|
||||
patch("controllers.console.datasets.data_source.Session") as mock_session_class,
|
||||
patch("controllers.console.datasets.data_source.sessionmaker") as mock_session_class,
|
||||
):
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_session_class.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
mock_session.execute.return_value.scalar_one_or_none.return_value = binding
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
@@ -176,10 +176,10 @@ class TestDataSourceApi:
|
||||
|
||||
with (
|
||||
app.test_request_context("/"),
|
||||
patch("controllers.console.datasets.data_source.Session") as mock_session_class,
|
||||
patch("controllers.console.datasets.data_source.sessionmaker") as mock_session_class,
|
||||
):
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_session_class.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
mock_session.execute.return_value.scalar_one_or_none.return_value = binding
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
@@ -282,7 +282,7 @@ class TestDataSourceNotionListApi:
|
||||
"controllers.console.datasets.data_source.DatasetService.get_dataset",
|
||||
return_value=dataset,
|
||||
),
|
||||
patch("controllers.console.datasets.data_source.Session") as mock_session_class,
|
||||
patch("controllers.console.datasets.data_source.sessionmaker") as mock_session_class,
|
||||
patch(
|
||||
"core.datasource.datasource_manager.DatasourceManager.get_datasource_runtime",
|
||||
return_value=MagicMock(
|
||||
@@ -292,7 +292,7 @@ class TestDataSourceNotionListApi:
|
||||
),
|
||||
):
|
||||
mock_session = MagicMock()
|
||||
mock_session_class.return_value.__enter__.return_value = mock_session
|
||||
mock_session_class.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
mock_session.scalars.return_value.all.return_value = [document]
|
||||
|
||||
response, status = method(api)
|
||||
@@ -315,7 +315,7 @@ class TestDataSourceNotionListApi:
|
||||
"controllers.console.datasets.data_source.DatasetService.get_dataset",
|
||||
return_value=dataset,
|
||||
),
|
||||
patch("controllers.console.datasets.data_source.Session"),
|
||||
patch("controllers.console.datasets.data_source.sessionmaker"),
|
||||
):
|
||||
with pytest.raises(ValueError):
|
||||
method(api)
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
"""Integration tests for console API key endpoints using testcontainers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from flask.testing import FlaskClient
|
||||
from sqlalchemy import delete
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from models.enums import ApiTokenType
|
||||
from models.model import ApiToken, App, AppMode
|
||||
from tests.test_containers_integration_tests.controllers.console.helpers import (
|
||||
authenticate_console_client,
|
||||
create_console_account_and_tenant,
|
||||
create_console_app,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def setup_app(
|
||||
db_session_with_containers: Session,
|
||||
test_client_with_containers: FlaskClient,
|
||||
) -> tuple[FlaskClient, dict[str, str], App]:
|
||||
"""Create an authenticated client with an app for API key tests."""
|
||||
account, tenant = create_console_account_and_tenant(db_session_with_containers)
|
||||
app = create_console_app(db_session_with_containers, tenant.id, account.id, AppMode.CHAT)
|
||||
headers = authenticate_console_client(test_client_with_containers, account)
|
||||
return test_client_with_containers, headers, app
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def cleanup_api_tokens(db_session_with_containers: Session):
|
||||
"""Remove API tokens created during each test."""
|
||||
yield
|
||||
db_session_with_containers.execute(delete(ApiToken))
|
||||
db_session_with_containers.commit()
|
||||
|
||||
|
||||
class TestAppApiKeyListResource:
|
||||
"""Tests for GET/POST /apps/<resource_id>/api-keys."""
|
||||
|
||||
def test_get_empty_keys(self, setup_app: tuple[FlaskClient, dict[str, str], App]) -> None:
|
||||
client, headers, app = setup_app
|
||||
resp = client.get(f"/console/api/apps/{app.id}/api-keys", headers=headers)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json is not None
|
||||
assert resp.json["data"] == []
|
||||
|
||||
def test_create_api_key(self, setup_app: tuple[FlaskClient, dict[str, str], App]) -> None:
|
||||
client, headers, app = setup_app
|
||||
resp = client.post(f"/console/api/apps/{app.id}/api-keys", headers=headers)
|
||||
assert resp.status_code == 201
|
||||
data = resp.json
|
||||
assert data is not None
|
||||
assert data["token"].startswith("app-")
|
||||
assert data["id"] is not None
|
||||
|
||||
def test_get_keys_after_create(self, setup_app: tuple[FlaskClient, dict[str, str], App]) -> None:
|
||||
client, headers, app = setup_app
|
||||
client.post(f"/console/api/apps/{app.id}/api-keys", headers=headers)
|
||||
client.post(f"/console/api/apps/{app.id}/api-keys", headers=headers)
|
||||
|
||||
resp = client.get(f"/console/api/apps/{app.id}/api-keys", headers=headers)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json is not None
|
||||
assert len(resp.json["data"]) == 2
|
||||
|
||||
def test_create_key_max_limit(
|
||||
self,
|
||||
setup_app: tuple[FlaskClient, dict[str, str], App],
|
||||
db_session_with_containers: Session,
|
||||
) -> None:
|
||||
client, headers, app = setup_app
|
||||
# Create 10 keys (the max)
|
||||
for _ in range(10):
|
||||
client.post(f"/console/api/apps/{app.id}/api-keys", headers=headers)
|
||||
|
||||
# 11th should fail
|
||||
resp = client.post(f"/console/api/apps/{app.id}/api-keys", headers=headers)
|
||||
assert resp.status_code == 400
|
||||
|
||||
def test_get_keys_for_nonexistent_app(
|
||||
self,
|
||||
setup_app: tuple[FlaskClient, dict[str, str], App],
|
||||
) -> None:
|
||||
client, headers, _ = setup_app
|
||||
resp = client.get(
|
||||
"/console/api/apps/00000000-0000-0000-0000-000000000000/api-keys",
|
||||
headers=headers,
|
||||
)
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
class TestAppApiKeyResource:
|
||||
"""Tests for DELETE /apps/<resource_id>/api-keys/<api_key_id>."""
|
||||
|
||||
def test_delete_key_success(self, setup_app: tuple[FlaskClient, dict[str, str], App]) -> None:
|
||||
client, headers, app = setup_app
|
||||
create_resp = client.post(f"/console/api/apps/{app.id}/api-keys", headers=headers)
|
||||
assert create_resp.json is not None
|
||||
key_id = create_resp.json["id"]
|
||||
|
||||
resp = client.delete(f"/console/api/apps/{app.id}/api-keys/{key_id}", headers=headers)
|
||||
assert resp.status_code == 204
|
||||
|
||||
def test_delete_nonexistent_key(self, setup_app: tuple[FlaskClient, dict[str, str], App]) -> None:
|
||||
client, headers, app = setup_app
|
||||
resp = client.delete(
|
||||
f"/console/api/apps/{app.id}/api-keys/00000000-0000-0000-0000-000000000000",
|
||||
headers=headers,
|
||||
)
|
||||
assert resp.status_code == 404
|
||||
|
||||
def test_delete_key_nonexistent_app(
|
||||
self,
|
||||
setup_app: tuple[FlaskClient, dict[str, str], App],
|
||||
) -> None:
|
||||
client, headers, _ = setup_app
|
||||
resp = client.delete(
|
||||
"/console/api/apps/00000000-0000-0000-0000-000000000000/api-keys/00000000-0000-0000-0000-000000000000",
|
||||
headers=headers,
|
||||
)
|
||||
assert resp.status_code == 404
|
||||
|
||||
def test_delete_forbidden_for_non_admin(
|
||||
self,
|
||||
flask_app_with_containers,
|
||||
) -> None:
|
||||
"""A non-admin member cannot delete API keys via the controller permission check."""
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from controllers.console.apikey import BaseApiKeyResource
|
||||
|
||||
resource = BaseApiKeyResource()
|
||||
resource.resource_type = ApiTokenType.APP
|
||||
resource.resource_model = MagicMock()
|
||||
resource.resource_id_field = "app_id"
|
||||
|
||||
non_admin = MagicMock()
|
||||
non_admin.is_admin_or_owner = False
|
||||
|
||||
with (
|
||||
flask_app_with_containers.test_request_context("/"),
|
||||
patch(
|
||||
"controllers.console.apikey.current_account_with_tenant",
|
||||
return_value=(non_admin, "tenant-id"),
|
||||
),
|
||||
patch("controllers.console.apikey._get_resource"),
|
||||
):
|
||||
with pytest.raises(Forbidden):
|
||||
BaseApiKeyResource.delete(resource, "rid", "kid")
|
||||
@@ -69,7 +69,7 @@ def client(flask_app_with_containers):
|
||||
return_value=(MagicMock(id="u1"), "t1"),
|
||||
autospec=True,
|
||||
)
|
||||
@patch("controllers.console.workspace.tool_providers.Session", autospec=True)
|
||||
@patch("controllers.console.workspace.tool_providers.sessionmaker", autospec=True)
|
||||
@patch("controllers.console.workspace.tool_providers.MCPToolManageService._reconnect_with_url", autospec=True)
|
||||
@pytest.mark.usefixtures("_mock_cache", "_mock_user_tenant")
|
||||
def test_create_mcp_provider_populates_tools(mock_reconnect, mock_session, mock_current_account_with_tenant, client):
|
||||
@@ -88,7 +88,7 @@ def test_create_mcp_provider_populates_tools(mock_reconnect, mock_session, mock_
|
||||
create_result.id = "provider-1"
|
||||
svc.create_provider.return_value = create_result
|
||||
svc.get_provider.return_value = MagicMock(id="provider-1", tenant_id="t1") # used by reload path
|
||||
mock_session.return_value.__enter__.return_value = MagicMock()
|
||||
mock_session.return_value.begin.return_value.__enter__.return_value = MagicMock()
|
||||
# Patch MCPToolManageService constructed inside controller
|
||||
with patch("controllers.console.workspace.tool_providers.MCPToolManageService", return_value=svc, autospec=True):
|
||||
payload = {
|
||||
|
||||
@@ -306,14 +306,14 @@ class TestTriggerSubscriptionCrud:
|
||||
app.test_request_context("/"),
|
||||
patch("controllers.console.workspace.trigger_providers.current_user", mock_user()),
|
||||
patch("controllers.console.workspace.trigger_providers.db") as mock_db,
|
||||
patch("controllers.console.workspace.trigger_providers.Session") as mock_session_cls,
|
||||
patch("controllers.console.workspace.trigger_providers.sessionmaker") as mock_session_cls,
|
||||
patch("controllers.console.workspace.trigger_providers.TriggerProviderService.delete_trigger_provider"),
|
||||
patch(
|
||||
"controllers.console.workspace.trigger_providers.TriggerSubscriptionOperatorService.delete_plugin_trigger_by_subscription"
|
||||
),
|
||||
):
|
||||
mock_db.engine = MagicMock()
|
||||
mock_session_cls.return_value.__enter__.return_value = mock_session
|
||||
mock_session_cls.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
|
||||
result = method(api, "sub1")
|
||||
|
||||
@@ -327,14 +327,14 @@ class TestTriggerSubscriptionCrud:
|
||||
app.test_request_context("/"),
|
||||
patch("controllers.console.workspace.trigger_providers.current_user", mock_user()),
|
||||
patch("controllers.console.workspace.trigger_providers.db") as mock_db,
|
||||
patch("controllers.console.workspace.trigger_providers.Session") as session_cls,
|
||||
patch("controllers.console.workspace.trigger_providers.sessionmaker") as session_cls,
|
||||
patch(
|
||||
"controllers.console.workspace.trigger_providers.TriggerProviderService.delete_trigger_provider",
|
||||
side_effect=ValueError("bad"),
|
||||
),
|
||||
):
|
||||
mock_db.engine = MagicMock()
|
||||
session_cls.return_value.__enter__.return_value = MagicMock()
|
||||
session_cls.return_value.begin.return_value.__enter__.return_value = MagicMock()
|
||||
|
||||
with pytest.raises(BadRequest):
|
||||
method(api, "sub1")
|
||||
|
||||
@@ -37,7 +37,7 @@ class TestForgotPasswordSendEmailApi:
|
||||
@patch("controllers.web.forgot_password.AccountService.get_account_by_email_with_case_fallback")
|
||||
@patch("controllers.web.forgot_password.AccountService.is_email_send_ip_limit", return_value=False)
|
||||
@patch("controllers.web.forgot_password.extract_remote_ip", return_value="127.0.0.1")
|
||||
@patch("controllers.web.forgot_password.Session")
|
||||
@patch("controllers.web.forgot_password.sessionmaker")
|
||||
def test_should_normalize_email_before_sending(
|
||||
self,
|
||||
mock_session_cls,
|
||||
@@ -51,7 +51,7 @@ class TestForgotPasswordSendEmailApi:
|
||||
mock_get_account.return_value = mock_account
|
||||
mock_send_mail.return_value = "token-123"
|
||||
mock_session = MagicMock()
|
||||
mock_session_cls.return_value.__enter__.return_value = mock_session
|
||||
mock_session_cls.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
|
||||
with patch("controllers.web.forgot_password.db", SimpleNamespace(engine="engine")):
|
||||
with app.test_request_context(
|
||||
@@ -153,7 +153,7 @@ class TestForgotPasswordResetApi:
|
||||
|
||||
@patch("controllers.web.forgot_password.ForgotPasswordResetApi._update_existing_account")
|
||||
@patch("controllers.web.forgot_password.AccountService.get_account_by_email_with_case_fallback")
|
||||
@patch("controllers.web.forgot_password.Session")
|
||||
@patch("controllers.web.forgot_password.sessionmaker")
|
||||
@patch("controllers.web.forgot_password.AccountService.revoke_reset_password_token")
|
||||
@patch("controllers.web.forgot_password.AccountService.get_reset_password_data")
|
||||
def test_should_fetch_account_with_fallback(
|
||||
@@ -169,7 +169,7 @@ class TestForgotPasswordResetApi:
|
||||
mock_account = MagicMock()
|
||||
mock_get_account.return_value = mock_account
|
||||
mock_session = MagicMock()
|
||||
mock_session_cls.return_value.__enter__.return_value = mock_session
|
||||
mock_session_cls.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
|
||||
with patch("controllers.web.forgot_password.db", SimpleNamespace(engine="engine")):
|
||||
with app.test_request_context(
|
||||
@@ -190,7 +190,7 @@ class TestForgotPasswordResetApi:
|
||||
|
||||
@patch("controllers.web.forgot_password.hash_password", return_value=b"hashed-value")
|
||||
@patch("controllers.web.forgot_password.secrets.token_bytes", return_value=b"0123456789abcdef")
|
||||
@patch("controllers.web.forgot_password.Session")
|
||||
@patch("controllers.web.forgot_password.sessionmaker")
|
||||
@patch("controllers.web.forgot_password.AccountService.revoke_reset_password_token")
|
||||
@patch("controllers.web.forgot_password.AccountService.get_reset_password_data")
|
||||
@patch("controllers.web.forgot_password.AccountService.get_account_by_email_with_case_fallback")
|
||||
@@ -208,7 +208,7 @@ class TestForgotPasswordResetApi:
|
||||
account = MagicMock()
|
||||
mock_get_account.return_value = account
|
||||
mock_session = MagicMock()
|
||||
mock_session_cls.return_value.__enter__.return_value = mock_session
|
||||
mock_session_cls.return_value.begin.return_value.__enter__.return_value = mock_session
|
||||
|
||||
with patch("controllers.web.forgot_password.db", SimpleNamespace(engine="engine")):
|
||||
with app.test_request_context(
|
||||
@@ -231,4 +231,3 @@ class TestForgotPasswordResetApi:
|
||||
assert account.password == expected_password
|
||||
expected_salt = base64.b64encode(b"0123456789abcdef").decode()
|
||||
assert account.password_salt == expected_salt
|
||||
mock_session.commit.assert_called_once()
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from controllers.console.apikey import (
|
||||
BaseApiKeyListResource,
|
||||
BaseApiKeyResource,
|
||||
_get_resource,
|
||||
)
|
||||
from models.enums import ApiTokenType
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tenant_context_admin():
|
||||
with patch("controllers.console.apikey.current_account_with_tenant") as mock:
|
||||
user = MagicMock()
|
||||
user.is_admin_or_owner = True
|
||||
mock.return_value = (user, "tenant-123")
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tenant_context_non_admin():
|
||||
with patch("controllers.console.apikey.current_account_with_tenant") as mock:
|
||||
user = MagicMock()
|
||||
user.is_admin_or_owner = False
|
||||
mock.return_value = (user, "tenant-123")
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db_mock():
|
||||
with patch("controllers.console.apikey.db") as mock_db:
|
||||
mock_db.session = MagicMock()
|
||||
yield mock_db
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def bypass_permissions():
|
||||
with patch(
|
||||
"controllers.console.apikey.edit_permission_required",
|
||||
lambda f: f,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
class DummyApiKeyListResource(BaseApiKeyListResource):
|
||||
resource_type = ApiTokenType.APP
|
||||
resource_model = MagicMock()
|
||||
resource_id_field = "app_id"
|
||||
token_prefix = "app-"
|
||||
|
||||
|
||||
class DummyApiKeyResource(BaseApiKeyResource):
|
||||
resource_type = ApiTokenType.APP
|
||||
resource_model = MagicMock()
|
||||
resource_id_field = "app_id"
|
||||
|
||||
|
||||
class TestGetResource:
|
||||
def test_get_resource_success(self):
|
||||
fake_resource = MagicMock()
|
||||
|
||||
with (
|
||||
patch("controllers.console.apikey.select") as mock_select,
|
||||
patch("controllers.console.apikey.Session") as mock_session,
|
||||
patch("controllers.console.apikey.db") as mock_db,
|
||||
):
|
||||
mock_db.engine = MagicMock()
|
||||
mock_select.return_value.filter_by.return_value = MagicMock()
|
||||
|
||||
session = mock_session.return_value.__enter__.return_value
|
||||
session.execute.return_value.scalar_one_or_none.return_value = fake_resource
|
||||
|
||||
result = _get_resource("rid", "tid", MagicMock)
|
||||
assert result == fake_resource
|
||||
|
||||
def test_get_resource_not_found(self):
|
||||
with (
|
||||
patch("controllers.console.apikey.select") as mock_select,
|
||||
patch("controllers.console.apikey.Session") as mock_session,
|
||||
patch("controllers.console.apikey.db") as mock_db,
|
||||
patch("controllers.console.apikey.flask_restx.abort") as abort,
|
||||
):
|
||||
mock_db.engine = MagicMock()
|
||||
mock_select.return_value.filter_by.return_value = MagicMock()
|
||||
|
||||
session = mock_session.return_value.__enter__.return_value
|
||||
session.execute.return_value.scalar_one_or_none.return_value = None
|
||||
|
||||
_get_resource("rid", "tid", MagicMock)
|
||||
|
||||
abort.assert_called_once()
|
||||
|
||||
|
||||
class TestBaseApiKeyListResource:
|
||||
def test_get_apikeys_success(self, tenant_context_admin, db_mock):
|
||||
resource = DummyApiKeyListResource()
|
||||
|
||||
with patch("controllers.console.apikey._get_resource"):
|
||||
db_mock.session.scalars.return_value.all.return_value = [MagicMock(), MagicMock()]
|
||||
|
||||
result = DummyApiKeyListResource.get.__wrapped__(resource, "resource-id")
|
||||
assert "items" in result
|
||||
|
||||
|
||||
class TestBaseApiKeyResource:
|
||||
def test_delete_forbidden(self, tenant_context_non_admin, db_mock):
|
||||
resource = DummyApiKeyResource()
|
||||
|
||||
with patch("controllers.console.apikey._get_resource"):
|
||||
with pytest.raises(Forbidden):
|
||||
DummyApiKeyResource.delete(resource, "rid", "kid")
|
||||
|
||||
def test_delete_key_not_found(self, tenant_context_admin, db_mock):
|
||||
resource = DummyApiKeyResource()
|
||||
db_mock.session.scalar.return_value = None
|
||||
|
||||
with patch("controllers.console.apikey._get_resource"):
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
DummyApiKeyResource.delete(resource, "rid", "kid")
|
||||
|
||||
# flask_restx.abort raises HTTPException with message in data attribute
|
||||
assert exc_info.value.data["message"] == "API key not found"
|
||||
|
||||
def test_delete_success(self, tenant_context_admin, db_mock):
|
||||
resource = DummyApiKeyResource()
|
||||
db_mock.session.scalar.return_value = MagicMock()
|
||||
|
||||
with (
|
||||
patch("controllers.console.apikey._get_resource"),
|
||||
patch("controllers.console.apikey.ApiTokenCache.delete"),
|
||||
):
|
||||
result, status = DummyApiKeyResource.delete(resource, "rid", "kid")
|
||||
|
||||
assert status == 204
|
||||
assert result == {"result": "success"}
|
||||
db_mock.session.commit.assert_called_once()
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "تطوير",
|
||||
"environment.testing": "اختبار",
|
||||
"error": "خطأ",
|
||||
"errorBoundary.componentStack": "مكدس المكون:",
|
||||
"errorBoundary.details": "تفاصيل الخطأ (التطوير فقط)",
|
||||
"errorBoundary.errorCount": "حدث هذا الخطأ {{count}} مرة",
|
||||
"errorBoundary.fallbackTitle": "عذراً! حدث خطأ ما",
|
||||
"errorBoundary.message": "حدث خطأ غير متوقع أثناء عرض هذا المكون.",
|
||||
"errorBoundary.reloadPage": "إعادة تحميل الصفحة",
|
||||
"errorBoundary.title": "حدث خطأ ما",
|
||||
"errorBoundary.tryAgain": "حاول مجدداً",
|
||||
"errorBoundary.tryAgainCompact": "حاول مجدداً",
|
||||
"errorMsg.fieldRequired": "{{field}} مطلوب",
|
||||
"errorMsg.urlError": "يجب أن يبدأ العنوان بـ http:// أو https://",
|
||||
"feedback.content": "محتوى التعليق",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "تصدير القيم السرية",
|
||||
"env.export.export": "تصدير DSL مع القيم السرية ",
|
||||
"env.export.ignore": "تصدير DSL",
|
||||
"env.export.name": "الاسم",
|
||||
"env.export.secret": "سري",
|
||||
"env.export.title": "تصدير متغيرات البيئة السرية؟",
|
||||
"env.export.value": "القيمة",
|
||||
"env.modal.description": "الوصف",
|
||||
"env.modal.descriptionPlaceholder": "وصف المتغير",
|
||||
"env.modal.editTitle": "تعديل متغير بيئة",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "ENTWICKLUNG",
|
||||
"environment.testing": "TESTEN",
|
||||
"error": "Fehler",
|
||||
"errorBoundary.componentStack": "Komponenten-Stack:",
|
||||
"errorBoundary.details": "Fehlerdetails (Nur Entwicklung)",
|
||||
"errorBoundary.errorCount": "Dieser Fehler ist {{count}} Mal aufgetreten",
|
||||
"errorBoundary.fallbackTitle": "Hoppla! Etwas ist schiefgelaufen",
|
||||
"errorBoundary.message": "Beim Rendern dieser Komponente ist ein unerwarteter Fehler aufgetreten.",
|
||||
"errorBoundary.reloadPage": "Seite neu laden",
|
||||
"errorBoundary.title": "Etwas ist schiefgelaufen",
|
||||
"errorBoundary.tryAgain": "Erneut versuchen",
|
||||
"errorBoundary.tryAgainCompact": "Erneut versuchen",
|
||||
"errorMsg.fieldRequired": "{{field}} ist erforderlich",
|
||||
"errorMsg.urlError": "Die URL sollte mit http:// oder https:// beginnen",
|
||||
"feedback.content": "Feedback-Inhalt",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Geheime Werte exportieren",
|
||||
"env.export.export": "DSL mit geheimen Werten exportieren",
|
||||
"env.export.ignore": "DSL exportieren",
|
||||
"env.export.name": "Name",
|
||||
"env.export.secret": "Geheim",
|
||||
"env.export.title": "Geheime Umgebungsvariablen exportieren?",
|
||||
"env.export.value": "Wert",
|
||||
"env.modal.description": "Beschreibung",
|
||||
"env.modal.descriptionPlaceholder": "Beschreiben Sie die Variable",
|
||||
"env.modal.editTitle": "Umgebungsvariable bearbeiten",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "DESARROLLO",
|
||||
"environment.testing": "PRUEBAS",
|
||||
"error": "Error",
|
||||
"errorBoundary.componentStack": "Pila de Componentes:",
|
||||
"errorBoundary.details": "Detalles del Error (Solo Desarrollo)",
|
||||
"errorBoundary.errorCount": "Este error ha ocurrido {{count}} veces",
|
||||
"errorBoundary.fallbackTitle": "¡Vaya! Algo salió mal",
|
||||
"errorBoundary.message": "Ocurrió un error inesperado al renderizar este componente.",
|
||||
"errorBoundary.reloadPage": "Recargar Página",
|
||||
"errorBoundary.title": "Algo salió mal",
|
||||
"errorBoundary.tryAgain": "Intentar de Nuevo",
|
||||
"errorBoundary.tryAgainCompact": "Intentar de nuevo",
|
||||
"errorMsg.fieldRequired": "{{field}} es requerido",
|
||||
"errorMsg.urlError": "la URL debe comenzar con http:// o https://",
|
||||
"feedback.content": "Contenido de retroalimentación",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Exportar valores secretos",
|
||||
"env.export.export": "Exportar DSL con valores secretos",
|
||||
"env.export.ignore": "Exportar DSL",
|
||||
"env.export.name": "Nombre",
|
||||
"env.export.secret": "Secreto",
|
||||
"env.export.title": "¿Exportar variables de entorno secretas?",
|
||||
"env.export.value": "Valor",
|
||||
"env.modal.description": "Descripción",
|
||||
"env.modal.descriptionPlaceholder": "Describa la variable",
|
||||
"env.modal.editTitle": "Editar Variable de Entorno",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "توسعه",
|
||||
"environment.testing": "آزمایشی",
|
||||
"error": "خطا",
|
||||
"errorBoundary.componentStack": "پشته کامپوننت:",
|
||||
"errorBoundary.details": "جزئیات خطا (فقط در محیط توسعه)",
|
||||
"errorBoundary.errorCount": "این خطا {{count}} بار رخ داده است",
|
||||
"errorBoundary.fallbackTitle": "اوه! مشکلی پیش آمد",
|
||||
"errorBoundary.message": "هنگام رندر کردن این کامپوننت، یک خطای غیرمنتظره رخ داد.",
|
||||
"errorBoundary.reloadPage": "بارگذاری مجدد صفحه",
|
||||
"errorBoundary.title": "مشکلی پیش آمد",
|
||||
"errorBoundary.tryAgain": "تلاش مجدد",
|
||||
"errorBoundary.tryAgainCompact": "تلاش مجدد",
|
||||
"errorMsg.fieldRequired": "{{field}} الزامی است",
|
||||
"errorMsg.urlError": "آدرس باید با http:// یا https:// شروع شود",
|
||||
"feedback.content": "محتوای بازخورد",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "خروجی مقادیر محرمانه",
|
||||
"env.export.export": "خروجی DSL با مقادیر محرمانه",
|
||||
"env.export.ignore": "خروجی DSL",
|
||||
"env.export.name": "نام",
|
||||
"env.export.secret": "محرمانه",
|
||||
"env.export.title": "آیا متغیرهای محیطی محرمانه صادر شوند؟",
|
||||
"env.export.value": "مقدار",
|
||||
"env.modal.description": "توضیحات",
|
||||
"env.modal.descriptionPlaceholder": "توصیف متغیر",
|
||||
"env.modal.editTitle": "ویرایش متغیر محیطی",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "DÉVELOPPEMENT",
|
||||
"environment.testing": "TESTER",
|
||||
"error": "Erreur",
|
||||
"errorBoundary.componentStack": "Pile du Composant :",
|
||||
"errorBoundary.details": "Détails de l'Erreur (Développement Uniquement)",
|
||||
"errorBoundary.errorCount": "Cette erreur s'est produite {{count}} fois",
|
||||
"errorBoundary.fallbackTitle": "Oups ! Quelque chose s'est mal passé",
|
||||
"errorBoundary.message": "Une erreur inattendue s'est produite lors du rendu de ce composant.",
|
||||
"errorBoundary.reloadPage": "Recharger la Page",
|
||||
"errorBoundary.title": "Quelque chose s'est mal passé",
|
||||
"errorBoundary.tryAgain": "Réessayer",
|
||||
"errorBoundary.tryAgainCompact": "Réessayer",
|
||||
"errorMsg.fieldRequired": "{{field}} est obligatoire",
|
||||
"errorMsg.urlError": "L’URL doit commencer par http:// ou https://",
|
||||
"feedback.content": "Contenu des retours",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Exporter les valeurs secrètes",
|
||||
"env.export.export": "Exporter les DSL avec des valeurs secrètes",
|
||||
"env.export.ignore": "Exporter DSL",
|
||||
"env.export.name": "Nom",
|
||||
"env.export.secret": "Secret",
|
||||
"env.export.title": "Exporter des variables d'environnement secrètes?",
|
||||
"env.export.value": "valeur",
|
||||
"env.modal.description": "Description",
|
||||
"env.modal.descriptionPlaceholder": "Décrivez la variable",
|
||||
"env.modal.editTitle": "Editer titre",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "विकास",
|
||||
"environment.testing": "परीक्षण",
|
||||
"error": "त्रुटि",
|
||||
"errorBoundary.componentStack": "कंपोनेंट स्टैक:",
|
||||
"errorBoundary.details": "त्रुटि विवरण (केवल डेवलपमेंट)",
|
||||
"errorBoundary.errorCount": "यह त्रुटि {{count}} बार हुई है",
|
||||
"errorBoundary.fallbackTitle": "उफ़! कुछ गलत हो गया",
|
||||
"errorBoundary.message": "इस कंपोनेंट को रेंडर करते समय एक अप्रत्याशित त्रुटि हुई।",
|
||||
"errorBoundary.reloadPage": "पेज रीलोड करें",
|
||||
"errorBoundary.title": "कुछ गलत हो गया",
|
||||
"errorBoundary.tryAgain": "पुनः प्रयास करें",
|
||||
"errorBoundary.tryAgainCompact": "पुनः प्रयास करें",
|
||||
"errorMsg.fieldRequired": "{{field}} आवश्यक है",
|
||||
"errorMsg.urlError": "url को http:// या https:// से शुरू होना चाहिए",
|
||||
"feedback.content": "प्रतिक्रिया सामग्री",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "गुप्त मान निर्यात करें",
|
||||
"env.export.export": "गुप्त मानों के साथ DSL निर्यात करें",
|
||||
"env.export.ignore": "DSL निर्यात करें",
|
||||
"env.export.name": "नाम",
|
||||
"env.export.secret": "गुप्त",
|
||||
"env.export.title": "गुप्त पर्यावरण चर निर्यात करें?",
|
||||
"env.export.value": "मान",
|
||||
"env.modal.description": "विवरण",
|
||||
"env.modal.descriptionPlaceholder": "चर का वर्णन करें",
|
||||
"env.modal.editTitle": "पर्यावरण चर संपादित करें",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "PENGEMBANGAN",
|
||||
"environment.testing": "PENGUJIAN",
|
||||
"error": "Kesalahan",
|
||||
"errorBoundary.componentStack": "Tumpukan Komponen:",
|
||||
"errorBoundary.details": "Detail Kesalahan (Hanya Pengembangan)",
|
||||
"errorBoundary.errorCount": "Kesalahan ini telah terjadi {{count}} kali",
|
||||
"errorBoundary.fallbackTitle": "Ups! Ada yang salah",
|
||||
"errorBoundary.message": "Terjadi kesalahan tak terduga saat merender komponen ini.",
|
||||
"errorBoundary.reloadPage": "Muat Ulang Halaman",
|
||||
"errorBoundary.title": "Ada yang salah",
|
||||
"errorBoundary.tryAgain": "Coba Lagi",
|
||||
"errorBoundary.tryAgainCompact": "Coba lagi",
|
||||
"errorMsg.fieldRequired": "{{field}} wajib diisi",
|
||||
"errorMsg.urlError": "URL harus dimulai dengan http:// atau https://",
|
||||
"feedback.content": "Konten Umpan Balik",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Mengekspor nilai rahasia",
|
||||
"env.export.export": "Mengekspor DSL dengan nilai rahasia",
|
||||
"env.export.ignore": "Ekspor DSL",
|
||||
"env.export.name": "Nama",
|
||||
"env.export.secret": "Rahasia",
|
||||
"env.export.title": "Mengekspor variabel lingkungan Rahasia?",
|
||||
"env.export.value": "Nilai",
|
||||
"env.modal.description": "Deskripsi",
|
||||
"env.modal.descriptionPlaceholder": "Jelaskan variabel",
|
||||
"env.modal.editTitle": "Edit Variabel Lingkungan",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "SVILUPPO",
|
||||
"environment.testing": "TEST",
|
||||
"error": "Errore",
|
||||
"errorBoundary.componentStack": "Stack del Componente:",
|
||||
"errorBoundary.details": "Dettagli Errore (Solo Sviluppo)",
|
||||
"errorBoundary.errorCount": "Questo errore si è verificato {{count}} volte",
|
||||
"errorBoundary.fallbackTitle": "Ops! Qualcosa è andato storto",
|
||||
"errorBoundary.message": "Si è verificato un errore imprevisto durante il rendering di questo componente.",
|
||||
"errorBoundary.reloadPage": "Ricarica Pagina",
|
||||
"errorBoundary.title": "Qualcosa è andato storto",
|
||||
"errorBoundary.tryAgain": "Riprova",
|
||||
"errorBoundary.tryAgainCompact": "Riprova",
|
||||
"errorMsg.fieldRequired": "{{field}} è obbligatorio",
|
||||
"errorMsg.urlError": "L'URL deve iniziare con http:// o https://",
|
||||
"feedback.content": "Contenuto del feedback",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Esporta valori segreti",
|
||||
"env.export.export": "Esporta DSL con valori segreti",
|
||||
"env.export.ignore": "Esporta DSL",
|
||||
"env.export.name": "Nome",
|
||||
"env.export.secret": "Segreto",
|
||||
"env.export.title": "Esportare variabili d'ambiente segrete?",
|
||||
"env.export.value": "Valore",
|
||||
"env.modal.description": "Descrizione",
|
||||
"env.modal.descriptionPlaceholder": "Descrivi la variabile",
|
||||
"env.modal.editTitle": "Modifica Variabile d'Ambiente",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "開発",
|
||||
"environment.testing": "テスト",
|
||||
"error": "エラー",
|
||||
"errorBoundary.componentStack": "コンポーネントスタック:",
|
||||
"errorBoundary.details": "エラー詳細(開発環境のみ)",
|
||||
"errorBoundary.errorCount": "このエラーは{{count}}回発生しました",
|
||||
"errorBoundary.fallbackTitle": "おっと!問題が発生しました",
|
||||
"errorBoundary.message": "このコンポーネントのレンダリング中に予期しないエラーが発生しました。",
|
||||
"errorBoundary.reloadPage": "ページを再読み込み",
|
||||
"errorBoundary.title": "問題が発生しました",
|
||||
"errorBoundary.tryAgain": "再試行",
|
||||
"errorBoundary.tryAgainCompact": "再試行",
|
||||
"errorMsg.fieldRequired": "{{field}}は必要です",
|
||||
"errorMsg.urlError": "URL は http:// または https:// で始まる必要があります",
|
||||
"feedback.content": "フィードバックコンテンツ",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "シークレット値を含む",
|
||||
"env.export.export": "シークレット値付きでエクスポート",
|
||||
"env.export.ignore": "DSL をエクスポート",
|
||||
"env.export.name": "名前",
|
||||
"env.export.secret": "シークレット",
|
||||
"env.export.title": "シークレット環境変数をエクスポートしますか?",
|
||||
"env.export.value": "値",
|
||||
"env.modal.description": "説明",
|
||||
"env.modal.descriptionPlaceholder": "変数の説明を入力",
|
||||
"env.modal.editTitle": "環境変数を編集",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "개발",
|
||||
"environment.testing": "테스트",
|
||||
"error": "오류",
|
||||
"errorBoundary.componentStack": "컴포넌트 스택:",
|
||||
"errorBoundary.details": "오류 세부 정보 (개발 환경 전용)",
|
||||
"errorBoundary.errorCount": "이 오류가 {{count}}번 발생했습니다",
|
||||
"errorBoundary.fallbackTitle": "이런! 문제가 발생했습니다",
|
||||
"errorBoundary.message": "이 컴포넌트를 렌더링하는 동안 예기치 않은 오류가 발생했습니다.",
|
||||
"errorBoundary.reloadPage": "페이지 새로고침",
|
||||
"errorBoundary.title": "문제가 발생했습니다",
|
||||
"errorBoundary.tryAgain": "다시 시도",
|
||||
"errorBoundary.tryAgainCompact": "다시 시도",
|
||||
"errorMsg.fieldRequired": "{{field}}는 필수입니다.",
|
||||
"errorMsg.urlError": "URL 은 http:// 또는 https:// 로 시작해야 합니다.",
|
||||
"feedback.content": "피드백 내용",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "비밀 값 내보내기",
|
||||
"env.export.export": "비밀 값이 포함된 DSL 내보내기",
|
||||
"env.export.ignore": "DSL 내보내기",
|
||||
"env.export.name": "이름",
|
||||
"env.export.secret": "비밀",
|
||||
"env.export.title": "비밀 환경 변수를 내보내시겠습니까?",
|
||||
"env.export.value": "값",
|
||||
"env.modal.description": "설명",
|
||||
"env.modal.descriptionPlaceholder": "변수에 대해 설명하세요",
|
||||
"env.modal.editTitle": "환경 변수 편집",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "DEVELOPMENT",
|
||||
"environment.testing": "TESTING",
|
||||
"error": "Error",
|
||||
"errorBoundary.componentStack": "Componentstack:",
|
||||
"errorBoundary.details": "Foutdetails (Alleen Ontwikkeling)",
|
||||
"errorBoundary.errorCount": "Deze fout is {{count}} keer opgetreden",
|
||||
"errorBoundary.fallbackTitle": "Oeps! Er is iets fout gegaan",
|
||||
"errorBoundary.message": "Er is een onverwachte fout opgetreden bij het renderen van dit component.",
|
||||
"errorBoundary.reloadPage": "Pagina herladen",
|
||||
"errorBoundary.title": "Er is iets fout gegaan",
|
||||
"errorBoundary.tryAgain": "Opnieuw proberen",
|
||||
"errorBoundary.tryAgainCompact": "Opnieuw proberen",
|
||||
"errorMsg.fieldRequired": "{{field}} is required",
|
||||
"errorMsg.urlError": "url should start with http:// or https://",
|
||||
"feedback.content": "Feedback Content",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Export secret values",
|
||||
"env.export.export": "Export DSL with secret values ",
|
||||
"env.export.ignore": "Export DSL",
|
||||
"env.export.name": "Naam",
|
||||
"env.export.secret": "Geheim",
|
||||
"env.export.title": "Export Secret environment variables?",
|
||||
"env.export.value": "Waarde",
|
||||
"env.modal.description": "Description",
|
||||
"env.modal.descriptionPlaceholder": "Describe the variable",
|
||||
"env.modal.editTitle": "Edit Environment Variable",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "ROZWOJOWA",
|
||||
"environment.testing": "TESTOWANIE",
|
||||
"error": "Błąd",
|
||||
"errorBoundary.componentStack": "Stos komponentów:",
|
||||
"errorBoundary.details": "Szczegóły błędu (tylko tryb deweloperski)",
|
||||
"errorBoundary.errorCount": "Ten błąd wystąpił {{count}} razy",
|
||||
"errorBoundary.fallbackTitle": "Ups! Coś poszło nie tak",
|
||||
"errorBoundary.message": "Wystąpił nieoczekiwany błąd podczas renderowania tego komponentu.",
|
||||
"errorBoundary.reloadPage": "Odśwież stronę",
|
||||
"errorBoundary.title": "Coś poszło nie tak",
|
||||
"errorBoundary.tryAgain": "Spróbuj ponownie",
|
||||
"errorBoundary.tryAgainCompact": "Spróbuj ponownie",
|
||||
"errorMsg.fieldRequired": "{{field}} jest wymagane",
|
||||
"errorMsg.urlError": "Adres URL powinien zaczynać się od http:// lub https://",
|
||||
"feedback.content": "Treść opinii",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Eksportuj tajne wartości",
|
||||
"env.export.export": "Eksportuj DSL z tajnymi wartościami",
|
||||
"env.export.ignore": "Eksportuj DSL",
|
||||
"env.export.name": "Nazwa",
|
||||
"env.export.secret": "Tajny",
|
||||
"env.export.title": "Eksportować tajne zmienne środowiskowe?",
|
||||
"env.export.value": "Wartość",
|
||||
"env.modal.description": "Opis",
|
||||
"env.modal.descriptionPlaceholder": "Opisz zmienną",
|
||||
"env.modal.editTitle": "Edytuj Zmienną Środowiskową",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "DESENVOLVIMENTO",
|
||||
"environment.testing": "TESTE",
|
||||
"error": "Erro",
|
||||
"errorBoundary.componentStack": "Stack do Componente:",
|
||||
"errorBoundary.details": "Detalhes do Erro (Somente Desenvolvimento)",
|
||||
"errorBoundary.errorCount": "Este erro ocorreu {{count}} vezes",
|
||||
"errorBoundary.fallbackTitle": "Ops! Algo deu errado",
|
||||
"errorBoundary.message": "Ocorreu um erro inesperado ao renderizar este componente.",
|
||||
"errorBoundary.reloadPage": "Recarregar Página",
|
||||
"errorBoundary.title": "Algo deu errado",
|
||||
"errorBoundary.tryAgain": "Tentar Novamente",
|
||||
"errorBoundary.tryAgainCompact": "Tentar novamente",
|
||||
"errorMsg.fieldRequired": "{{field}} é obrigatório",
|
||||
"errorMsg.urlError": "URL deve começar com http:// ou https://",
|
||||
"feedback.content": "Conteúdo do feedback",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Exportar valores secretos",
|
||||
"env.export.export": "Exportar DSL com valores secretos",
|
||||
"env.export.ignore": "Exportar DSL",
|
||||
"env.export.name": "Nome",
|
||||
"env.export.secret": "Secreto",
|
||||
"env.export.title": "Exportar variáveis de ambiente secretas?",
|
||||
"env.export.value": "Valor",
|
||||
"env.modal.description": "Descrição",
|
||||
"env.modal.descriptionPlaceholder": "Descreva a variável",
|
||||
"env.modal.editTitle": "Editar Variável de Ambiente",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "DEZVOLTARE",
|
||||
"environment.testing": "TESTARE",
|
||||
"error": "Eroare",
|
||||
"errorBoundary.componentStack": "Stiva componentelor:",
|
||||
"errorBoundary.details": "Detalii eroare (Numai în dezvoltare)",
|
||||
"errorBoundary.errorCount": "Această eroare a apărut de {{count}} ori",
|
||||
"errorBoundary.fallbackTitle": "Ups! Ceva a mers prost",
|
||||
"errorBoundary.message": "A apărut o eroare neașteptată la redarea acestei componente.",
|
||||
"errorBoundary.reloadPage": "Reîncarcă pagina",
|
||||
"errorBoundary.title": "Ceva a mers prost",
|
||||
"errorBoundary.tryAgain": "Încearcă din nou",
|
||||
"errorBoundary.tryAgainCompact": "Încearcă din nou",
|
||||
"errorMsg.fieldRequired": "{{field}} este obligatoriu",
|
||||
"errorMsg.urlError": "URL-ul ar trebui să înceapă cu http:// sau https://",
|
||||
"feedback.content": "Conținut de feedback",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Exportă valori secrete",
|
||||
"env.export.export": "Exportă DSL cu valori secrete",
|
||||
"env.export.ignore": "Exportă DSL",
|
||||
"env.export.name": "Nume",
|
||||
"env.export.secret": "Secret",
|
||||
"env.export.title": "Exportă variabile de mediu secrete?",
|
||||
"env.export.value": "Valoare",
|
||||
"env.modal.description": "Descriere",
|
||||
"env.modal.descriptionPlaceholder": "Descrieți variabila",
|
||||
"env.modal.editTitle": "Editează Variabilă de Mediu",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "РАЗРАБОТКА",
|
||||
"environment.testing": "ТЕСТИРОВАНИЕ",
|
||||
"error": "Ошибка",
|
||||
"errorBoundary.componentStack": "Стек компонентов:",
|
||||
"errorBoundary.details": "Детали ошибки (только разработка)",
|
||||
"errorBoundary.errorCount": "Эта ошибка произошла {{count}} раз(а)",
|
||||
"errorBoundary.fallbackTitle": "Упс! Что-то пошло не так",
|
||||
"errorBoundary.message": "При рендеринге этого компонента произошла непредвиденная ошибка.",
|
||||
"errorBoundary.reloadPage": "Перезагрузить страницу",
|
||||
"errorBoundary.title": "Что-то пошло не так",
|
||||
"errorBoundary.tryAgain": "Попробовать снова",
|
||||
"errorBoundary.tryAgainCompact": "Попробовать снова",
|
||||
"errorMsg.fieldRequired": "{{field}} обязательно",
|
||||
"errorMsg.urlError": "URL должен начинаться с http:// или https://",
|
||||
"feedback.content": "Содержимое обратной связи",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Экспортировать секретные значения",
|
||||
"env.export.export": "Экспортировать DSL с секретными значениями ",
|
||||
"env.export.ignore": "Экспортировать DSL",
|
||||
"env.export.name": "Имя",
|
||||
"env.export.secret": "Секрет",
|
||||
"env.export.title": "Экспортировать секретные переменные среды?",
|
||||
"env.export.value": "Значение",
|
||||
"env.modal.description": "Описание",
|
||||
"env.modal.descriptionPlaceholder": "Опишите переменную",
|
||||
"env.modal.editTitle": "Редактировать переменную среды",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "RAZVOJ",
|
||||
"environment.testing": "PREIZKUŠANJE",
|
||||
"error": "Napaka",
|
||||
"errorBoundary.componentStack": "Sklad komponent:",
|
||||
"errorBoundary.details": "Podrobnosti napake (samo razvojna okolja)",
|
||||
"errorBoundary.errorCount": "Ta napaka se je pojavila {{count}} krat",
|
||||
"errorBoundary.fallbackTitle": "Ojoj! Nekaj je šlo narobe",
|
||||
"errorBoundary.message": "Med prikazovanjem te komponente je prišlo do nepričakovane napake.",
|
||||
"errorBoundary.reloadPage": "Znova naloži stran",
|
||||
"errorBoundary.title": "Nekaj je šlo narobe",
|
||||
"errorBoundary.tryAgain": "Poskusi znova",
|
||||
"errorBoundary.tryAgainCompact": "Poskusi znova",
|
||||
"errorMsg.fieldRequired": "{{field}} je obvezno",
|
||||
"errorMsg.urlError": "url mora začeti z http:// ali https://",
|
||||
"feedback.content": "Vsebina povratnih informacij",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Izvozi tajne vrednosti",
|
||||
"env.export.export": "Izvozi DSL z skrivnimi vrednostmi",
|
||||
"env.export.ignore": "Izvoz DSL",
|
||||
"env.export.name": "Ime",
|
||||
"env.export.secret": "Skrivnost",
|
||||
"env.export.title": "Izvozi skrivne okoljske spremenljivke?",
|
||||
"env.export.value": "Vrednost",
|
||||
"env.modal.description": "Opis",
|
||||
"env.modal.descriptionPlaceholder": "Opisujte spremenljivko",
|
||||
"env.modal.editTitle": "Uredi okoljsko spremenljivko",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "พัฒนาการ",
|
||||
"environment.testing": "การทดสอบ",
|
||||
"error": "ข้อผิดพลาด",
|
||||
"errorBoundary.componentStack": "สแตกของคอมโพเนนต์:",
|
||||
"errorBoundary.details": "รายละเอียดข้อผิดพลาด (สำหรับการพัฒนาเท่านั้น)",
|
||||
"errorBoundary.errorCount": "ข้อผิดพลาดนี้เกิดขึ้น {{count}} ครั้ง",
|
||||
"errorBoundary.fallbackTitle": "อุ๊ปส์! มีบางอย่างผิดพลาด",
|
||||
"errorBoundary.message": "เกิดข้อผิดพลาดที่ไม่คาดคิดขณะแสดงผลคอมโพเนนต์นี้",
|
||||
"errorBoundary.reloadPage": "โหลดหน้าใหม่",
|
||||
"errorBoundary.title": "มีบางอย่างผิดพลาด",
|
||||
"errorBoundary.tryAgain": "ลองอีกครั้ง",
|
||||
"errorBoundary.tryAgainCompact": "ลองอีกครั้ง",
|
||||
"errorMsg.fieldRequired": "{{field}} เป็นสิ่งจําเป็น",
|
||||
"errorMsg.urlError": "url ควรขึ้นต้นด้วย http:// หรือ https://",
|
||||
"feedback.content": "เนื้อหาข้อเสนอแนะ",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "ส่งออกค่าข้อมูลลับ",
|
||||
"env.export.export": "ส่งออก DSL ด้วยค่าลับ",
|
||||
"env.export.ignore": "ส่งออก DSL",
|
||||
"env.export.name": "ชื่อ",
|
||||
"env.export.secret": "Secret",
|
||||
"env.export.title": "ส่งออกตัวแปรสภาพแวดล้อม Secret หรือไม่",
|
||||
"env.export.value": "ค่า",
|
||||
"env.modal.description": "คำอธิบาย",
|
||||
"env.modal.descriptionPlaceholder": "อธิบายตัวแปร",
|
||||
"env.modal.editTitle": "แก้ไขตัวแปรสภาพแวดล้อม",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "GELİŞTİRME",
|
||||
"environment.testing": "TEST",
|
||||
"error": "Hata",
|
||||
"errorBoundary.componentStack": "Bileşen Yığını:",
|
||||
"errorBoundary.details": "Hata Ayrıntıları (Yalnızca Geliştirme)",
|
||||
"errorBoundary.errorCount": "Bu hata {{count}} kez oluştu",
|
||||
"errorBoundary.fallbackTitle": "Hay aksi! Bir şeyler ters gitti",
|
||||
"errorBoundary.message": "Bu bileşen işlenirken beklenmedik bir hata oluştu.",
|
||||
"errorBoundary.reloadPage": "Sayfayı Yenile",
|
||||
"errorBoundary.title": "Bir şeyler ters gitti",
|
||||
"errorBoundary.tryAgain": "Tekrar Dene",
|
||||
"errorBoundary.tryAgainCompact": "Tekrar dene",
|
||||
"errorMsg.fieldRequired": "{{field}} gereklidir",
|
||||
"errorMsg.urlError": "URL http:// veya https:// ile başlamalıdır",
|
||||
"feedback.content": "Geri Bildirim İçeriği",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Gizli değerleri dışa aktar",
|
||||
"env.export.export": "Gizli değerlerle DSL'yi dışa aktar",
|
||||
"env.export.ignore": "DSL'yi dışa aktar",
|
||||
"env.export.name": "Ad",
|
||||
"env.export.secret": "Gizli",
|
||||
"env.export.title": "Gizli çevre değişkenleri dışa aktarılsın mı?",
|
||||
"env.export.value": "Değer",
|
||||
"env.modal.description": "Açıklama",
|
||||
"env.modal.descriptionPlaceholder": "Değişkeni açıklayın",
|
||||
"env.modal.editTitle": "Çevre Değişkenini Düzenle",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "РОЗРОБКА",
|
||||
"environment.testing": "ТЕСТУВАННЯ",
|
||||
"error": "Помилка",
|
||||
"errorBoundary.componentStack": "Стек компонентів:",
|
||||
"errorBoundary.details": "Деталі помилки (тільки розробка)",
|
||||
"errorBoundary.errorCount": "Ця помилка сталася {{count}} раз(ів)",
|
||||
"errorBoundary.fallbackTitle": "Ой! Щось пішло не так",
|
||||
"errorBoundary.message": "Під час відображення цього компонента сталася непередбачена помилка.",
|
||||
"errorBoundary.reloadPage": "Перезавантажити сторінку",
|
||||
"errorBoundary.title": "Щось пішло не так",
|
||||
"errorBoundary.tryAgain": "Спробувати знову",
|
||||
"errorBoundary.tryAgainCompact": "Спробувати знову",
|
||||
"errorMsg.fieldRequired": "{{field}} є обов'язковим",
|
||||
"errorMsg.urlError": "URL-адреса повинна починатися з http:// або https://",
|
||||
"feedback.content": "Зміст відгуку",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Експортувати секретні значення",
|
||||
"env.export.export": "Експортувати DSL з секретними значеннями",
|
||||
"env.export.ignore": "Експортувати DSL",
|
||||
"env.export.name": "Назва",
|
||||
"env.export.secret": "Секрет",
|
||||
"env.export.title": "Експортувати секретні змінні середовища?",
|
||||
"env.export.value": "Значення",
|
||||
"env.modal.description": "Опис",
|
||||
"env.modal.descriptionPlaceholder": "Опишіть змінну",
|
||||
"env.modal.editTitle": "Редагувати змінну середовища",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "DEVELOPMENT",
|
||||
"environment.testing": "TESTING",
|
||||
"error": "Lỗi",
|
||||
"errorBoundary.componentStack": "Ngăn xếp thành phần:",
|
||||
"errorBoundary.details": "Chi tiết lỗi (Chỉ dành cho phát triển)",
|
||||
"errorBoundary.errorCount": "Lỗi này đã xảy ra {{count}} lần",
|
||||
"errorBoundary.fallbackTitle": "Ôi! Đã xảy ra sự cố",
|
||||
"errorBoundary.message": "Đã xảy ra lỗi không mong muốn khi hiển thị thành phần này.",
|
||||
"errorBoundary.reloadPage": "Tải lại trang",
|
||||
"errorBoundary.title": "Đã xảy ra sự cố",
|
||||
"errorBoundary.tryAgain": "Thử lại",
|
||||
"errorBoundary.tryAgainCompact": "Thử lại",
|
||||
"errorMsg.fieldRequired": "{{field}} là bắt buộc",
|
||||
"errorMsg.urlError": "URL phải bắt đầu bằng http:// hoặc https://",
|
||||
"feedback.content": "Nội dung phản hồi",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "Xuất giá trị bí mật",
|
||||
"env.export.export": "Xuất DSL với giá trị bí mật",
|
||||
"env.export.ignore": "Xuất DSL",
|
||||
"env.export.name": "Tên",
|
||||
"env.export.secret": "Bí mật",
|
||||
"env.export.title": "Xuất biến môi trường bí mật?",
|
||||
"env.export.value": "Giá trị",
|
||||
"env.modal.description": "Mô tả",
|
||||
"env.modal.descriptionPlaceholder": "Mô tả biến",
|
||||
"env.modal.editTitle": "Sửa Biến Môi Trường",
|
||||
|
||||
@@ -164,6 +164,15 @@
|
||||
"environment.development": "开发环境",
|
||||
"environment.testing": "测试环境",
|
||||
"error": "错误",
|
||||
"errorBoundary.componentStack": "组件堆栈:",
|
||||
"errorBoundary.details": "错误详情(仅开发模式)",
|
||||
"errorBoundary.errorCount": "此错误已发生 {{count}} 次",
|
||||
"errorBoundary.fallbackTitle": "哎呀!出了点问题",
|
||||
"errorBoundary.message": "渲染此组件时发生了意外错误。",
|
||||
"errorBoundary.reloadPage": "重新加载页面",
|
||||
"errorBoundary.title": "出了点问题",
|
||||
"errorBoundary.tryAgain": "重试",
|
||||
"errorBoundary.tryAgainCompact": "重试",
|
||||
"errorMsg.fieldRequired": "{{field}} 为必填项",
|
||||
"errorMsg.urlError": "url 应该以 http:// 或 https:// 开头",
|
||||
"feedback.content": "反馈内容",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "导出 secret 值",
|
||||
"env.export.export": "导出包含 Secret 值的 DSL",
|
||||
"env.export.ignore": "导出 DSL",
|
||||
"env.export.name": "名称",
|
||||
"env.export.secret": "Secret",
|
||||
"env.export.title": "导出 Secret 类型环境变量?",
|
||||
"env.export.value": "值",
|
||||
"env.modal.description": "描述",
|
||||
"env.modal.descriptionPlaceholder": "变量的描述",
|
||||
"env.modal.editTitle": "编辑环境变量",
|
||||
|
||||
@@ -162,6 +162,15 @@
|
||||
"environment.development": "開發環境",
|
||||
"environment.testing": "測試環境",
|
||||
"error": "錯誤",
|
||||
"errorBoundary.componentStack": "元件堆疊:",
|
||||
"errorBoundary.details": "錯誤詳情(僅開發模式)",
|
||||
"errorBoundary.errorCount": "此錯誤已發生 {{count}} 次",
|
||||
"errorBoundary.fallbackTitle": "哎呀!出了點問題",
|
||||
"errorBoundary.message": "渲染此元件時發生了意外錯誤。",
|
||||
"errorBoundary.reloadPage": "重新載入頁面",
|
||||
"errorBoundary.title": "出了點問題",
|
||||
"errorBoundary.tryAgain": "重試",
|
||||
"errorBoundary.tryAgainCompact": "重試",
|
||||
"errorMsg.fieldRequired": "{{field}} 為必填項",
|
||||
"errorMsg.urlError": "URL 應以 http:// 或 https:// 開頭",
|
||||
"feedback.content": "反饋內容",
|
||||
|
||||
@@ -287,7 +287,10 @@
|
||||
"env.export.checkbox": "導出機密值",
|
||||
"env.export.export": "導出帶有機密值的 DSL",
|
||||
"env.export.ignore": "導出 DSL",
|
||||
"env.export.name": "名稱",
|
||||
"env.export.secret": "機密",
|
||||
"env.export.title": "導出機密環境變數?",
|
||||
"env.export.value": "值",
|
||||
"env.modal.description": "描述",
|
||||
"env.modal.descriptionPlaceholder": "描述此變數",
|
||||
"env.modal.editTitle": "編輯環境變數",
|
||||
|
||||
Reference in New Issue
Block a user