mirror of
https://github.com/langgenius/dify.git
synced 2026-07-26 06:08:38 +08:00
refactor: remove backend-only system feature fields (#39543)
This commit is contained in:
parent
58f83fa7e7
commit
aff5c47541
@ -213,6 +213,17 @@ Before opening a PR / submitting:
|
||||
DTOs with `register_response_schema_models(...)`, serialize response DTOs with `dump_response(...)`,
|
||||
and avoid adding new legacy `ns.model(...)`, `@marshal_with(...)`, or GET `@ns.expect(...)` patterns.
|
||||
|
||||
### System Features Contract
|
||||
|
||||
- Treat the shared Console/Web `/system-features` response as a minimal unauthenticated bootstrap allowlist, not a
|
||||
general configuration or feature-discovery endpoint. Existing fields do not establish precedent.
|
||||
- Before adding a field, read `controllers/API_SCHEMA_GUIDE.md#public-system-features-contract` and provide evidence
|
||||
that both Console and Web have production consumers that require it before authentication.
|
||||
- Never place backend-only policy, surface-specific configuration, post-authentication state, speculative values, or
|
||||
large/slow payloads in `SystemFeatureModel`. Use the consumer or domain owner described in the schema guide.
|
||||
- Agents and reviewers must reject additions whose owner, public exposure, pre-authentication need, or root SSR cost
|
||||
is not explicit.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- Use `configs.dify_config` for configuration—never read environment variables directly.
|
||||
|
||||
@ -12,6 +12,47 @@ parameters, response schemas, and Swagger documentation.
|
||||
- Do not add new Flask-RESTX `fields.*` dictionaries, `Namespace.model(...)` exports, or `@marshal_with(...)` for migrated or new endpoints.
|
||||
- Do not use `@ns.expect(...)` for GET query parameters. Flask-RESTX documents that as a request body.
|
||||
|
||||
## Public System Features Contract
|
||||
|
||||
The Console and Web `/system-features` endpoints share `SystemFeatureModel`. They are unauthenticated and may be
|
||||
requested during root SSR, so treat this response as a minimal public bootstrap allowlist. It is not a general
|
||||
configuration endpoint, a feature registry, or a mirror of environment and Enterprise settings. Existing fields are
|
||||
legacy inventory and do not establish precedent for new fields.
|
||||
|
||||
A new field is eligible only when all of the following are true:
|
||||
|
||||
1. Both Console and Web have named production consumers for the field.
|
||||
2. Both consumers need the value before authentication and tenant/workspace bootstrap to render initial state or
|
||||
choose an authentication flow.
|
||||
3. The value varies at runtime or by deployment and cannot be safely derived from an existing public contract.
|
||||
4. The value is non-sensitive, safe to disclose without authentication, and has stable public API semantics.
|
||||
5. Sending the value on every root bootstrap is demonstrably clearer and cheaper than a consumer-owned query.
|
||||
|
||||
Do not add:
|
||||
|
||||
- Backend-only policy or enforcement inputs, including security decisions, upload limits, or integration toggles.
|
||||
- Console-only or Web-only configuration.
|
||||
- Tenant, workspace, account, permission, billing-detail, or other post-authentication state.
|
||||
- Provider payloads, operational diagnostics, large nested objects, or values without active consumers.
|
||||
- Speculative fields added for possible future use.
|
||||
|
||||
Route excluded values to their actual owner:
|
||||
|
||||
- Keep backend enforcement behind a narrow service method or domain policy.
|
||||
- Serve post-authentication state from an authenticated domain endpoint.
|
||||
- Serve surface-specific bootstrap state from a Console- or Web-specific endpoint and account for its SSR, caching,
|
||||
and failure cost explicitly.
|
||||
- Load large, slow, or page-specific data lazily through a consumer-owned query.
|
||||
|
||||
Every pull request that adds a System Features field must:
|
||||
|
||||
- Name both production consumer paths and explain why they require the value before authentication.
|
||||
- Document the root SSR request, payload, caching, and failure-mode impact.
|
||||
- Update the Pydantic owner, regenerate OpenAPI Markdown and TypeScript/Zod contracts, and update shared fixtures.
|
||||
- Add Console and Web schema regression coverage. Do not hand-edit generated contracts or add compatibility defaults.
|
||||
|
||||
Reviewers should reject a field when its owner, pre-authentication need, or consumers are unclear.
|
||||
|
||||
## Naming
|
||||
|
||||
- Request body models: use a `Payload` suffix.
|
||||
|
||||
@ -198,6 +198,6 @@ class ForgotPasswordResetApi(Resource):
|
||||
# Create workspace if needed
|
||||
if (
|
||||
not TenantService.get_join_tenants(account, session=db.session())
|
||||
and FeatureService.get_system_features().is_allow_create_workspace
|
||||
and FeatureService.is_workspace_creation_allowed()
|
||||
):
|
||||
TenantService.create_owner_tenant(account, session=db.session())
|
||||
|
||||
@ -163,7 +163,7 @@ class LoginApi(Resource):
|
||||
tenants = TenantService.get_join_tenants(account, session=db.session())
|
||||
if len(tenants) == 0:
|
||||
if (
|
||||
FeatureService.get_system_features().is_allow_create_workspace
|
||||
FeatureService.is_workspace_creation_allowed()
|
||||
and not FeatureService.get_license().workspaces.is_available()
|
||||
):
|
||||
raise WorkspacesLimitExceeded()
|
||||
@ -314,7 +314,7 @@ class EmailCodeLoginApi(Resource):
|
||||
workspaces = FeatureService.get_license().workspaces
|
||||
if not workspaces.is_available():
|
||||
raise WorkspacesLimitExceeded()
|
||||
if not FeatureService.get_system_features().is_allow_create_workspace:
|
||||
if not FeatureService.is_workspace_creation_allowed():
|
||||
raise NotAllowedCreateWorkspace()
|
||||
else:
|
||||
TenantService.create_owner_tenant(account, session=db.session())
|
||||
|
||||
@ -292,7 +292,7 @@ def _generate_account(
|
||||
if account:
|
||||
tenants = TenantService.get_join_tenants(account, session=db.session())
|
||||
if not tenants:
|
||||
if not FeatureService.get_system_features().is_allow_create_workspace:
|
||||
if not FeatureService.is_workspace_creation_allowed():
|
||||
raise WorkSpaceNotAllowedCreateError()
|
||||
else:
|
||||
TenantService.create_owner_tenant(account, session=db.session())
|
||||
|
||||
@ -123,24 +123,22 @@ class AppDslVersionApi(Resource):
|
||||
@console_ns.route("/system-features")
|
||||
class SystemFeatureApi(Resource):
|
||||
@console_ns.doc("get_system_features")
|
||||
@console_ns.doc(description="Get system-wide feature configuration")
|
||||
@console_ns.doc(
|
||||
description="Get the non-sensitive bootstrap snapshot exposed before Console or Web authentication. "
|
||||
"This is not a general feature registry."
|
||||
)
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Success",
|
||||
console_ns.models[SystemFeatureModel.__name__],
|
||||
)
|
||||
def get(self):
|
||||
"""Get system-wide feature configuration
|
||||
"""Get the non-sensitive bootstrap snapshot exposed before authentication.
|
||||
|
||||
NOTE: This endpoint is unauthenticated by design, as it provides system features
|
||||
data required for dashboard initialization.
|
||||
|
||||
Authentication would create circular dependency (can't login without dashboard loading).
|
||||
|
||||
Only non-sensitive configuration data should be returned by this endpoint. Authenticated
|
||||
license detail is served separately by SystemFeatureLicenseApi.
|
||||
Authentication configuration must be available before the authentication flow can be selected.
|
||||
Authenticated license detail is served separately by SystemFeatureLicenseApi.
|
||||
"""
|
||||
return FeatureService.get_system_features().model_dump()
|
||||
return dump_response(SystemFeatureModel, FeatureService.get_system_features())
|
||||
|
||||
|
||||
@console_ns.route("/system-features/license")
|
||||
|
||||
@ -2,6 +2,7 @@ from flask_restx import Resource
|
||||
|
||||
from controllers.common.schema import register_response_schema_models
|
||||
from controllers.web import web_ns
|
||||
from libs.helper import dump_response
|
||||
from services.feature_service import FeatureService, SystemFeatureModel
|
||||
|
||||
register_response_schema_models(web_ns, SystemFeatureModel)
|
||||
@ -10,7 +11,10 @@ register_response_schema_models(web_ns, SystemFeatureModel)
|
||||
@web_ns.route("/system-features")
|
||||
class SystemFeatureApi(Resource):
|
||||
@web_ns.doc("get_system_features")
|
||||
@web_ns.doc(description="Get system feature flags and configuration")
|
||||
@web_ns.doc(
|
||||
description="Get the non-sensitive bootstrap snapshot exposed before Console or Web authentication. "
|
||||
"This is not a general feature registry."
|
||||
)
|
||||
@web_ns.doc(responses={200: "System features retrieved successfully", 500: "Internal server error"})
|
||||
@web_ns.response(
|
||||
200,
|
||||
@ -18,22 +22,11 @@ class SystemFeatureApi(Resource):
|
||||
web_ns.models[SystemFeatureModel.__name__],
|
||||
)
|
||||
def get(self):
|
||||
"""Get system feature flags and configuration.
|
||||
|
||||
Returns the current system feature flags and configuration
|
||||
that control various functionalities across the platform.
|
||||
|
||||
Returns:
|
||||
dict: System feature configuration object
|
||||
"""Get the non-sensitive bootstrap snapshot exposed before authentication.
|
||||
|
||||
This endpoint is akin to the `SystemFeatureApi` endpoint in api/controllers/console/feature.py,
|
||||
except it is intended for use by the web app, instead of the console dashboard.
|
||||
|
||||
NOTE: This endpoint is unauthenticated by design, as it provides system features
|
||||
data required for webapp initialization.
|
||||
|
||||
Authentication would create circular dependency (can't authenticate without webapp loading).
|
||||
|
||||
Only non-sensitive configuration data should be returned by this endpoint.
|
||||
Authentication configuration must be available before the authentication flow can be selected.
|
||||
"""
|
||||
return FeatureService.get_system_features().model_dump()
|
||||
return dump_response(SystemFeatureModel, FeatureService.get_system_features())
|
||||
|
||||
@ -71,7 +71,7 @@ def check_credential_policy_compliance(
|
||||
)
|
||||
from services.feature_service import FeatureService
|
||||
|
||||
if not FeatureService.get_system_features().plugin_manager.enabled or not credential_id:
|
||||
if not FeatureService.is_plugin_manager_enabled() or not credential_id:
|
||||
return
|
||||
|
||||
# Check if credential exists in database first (if requested)
|
||||
|
||||
@ -9475,16 +9475,11 @@ Used for frontend component type mapping
|
||||
| 200 | Success | **application/json**: [SchemaDefinitionsResponse](#schemadefinitionsresponse)<br> |
|
||||
|
||||
### [GET] /system-features
|
||||
**Get system-wide feature configuration**
|
||||
**Get the non-sensitive bootstrap snapshot exposed before authentication**
|
||||
|
||||
Get system-wide feature configuration
|
||||
NOTE: This endpoint is unauthenticated by design, as it provides system features
|
||||
data required for dashboard initialization.
|
||||
|
||||
Authentication would create circular dependency (can't login without dashboard loading).
|
||||
|
||||
Only non-sensitive configuration data should be returned by this endpoint. Authenticated
|
||||
license detail is served separately by SystemFeatureLicenseApi.
|
||||
Get the non-sensitive bootstrap snapshot exposed before Console or Web authentication. This is not a general feature registry.
|
||||
Authentication configuration must be available before the authentication flow can be selected.
|
||||
Authenticated license detail is served separately by SystemFeatureLicenseApi.
|
||||
|
||||
#### Responses
|
||||
|
||||
@ -20521,12 +20516,6 @@ Shared permission levels for resources (datasets, credentials, etc.)
|
||||
| plugins | [ [PluginEntity](#pluginentity) ] | | Yes |
|
||||
| total | integer | | Yes |
|
||||
|
||||
#### PluginManagerModel
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| enabled | boolean | | Yes |
|
||||
|
||||
#### PluginManifestResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
@ -22052,6 +22041,8 @@ Model class for provider system configuration response.
|
||||
|
||||
#### SystemFeatureModel
|
||||
|
||||
Non-sensitive bootstrap snapshot exposed before Console or Web authentication.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| branding | [BrandingModel](#brandingmodel) | | Yes |
|
||||
@ -22068,14 +22059,11 @@ Model class for provider system configuration response.
|
||||
| enable_social_oauth_login | boolean | | Yes |
|
||||
| enable_step_by_step_tour | boolean | | Yes |
|
||||
| enable_trial_app | boolean | | Yes |
|
||||
| is_allow_create_workspace | boolean | | Yes |
|
||||
| is_allow_register | boolean | | Yes |
|
||||
| is_email_setup | boolean | | Yes |
|
||||
| knowledge_fs_enabled | boolean | | Yes |
|
||||
| license | [LicenseStatusModel](#licensestatusmodel) | | Yes |
|
||||
| max_plugin_package_size | integer, <br>**Default:** 15728640 | | Yes |
|
||||
| plugin_installation_permission | [PluginInstallationPermissionModel](#plugininstallationpermissionmodel) | | Yes |
|
||||
| plugin_manager | [PluginManagerModel](#pluginmanagermodel) | | Yes |
|
||||
| rbac_enabled | boolean | | Yes |
|
||||
| sso_enforced_for_signin | boolean | | Yes |
|
||||
| sso_enforced_for_signin_protocol | string | | Yes |
|
||||
|
||||
@ -774,24 +774,13 @@ Retrieve app site information and configuration.
|
||||
| 500 | Internal Server Error | |
|
||||
|
||||
### [GET] /system-features
|
||||
**Get system feature flags and configuration**
|
||||
|
||||
Get system feature flags and configuration
|
||||
Returns the current system feature flags and configuration
|
||||
that control various functionalities across the platform.
|
||||
|
||||
Returns:
|
||||
dict: System feature configuration object
|
||||
**Get the non-sensitive bootstrap snapshot exposed before authentication**
|
||||
|
||||
Get the non-sensitive bootstrap snapshot exposed before Console or Web authentication. This is not a general feature registry.
|
||||
This endpoint is akin to the `SystemFeatureApi` endpoint in api/controllers/console/feature.py,
|
||||
except it is intended for use by the web app, instead of the console dashboard.
|
||||
|
||||
NOTE: This endpoint is unauthenticated by design, as it provides system features
|
||||
data required for webapp initialization.
|
||||
|
||||
Authentication would create circular dependency (can't authenticate without webapp loading).
|
||||
|
||||
Only non-sensitive configuration data should be returned by this endpoint.
|
||||
Authentication configuration must be available before the authentication flow can be selected.
|
||||
|
||||
#### Responses
|
||||
|
||||
@ -1412,12 +1401,6 @@ Form input definition.
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| PluginInstallationScope | string | | |
|
||||
|
||||
#### PluginManagerModel
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| enabled | boolean | | Yes |
|
||||
|
||||
#### RemoteFileInfo
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
@ -1557,6 +1540,8 @@ Default configuration for form inputs.
|
||||
|
||||
#### SystemFeatureModel
|
||||
|
||||
Non-sensitive bootstrap snapshot exposed before Console or Web authentication.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| branding | [BrandingModel](#brandingmodel) | | Yes |
|
||||
@ -1573,14 +1558,11 @@ Default configuration for form inputs.
|
||||
| enable_social_oauth_login | boolean | | Yes |
|
||||
| enable_step_by_step_tour | boolean | | Yes |
|
||||
| enable_trial_app | boolean | | Yes |
|
||||
| is_allow_create_workspace | boolean | | Yes |
|
||||
| is_allow_register | boolean | | Yes |
|
||||
| is_email_setup | boolean | | Yes |
|
||||
| knowledge_fs_enabled | boolean | | Yes |
|
||||
| license | [LicenseStatusModel](#licensestatusmodel) | | Yes |
|
||||
| max_plugin_package_size | integer, <br>**Default:** 15728640 | | Yes |
|
||||
| plugin_installation_permission | [PluginInstallationPermissionModel](#plugininstallationpermissionmodel) | | Yes |
|
||||
| plugin_manager | [PluginManagerModel](#pluginmanagermodel) | | Yes |
|
||||
| rbac_enabled | boolean | | Yes |
|
||||
| sso_enforced_for_signin | boolean | | Yes |
|
||||
| sso_enforced_for_signin_protocol | string | | Yes |
|
||||
|
||||
@ -1258,11 +1258,7 @@ class TenantService:
|
||||
session: Session,
|
||||
) -> Tenant:
|
||||
"""Create tenant"""
|
||||
if (
|
||||
not FeatureService.get_system_features().is_allow_create_workspace
|
||||
and not is_setup
|
||||
and not is_from_dashboard
|
||||
):
|
||||
if not FeatureService.is_workspace_creation_allowed() and not is_setup and not is_from_dashboard:
|
||||
from controllers.console.error import NotAllowedCreateWorkspace
|
||||
|
||||
raise NotAllowedCreateWorkspace()
|
||||
@ -1325,11 +1321,7 @@ class TenantService:
|
||||
owner. It persists the legacy membership before creating the matching
|
||||
RBAC role binding, then makes the workspace current for the account.
|
||||
"""
|
||||
if (
|
||||
not FeatureService.get_system_features().is_allow_create_workspace
|
||||
and not is_setup
|
||||
and not is_from_dashboard
|
||||
):
|
||||
if not FeatureService.is_workspace_creation_allowed() and not is_setup and not is_from_dashboard:
|
||||
raise WorkSpaceNotAllowedCreateError()
|
||||
|
||||
workspaces = FeatureService.get_license().workspaces
|
||||
@ -2010,7 +2002,7 @@ class RegisterService:
|
||||
AccountService.link_account_integrate(provider, open_id, account, session=session)
|
||||
|
||||
if (
|
||||
FeatureService.get_system_features().is_allow_create_workspace
|
||||
FeatureService.is_workspace_creation_allowed()
|
||||
and create_workspace_required
|
||||
and FeatureService.get_license().workspaces.is_available()
|
||||
):
|
||||
|
||||
@ -161,30 +161,25 @@ class KnowledgeRateLimitModel(FeatureResponseModel):
|
||||
subscription_plan: str = ""
|
||||
|
||||
|
||||
class PluginManagerModel(FeatureResponseModel):
|
||||
enabled: bool = False
|
||||
|
||||
|
||||
class SystemFeatureModel(FeatureResponseModel):
|
||||
"""Non-sensitive bootstrap snapshot exposed before Console or Web authentication."""
|
||||
|
||||
deployment_edition: DeploymentEdition
|
||||
enable_app_deploy: bool = False
|
||||
sso_enforced_for_signin: bool = False
|
||||
sso_enforced_for_signin_protocol: str = ""
|
||||
enable_marketplace: bool = False
|
||||
max_plugin_package_size: int = dify_config.PLUGIN_MAX_PACKAGE_SIZE
|
||||
enable_email_code_login: bool = False
|
||||
enable_email_password_login: bool = True
|
||||
enable_social_oauth_login: bool = False
|
||||
enable_collaboration_mode: bool = True
|
||||
is_allow_register: bool = False
|
||||
is_allow_create_workspace: bool = False
|
||||
is_email_setup: bool = False
|
||||
license: LicenseStatusModel = LicenseStatusModel()
|
||||
branding: BrandingModel = BrandingModel()
|
||||
webapp_auth: WebAppAuthModel = WebAppAuthModel()
|
||||
plugin_installation_permission: PluginInstallationPermissionModel = PluginInstallationPermissionModel()
|
||||
enable_change_email: bool = True
|
||||
plugin_manager: PluginManagerModel = PluginManagerModel()
|
||||
enable_creators_platform: bool = False
|
||||
enable_trial_app: bool = False
|
||||
enable_explore_banner: bool = False
|
||||
@ -266,7 +261,6 @@ class FeatureService:
|
||||
system_features.branding.enabled = True
|
||||
system_features.webapp_auth.enabled = True
|
||||
system_features.enable_change_email = False
|
||||
system_features.plugin_manager.enabled = True
|
||||
cls._fulfill_params_from_enterprise(system_features)
|
||||
|
||||
if dify_config.MARKETPLACE_ENABLED:
|
||||
@ -277,6 +271,21 @@ class FeatureService:
|
||||
|
||||
return system_features
|
||||
|
||||
@classmethod
|
||||
def is_workspace_creation_allowed(cls) -> bool:
|
||||
"""Resolve the backend workspace-creation policy, including the Enterprise override."""
|
||||
is_allowed = dify_config.ALLOW_CREATE_WORKSPACE
|
||||
if not dify_config.ENTERPRISE_ENABLED:
|
||||
return is_allowed
|
||||
|
||||
enterprise_info = EnterpriseService.get_info()
|
||||
return bool(enterprise_info.get("IsAllowCreateWorkspace", is_allowed))
|
||||
|
||||
@classmethod
|
||||
def is_plugin_manager_enabled(cls) -> bool:
|
||||
"""Return whether Enterprise plugin credential policies must be enforced."""
|
||||
return dify_config.ENTERPRISE_ENABLED
|
||||
|
||||
@classmethod
|
||||
def get_license(cls) -> LicenseModel:
|
||||
"""Return full license detail. Enterprise-only; requires an authenticated caller.
|
||||
@ -299,7 +308,6 @@ class FeatureService:
|
||||
system_features.enable_social_oauth_login = dify_config.ENABLE_SOCIAL_OAUTH_LOGIN
|
||||
system_features.enable_collaboration_mode = dify_config.ENABLE_COLLABORATION_MODE
|
||||
system_features.is_allow_register = dify_config.ALLOW_REGISTER
|
||||
system_features.is_allow_create_workspace = dify_config.ALLOW_CREATE_WORKSPACE
|
||||
system_features.is_email_setup = dify_config.MAIL_TYPE is not None and dify_config.MAIL_TYPE != ""
|
||||
system_features.enable_change_email = dify_config.ENABLE_CHANGE_EMAIL
|
||||
system_features.enable_trial_app = dify_config.ENABLE_TRIAL_APP
|
||||
@ -465,9 +473,6 @@ class FeatureService:
|
||||
if "IsAllowRegister" in enterprise_info:
|
||||
features.is_allow_register = enterprise_info["IsAllowRegister"]
|
||||
|
||||
if "IsAllowCreateWorkspace" in enterprise_info:
|
||||
features.is_allow_create_workspace = enterprise_info["IsAllowCreateWorkspace"]
|
||||
|
||||
if "EnableAppDeploy" in enterprise_info:
|
||||
features.enable_app_deploy = enterprise_info["EnableAppDeploy"]
|
||||
|
||||
|
||||
@ -542,7 +542,7 @@ class WorkflowService:
|
||||
# Validate credentials before publishing, for credential policy check
|
||||
from services.feature_service import FeatureService
|
||||
|
||||
if FeatureService.get_system_features().plugin_manager.enabled:
|
||||
if FeatureService.is_plugin_manager_enabled():
|
||||
self._validate_workflow_credentials(draft_workflow, session=session)
|
||||
|
||||
# validate graph structure
|
||||
|
||||
@ -61,7 +61,7 @@ def add_tenant_for_account(
|
||||
) -> Tenant:
|
||||
"""Create an additional tenant and join ``account`` to it (real service calls)."""
|
||||
with patch("services.account_service.FeatureService") as mock_feature_service:
|
||||
mock_feature_service.get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_feature_service.is_workspace_creation_allowed.return_value = True
|
||||
tenant = TenantService.create_tenant(name=name, session=session)
|
||||
TenantService.create_tenant_member(tenant, account, session, role=role)
|
||||
return tenant
|
||||
|
||||
@ -37,7 +37,7 @@ class TestAccountService:
|
||||
):
|
||||
# Setup default mock returns
|
||||
mock_feature_service.get_system_features.return_value.is_allow_register = True
|
||||
mock_feature_service.get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_feature_service.is_workspace_creation_allowed.return_value = True
|
||||
mock_feature_service.get_license.return_value.workspaces.is_available.return_value = True
|
||||
mock_feature_service.get_license.return_value.seats.is_available.return_value = True
|
||||
mock_billing_service.is_email_in_freeze.return_value = False
|
||||
@ -401,9 +401,7 @@ class TestAccountService:
|
||||
password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -436,9 +434,7 @@ class TestAccountService:
|
||||
password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = False
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = False
|
||||
mock_external_service_dependencies["billing_service"].is_email_in_freeze.return_value = False
|
||||
|
||||
with pytest.raises(WorkSpaceNotAllowedCreateError):
|
||||
@ -462,9 +458,7 @@ class TestAccountService:
|
||||
password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = False
|
||||
@ -1274,7 +1268,7 @@ class TestTenantService:
|
||||
patch("services.account_service.BillingService") as mock_billing_service,
|
||||
):
|
||||
# Setup default mock returns
|
||||
mock_feature_service.get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_feature_service.is_workspace_creation_allowed.return_value = True
|
||||
mock_feature_service.get_license.return_value.workspaces.is_available.return_value = True
|
||||
mock_feature_service.get_license.return_value.seats.is_available.return_value = True
|
||||
mock_billing_service.is_email_in_freeze.return_value = False
|
||||
@ -1291,9 +1285,7 @@ class TestTenantService:
|
||||
fake = Faker()
|
||||
tenant_name = fake.company()
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1312,9 +1304,7 @@ class TestTenantService:
|
||||
fake = Faker()
|
||||
tenant_name = fake.company()
|
||||
# Setup mocks to disable workspace creation
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = False
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = False
|
||||
|
||||
with pytest.raises(NotAllowedCreateWorkspace): # NotAllowedCreateWorkspace exception
|
||||
TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1328,9 +1318,7 @@ class TestTenantService:
|
||||
fake = Faker()
|
||||
custom_tenant_name = fake.company()
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = False
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = False
|
||||
|
||||
# Create tenant with setup flag (should bypass workspace creation restriction)
|
||||
tenant = TenantService.create_tenant(
|
||||
@ -1354,9 +1342,7 @@ class TestTenantService:
|
||||
name = fake.name()
|
||||
password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and account
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1390,9 +1376,7 @@ class TestTenantService:
|
||||
name2 = fake.name()
|
||||
password2 = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and accounts
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1430,9 +1414,7 @@ class TestTenantService:
|
||||
name = fake.name()
|
||||
password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and account
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1465,9 +1447,7 @@ class TestTenantService:
|
||||
tenant1_name = fake.company()
|
||||
tenant2_name = fake.company()
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create account and tenants
|
||||
account = AccountService.create_account(
|
||||
@ -1504,9 +1484,7 @@ class TestTenantService:
|
||||
password = generate_valid_password(fake)
|
||||
tenant_name = fake.company()
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create account and tenant
|
||||
account = AccountService.create_account(
|
||||
@ -1542,9 +1520,7 @@ class TestTenantService:
|
||||
name = fake.name()
|
||||
password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create account without setting current tenant
|
||||
account = AccountService.create_account(
|
||||
@ -1570,9 +1546,7 @@ class TestTenantService:
|
||||
tenant1_name = fake.company()
|
||||
tenant2_name = fake.company()
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create account and tenants
|
||||
account = AccountService.create_account(
|
||||
@ -1610,9 +1584,7 @@ class TestTenantService:
|
||||
name = fake.name()
|
||||
password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create account
|
||||
account = AccountService.create_account(
|
||||
@ -1639,9 +1611,7 @@ class TestTenantService:
|
||||
password = generate_valid_password(fake)
|
||||
tenant_name = fake.company()
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create account and tenant
|
||||
account = AccountService.create_account(
|
||||
@ -1670,9 +1640,7 @@ class TestTenantService:
|
||||
admin_name = fake.name()
|
||||
admin_password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and accounts
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1717,9 +1685,7 @@ class TestTenantService:
|
||||
tenant_name = fake.company()
|
||||
invalid_role = fake.word()
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1738,9 +1704,7 @@ class TestTenantService:
|
||||
name = fake.name()
|
||||
password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and account
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1775,9 +1739,7 @@ class TestTenantService:
|
||||
member_name = fake.name()
|
||||
member_password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and accounts
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1818,9 +1780,7 @@ class TestTenantService:
|
||||
password = generate_valid_password(fake)
|
||||
invalid_action = "invalid_action_that_doesnt_exist"
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and account
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1853,9 +1813,7 @@ class TestTenantService:
|
||||
name = fake.name()
|
||||
password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and account
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1891,9 +1849,7 @@ class TestTenantService:
|
||||
member_name = fake.name()
|
||||
member_password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and accounts
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -1981,9 +1937,7 @@ class TestTenantService:
|
||||
name = fake.name()
|
||||
password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and account
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -2017,9 +1971,7 @@ class TestTenantService:
|
||||
non_member_name = fake.name()
|
||||
non_member_password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and accounts
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -2060,9 +2012,7 @@ class TestTenantService:
|
||||
member_name = fake.name()
|
||||
member_password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and accounts
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -2113,9 +2063,7 @@ class TestTenantService:
|
||||
member_name = fake.name()
|
||||
member_password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and accounts
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -2174,9 +2122,7 @@ class TestTenantService:
|
||||
member_name = fake.name()
|
||||
member_password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and accounts
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -2214,9 +2160,7 @@ class TestTenantService:
|
||||
tenant2_name = fake.company()
|
||||
tenant3_name = fake.company()
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create multiple tenants
|
||||
tenant1 = TenantService.create_tenant(name=tenant1_name, session=db_session_with_containers)
|
||||
@ -2241,9 +2185,7 @@ class TestTenantService:
|
||||
password = generate_valid_password(fake)
|
||||
workspace_name = fake.company()
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -2282,9 +2224,7 @@ class TestTenantService:
|
||||
existing_tenant_name = fake.company()
|
||||
new_workspace_name = fake.company()
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -2325,9 +2265,7 @@ class TestTenantService:
|
||||
password = generate_valid_password(fake)
|
||||
workspace_name = fake.company()
|
||||
# Setup mocks to disable workspace creation
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = False
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = False
|
||||
|
||||
# Create account
|
||||
account = AccountService.create_account(
|
||||
@ -2360,9 +2298,7 @@ class TestTenantService:
|
||||
normal_name = fake.name()
|
||||
normal_password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and accounts
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -2429,9 +2365,7 @@ class TestTenantService:
|
||||
normal_name = fake.name()
|
||||
normal_password = generate_valid_password(fake)
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant and accounts
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -2480,9 +2414,7 @@ class TestTenantService:
|
||||
theme = fake.random_element(elements=("dark", "light"))
|
||||
language = fake.random_element(elements=("zh-CN", "en-US"))
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Create tenant with custom config
|
||||
tenant = TenantService.create_tenant(name=tenant_name, session=db_session_with_containers)
|
||||
@ -2515,7 +2447,7 @@ class TestRegisterService:
|
||||
):
|
||||
# Setup default mock returns
|
||||
mock_feature_service.get_system_features.return_value.is_allow_register = True
|
||||
mock_feature_service.get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_feature_service.is_workspace_creation_allowed.return_value = True
|
||||
mock_feature_service.get_license.return_value.workspaces.is_available.return_value = True
|
||||
mock_feature_service.get_license.return_value.seats.is_available.return_value = True
|
||||
mock_billing_service.is_email_in_freeze.return_value = False
|
||||
@ -2629,9 +2561,7 @@ class TestRegisterService:
|
||||
language = fake.random_element(elements=("en-US", "zh-CN"))
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -2673,9 +2603,7 @@ class TestRegisterService:
|
||||
language = fake.random_element(elements=("en-US", "zh-CN"))
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -2722,9 +2650,7 @@ class TestRegisterService:
|
||||
language = fake.random_element(elements=("en-US", "zh-CN"))
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -2768,9 +2694,7 @@ class TestRegisterService:
|
||||
language = fake.random_element(elements=("en-US", "zh-CN"))
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = False
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = False
|
||||
mock_external_service_dependencies["billing_service"].is_email_in_freeze.return_value = False
|
||||
|
||||
# with pytest.raises(AccountRegisterError, match="Workspace is not allowed to create."):
|
||||
@ -2807,9 +2731,7 @@ class TestRegisterService:
|
||||
language = fake.random_element(elements=("en-US", "zh-CN"))
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = False
|
||||
@ -2886,9 +2808,7 @@ class TestRegisterService:
|
||||
language = fake.random_element(elements=("en-US", "zh-CN"))
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
|
||||
@ -285,7 +285,6 @@ class TestFeatureService:
|
||||
mock_config.ALLOW_REGISTER = False
|
||||
mock_config.ALLOW_CREATE_WORKSPACE = False
|
||||
mock_config.MAIL_TYPE = "smtp"
|
||||
mock_config.PLUGIN_MAX_PACKAGE_SIZE = 100
|
||||
|
||||
# Act: Execute the method under test
|
||||
result = FeatureService.get_system_features()
|
||||
@ -309,7 +308,6 @@ class TestFeatureService:
|
||||
assert result.enable_email_password_login is False
|
||||
assert result.enable_collaboration_mode is True
|
||||
assert result.is_allow_register is False
|
||||
assert result.is_allow_create_workspace is False
|
||||
|
||||
# Verify branding configuration
|
||||
assert result.branding.application_title == "Test Enterprise"
|
||||
@ -480,15 +478,11 @@ class TestFeatureService:
|
||||
assert result.enable_social_oauth_login is False
|
||||
assert result.enable_collaboration_mode is False
|
||||
assert result.is_allow_register is True
|
||||
assert result.is_allow_create_workspace is True
|
||||
assert result.is_email_setup is True
|
||||
|
||||
# Verify marketplace configuration
|
||||
assert result.enable_marketplace is False
|
||||
|
||||
# Verify plugin package size (uses default value from dify_config)
|
||||
assert result.max_plugin_package_size == 15728640
|
||||
|
||||
def test_get_features_billing_disabled(
|
||||
self, db_session_with_containers: Session, mock_external_service_dependencies
|
||||
):
|
||||
@ -683,15 +677,11 @@ class TestFeatureService:
|
||||
assert result.enable_email_password_login is True
|
||||
assert result.enable_social_oauth_login is True
|
||||
assert result.is_allow_register is False
|
||||
assert result.is_allow_create_workspace is False
|
||||
assert result.is_email_setup is False
|
||||
|
||||
# Verify marketplace configuration
|
||||
assert result.enable_marketplace is True
|
||||
|
||||
# Verify plugin package size (uses default value from dify_config)
|
||||
assert result.max_plugin_package_size == 15728640
|
||||
|
||||
# Verify default license status
|
||||
assert result.license.status == "none"
|
||||
assert not hasattr(result.license, "expired_at")
|
||||
@ -923,7 +913,6 @@ class TestFeatureService:
|
||||
assert result.enable_email_code_login is False
|
||||
assert result.enable_email_password_login is True
|
||||
assert result.is_allow_register is False
|
||||
assert result.is_allow_create_workspace is False
|
||||
|
||||
# Verify mock interactions
|
||||
mock_external_service_dependencies["enterprise_service"].get_info.assert_called_once()
|
||||
@ -1672,7 +1661,6 @@ class TestFeatureService:
|
||||
assert result.enable_email_code_login is False
|
||||
assert result.enable_email_password_login is True
|
||||
assert result.is_allow_register is False
|
||||
assert result.is_allow_create_workspace is False
|
||||
|
||||
# Verify mock interactions
|
||||
mock_external_service_dependencies["enterprise_service"].get_info.assert_called_once()
|
||||
@ -1851,7 +1839,6 @@ class TestFeatureService:
|
||||
assert result.enable_email_code_login is False
|
||||
assert result.enable_email_password_login is True
|
||||
assert result.is_allow_register is False
|
||||
assert result.is_allow_create_workspace is False
|
||||
assert result.license.status == LicenseStatus.LOST
|
||||
|
||||
# Verify mock interactions
|
||||
|
||||
@ -41,7 +41,7 @@ class TestWebhookService:
|
||||
|
||||
# Mock feature service
|
||||
mock_feature_service.get_system_features.return_value.is_allow_register = True
|
||||
mock_feature_service.get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_feature_service.is_workspace_creation_allowed.return_value = True
|
||||
|
||||
yield {
|
||||
"async_service": mock_async_service,
|
||||
|
||||
@ -112,8 +112,8 @@ def test_publish_blocks_start_and_trigger_coexistence(
|
||||
|
||||
monkeypatch.setattr(
|
||||
feature_service_module.FeatureService,
|
||||
"get_system_features",
|
||||
classmethod(lambda _cls: SimpleNamespace(plugin_manager=SimpleNamespace(enabled=False))),
|
||||
"is_plugin_manager_enabled",
|
||||
classmethod(lambda _cls: False),
|
||||
)
|
||||
monkeypatch.setattr("services.workflow_service.dify_config", SimpleNamespace(BILLING_ENABLED=False))
|
||||
|
||||
|
||||
@ -110,6 +110,26 @@ def test_generate_specs_writes_unique_operation_ids(tmp_path):
|
||||
assert len(operation_ids) == len(set(operation_ids))
|
||||
|
||||
|
||||
def test_system_features_specs_exclude_backend_only_fields(tmp_path):
|
||||
module = _load_generate_swagger_specs_module()
|
||||
|
||||
written_paths = module.generate_specs(tmp_path)
|
||||
excluded_fields = {
|
||||
"is_allow_create_workspace",
|
||||
"max_plugin_package_size",
|
||||
"plugin_manager",
|
||||
}
|
||||
|
||||
for spec_name in ("console-openapi.json", "web-openapi.json"):
|
||||
spec_path = next(path for path in written_paths if path.name == spec_name)
|
||||
payload = json.loads(spec_path.read_text(encoding="utf-8"))
|
||||
schemas = payload["components"]["schemas"]
|
||||
system_features_schema = schemas["SystemFeatureModel"]
|
||||
|
||||
assert excluded_fields.isdisjoint(system_features_schema["properties"])
|
||||
assert "PluginManagerModel" not in schemas
|
||||
|
||||
|
||||
def test_generate_specs_writes_get_operations_without_request_bodies(tmp_path):
|
||||
module = _load_generate_swagger_specs_module()
|
||||
|
||||
|
||||
@ -442,10 +442,10 @@ class TestEmailCodeLoginApi:
|
||||
@patch("controllers.console.auth.login.AccountService.revoke_email_code_login_token")
|
||||
@patch("controllers.console.auth.login.AccountService.get_user_through_email")
|
||||
@patch("controllers.console.auth.login.TenantService.get_join_tenants")
|
||||
@patch("controllers.console.auth.login.FeatureService.get_system_features")
|
||||
@patch("controllers.console.auth.login.FeatureService.is_workspace_creation_allowed")
|
||||
def test_email_code_login_creates_workspace_for_user_without_tenant(
|
||||
self,
|
||||
mock_get_features,
|
||||
mock_is_workspace_creation_allowed,
|
||||
mock_get_tenants,
|
||||
mock_get_user,
|
||||
mock_revoke_token,
|
||||
@ -465,10 +465,7 @@ class TestEmailCodeLoginApi:
|
||||
mock_get_data.return_value = {"email": "test@example.com", "code": "123456"}
|
||||
mock_get_user.return_value = mock_account
|
||||
mock_get_tenants.return_value = []
|
||||
mock_features = MagicMock()
|
||||
mock_features.is_allow_create_workspace = True
|
||||
mock_features.license.workspaces.is_available.return_value = True
|
||||
mock_get_features.return_value = mock_features
|
||||
mock_is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Act & Assert - Should not raise WorkspacesLimitExceeded
|
||||
with app.test_request_context(
|
||||
@ -486,10 +483,10 @@ class TestEmailCodeLoginApi:
|
||||
@patch("controllers.console.auth.login.AccountService.get_user_through_email")
|
||||
@patch("controllers.console.auth.login.TenantService.get_join_tenants")
|
||||
@patch("controllers.console.auth.login.FeatureService.get_license")
|
||||
@patch("controllers.console.auth.login.FeatureService.get_system_features")
|
||||
@patch("controllers.console.auth.login.FeatureService.is_workspace_creation_allowed")
|
||||
def test_email_code_login_workspace_limit_exceeded(
|
||||
self,
|
||||
mock_get_features,
|
||||
mock_is_workspace_creation_allowed,
|
||||
mock_get_license,
|
||||
mock_get_tenants,
|
||||
mock_get_user,
|
||||
@ -510,6 +507,7 @@ class TestEmailCodeLoginApi:
|
||||
mock_get_user.return_value = mock_account
|
||||
mock_get_tenants.return_value = []
|
||||
mock_get_license.return_value.workspaces.is_available.return_value = False
|
||||
mock_is_workspace_creation_allowed.return_value = True
|
||||
|
||||
# Act & Assert
|
||||
with app.test_request_context(
|
||||
@ -526,10 +524,10 @@ class TestEmailCodeLoginApi:
|
||||
@patch("controllers.console.auth.login.AccountService.revoke_email_code_login_token")
|
||||
@patch("controllers.console.auth.login.AccountService.get_user_through_email")
|
||||
@patch("controllers.console.auth.login.TenantService.get_join_tenants")
|
||||
@patch("controllers.console.auth.login.FeatureService.get_system_features")
|
||||
@patch("controllers.console.auth.login.FeatureService.is_workspace_creation_allowed")
|
||||
def test_email_code_login_workspace_creation_not_allowed(
|
||||
self,
|
||||
mock_get_features,
|
||||
mock_is_workspace_creation_allowed,
|
||||
mock_get_tenants,
|
||||
mock_get_user,
|
||||
mock_revoke_token,
|
||||
@ -548,9 +546,7 @@ class TestEmailCodeLoginApi:
|
||||
mock_get_data.return_value = {"email": "test@example.com", "code": "123456"}
|
||||
mock_get_user.return_value = mock_account
|
||||
mock_get_tenants.return_value = []
|
||||
mock_features = MagicMock()
|
||||
mock_features.is_allow_create_workspace = False
|
||||
mock_get_features.return_value = mock_features
|
||||
mock_is_workspace_creation_allowed.return_value = False
|
||||
|
||||
# Act & Assert
|
||||
with app.test_request_context(
|
||||
|
||||
@ -344,10 +344,10 @@ class TestLoginApi:
|
||||
@patch("controllers.console.auth.login.AccountService.authenticate")
|
||||
@patch("controllers.console.auth.login.TenantService.get_join_tenants")
|
||||
@patch("controllers.console.auth.login.FeatureService.get_license")
|
||||
@patch("controllers.console.auth.login.FeatureService.get_system_features")
|
||||
@patch("controllers.console.auth.login.FeatureService.is_workspace_creation_allowed")
|
||||
def test_login_fails_when_no_workspace_and_limit_exceeded(
|
||||
self,
|
||||
mock_get_features: MagicMock,
|
||||
mock_is_workspace_creation_allowed: MagicMock,
|
||||
mock_get_license: MagicMock,
|
||||
mock_get_tenants: MagicMock,
|
||||
mock_authenticate: MagicMock,
|
||||
@ -370,9 +370,7 @@ class TestLoginApi:
|
||||
mock_authenticate.return_value = mock_account
|
||||
mock_get_tenants.return_value = [] # No tenants
|
||||
|
||||
mock_features = MagicMock()
|
||||
mock_features.is_allow_create_workspace = True
|
||||
mock_get_features.return_value = mock_features
|
||||
mock_is_workspace_creation_allowed.return_value = True
|
||||
mock_get_license.return_value.workspaces.is_available.return_value = False
|
||||
|
||||
# Act & Assert
|
||||
|
||||
@ -646,7 +646,7 @@ class TestAccountGeneration:
|
||||
):
|
||||
mock_get_account.return_value = mock_account
|
||||
mock_tenant_service.get_join_tenants.return_value = []
|
||||
mock_feature_service.get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_feature_service.is_workspace_creation_allowed.return_value = True
|
||||
|
||||
with app.test_request_context(headers={"Accept-Language": "en-US,en;q=0.9"}):
|
||||
result, oauth_new_user = _generate_account("github", user_info)
|
||||
|
||||
@ -102,6 +102,7 @@ class TestSystemFeatureApi:
|
||||
result = api.get()
|
||||
|
||||
assert result == system_features.model_dump()
|
||||
assert result["is_allow_register"] is True
|
||||
assert result["enable_learn_app"] is True
|
||||
assert result["license"] == {"status": LicenseStatus.NONE}
|
||||
get_system_features.assert_called_once_with()
|
||||
|
||||
@ -7,27 +7,26 @@ from unittest.mock import MagicMock, patch
|
||||
from flask import Flask
|
||||
|
||||
from controllers.web.feature import SystemFeatureApi
|
||||
from enums.deployment_edition import DeploymentEdition
|
||||
from services.feature_service import SystemFeatureModel
|
||||
|
||||
|
||||
class TestSystemFeatureApi:
|
||||
@patch("controllers.web.feature.FeatureService.get_system_features")
|
||||
def test_returns_system_features(self, mock_features: MagicMock, app: Flask) -> None:
|
||||
mock_model = MagicMock()
|
||||
mock_model.model_dump.return_value = {"sso_enforced_for_signin": False, "webapp_auth": {"enabled": False}}
|
||||
mock_features.return_value = mock_model
|
||||
system_features = SystemFeatureModel(deployment_edition=DeploymentEdition.COMMUNITY)
|
||||
mock_features.return_value = system_features
|
||||
|
||||
with app.test_request_context("/system-features"):
|
||||
result = SystemFeatureApi().get()
|
||||
|
||||
assert result == {"sso_enforced_for_signin": False, "webapp_auth": {"enabled": False}}
|
||||
assert result == system_features.model_dump()
|
||||
mock_features.assert_called_once()
|
||||
|
||||
@patch("controllers.web.feature.FeatureService.get_system_features")
|
||||
def test_unauthenticated_access(self, mock_features: MagicMock, app: Flask) -> None:
|
||||
"""SystemFeatureApi is unauthenticated by design — no WebApiResource decorator."""
|
||||
mock_model = MagicMock()
|
||||
mock_model.model_dump.return_value = {}
|
||||
mock_features.return_value = mock_model
|
||||
mock_features.return_value = SystemFeatureModel(deployment_edition=DeploymentEdition.COMMUNITY)
|
||||
|
||||
# Verify it's a bare Resource, not WebApiResource
|
||||
from flask_restx import Resource
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
@ -51,8 +50,8 @@ def test_check_credential_policy_compliance_returns_when_feature_disabled(
|
||||
mocker: MockerFixture,
|
||||
) -> None:
|
||||
mocker.patch(
|
||||
"services.feature_service.FeatureService.get_system_features",
|
||||
return_value=SimpleNamespace(plugin_manager=SimpleNamespace(enabled=False)),
|
||||
"services.feature_service.FeatureService.is_plugin_manager_enabled",
|
||||
return_value=False,
|
||||
)
|
||||
check_call = mocker.patch(
|
||||
"services.enterprise.plugin_manager_service.PluginManagerService.check_credential_policy_compliance"
|
||||
@ -67,8 +66,8 @@ def test_check_credential_policy_compliance_raises_when_credential_missing(
|
||||
mocker: MockerFixture,
|
||||
) -> None:
|
||||
mocker.patch(
|
||||
"services.feature_service.FeatureService.get_system_features",
|
||||
return_value=SimpleNamespace(plugin_manager=SimpleNamespace(enabled=True)),
|
||||
"services.feature_service.FeatureService.is_plugin_manager_enabled",
|
||||
return_value=True,
|
||||
)
|
||||
mocker.patch("core.helper.credential_utils.is_credential_exists", return_value=False)
|
||||
|
||||
@ -80,8 +79,8 @@ def test_check_credential_policy_compliance_calls_plugin_manager_with_request(
|
||||
mocker: MockerFixture,
|
||||
) -> None:
|
||||
mocker.patch(
|
||||
"services.feature_service.FeatureService.get_system_features",
|
||||
return_value=SimpleNamespace(plugin_manager=SimpleNamespace(enabled=True)),
|
||||
"services.feature_service.FeatureService.is_plugin_manager_enabled",
|
||||
return_value=True,
|
||||
)
|
||||
mocker.patch("core.helper.credential_utils.is_credential_exists", return_value=True)
|
||||
check_call = mocker.patch(
|
||||
@ -101,8 +100,8 @@ def test_check_credential_policy_compliance_skips_existence_check_when_disabled(
|
||||
mocker: MockerFixture,
|
||||
) -> None:
|
||||
mocker.patch(
|
||||
"services.feature_service.FeatureService.get_system_features",
|
||||
return_value=SimpleNamespace(plugin_manager=SimpleNamespace(enabled=True)),
|
||||
"services.feature_service.FeatureService.is_plugin_manager_enabled",
|
||||
return_value=True,
|
||||
)
|
||||
exists_call = mocker.patch("core.helper.credential_utils.is_credential_exists")
|
||||
check_call = mocker.patch(
|
||||
@ -124,8 +123,8 @@ def test_check_credential_policy_compliance_returns_when_credential_id_empty(
|
||||
mocker: MockerFixture,
|
||||
) -> None:
|
||||
mocker.patch(
|
||||
"services.feature_service.FeatureService.get_system_features",
|
||||
return_value=SimpleNamespace(plugin_manager=SimpleNamespace(enabled=True)),
|
||||
"services.feature_service.FeatureService.is_plugin_manager_enabled",
|
||||
return_value=True,
|
||||
)
|
||||
exists_call = mocker.patch("core.helper.credential_utils.is_credential_exists")
|
||||
check_call = mocker.patch(
|
||||
|
||||
@ -803,9 +803,7 @@ class TestTenantService:
|
||||
"""Creating an owner workspace persists both the tenant and owner membership."""
|
||||
mock_account = TestAccountAssociatedDataFactory.create_account_mock()
|
||||
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -1023,9 +1021,7 @@ class TestTenantService:
|
||||
self, sqlite_session: Session, mock_external_service_dependencies
|
||||
):
|
||||
mock_account = TestAccountAssociatedDataFactory.create_account_mock(account_id="user-rbac", name="RBAC User")
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -1476,9 +1472,7 @@ class TestRegisterService:
|
||||
"""Test successful account registration."""
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -1585,9 +1579,7 @@ class TestRegisterService:
|
||||
|
||||
monkeypatch.setattr(dify_config, "ENTERPRISE_ENABLED", True, raising=False)
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -1624,9 +1616,7 @@ class TestRegisterService:
|
||||
|
||||
monkeypatch.setattr(dify_config, "ENTERPRISE_ENABLED", True, raising=False)
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -1659,9 +1649,7 @@ class TestRegisterService:
|
||||
"""Test account registration with OAuth integration."""
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -1703,9 +1691,7 @@ class TestRegisterService:
|
||||
"""Test account registration with pending status."""
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
@ -1745,9 +1731,7 @@ class TestRegisterService:
|
||||
"""Test registration when workspace creation is not allowed."""
|
||||
# Setup mocks
|
||||
mock_external_service_dependencies["feature_service"].get_system_features.return_value.is_allow_register = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_system_features.return_value.is_allow_create_workspace = True
|
||||
mock_external_service_dependencies["feature_service"].is_workspace_creation_allowed.return_value = True
|
||||
mock_external_service_dependencies[
|
||||
"feature_service"
|
||||
].get_license.return_value.workspaces.is_available.return_value = True
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
import pytest
|
||||
|
||||
from services.feature_service import FeatureService
|
||||
|
||||
|
||||
def test_workspace_creation_uses_environment_policy(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr("services.feature_service.dify_config.ENTERPRISE_ENABLED", False)
|
||||
monkeypatch.setattr("services.feature_service.dify_config.ALLOW_CREATE_WORKSPACE", True)
|
||||
monkeypatch.setattr(
|
||||
"services.feature_service.EnterpriseService.get_info",
|
||||
lambda: (_ for _ in ()).throw(AssertionError("enterprise API should not be called")),
|
||||
)
|
||||
|
||||
assert FeatureService.is_workspace_creation_allowed() is True
|
||||
|
||||
|
||||
def test_workspace_creation_uses_enterprise_policy(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr("services.feature_service.dify_config.ENTERPRISE_ENABLED", True)
|
||||
monkeypatch.setattr(
|
||||
"services.feature_service.EnterpriseService.get_info",
|
||||
lambda: {"IsAllowCreateWorkspace": False},
|
||||
)
|
||||
|
||||
assert FeatureService.is_workspace_creation_allowed() is False
|
||||
|
||||
|
||||
def test_workspace_creation_keeps_environment_policy_when_enterprise_value_is_missing(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr("services.feature_service.dify_config.ENTERPRISE_ENABLED", True)
|
||||
monkeypatch.setattr("services.feature_service.dify_config.ALLOW_CREATE_WORKSPACE", True)
|
||||
monkeypatch.setattr("services.feature_service.EnterpriseService.get_info", lambda: {})
|
||||
|
||||
assert FeatureService.is_workspace_creation_allowed() is True
|
||||
|
||||
|
||||
def test_plugin_manager_is_enabled_only_for_enterprise(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr("services.feature_service.dify_config.ENTERPRISE_ENABLED", True)
|
||||
assert FeatureService.is_plugin_manager_enabled() is True
|
||||
|
||||
monkeypatch.setattr("services.feature_service.dify_config.ENTERPRISE_ENABLED", False)
|
||||
assert FeatureService.is_plugin_manager_enabled() is False
|
||||
@ -28,26 +28,21 @@ export const license = {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get system-wide feature configuration
|
||||
* Get the non-sensitive bootstrap snapshot exposed before authentication
|
||||
*
|
||||
* Get system-wide feature configuration
|
||||
* NOTE: This endpoint is unauthenticated by design, as it provides system features
|
||||
* data required for dashboard initialization.
|
||||
*
|
||||
* Authentication would create circular dependency (can't login without dashboard loading).
|
||||
*
|
||||
* Only non-sensitive configuration data should be returned by this endpoint. Authenticated
|
||||
* license detail is served separately by SystemFeatureLicenseApi.
|
||||
* Get the non-sensitive bootstrap snapshot exposed before Console or Web authentication. This is not a general feature registry.
|
||||
* Authentication configuration must be available before the authentication flow can be selected.
|
||||
* Authenticated license detail is served separately by SystemFeatureLicenseApi.
|
||||
*/
|
||||
export const get2 = oc
|
||||
.route({
|
||||
description:
|
||||
"Get system-wide feature configuration\nNOTE: This endpoint is unauthenticated by design, as it provides system features\ndata required for dashboard initialization.\n\nAuthentication would create circular dependency (can't login without dashboard loading).\n\nOnly non-sensitive configuration data should be returned by this endpoint. Authenticated\nlicense detail is served separately by SystemFeatureLicenseApi.",
|
||||
'Get the non-sensitive bootstrap snapshot exposed before Console or Web authentication. This is not a general feature registry.\nAuthentication configuration must be available before the authentication flow can be selected.\nAuthenticated license detail is served separately by SystemFeatureLicenseApi.',
|
||||
inputStructure: 'detailed',
|
||||
method: 'GET',
|
||||
operationId: 'getSystemFeatures',
|
||||
path: '/system-features',
|
||||
summary: 'Get system-wide feature configuration',
|
||||
summary: 'Get the non-sensitive bootstrap snapshot exposed before authentication',
|
||||
tags: ['console'],
|
||||
})
|
||||
.output(zGetSystemFeaturesResponse)
|
||||
|
||||
@ -19,14 +19,11 @@ export type SystemFeatureModel = {
|
||||
enable_social_oauth_login: boolean
|
||||
enable_step_by_step_tour: boolean
|
||||
enable_trial_app: boolean
|
||||
is_allow_create_workspace: boolean
|
||||
is_allow_register: boolean
|
||||
is_email_setup: boolean
|
||||
knowledge_fs_enabled: boolean
|
||||
license: LicenseStatusModel
|
||||
max_plugin_package_size: number
|
||||
plugin_installation_permission: PluginInstallationPermissionModel
|
||||
plugin_manager: PluginManagerModel
|
||||
rbac_enabled: boolean
|
||||
sso_enforced_for_signin: boolean
|
||||
sso_enforced_for_signin_protocol: string
|
||||
@ -59,10 +56,6 @@ export type PluginInstallationPermissionModel = {
|
||||
restrict_to_marketplace_only: boolean
|
||||
}
|
||||
|
||||
export type PluginManagerModel = {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export type WebAppAuthModel = {
|
||||
allow_email_code_login: boolean
|
||||
allow_email_password_login: boolean
|
||||
|
||||
@ -20,13 +20,6 @@ export const zBrandingModel = z.object({
|
||||
*/
|
||||
export const zDeploymentEdition = z.enum(['CLOUD', 'COMMUNITY', 'ENTERPRISE'])
|
||||
|
||||
/**
|
||||
* PluginManagerModel
|
||||
*/
|
||||
export const zPluginManagerModel = z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
})
|
||||
|
||||
/**
|
||||
* LicenseLimitationModel
|
||||
*
|
||||
@ -109,6 +102,8 @@ export const zWebAppAuthModel = z.object({
|
||||
|
||||
/**
|
||||
* SystemFeatureModel
|
||||
*
|
||||
* Non-sensitive bootstrap snapshot exposed before Console or Web authentication.
|
||||
*/
|
||||
export const zSystemFeatureModel = z.object({
|
||||
branding: zBrandingModel.default({
|
||||
@ -131,17 +126,14 @@ export const zSystemFeatureModel = z.object({
|
||||
enable_social_oauth_login: z.boolean().default(false),
|
||||
enable_step_by_step_tour: z.boolean().default(false),
|
||||
enable_trial_app: z.boolean().default(false),
|
||||
is_allow_create_workspace: z.boolean().default(false),
|
||||
is_allow_register: z.boolean().default(false),
|
||||
is_email_setup: z.boolean().default(false),
|
||||
knowledge_fs_enabled: z.boolean().default(false),
|
||||
license: zLicenseStatusModel.default({ status: 'none' }),
|
||||
max_plugin_package_size: z.int().default(15728640),
|
||||
plugin_installation_permission: zPluginInstallationPermissionModel.default({
|
||||
plugin_installation_scope: 'all',
|
||||
restrict_to_marketplace_only: false,
|
||||
}),
|
||||
plugin_manager: zPluginManagerModel.default({ enabled: false }),
|
||||
rbac_enabled: z.boolean().default(false),
|
||||
sso_enforced_for_signin: z.boolean().default(false),
|
||||
sso_enforced_for_signin_protocol: z.string().default(''),
|
||||
|
||||
@ -939,34 +939,23 @@ export const site = {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get system feature flags and configuration
|
||||
*
|
||||
* Get system feature flags and configuration
|
||||
* Returns the current system feature flags and configuration
|
||||
* that control various functionalities across the platform.
|
||||
*
|
||||
* Returns:
|
||||
* dict: System feature configuration object
|
||||
* Get the non-sensitive bootstrap snapshot exposed before authentication
|
||||
*
|
||||
* Get the non-sensitive bootstrap snapshot exposed before Console or Web authentication. This is not a general feature registry.
|
||||
* This endpoint is akin to the `SystemFeatureApi` endpoint in api/controllers/console/feature.py,
|
||||
* except it is intended for use by the web app, instead of the console dashboard.
|
||||
*
|
||||
* NOTE: This endpoint is unauthenticated by design, as it provides system features
|
||||
* data required for webapp initialization.
|
||||
*
|
||||
* Authentication would create circular dependency (can't authenticate without webapp loading).
|
||||
*
|
||||
* Only non-sensitive configuration data should be returned by this endpoint.
|
||||
* Authentication configuration must be available before the authentication flow can be selected.
|
||||
*/
|
||||
export const get13 = oc
|
||||
.route({
|
||||
description:
|
||||
"Get system feature flags and configuration\nReturns the current system feature flags and configuration\nthat control various functionalities across the platform.\n\nReturns:\n dict: System feature configuration object\n\nThis endpoint is akin to the `SystemFeatureApi` endpoint in api/controllers/console/feature.py,\nexcept it is intended for use by the web app, instead of the console dashboard.\n\nNOTE: This endpoint is unauthenticated by design, as it provides system features\ndata required for webapp initialization.\n\nAuthentication would create circular dependency (can't authenticate without webapp loading).\n\nOnly non-sensitive configuration data should be returned by this endpoint.",
|
||||
'Get the non-sensitive bootstrap snapshot exposed before Console or Web authentication. This is not a general feature registry.\nThis endpoint is akin to the `SystemFeatureApi` endpoint in api/controllers/console/feature.py,\nexcept it is intended for use by the web app, instead of the console dashboard.\n\nAuthentication configuration must be available before the authentication flow can be selected.',
|
||||
inputStructure: 'detailed',
|
||||
method: 'GET',
|
||||
operationId: 'getSystemFeatures',
|
||||
path: '/system-features',
|
||||
summary: 'Get system feature flags and configuration',
|
||||
summary: 'Get the non-sensitive bootstrap snapshot exposed before authentication',
|
||||
tags: ['web'],
|
||||
})
|
||||
.output(zGetSystemFeaturesResponse)
|
||||
|
||||
@ -390,10 +390,6 @@ export type PluginInstallationScope =
|
||||
| 'official_and_specific_partners'
|
||||
| 'official_only'
|
||||
|
||||
export type PluginManagerModel = {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export type RemoteFileInfo = {
|
||||
file_length: number
|
||||
file_type: string
|
||||
@ -516,14 +512,11 @@ export type SystemFeatureModel = {
|
||||
enable_social_oauth_login: boolean
|
||||
enable_step_by_step_tour: boolean
|
||||
enable_trial_app: boolean
|
||||
is_allow_create_workspace: boolean
|
||||
is_allow_register: boolean
|
||||
is_email_setup: boolean
|
||||
knowledge_fs_enabled: boolean
|
||||
license: LicenseStatusModel
|
||||
max_plugin_package_size: number
|
||||
plugin_installation_permission: PluginInstallationPermissionModel
|
||||
plugin_manager: PluginManagerModel
|
||||
rbac_enabled: boolean
|
||||
sso_enforced_for_signin: boolean
|
||||
sso_enforced_for_signin_protocol: string
|
||||
|
||||
@ -454,13 +454,6 @@ export const zPluginInstallationPermissionModel = z.object({
|
||||
restrict_to_marketplace_only: z.boolean().default(false),
|
||||
})
|
||||
|
||||
/**
|
||||
* PluginManagerModel
|
||||
*/
|
||||
export const zPluginManagerModel = z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
})
|
||||
|
||||
/**
|
||||
* RemoteFileInfo
|
||||
*/
|
||||
@ -756,6 +749,8 @@ export const zWebAppAuthModel = z.object({
|
||||
|
||||
/**
|
||||
* SystemFeatureModel
|
||||
*
|
||||
* Non-sensitive bootstrap snapshot exposed before Console or Web authentication.
|
||||
*/
|
||||
export const zSystemFeatureModel = z.object({
|
||||
branding: zBrandingModel.default({
|
||||
@ -778,17 +773,14 @@ export const zSystemFeatureModel = z.object({
|
||||
enable_social_oauth_login: z.boolean().default(false),
|
||||
enable_step_by_step_tour: z.boolean().default(false),
|
||||
enable_trial_app: z.boolean().default(false),
|
||||
is_allow_create_workspace: z.boolean().default(false),
|
||||
is_allow_register: z.boolean().default(false),
|
||||
is_email_setup: z.boolean().default(false),
|
||||
knowledge_fs_enabled: z.boolean().default(false),
|
||||
license: zLicenseStatusModel.default({ status: 'none' }),
|
||||
max_plugin_package_size: z.int().default(15728640),
|
||||
plugin_installation_permission: zPluginInstallationPermissionModel.default({
|
||||
plugin_installation_scope: 'all',
|
||||
restrict_to_marketplace_only: false,
|
||||
}),
|
||||
plugin_manager: zPluginManagerModel.default({ enabled: false }),
|
||||
rbac_enabled: z.boolean().default(false),
|
||||
sso_enforced_for_signin: z.boolean().default(false),
|
||||
sso_enforced_for_signin_protocol: z.string().default(''),
|
||||
|
||||
@ -21,11 +21,9 @@ const baseSystemFeatures = {
|
||||
enable_email_password_login: true,
|
||||
enable_social_oauth_login: false,
|
||||
enable_collaboration_mode: true,
|
||||
is_allow_create_workspace: false,
|
||||
is_allow_register: false,
|
||||
is_email_setup: false,
|
||||
enable_change_email: true,
|
||||
max_plugin_package_size: 15728640,
|
||||
license: {
|
||||
status: LicenseStatus.NONE,
|
||||
},
|
||||
@ -50,9 +48,6 @@ const baseSystemFeatures = {
|
||||
plugin_installation_scope: InstallationScope.ALL,
|
||||
restrict_to_marketplace_only: false,
|
||||
},
|
||||
plugin_manager: {
|
||||
enabled: false,
|
||||
},
|
||||
rbac_enabled: false,
|
||||
enable_creators_platform: false,
|
||||
enable_trial_app: false,
|
||||
@ -102,10 +97,6 @@ export const createSystemFeaturesFixture = (
|
||||
...baseSystemFeatures.license,
|
||||
...overrides.license,
|
||||
},
|
||||
plugin_manager: {
|
||||
...baseSystemFeatures.plugin_manager,
|
||||
...overrides.plugin_manager,
|
||||
},
|
||||
})
|
||||
|
||||
export const createSystemFeaturesLicenseFixture = (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user