Merge branch 'main' into fix/issue-35910

This commit is contained in:
FFXN 2026-05-08 14:02:27 +08:00 committed by GitHub
commit 18e2ecd6c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 68 additions and 47 deletions

View File

@ -876,10 +876,10 @@ class ToolBuiltinProviderSetDefaultApi(Resource):
@login_required
@account_initialization_required
def post(self, provider):
current_user, current_tenant_id = current_account_with_tenant()
_, current_tenant_id = current_account_with_tenant()
payload = BuiltinProviderDefaultCredentialPayload.model_validate(console_ns.payload or {})
return BuiltinToolManageService.set_default_provider(
tenant_id=current_tenant_id, user_id=current_user.id, provider=provider, id=payload.id
tenant_id=current_tenant_id, provider=provider, id=payload.id
)

View File

@ -408,7 +408,7 @@ class BuiltinToolManageService:
return {"result": "success"}
@staticmethod
def set_default_provider(tenant_id: str, user_id: str, provider: str, id: str):
def set_default_provider(tenant_id: str, provider: str, id: str):
"""
set default provider
"""
@ -422,12 +422,11 @@ class BuiltinToolManageService:
if target_provider is None:
raise ValueError("provider not found")
# clear default provider
# clear default provider (tenant-scoped: only one default per provider per workspace)
session.execute(
update(BuiltinToolProvider)
.where(
BuiltinToolProvider.tenant_id == tenant_id,
BuiltinToolProvider.user_id == user_id,
BuiltinToolProvider.provider == provider,
BuiltinToolProvider.is_default.is_(True),
)

View File

@ -3,7 +3,7 @@ from __future__ import annotations
import base64
import json
from types import SimpleNamespace
from typing import Any, cast
from typing import Any
from unittest.mock import MagicMock, patch
from uuid import uuid4
@ -71,6 +71,7 @@ def _pending_yaml_content(version: str = "99.0.0") -> bytes:
def _app_stub(**overrides: Any) -> App:
"""Create a stub App object for testing without hitting the database."""
defaults = {
"id": str(uuid4()),
"tenant_id": _DEFAULT_TENANT_ID,
@ -83,7 +84,10 @@ def _app_stub(**overrides: Any) -> App:
"use_icon_as_answer_icon": False,
"app_model_config": None,
}
return cast(App, SimpleNamespace(**(defaults | overrides)))
app = MagicMock(spec=App)
for key, value in (defaults | overrides).items():
object.__setattr__(app, key, value)
return app
class TestAppDslService:

View File

@ -180,7 +180,7 @@ class TestSetDefaultProvider:
session.scalar.return_value = None
with pytest.raises(ValueError, match="provider not found"):
BuiltinToolManageService.set_default_provider("t", "u", "p", "id")
BuiltinToolManageService.set_default_provider("t", "p", "id")
@patch(f"{MODULE}.sessionmaker")
@patch(f"{MODULE}.db")
@ -189,11 +189,29 @@ class TestSetDefaultProvider:
target = MagicMock()
session.scalar.return_value = target
result = BuiltinToolManageService.set_default_provider("t", "u", "p", "id")
result = BuiltinToolManageService.set_default_provider("t", "p", "id")
assert result == {"result": "success"}
assert target.is_default is True
@patch(f"{MODULE}.sessionmaker")
@patch(f"{MODULE}.db")
def test_clear_default_is_tenant_scoped_not_user_scoped(self, mock_db, mock_sm_cls):
# Regression: clearing prior defaults must NOT filter by user_id, otherwise
# two workspace members can each leave their own credential as default at
# the same time (the default flag is tenant-scoped, not per-user).
session = _mock_sessionmaker(mock_sm_cls)
session.scalar.return_value = MagicMock()
BuiltinToolManageService.set_default_provider("tenant-1", "google", "cred-id")
session.execute.assert_called_once()
update_stmt = session.execute.call_args.args[0]
compiled = str(update_stmt.compile(compile_kwargs={"literal_binds": True}))
assert "user_id" not in compiled
assert "tenant_id" in compiled
assert "provider" in compiled
class TestUpdateBuiltinToolProvider:
@patch(f"{MODULE}.sessionmaker")

View File

@ -261,14 +261,14 @@
"notSetAPIKey.trailFinished": "Deneme süresi sona erdi",
"notSetVar": "Değişkenler, kullanıcıların form doldururken prompt kelimelerini veya açılış ifadelerini getirmesine izin verir. Prompt kelimelerine \"{{input}}\" yazmayı deneyebilirsiniz.",
"openingStatement.add": "Ekle",
"openingStatement.editorTitle": "Acilis Mesaji",
"openingStatement.editorTitle": "Açılış Mesajı",
"openingStatement.noDataPlaceHolder": "Kullanıcı ile konuşmayı başlatmak, AI'ın konuşma uygulamalarında onlarla daha yakın bir bağlantı kurmasına yardımcı olabilir.",
"openingStatement.notIncludeKey": "Başlangıç promptu değişkeni içermiyor: {{key}}. Lütfen bunu başlangıç promptuna ekleyin.",
"openingStatement.openingQuestion": "Açılış Soruları",
"openingStatement.openingQuestionDescription": "Acilis mesajindan sonra gosterilen istege bagli yonlendirmeler; kullanicilarin konusmayi surdurmesine yardimci olur.",
"openingStatement.openingQuestionPlaceholder": "Bir acilis sorusu girin.",
"openingStatement.placeholderLine1": "Buradan baslayin. Yapay zekanin gondermesi gereken ilk mesaji yazin.",
"openingStatement.placeholderLine2": "Değişkenler kullanabilirsiniz, {{variable}} yazmayı deneyin.",
"openingStatement.openingQuestionDescription": "Açılış mesajından sonra gösterilen isteğe bağlı yönlendirmeler; kullanıcıların konuşmayı sürdürmesine yardımcı olur.",
"openingStatement.openingQuestionPlaceholder": "Bir açılış sorusu girin.",
"openingStatement.placeholderLine1": "Buradan başlayın. Yapay zekânın göndermesi gereken ilk mesajı yazın.",
"openingStatement.placeholderLine2": "Değişkenleri kullanabilirsiniz; {{variable}} yazmayı deneyin.",
"openingStatement.title": "Konuşma Başlatıcı",
"openingStatement.tooShort": "Konuşma için açılış ifadeleri oluşturmak için en az 20 kelimelik başlangıç promptu gereklidir.",
"openingStatement.varTip": "Değişkenler kullanabilirsiniz, örneğin {{variable}} yazmayı deneyin",

View File

@ -136,11 +136,11 @@
"marketplace.template.fetchFailed": "Şablon alınamadı",
"marketplace.template.importConfirm": "İçe Aktar",
"marketplace.template.importFailed": "Şablon içe aktarılamadı",
"marketplace.template.modalTitle": "Marketplace'den İçe Aktar",
"marketplace.template.modalTitle": "Pazar Yeri'nden İçe Aktar",
"marketplace.template.overview": "Genel Bakış",
"marketplace.template.publishedBy": "Yayıncı",
"marketplace.template.usageCount": "Kullanım",
"marketplace.template.viewOnMarketplace": "Marketplace'de Görüntüle",
"marketplace.template.viewOnMarketplace": "Pazar Yeri'nde Görüntüle",
"maxActiveRequests": "Maksimum eş zamanlı istekler",
"maxActiveRequestsPlaceholder": "Sınırsız için 0 girin",
"maxActiveRequestsTip": "Her uygulama için maksimum eşzamanlı aktif istek sayısı (sınırsız için 0)",

View File

@ -1,24 +1,24 @@
{
"applied.activeSubscription.description": "Aktif bir aboneliğiniz var. Aboneliğinizin süresi dolduktan sonra eğitim indirimini kullanabilirsiniz. Aboneliğinizi <stripeLink>Stripe</stripeLink>'da onaylayın.",
"applied.description": "Tebrikler! Eğitim indirimi için başarıyla başvurdunuz.",
"applied.noPaymentPermission.description": "Bu workspace'te ödeme izniniz yok. Eğitim indirimini kullanmak için lütfen faturalamayı yönetebileceğiniz bir workspace'e geçin.",
"applied.noPaymentPermission.description": "Bu çalışma alanında ödeme izniniz yok. Eğitim indirimini kullanmak için lütfen faturalamayı yönetebileceğiniz bir çalışma alanına geçin.",
"applied.noPaymentPermission.returnHome": "Dify'e geri dön",
"applied.step1.description": "Eğitim indirimi için başarıyla başvurdunuz.",
"applied.step1.title": "Adım 1",
"applied.step2.description": "Eğitim indirimiyle kullanmak istediğiniz workspace'i seçin.",
"applied.step2.description": "Eğitim indirimiyle kullanmak istediğiniz çalışma alanını seçin.",
"applied.step2.title": "Adım 2",
"applied.tabs.activeSubscription": "Abonelikte",
"applied.tabs.eligible": "Satın alabilir",
"applied.tabs.noPaymentPermission": "Ödeme izni yok",
"applied.title": "Eğitim indirimi uygulandı",
"applied.workspace.plan": "Ücretli plan",
"applied.workspace.title": "Mevcut Workspace",
"applied.workspace.title": "Mevcut Çalışma Alanı",
"currentSigned": "ŞU ANDA GİRİŞ YAPILDIĞI KİŞİ",
"educationPricingConfirm.billingPeriod.monthly": "aylık",
"educationPricingConfirm.billingPeriod.yearly": "yıllık",
"educationPricingConfirm.cancel": "İptal",
"educationPricingConfirm.continue": "İndirim olmadan devam et",
"educationPricingConfirm.description": "{{planName}} {{billingPeriod}} planınız eğitim indirimini desteklemiyor. Yalnızca Professional yıllık plan uygun.",
"educationPricingConfirm.description": "{{planName}} {{billingPeriod}} planınız eğitim indirimini desteklemiyor. Yalnızca yıllık Professional planı uygundur.",
"educationPricingConfirm.title": "Eğitim indirimi mevcut değil",
"emailLabel": "Şu anki e-posta adresin",
"form.schoolName.placeholder": "Okulunuzun resmi, kısaltılmamış adını girin",

View File

@ -16,8 +16,8 @@
"chat.privacyPolicyMiddle": "gizlilik politikası",
"chat.privacyPolicyRight": "uygulama geliştiricisi tarafından sağlanmıştır.",
"chat.privatePromptConfigTitle": "Konuşma ayarları",
"chat.prompt": "Prompt",
"chat.publicPromptConfigTitle": "Başlangıç Promptu",
"chat.prompt": "İstem",
"chat.publicPromptConfigTitle": "Başlangıç İstemi",
"chat.resetChat": "Konuşmayı sıfırla",
"chat.startChat": "Sohbete Başla",
"chat.temporarySystemIssue": "Üzgünüz, geçici sistem sorunu.",

View File

@ -111,33 +111,33 @@
"chatVariable.updatedAt": "Güncellenme zamanı: ",
"collaboration.historyAction.generic": "Bir işbirlikçi geri alma/yeniden yapma gerçekleştirdi",
"comments.actions.addComment": "Yorum ekle",
"comments.actions.deleteReply": "Delete reply",
"comments.actions.deleteReply": "Yanıtı sil",
"comments.actions.editComment": "Yorumu düzenle",
"comments.actions.editReply": "Edit reply",
"comments.aria.closeComment": "Close comment",
"comments.actions.editReply": "Yanıtı düzenle",
"comments.aria.closeComment": "Yorumu kapat",
"comments.aria.commentActions": "Yorum işlemleri",
"comments.aria.deleteComment": "Delete thread",
"comments.aria.deleteComment": "Konuyu sil",
"comments.aria.filterComments": "Yorumları filtrele",
"comments.aria.nextComment": "Next comment",
"comments.aria.previousComment": "Previous comment",
"comments.aria.replyActions": "Reply actions",
"comments.aria.resolveComment": "Resolve",
"comments.confirm.deleteReplyDesc": "This reply will be removed permanently.",
"comments.confirm.deleteReplyTitle": "Delete this reply?",
"comments.confirm.deleteThreadDesc": "This action will permanently delete the thread and all its replies. This cannot be undone.",
"comments.confirm.deleteThreadTitle": "Delete this thread?",
"comments.fallback.user": "User",
"comments.aria.nextComment": "Sonraki yorum",
"comments.aria.previousComment": "Önceki yorum",
"comments.aria.replyActions": "Yanıt işlemleri",
"comments.aria.resolveComment": "Çöz",
"comments.confirm.deleteReplyDesc": "Bu yanıt kalıcı olarak kaldırılacak.",
"comments.confirm.deleteReplyTitle": "Bu yanıt silinsin mi?",
"comments.confirm.deleteThreadDesc": "Bu işlem konuyu ve tüm yanıtlarını kalıcı olarak siler. Bu işlem geri alınamaz.",
"comments.confirm.deleteThreadTitle": "Bu konu silinsin mi?",
"comments.fallback.user": "Kullanıcı",
"comments.filter.all": "Tümü",
"comments.filter.onlyYourThreads": "Yalnızca senin başlıkların",
"comments.filter.onlyYourThreads": "Yalnızca kendi konuların",
"comments.filter.showResolved": "Çözülenleri göster",
"comments.loading": "Loading…",
"comments.noComments": "No comments yet",
"comments.panelTitle": "Comment",
"comments.placeholder.add": "Add a comment",
"comments.loading": "Yükleniyor…",
"comments.noComments": "Henüz yorum yok",
"comments.panelTitle": "Yorum",
"comments.placeholder.add": "Yorum ekle",
"comments.placeholder.editComment": "Yorumu düzenle",
"comments.placeholder.editReply": "Edit reply",
"comments.placeholder.reply": "Reply",
"comments.reply": "Reply",
"comments.placeholder.editReply": "Yanıtı düzenle",
"comments.placeholder.reply": "Yanıtla",
"comments.reply": "Yanıtla",
"common.ImageUploadLegacyTip": "Artık başlangıç formunda dosya türü değişkenleri oluşturabilirsiniz. Gelecekte resim yükleme özelliğini artık desteklemeyeceğiz.",
"common.accessAPIReference": "API Referansına Eriş",
"common.addBlock": "Düğüm Ekle",
@ -229,8 +229,8 @@
"common.previewPlaceholder": "Sohbet Robotunu hata ayıklamak için aşağıdaki kutuya içerik girin",
"common.processData": "Veriyi İşle",
"common.publish": "Yayınla",
"common.publishToMarketplace": "Marketplace'de Yayınla",
"common.publishToMarketplaceFailed": "Marketplace'de Yayınlama Başarısız",
"common.publishToMarketplace": "Pazar Yeri'nde Yayınla",
"common.publishToMarketplaceFailed": "Pazar Yeri'nde yayınlanamadı",
"common.publishUpdate": "Güncellemeyi Yayınla",
"common.published": "Yayınlandı",
"common.publishedAt": "Yayınlandı",
@ -812,7 +812,7 @@
"nodes.llm.outputVars.output": "İçerik Üret",
"nodes.llm.outputVars.reasoning_content": "Akıl yürütme içeriği",
"nodes.llm.outputVars.usage": "Model Kullanım Bilgileri",
"nodes.llm.prompt": "prompt",
"nodes.llm.prompt": "istem",
"nodes.llm.reasoningFormat.separated": "Ayrı düşünce etiketleri",
"nodes.llm.reasoningFormat.tagged": "Etiketleri düşünmeye devam et",
"nodes.llm.reasoningFormat.title": "Akıl yürütme etiket ayrımını etkinleştir",
@ -891,7 +891,7 @@
"nodes.parameterExtractor.outputVars.usage": "Model Kullanım Bilgileri",
"nodes.parameterExtractor.reasoningMode": "Akıl Yürütme Modu",
"nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Fonksiyon/Araç Çağrısı",
"nodes.parameterExtractor.reasoningModePrompt": "Prompt",
"nodes.parameterExtractor.reasoningModePrompt": "İstem",
"nodes.parameterExtractor.reasoningModeTip": "Modelin fonksiyon çağırma veya istemler için talimatlara yanıt verme yeteneğine bağlı olarak uygun akıl yürütme modunu seçebilirsiniz.",
"nodes.questionClassifiers.addClass": "Sınıf Ekle",
"nodes.questionClassifiers.advancedSetting": "Gelişmiş Ayarlar",