mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-26 13:44:41 +00:00
Resolve "Allow to create sub domains"
This commit is contained in:
parent
645eb09ed4
commit
55533d021e
41 changed files with 865 additions and 175 deletions
backend
src/baserow
tests/baserow/contrib/builder
docs/installation
web-frontend/modules
builder
core
|
@ -1184,6 +1184,11 @@ if POSTHOG_ENABLED:
|
||||||
else:
|
else:
|
||||||
posthog.disabled = True
|
posthog.disabled = True
|
||||||
|
|
||||||
|
BASEROW_BUILDER_DOMAINS = os.getenv("BASEROW_BUILDER_DOMAINS", None)
|
||||||
|
BASEROW_BUILDER_DOMAINS = (
|
||||||
|
BASEROW_BUILDER_DOMAINS.split(",") if BASEROW_BUILDER_DOMAINS is not None else []
|
||||||
|
)
|
||||||
|
|
||||||
# Indicates whether we are running the tests or not. Set to True in the test.py settings
|
# Indicates whether we are running the tests or not. Set to True in the test.py settings
|
||||||
# file used by pytest.ini
|
# file used by pytest.ini
|
||||||
TESTS = False
|
TESTS = False
|
||||||
|
|
|
@ -11,3 +11,16 @@ ERROR_DOMAIN_NOT_IN_BUILDER = (
|
||||||
HTTP_400_BAD_REQUEST,
|
HTTP_400_BAD_REQUEST,
|
||||||
"The domain id {e.domain_id} does not belong to the builder.",
|
"The domain id {e.domain_id} does not belong to the builder.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ERROR_DOMAIN_NAME_NOT_UNIQUE = (
|
||||||
|
"ERROR_DOMAIN_NAME_NOT_UNIQUE",
|
||||||
|
HTTP_400_BAD_REQUEST,
|
||||||
|
"The domain name {e.domain_name} already exists.",
|
||||||
|
)
|
||||||
|
|
||||||
|
ERROR_SUB_DOMAIN_HAS_INVALID_DOMAIN_NAME = (
|
||||||
|
"ERROR_SUB_DOMAIN_HAS_INVALID_DOMAIN_NAME",
|
||||||
|
HTTP_400_BAD_REQUEST,
|
||||||
|
"The subdomain {e.domain_name} has an invalid domain name, "
|
||||||
|
"you can only use {e.available_domain_names}",
|
||||||
|
)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
from django.utils.functional import lazy
|
||||||
|
|
||||||
from drf_spectacular.types import OpenApiTypes
|
from drf_spectacular.types import OpenApiTypes
|
||||||
from drf_spectacular.utils import extend_schema_field
|
from drf_spectacular.utils import extend_schema_field
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
@ -8,6 +10,7 @@ from baserow.api.services.serializers import PublicServiceSerializer
|
||||||
from baserow.contrib.builder.api.pages.serializers import PathParamSerializer
|
from baserow.contrib.builder.api.pages.serializers import PathParamSerializer
|
||||||
from baserow.contrib.builder.data_sources.models import DataSource
|
from baserow.contrib.builder.data_sources.models import DataSource
|
||||||
from baserow.contrib.builder.domains.models import Domain
|
from baserow.contrib.builder.domains.models import Domain
|
||||||
|
from baserow.contrib.builder.domains.registries import domain_type_registry
|
||||||
from baserow.contrib.builder.elements.models import Element
|
from baserow.contrib.builder.elements.models import Element
|
||||||
from baserow.contrib.builder.elements.registries import element_type_registry
|
from baserow.contrib.builder.elements.registries import element_type_registry
|
||||||
from baserow.contrib.builder.models import Builder
|
from baserow.contrib.builder.models import Builder
|
||||||
|
@ -16,9 +19,15 @@ from baserow.core.services.registries import service_type_registry
|
||||||
|
|
||||||
|
|
||||||
class DomainSerializer(serializers.ModelSerializer):
|
class DomainSerializer(serializers.ModelSerializer):
|
||||||
|
type = serializers.SerializerMethodField(help_text="The type of the domain.")
|
||||||
|
|
||||||
|
@extend_schema_field(OpenApiTypes.STR)
|
||||||
|
def get_type(self, instance):
|
||||||
|
return domain_type_registry.get_by_model(instance.specific_class).type
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Domain
|
model = Domain
|
||||||
fields = ("id", "domain_name", "order", "builder_id", "last_published")
|
fields = ("id", "type", "domain_name", "order", "builder_id", "last_published")
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"id": {"read_only": True},
|
"id": {"read_only": True},
|
||||||
"builder_id": {"read_only": True},
|
"builder_id": {"read_only": True},
|
||||||
|
@ -27,9 +36,37 @@ class DomainSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class CreateDomainSerializer(serializers.ModelSerializer):
|
class CreateDomainSerializer(serializers.ModelSerializer):
|
||||||
|
type = serializers.ChoiceField(
|
||||||
|
choices=lazy(domain_type_registry.get_types, list)(),
|
||||||
|
required=True,
|
||||||
|
help_text="The type of the domain.",
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Domain
|
model = Domain
|
||||||
fields = ("domain_name",)
|
fields = (
|
||||||
|
"type",
|
||||||
|
"domain_name",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateDomainSerializer(serializers.ModelSerializer):
|
||||||
|
type = serializers.ChoiceField(
|
||||||
|
choices=lazy(domain_type_registry.get_types, list)(),
|
||||||
|
required=False,
|
||||||
|
help_text="The type of the domain.",
|
||||||
|
)
|
||||||
|
|
||||||
|
domain_name = serializers.CharField(
|
||||||
|
required=False, help_text=Domain._meta.get_field("domain_name").help_text
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Domain
|
||||||
|
fields = (
|
||||||
|
"type",
|
||||||
|
"domain_name",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OrderDomainsSerializer(serializers.Serializer):
|
class OrderDomainsSerializer(serializers.Serializer):
|
||||||
|
|
|
@ -10,29 +10,43 @@ from rest_framework.status import HTTP_202_ACCEPTED
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from baserow.api.applications.errors import ERROR_APPLICATION_DOES_NOT_EXIST
|
from baserow.api.applications.errors import ERROR_APPLICATION_DOES_NOT_EXIST
|
||||||
from baserow.api.decorators import map_exceptions, validate_body
|
from baserow.api.decorators import (
|
||||||
|
map_exceptions,
|
||||||
|
validate_body,
|
||||||
|
validate_body_custom_fields,
|
||||||
|
)
|
||||||
from baserow.api.jobs.serializers import JobSerializer
|
from baserow.api.jobs.serializers import JobSerializer
|
||||||
from baserow.api.schemas import CLIENT_SESSION_ID_SCHEMA_PARAMETER, get_error_schema
|
from baserow.api.schemas import CLIENT_SESSION_ID_SCHEMA_PARAMETER, get_error_schema
|
||||||
from baserow.api.utils import DiscriminatorCustomFieldsMappingSerializer
|
from baserow.api.utils import (
|
||||||
|
DiscriminatorCustomFieldsMappingSerializer,
|
||||||
|
type_from_data_or_registry,
|
||||||
|
validate_data_custom_fields,
|
||||||
|
)
|
||||||
from baserow.contrib.builder.api.data_sources.serializers import DataSourceSerializer
|
from baserow.contrib.builder.api.data_sources.serializers import DataSourceSerializer
|
||||||
from baserow.contrib.builder.api.domains.errors import (
|
from baserow.contrib.builder.api.domains.errors import (
|
||||||
ERROR_DOMAIN_DOES_NOT_EXIST,
|
ERROR_DOMAIN_DOES_NOT_EXIST,
|
||||||
|
ERROR_DOMAIN_NAME_NOT_UNIQUE,
|
||||||
ERROR_DOMAIN_NOT_IN_BUILDER,
|
ERROR_DOMAIN_NOT_IN_BUILDER,
|
||||||
|
ERROR_SUB_DOMAIN_HAS_INVALID_DOMAIN_NAME,
|
||||||
)
|
)
|
||||||
from baserow.contrib.builder.api.domains.serializers import (
|
from baserow.contrib.builder.api.domains.serializers import (
|
||||||
CreateDomainSerializer,
|
CreateDomainSerializer,
|
||||||
DomainSerializer,
|
DomainSerializer,
|
||||||
OrderDomainsSerializer,
|
OrderDomainsSerializer,
|
||||||
PublicBuilderSerializer,
|
PublicBuilderSerializer,
|
||||||
|
UpdateDomainSerializer,
|
||||||
)
|
)
|
||||||
from baserow.contrib.builder.api.pages.errors import ERROR_PAGE_DOES_NOT_EXIST
|
from baserow.contrib.builder.api.pages.errors import ERROR_PAGE_DOES_NOT_EXIST
|
||||||
from baserow.contrib.builder.data_sources.service import DataSourceService
|
from baserow.contrib.builder.data_sources.service import DataSourceService
|
||||||
from baserow.contrib.builder.domains.exceptions import (
|
from baserow.contrib.builder.domains.exceptions import (
|
||||||
DomainDoesNotExist,
|
DomainDoesNotExist,
|
||||||
|
DomainNameNotUniqueError,
|
||||||
DomainNotInBuilder,
|
DomainNotInBuilder,
|
||||||
|
SubDomainHasInvalidDomainName,
|
||||||
)
|
)
|
||||||
from baserow.contrib.builder.domains.handler import DomainHandler
|
from baserow.contrib.builder.domains.handler import DomainHandler
|
||||||
from baserow.contrib.builder.domains.models import Domain
|
from baserow.contrib.builder.domains.models import Domain
|
||||||
|
from baserow.contrib.builder.domains.registries import domain_type_registry
|
||||||
from baserow.contrib.builder.domains.service import DomainService
|
from baserow.contrib.builder.domains.service import DomainService
|
||||||
from baserow.contrib.builder.elements.registries import element_type_registry
|
from baserow.contrib.builder.elements.registries import element_type_registry
|
||||||
from baserow.contrib.builder.elements.service import ElementService
|
from baserow.contrib.builder.elements.service import ElementService
|
||||||
|
@ -66,9 +80,13 @@ class DomainsView(APIView):
|
||||||
tags=["Builder domains"],
|
tags=["Builder domains"],
|
||||||
operation_id="create_builder_domain",
|
operation_id="create_builder_domain",
|
||||||
description="Creates a new domain for an application builder",
|
description="Creates a new domain for an application builder",
|
||||||
request=CreateDomainSerializer,
|
request=DiscriminatorCustomFieldsMappingSerializer(
|
||||||
|
domain_type_registry, CreateDomainSerializer, request=True
|
||||||
|
),
|
||||||
responses={
|
responses={
|
||||||
200: DomainSerializer,
|
200: DiscriminatorCustomFieldsMappingSerializer(
|
||||||
|
domain_type_registry, DomainSerializer
|
||||||
|
),
|
||||||
400: get_error_schema(
|
400: get_error_schema(
|
||||||
[
|
[
|
||||||
"ERROR_USER_NOT_IN_GROUP",
|
"ERROR_USER_NOT_IN_GROUP",
|
||||||
|
@ -82,17 +100,22 @@ class DomainsView(APIView):
|
||||||
@map_exceptions(
|
@map_exceptions(
|
||||||
{
|
{
|
||||||
ApplicationDoesNotExist: ERROR_APPLICATION_DOES_NOT_EXIST,
|
ApplicationDoesNotExist: ERROR_APPLICATION_DOES_NOT_EXIST,
|
||||||
|
SubDomainHasInvalidDomainName: ERROR_SUB_DOMAIN_HAS_INVALID_DOMAIN_NAME,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@validate_body(CreateDomainSerializer)
|
@validate_body_custom_fields(
|
||||||
|
domain_type_registry, base_serializer_class=CreateDomainSerializer
|
||||||
|
)
|
||||||
def post(self, request, data: Dict, builder_id: int):
|
def post(self, request, data: Dict, builder_id: int):
|
||||||
builder = BuilderHandler().get_builder(builder_id)
|
builder = BuilderHandler().get_builder(builder_id)
|
||||||
|
type_name = data.pop("type")
|
||||||
|
|
||||||
|
domain_type = domain_type_registry.get(type_name)
|
||||||
domain = DomainService().create_domain(
|
domain = DomainService().create_domain(
|
||||||
request.user, builder, data["domain_name"]
|
request.user, domain_type, builder, **data
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = DomainSerializer(domain)
|
serializer = domain_type_registry.get_serializer(domain, DomainSerializer)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
|
@ -109,7 +132,9 @@ class DomainsView(APIView):
|
||||||
operation_id="get_builder_domains",
|
operation_id="get_builder_domains",
|
||||||
description="Gets all the domains of a builder",
|
description="Gets all the domains of a builder",
|
||||||
responses={
|
responses={
|
||||||
200: DomainSerializer(many=True),
|
200: DiscriminatorCustomFieldsMappingSerializer(
|
||||||
|
domain_type_registry, DomainSerializer, many=True
|
||||||
|
),
|
||||||
400: get_error_schema(
|
400: get_error_schema(
|
||||||
[
|
[
|
||||||
"ERROR_USER_NOT_IN_GROUP",
|
"ERROR_USER_NOT_IN_GROUP",
|
||||||
|
@ -132,9 +157,12 @@ class DomainsView(APIView):
|
||||||
builder,
|
builder,
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = DomainSerializer(domains, many=True)
|
data = [
|
||||||
|
domain_type_registry.get_serializer(domain, DomainSerializer).data
|
||||||
|
for domain in domains
|
||||||
|
]
|
||||||
|
|
||||||
return Response(serializer.data)
|
return Response(data)
|
||||||
|
|
||||||
|
|
||||||
class DomainView(APIView):
|
class DomainView(APIView):
|
||||||
|
@ -151,9 +179,11 @@ class DomainView(APIView):
|
||||||
tags=["Builder domains"],
|
tags=["Builder domains"],
|
||||||
operation_id="update_builder_domain",
|
operation_id="update_builder_domain",
|
||||||
description="Updates an existing domain of an application builder",
|
description="Updates an existing domain of an application builder",
|
||||||
request=CreateDomainSerializer,
|
request=UpdateDomainSerializer,
|
||||||
responses={
|
responses={
|
||||||
200: DomainSerializer,
|
200: DiscriminatorCustomFieldsMappingSerializer(
|
||||||
|
domain_type_registry, DomainSerializer
|
||||||
|
),
|
||||||
400: get_error_schema(
|
400: get_error_schema(
|
||||||
[
|
[
|
||||||
"ERROR_USER_NOT_IN_GROUP",
|
"ERROR_USER_NOT_IN_GROUP",
|
||||||
|
@ -167,19 +197,35 @@ class DomainView(APIView):
|
||||||
@map_exceptions(
|
@map_exceptions(
|
||||||
{
|
{
|
||||||
DomainDoesNotExist: ERROR_DOMAIN_DOES_NOT_EXIST,
|
DomainDoesNotExist: ERROR_DOMAIN_DOES_NOT_EXIST,
|
||||||
|
DomainNameNotUniqueError: ERROR_DOMAIN_NAME_NOT_UNIQUE,
|
||||||
|
SubDomainHasInvalidDomainName: ERROR_SUB_DOMAIN_HAS_INVALID_DOMAIN_NAME,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@validate_body(CreateDomainSerializer)
|
def patch(self, request, domain_id: int):
|
||||||
def patch(self, request, data: Dict, domain_id: int):
|
base_queryset = Domain.objects
|
||||||
base_queryset = Domain.objects.select_for_update(of=("self",))
|
|
||||||
|
|
||||||
domain = DomainService().get_domain(
|
domain = (
|
||||||
request.user, domain_id, base_queryset=base_queryset
|
DomainService()
|
||||||
|
.get_domain(request.user, domain_id, base_queryset=base_queryset)
|
||||||
|
.specific
|
||||||
|
)
|
||||||
|
domain_type = type_from_data_or_registry(
|
||||||
|
request.data, domain_type_registry, domain
|
||||||
|
)
|
||||||
|
|
||||||
|
data = validate_data_custom_fields(
|
||||||
|
domain_type.type,
|
||||||
|
domain_type_registry,
|
||||||
|
request.data,
|
||||||
|
base_serializer_class=UpdateDomainSerializer,
|
||||||
|
partial=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
domain_updated = DomainService().update_domain(request.user, domain, **data)
|
domain_updated = DomainService().update_domain(request.user, domain, **data)
|
||||||
|
|
||||||
serializer = DomainSerializer(domain_updated)
|
serializer = domain_type_registry.get_serializer(
|
||||||
|
domain_updated, DomainSerializer
|
||||||
|
)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
|
|
|
@ -151,6 +151,12 @@ class BuilderConfig(AppConfig):
|
||||||
element_type_registry.register(InputTextElementType())
|
element_type_registry.register(InputTextElementType())
|
||||||
element_type_registry.register(ColumnElementType())
|
element_type_registry.register(ColumnElementType())
|
||||||
|
|
||||||
|
from .domains.domain_types import CustomDomainType, SubDomainType
|
||||||
|
from .domains.registries import domain_type_registry
|
||||||
|
|
||||||
|
domain_type_registry.register(CustomDomainType())
|
||||||
|
domain_type_registry.register(SubDomainType())
|
||||||
|
|
||||||
from .domains.trash_types import DomainTrashableItemType
|
from .domains.trash_types import DomainTrashableItemType
|
||||||
|
|
||||||
trash_item_type_registry.register(DomainTrashableItemType())
|
trash_item_type_registry.register(DomainTrashableItemType())
|
||||||
|
|
41
backend/src/baserow/contrib/builder/domains/domain_types.py
Normal file
41
backend/src/baserow/contrib/builder/domains/domain_types.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from baserow.contrib.builder.domains.exceptions import SubDomainHasInvalidDomainName
|
||||||
|
from baserow.contrib.builder.domains.models import CustomDomain, SubDomain
|
||||||
|
from baserow.contrib.builder.domains.registries import DomainType
|
||||||
|
|
||||||
|
|
||||||
|
class CustomDomainType(DomainType):
|
||||||
|
type = "custom"
|
||||||
|
model_class = CustomDomain
|
||||||
|
|
||||||
|
|
||||||
|
class SubDomainType(DomainType):
|
||||||
|
type = "sub_domain"
|
||||||
|
model_class = SubDomain
|
||||||
|
|
||||||
|
def prepare_values(self, values: Dict) -> Dict:
|
||||||
|
domain_name = values.get("domain_name", None)
|
||||||
|
|
||||||
|
if domain_name is not None:
|
||||||
|
self._validate_domain_name(domain_name)
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
def _validate_domain_name(self, domain_name: str):
|
||||||
|
"""
|
||||||
|
Checks if the subdomain uses a domain name that is among the available domain
|
||||||
|
names defined in settings.
|
||||||
|
|
||||||
|
:param domain_name: The name that is being proposed
|
||||||
|
:raises SubDomainHasInvalidDomainName: If the domain name is not registered
|
||||||
|
"""
|
||||||
|
|
||||||
|
for domain in settings.BASEROW_BUILDER_DOMAINS:
|
||||||
|
if domain_name.endswith(f".{domain}"):
|
||||||
|
# The domain suffix is valid
|
||||||
|
return
|
||||||
|
|
||||||
|
raise SubDomainHasInvalidDomainName(domain_name)
|
|
@ -1,3 +1,6 @@
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
class DomainDoesNotExist(Exception):
|
class DomainDoesNotExist(Exception):
|
||||||
"""Raised when trying to get a domain that doesn't exist."""
|
"""Raised when trying to get a domain that doesn't exist."""
|
||||||
|
|
||||||
|
@ -12,3 +15,27 @@ class DomainNotInBuilder(Exception):
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DomainNameNotUniqueError(Exception):
|
||||||
|
"""Raised when trying to set a domain name that already exists"""
|
||||||
|
|
||||||
|
def __init__(self, domain_name, *args, **kwargs):
|
||||||
|
self.domain_name = domain_name
|
||||||
|
super().__init__(
|
||||||
|
f"The domain name {domain_name} already exists", *args, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SubDomainHasInvalidDomainName(Exception):
|
||||||
|
"""Raised when a subdomain is using an invalid domain name"""
|
||||||
|
|
||||||
|
def __init__(self, domain_name, *args, **kwargs):
|
||||||
|
self.domain_name = domain_name
|
||||||
|
self.available_domain_names = settings.BASEROW_BUILDER_DOMAINS
|
||||||
|
super().__init__(
|
||||||
|
f"The subdomain {domain_name} has an invalid domain name, you can only use "
|
||||||
|
f"{self.available_domain_names}",
|
||||||
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
|
@ -1,23 +1,30 @@
|
||||||
from typing import List
|
from typing import Iterable, List, cast
|
||||||
|
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
|
from django.db.utils import IntegrityError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from baserow.contrib.builder.domains.exceptions import (
|
from baserow.contrib.builder.domains.exceptions import (
|
||||||
DomainDoesNotExist,
|
DomainDoesNotExist,
|
||||||
|
DomainNameNotUniqueError,
|
||||||
DomainNotInBuilder,
|
DomainNotInBuilder,
|
||||||
)
|
)
|
||||||
from baserow.contrib.builder.domains.models import Domain
|
from baserow.contrib.builder.domains.models import Domain
|
||||||
|
from baserow.contrib.builder.domains.registries import DomainType
|
||||||
from baserow.contrib.builder.exceptions import BuilderDoesNotExist
|
from baserow.contrib.builder.exceptions import BuilderDoesNotExist
|
||||||
from baserow.contrib.builder.models import Builder
|
from baserow.contrib.builder.models import Builder
|
||||||
|
from baserow.core.db import specific_iterator
|
||||||
from baserow.core.exceptions import IdDoesNotExist
|
from baserow.core.exceptions import IdDoesNotExist
|
||||||
from baserow.core.registries import ImportExportConfig, application_type_registry
|
from baserow.core.registries import ImportExportConfig, application_type_registry
|
||||||
from baserow.core.trash.handler import TrashHandler
|
from baserow.core.trash.handler import TrashHandler
|
||||||
from baserow.core.utils import Progress
|
from baserow.core.utils import Progress, extract_allowed
|
||||||
|
|
||||||
|
|
||||||
class DomainHandler:
|
class DomainHandler:
|
||||||
|
allowed_fields_create = ["domain_name"]
|
||||||
|
allowed_fields_update = ["domain_name", "last_published"]
|
||||||
|
|
||||||
def get_domain(self, domain_id: int, base_queryset: QuerySet = None) -> Domain:
|
def get_domain(self, domain_id: int, base_queryset: QuerySet = None) -> Domain:
|
||||||
"""
|
"""
|
||||||
Gets a domain by ID
|
Gets a domain by ID
|
||||||
|
@ -39,20 +46,20 @@ class DomainHandler:
|
||||||
|
|
||||||
def get_domains(
|
def get_domains(
|
||||||
self, builder: Builder, base_queryset: QuerySet = None
|
self, builder: Builder, base_queryset: QuerySet = None
|
||||||
) -> QuerySet[Domain]:
|
) -> Iterable[Domain]:
|
||||||
"""
|
"""
|
||||||
Gets all the domains of a builder.
|
Gets all the domains of a builder.
|
||||||
|
|
||||||
:param builder: The builder we are trying to get all domains for
|
:param builder: The builder we are trying to get all domains for
|
||||||
:param base_queryset: Can be provided to already filter or apply performance
|
:param base_queryset: Can be provided to already filter or apply performance
|
||||||
improvements to the queryset when it's being executed
|
improvements to the queryset when it's being executed
|
||||||
:return: A queryset with all the domains
|
:return: An iterable of all the specific domains
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if base_queryset is None:
|
if base_queryset is None:
|
||||||
base_queryset = Domain.objects
|
base_queryset = Domain.objects
|
||||||
|
|
||||||
return base_queryset.filter(builder=builder)
|
return specific_iterator(base_queryset.filter(builder=builder))
|
||||||
|
|
||||||
def get_public_builder_by_domain_name(self, domain_name: str) -> Builder:
|
def get_public_builder_by_domain_name(self, domain_name: str) -> Builder:
|
||||||
"""
|
"""
|
||||||
|
@ -78,20 +85,31 @@ class DomainHandler:
|
||||||
|
|
||||||
return domain.published_to
|
return domain.published_to
|
||||||
|
|
||||||
def create_domain(self, builder: Builder, domain_name: str) -> Domain:
|
def create_domain(
|
||||||
|
self, domain_type: DomainType, builder: Builder, **kwargs
|
||||||
|
) -> Domain:
|
||||||
"""
|
"""
|
||||||
Creates a new domain
|
Creates a new domain
|
||||||
|
|
||||||
|
:param domain_type: The type of domain that's being created
|
||||||
:param builder: The builder the domain belongs to
|
:param builder: The builder the domain belongs to
|
||||||
:param domain_name: The name of the domain
|
:param kwargs: Additional attributes of the domain
|
||||||
:return: The newly created domain instance
|
:return: The newly created domain instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
last_order = Domain.get_last_order(builder)
|
last_order = Domain.get_last_order(builder)
|
||||||
domain = Domain.objects.create(
|
|
||||||
builder=builder, domain_name=domain_name, order=last_order
|
model_class = cast(Domain, domain_type.model_class)
|
||||||
|
|
||||||
|
allowed_values = extract_allowed(
|
||||||
|
kwargs, self.allowed_fields_create + domain_type.allowed_fields
|
||||||
)
|
)
|
||||||
|
|
||||||
|
prepared_values = domain_type.prepare_values(allowed_values)
|
||||||
|
|
||||||
|
domain = model_class(builder=builder, order=last_order, **prepared_values)
|
||||||
|
domain.save()
|
||||||
|
|
||||||
return domain
|
return domain
|
||||||
|
|
||||||
def delete_domain(self, domain: Domain):
|
def delete_domain(self, domain: Domain):
|
||||||
|
@ -112,10 +130,23 @@ class DomainHandler:
|
||||||
:return: The updated domain
|
:return: The updated domain
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for key, value in kwargs.items():
|
domain_type = domain.get_type()
|
||||||
|
|
||||||
|
allowed_values = extract_allowed(
|
||||||
|
kwargs, self.allowed_fields_update + domain_type.allowed_fields
|
||||||
|
)
|
||||||
|
|
||||||
|
prepared_values = domain_type.prepare_values(allowed_values)
|
||||||
|
|
||||||
|
for key, value in prepared_values.items():
|
||||||
setattr(domain, key, value)
|
setattr(domain, key, value)
|
||||||
|
|
||||||
domain.save()
|
try:
|
||||||
|
domain.save()
|
||||||
|
except IntegrityError as error:
|
||||||
|
if "unique" in str(error) and "domain_name" in prepared_values:
|
||||||
|
raise DomainNameNotUniqueError(prepared_values["domain_name"])
|
||||||
|
raise error
|
||||||
|
|
||||||
return domain
|
return domain
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import CASCADE, SET_NULL
|
from django.db.models import CASCADE, SET_NULL
|
||||||
|
|
||||||
import validators
|
import validators
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
|
from baserow.contrib.builder.domains.registries import domain_type_registry
|
||||||
from baserow.core.jobs.mixins import JobWithUserIpAddress
|
from baserow.core.jobs.mixins import JobWithUserIpAddress
|
||||||
from baserow.core.jobs.models import Job
|
from baserow.core.jobs.models import Job
|
||||||
from baserow.core.mixins import (
|
from baserow.core.mixins import (
|
||||||
HierarchicalModelMixin,
|
HierarchicalModelMixin,
|
||||||
OrderableMixin,
|
OrderableMixin,
|
||||||
|
PolymorphicContentTypeMixin,
|
||||||
TrashableModelMixin,
|
TrashableModelMixin,
|
||||||
|
WithRegistry,
|
||||||
)
|
)
|
||||||
|
from baserow.core.registry import ModelRegistryMixin
|
||||||
|
|
||||||
|
|
||||||
def validate_domain(value: str):
|
def validate_domain(value: str):
|
||||||
|
@ -29,7 +34,24 @@ def validate_domain(value: str):
|
||||||
raise ValidationError("Invalid domain syntax")
|
raise ValidationError("Invalid domain syntax")
|
||||||
|
|
||||||
|
|
||||||
class Domain(HierarchicalModelMixin, TrashableModelMixin, OrderableMixin, models.Model):
|
def get_default_domain_content_type():
|
||||||
|
return ContentType.objects.get_for_model(CustomDomain)
|
||||||
|
|
||||||
|
|
||||||
|
class Domain(
|
||||||
|
HierarchicalModelMixin,
|
||||||
|
TrashableModelMixin,
|
||||||
|
OrderableMixin,
|
||||||
|
WithRegistry,
|
||||||
|
PolymorphicContentTypeMixin,
|
||||||
|
models.Model,
|
||||||
|
):
|
||||||
|
content_type = models.ForeignKey(
|
||||||
|
ContentType,
|
||||||
|
verbose_name="content type",
|
||||||
|
related_name="builder_domains",
|
||||||
|
on_delete=models.SET(get_default_domain_content_type),
|
||||||
|
)
|
||||||
builder = models.ForeignKey(
|
builder = models.ForeignKey(
|
||||||
"builder.Builder",
|
"builder.Builder",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
|
@ -63,6 +85,18 @@ class Domain(HierarchicalModelMixin, TrashableModelMixin, OrderableMixin, models
|
||||||
queryset = Domain.objects.filter(builder=builder)
|
queryset = Domain.objects.filter(builder=builder)
|
||||||
return cls.get_highest_order_of_queryset(queryset) + 1
|
return cls.get_highest_order_of_queryset(queryset) + 1
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_type_registry() -> ModelRegistryMixin:
|
||||||
|
return domain_type_registry
|
||||||
|
|
||||||
|
|
||||||
|
class CustomDomain(Domain):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SubDomain(Domain):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PublishDomainJob(JobWithUserIpAddress, Job):
|
class PublishDomainJob(JobWithUserIpAddress, Job):
|
||||||
domain: Domain = models.ForeignKey(Domain, null=True, on_delete=models.SET_NULL)
|
domain: Domain = models.ForeignKey(Domain, null=True, on_delete=models.SET_NULL)
|
||||||
|
|
34
backend/src/baserow/contrib/builder/domains/registries.py
Normal file
34
backend/src/baserow/contrib/builder/domains/registries.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
from abc import ABC
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from baserow.core.registry import (
|
||||||
|
CustomFieldsInstanceMixin,
|
||||||
|
CustomFieldsRegistryMixin,
|
||||||
|
Instance,
|
||||||
|
ModelInstanceMixin,
|
||||||
|
ModelRegistryMixin,
|
||||||
|
Registry,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DomainType(Instance, ModelInstanceMixin, CustomFieldsInstanceMixin, ABC):
|
||||||
|
def prepare_values(self, values: Dict) -> Dict:
|
||||||
|
"""
|
||||||
|
Called before a domain is saved/updates to validate the data or transform the
|
||||||
|
data before it is being saved.
|
||||||
|
:param values: The values that are about to be saved
|
||||||
|
:return: The values after validation/transformation
|
||||||
|
"""
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
class DomainTypeRegistry(Registry, ModelRegistryMixin, CustomFieldsRegistryMixin):
|
||||||
|
"""
|
||||||
|
Contains all the registered domain types.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "domain_type"
|
||||||
|
|
||||||
|
|
||||||
|
domain_type_registry = DomainTypeRegistry()
|
|
@ -29,6 +29,7 @@ from baserow.core.utils import Progress, extract_allowed
|
||||||
|
|
||||||
from .job_types import PublishDomainJobType
|
from .job_types import PublishDomainJobType
|
||||||
from .operations import PublishDomainOperationType
|
from .operations import PublishDomainOperationType
|
||||||
|
from .registries import DomainType
|
||||||
|
|
||||||
|
|
||||||
class DomainService:
|
class DomainService:
|
||||||
|
@ -121,14 +122,19 @@ class DomainService:
|
||||||
return builder
|
return builder
|
||||||
|
|
||||||
def create_domain(
|
def create_domain(
|
||||||
self, user: AbstractUser, builder: Builder, domain_name: str
|
self,
|
||||||
|
user: AbstractUser,
|
||||||
|
domain_type: DomainType,
|
||||||
|
builder: Builder,
|
||||||
|
**kwargs,
|
||||||
) -> Domain:
|
) -> Domain:
|
||||||
"""
|
"""
|
||||||
Creates a new domain
|
Creates a new domain
|
||||||
|
|
||||||
:param user: The user trying to create the domain
|
:param user: The user trying to create the domain
|
||||||
|
:param domain_type: The type of domain that's being created
|
||||||
:param builder: The builder the domain belongs to
|
:param builder: The builder the domain belongs to
|
||||||
:param domain_name: The name of the domain
|
:param kwargs: Additional attributes of the domain
|
||||||
:return: The newly created domain instance
|
:return: The newly created domain instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -139,7 +145,7 @@ class DomainService:
|
||||||
context=builder,
|
context=builder,
|
||||||
)
|
)
|
||||||
|
|
||||||
domain = self.handler.create_domain(builder, domain_name)
|
domain = self.handler.create_domain(domain_type, builder, **kwargs)
|
||||||
|
|
||||||
domain_created.send(self, domain=domain, user=user)
|
domain_created.send(self, domain=domain, user=user)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Generated by Django 3.2.21 on 2023-09-14 13:34
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import baserow.contrib.builder.domains.models
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_domain_content_type_id():
|
||||||
|
return baserow.contrib.builder.domains.models.get_default_domain_content_type().id
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("contenttypes", "0002_remove_content_type_name"),
|
||||||
|
("builder", "0017_mainthemeconfigblock"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="CustomDomain",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"domain_ptr",
|
||||||
|
models.OneToOneField(
|
||||||
|
auto_created=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
parent_link=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
to="builder.domain",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
bases=("builder.domain",),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="SubDomain",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"domain_ptr",
|
||||||
|
models.OneToOneField(
|
||||||
|
auto_created=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
parent_link=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
to="builder.domain",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
bases=("builder.domain",),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="domain",
|
||||||
|
name="content_type",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
default=get_default_domain_content_type_id,
|
||||||
|
on_delete=models.SET(
|
||||||
|
baserow.contrib.builder.domains.models.get_default_domain_content_type
|
||||||
|
),
|
||||||
|
related_name="builder_domains",
|
||||||
|
to="contenttypes.contenttype",
|
||||||
|
verbose_name="content type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,34 @@
|
||||||
|
from django.db import migrations, transaction
|
||||||
|
|
||||||
|
from baserow.contrib.builder.domains.models import CustomDomain, Domain
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
"""
|
||||||
|
This migration introduces polymorphism, so we need to decide which type of
|
||||||
|
domain all the existing domains now become. In this case we are turning
|
||||||
|
all the existing domains into custom domains.
|
||||||
|
"""
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
domains = Domain.objects.all()
|
||||||
|
|
||||||
|
for domain in domains:
|
||||||
|
domain.delete()
|
||||||
|
CustomDomain.objects.create(
|
||||||
|
domain_name=domain.domain_name,
|
||||||
|
order=domain.order,
|
||||||
|
builder=domain.builder,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def reverse(apps, schema_editor):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [("builder", "0018_sub_domains")]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(forward, reverse),
|
||||||
|
]
|
|
@ -4,7 +4,7 @@ from django.db import transaction
|
||||||
from faker import Faker
|
from faker import Faker
|
||||||
|
|
||||||
from baserow.contrib.builder.data_sources.handler import DataSourceHandler
|
from baserow.contrib.builder.data_sources.handler import DataSourceHandler
|
||||||
from baserow.contrib.builder.domains.models import Domain
|
from baserow.contrib.builder.domains.models import CustomDomain
|
||||||
from baserow.contrib.builder.elements.handler import ElementHandler
|
from baserow.contrib.builder.elements.handler import ElementHandler
|
||||||
from baserow.contrib.builder.elements.registries import element_type_registry
|
from baserow.contrib.builder.elements.registries import element_type_registry
|
||||||
from baserow.contrib.builder.models import Builder
|
from baserow.contrib.builder.models import Builder
|
||||||
|
@ -38,12 +38,18 @@ def load_test_data():
|
||||||
user, workspace, "builder", name="Back to local website"
|
user, workspace, "builder", name="Back to local website"
|
||||||
)
|
)
|
||||||
|
|
||||||
Domain.objects.filter(domain_name="test1.getbaserow.io").delete()
|
CustomDomain.objects.filter(domain_name="test1.getbaserow.io").delete()
|
||||||
Domain.objects.filter(domain_name="test2.getbaserow.io").delete()
|
CustomDomain.objects.filter(domain_name="test2.getbaserow.io").delete()
|
||||||
Domain.objects.filter(domain_name="test3.getbaserow.io").delete()
|
CustomDomain.objects.filter(domain_name="test3.getbaserow.io").delete()
|
||||||
Domain.objects.create(builder=builder, domain_name="test1.getbaserow.io", order=1)
|
CustomDomain.objects.create(
|
||||||
Domain.objects.create(builder=builder, domain_name="test2.getbaserow.io", order=2)
|
builder=builder, domain_name="test1.getbaserow.io", order=1
|
||||||
Domain.objects.create(builder=builder, domain_name="test3.getbaserow.io", order=3)
|
)
|
||||||
|
CustomDomain.objects.create(
|
||||||
|
builder=builder, domain_name="test2.getbaserow.io", order=2
|
||||||
|
)
|
||||||
|
CustomDomain.objects.create(
|
||||||
|
builder=builder, domain_name="test3.getbaserow.io", order=3
|
||||||
|
)
|
||||||
|
|
||||||
integration_type = integration_type_registry.get("local_baserow")
|
integration_type = integration_type_registry.get("local_baserow")
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
from baserow.contrib.builder.domains.models import Domain
|
from baserow.contrib.builder.domains.models import CustomDomain, SubDomain
|
||||||
|
|
||||||
|
|
||||||
class DomainFixtures:
|
class DomainFixtures:
|
||||||
def create_builder_domain(self, user=None, **kwargs):
|
def create_builder_custom_domain(self, user=None, **kwargs):
|
||||||
|
return self.create_builder_domain(CustomDomain, user, **kwargs)
|
||||||
|
|
||||||
|
def create_builder_sub_domain(self, user=None, **kwargs):
|
||||||
|
return self.create_builder_domain(SubDomain, user, **kwargs)
|
||||||
|
|
||||||
|
def create_builder_domain(self, model_class, user=None, **kwargs):
|
||||||
if user is None:
|
if user is None:
|
||||||
user = self.create_user()
|
user = self.create_user()
|
||||||
|
|
||||||
|
@ -15,6 +21,6 @@ class DomainFixtures:
|
||||||
if "order" not in kwargs:
|
if "order" not in kwargs:
|
||||||
kwargs["order"] = 0
|
kwargs["order"] = 0
|
||||||
|
|
||||||
domain = Domain.objects.create(**kwargs)
|
domain = model_class.objects.create(**kwargs)
|
||||||
|
|
||||||
return domain
|
return domain
|
||||||
|
|
|
@ -18,7 +18,7 @@ def test_get_public_builder_by_domain_name(api_client, data_fixture):
|
||||||
page = data_fixture.create_builder_page(user=user, builder=builder_to)
|
page = data_fixture.create_builder_page(user=user, builder=builder_to)
|
||||||
page2 = data_fixture.create_builder_page(user=user, builder=builder_to)
|
page2 = data_fixture.create_builder_page(user=user, builder=builder_to)
|
||||||
|
|
||||||
domain = data_fixture.create_builder_domain(
|
domain = data_fixture.create_builder_custom_domain(
|
||||||
domain_name="test.getbaserow.io", published_to=builder_to
|
domain_name="test.getbaserow.io", published_to=builder_to
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ def test_get_builder_missing_domain_name(api_client, data_fixture):
|
||||||
page = data_fixture.create_builder_page(user=user)
|
page = data_fixture.create_builder_page(user=user)
|
||||||
page2 = data_fixture.create_builder_page(builder=page.builder, user=user)
|
page2 = data_fixture.create_builder_page(builder=page.builder, user=user)
|
||||||
|
|
||||||
domain = data_fixture.create_builder_domain(
|
domain = data_fixture.create_builder_custom_domain(
|
||||||
domain_name="test.getbaserow.io", published_to=page.builder
|
domain_name="test.getbaserow.io", published_to=page.builder
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ def test_get_non_public_builder(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
page = data_fixture.create_builder_page(user=user)
|
page = data_fixture.create_builder_page(user=user)
|
||||||
page2 = data_fixture.create_builder_page(builder=page.builder, user=user)
|
page2 = data_fixture.create_builder_page(builder=page.builder, user=user)
|
||||||
domain = data_fixture.create_builder_domain(
|
domain = data_fixture.create_builder_custom_domain(
|
||||||
domain_name="test.getbaserow.io", builder=page.builder
|
domain_name="test.getbaserow.io", builder=page.builder
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ def test_publish_builder(mock_run_async_job, api_client, data_fixture):
|
||||||
page = data_fixture.create_builder_page(builder=builder_from, user=user)
|
page = data_fixture.create_builder_page(builder=builder_from, user=user)
|
||||||
page2 = data_fixture.create_builder_page(builder=builder_from, user=user)
|
page2 = data_fixture.create_builder_page(builder=builder_from, user=user)
|
||||||
|
|
||||||
domain = data_fixture.create_builder_domain(
|
domain = data_fixture.create_builder_custom_domain(
|
||||||
domain_name="test.getbaserow.io", builder=builder_from
|
domain_name="test.getbaserow.io", builder=builder_from
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ def test_get_elements_of_public_builder(api_client, data_fixture):
|
||||||
element2 = data_fixture.create_builder_heading_element(page=page)
|
element2 = data_fixture.create_builder_heading_element(page=page)
|
||||||
element3 = data_fixture.create_builder_paragraph_element(page=page)
|
element3 = data_fixture.create_builder_paragraph_element(page=page)
|
||||||
|
|
||||||
domain = data_fixture.create_builder_domain(
|
domain = data_fixture.create_builder_custom_domain(
|
||||||
domain_name="test.getbaserow.io",
|
domain_name="test.getbaserow.io",
|
||||||
published_to=page.builder,
|
published_to=page.builder,
|
||||||
builder=builder_from,
|
builder=builder_from,
|
||||||
|
@ -238,7 +238,7 @@ def test_get_data_source_of_public_builder(api_client, data_fixture):
|
||||||
page=page
|
page=page
|
||||||
)
|
)
|
||||||
|
|
||||||
domain = data_fixture.create_builder_domain(
|
domain = data_fixture.create_builder_custom_domain(
|
||||||
domain_name="test.getbaserow.io",
|
domain_name="test.getbaserow.io",
|
||||||
published_to=page.builder,
|
published_to=page.builder,
|
||||||
builder=builder_from,
|
builder=builder_from,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from django.test.utils import override_settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -9,13 +10,15 @@ from rest_framework.status import (
|
||||||
HTTP_404_NOT_FOUND,
|
HTTP_404_NOT_FOUND,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from baserow.contrib.builder.domains.domain_types import CustomDomainType, SubDomainType
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_get_domains(api_client, data_fixture):
|
def test_get_domains(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
domain_two = data_fixture.create_builder_domain(builder=builder)
|
domain_two = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
url = reverse(
|
url = reverse(
|
||||||
"api:builder:builder_id:domains:list", kwargs={"builder_id": builder.id}
|
"api:builder:builder_id:domains:list", kwargs={"builder_id": builder.id}
|
||||||
|
@ -62,7 +65,7 @@ def test_create_domain(api_client, data_fixture):
|
||||||
)
|
)
|
||||||
response = api_client.post(
|
response = api_client.post(
|
||||||
url,
|
url,
|
||||||
{"domain_name": domain_name},
|
{"type": CustomDomainType.type, "domain_name": domain_name},
|
||||||
format="json",
|
format="json",
|
||||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
)
|
)
|
||||||
|
@ -82,7 +85,7 @@ def test_create_domain_user_not_in_workspace(api_client, data_fixture):
|
||||||
)
|
)
|
||||||
response = api_client.post(
|
response = api_client.post(
|
||||||
url,
|
url,
|
||||||
{"domain_name": "test.com"},
|
{"type": CustomDomainType.type, "domain_name": "test.com"},
|
||||||
format="json",
|
format="json",
|
||||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
)
|
)
|
||||||
|
@ -98,7 +101,7 @@ def test_create_domain_application_does_not_exist(api_client, data_fixture):
|
||||||
url = reverse("api:builder:builder_id:domains:list", kwargs={"builder_id": 9999})
|
url = reverse("api:builder:builder_id:domains:list", kwargs={"builder_id": 9999})
|
||||||
response = api_client.post(
|
response = api_client.post(
|
||||||
url,
|
url,
|
||||||
{"domain_name": "test.com"},
|
{"type": CustomDomainType.type, "domain_name": "test.com"},
|
||||||
format="json",
|
format="json",
|
||||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
)
|
)
|
||||||
|
@ -114,14 +117,14 @@ def test_create_domain_domain_already_exists(api_client, data_fixture):
|
||||||
|
|
||||||
domain_name = "test.com"
|
domain_name = "test.com"
|
||||||
|
|
||||||
data_fixture.create_builder_domain(builder=builder, domain_name=domain_name)
|
data_fixture.create_builder_custom_domain(builder=builder, domain_name=domain_name)
|
||||||
|
|
||||||
url = reverse(
|
url = reverse(
|
||||||
"api:builder:builder_id:domains:list", kwargs={"builder_id": builder.id}
|
"api:builder:builder_id:domains:list", kwargs={"builder_id": builder.id}
|
||||||
)
|
)
|
||||||
response = api_client.post(
|
response = api_client.post(
|
||||||
url,
|
url,
|
||||||
{"domain_name": domain_name},
|
{"type": CustomDomainType.type, "domain_name": domain_name},
|
||||||
format="json",
|
format="json",
|
||||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
)
|
)
|
||||||
|
@ -142,7 +145,7 @@ def test_create_domain_invalid_domain_name(api_client, data_fixture):
|
||||||
)
|
)
|
||||||
response = api_client.post(
|
response = api_client.post(
|
||||||
url,
|
url,
|
||||||
{"domain_name": domain_name},
|
{"type": CustomDomainType.type, "domain_name": domain_name},
|
||||||
format="json",
|
format="json",
|
||||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
)
|
)
|
||||||
|
@ -151,11 +154,36 @@ def test_create_domain_invalid_domain_name(api_client, data_fixture):
|
||||||
assert response.json()["error"] == "ERROR_REQUEST_BODY_VALIDATION"
|
assert response.json()["error"] == "ERROR_REQUEST_BODY_VALIDATION"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
@override_settings(BASEROW_BUILDER_DOMAINS=["test.com"])
|
||||||
|
def test_create_invalid_sub_domain(api_client, data_fixture):
|
||||||
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
|
|
||||||
|
domain_name = "hello.nottest.com"
|
||||||
|
|
||||||
|
url = reverse(
|
||||||
|
"api:builder:builder_id:domains:list", kwargs={"builder_id": builder.id}
|
||||||
|
)
|
||||||
|
response = api_client.post(
|
||||||
|
url,
|
||||||
|
{"type": SubDomainType.type, "domain_name": domain_name},
|
||||||
|
format="json",
|
||||||
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
|
)
|
||||||
|
|
||||||
|
response_json = response.json()
|
||||||
|
assert response.status_code == HTTP_400_BAD_REQUEST
|
||||||
|
assert response_json["error"] == "ERROR_SUB_DOMAIN_HAS_INVALID_DOMAIN_NAME"
|
||||||
|
assert "test.com" in response_json["detail"]
|
||||||
|
assert "nottest.com" in response_json["detail"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_update_domain(api_client, data_fixture):
|
def test_update_domain(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain = data_fixture.create_builder_domain(
|
domain = data_fixture.create_builder_custom_domain(
|
||||||
builder=builder, domain_name="something.com"
|
builder=builder, domain_name="something.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -187,12 +215,45 @@ def test_update_domain_domain_does_not_exist(api_client, data_fixture):
|
||||||
assert response.json()["error"] == "ERROR_DOMAIN_DOES_NOT_EXIST"
|
assert response.json()["error"] == "ERROR_DOMAIN_DOES_NOT_EXIST"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_update_domain_with_same_name(api_client, data_fixture):
|
||||||
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
domain = data_fixture.create_builder_custom_domain(user=user)
|
||||||
|
|
||||||
|
url = reverse("api:builder:domains:item", kwargs={"domain_id": domain.id})
|
||||||
|
response = api_client.patch(
|
||||||
|
url,
|
||||||
|
{"domain_name": domain.domain_name},
|
||||||
|
format="json",
|
||||||
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == HTTP_200_OK
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_update_domain_name_uniqueness(api_client, data_fixture):
|
||||||
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
domain = data_fixture.create_builder_custom_domain(user=user)
|
||||||
|
domain_2 = data_fixture.create_builder_custom_domain(user=user)
|
||||||
|
|
||||||
|
url = reverse("api:builder:domains:item", kwargs={"domain_id": domain.id})
|
||||||
|
response = api_client.patch(
|
||||||
|
url,
|
||||||
|
{"domain_name": domain_2.domain_name},
|
||||||
|
format="json",
|
||||||
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == HTTP_400_BAD_REQUEST, response.json()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_domains(api_client, data_fixture):
|
def test_order_domains(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
domain_two = data_fixture.create_builder_domain(builder=builder, order=2)
|
domain_two = data_fixture.create_builder_custom_domain(builder=builder, order=2)
|
||||||
|
|
||||||
url = reverse(
|
url = reverse(
|
||||||
"api:builder:builder_id:domains:order", kwargs={"builder_id": builder.id}
|
"api:builder:builder_id:domains:order", kwargs={"builder_id": builder.id}
|
||||||
|
@ -211,8 +272,8 @@ def test_order_domains(api_client, data_fixture):
|
||||||
def test_order_domains_user_not_in_workspace(api_client, data_fixture):
|
def test_order_domains_user_not_in_workspace(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
domain_two = data_fixture.create_builder_domain(builder=builder, order=2)
|
domain_two = data_fixture.create_builder_custom_domain(builder=builder, order=2)
|
||||||
|
|
||||||
url = reverse(
|
url = reverse(
|
||||||
"api:builder:builder_id:domains:order", kwargs={"builder_id": builder.id}
|
"api:builder:builder_id:domains:order", kwargs={"builder_id": builder.id}
|
||||||
|
@ -232,8 +293,8 @@ def test_order_domains_user_not_in_workspace(api_client, data_fixture):
|
||||||
def test_order_domains_domain_not_in_builder(api_client, data_fixture):
|
def test_order_domains_domain_not_in_builder(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
domain_two = data_fixture.create_builder_domain(order=2)
|
domain_two = data_fixture.create_builder_custom_domain(order=2)
|
||||||
|
|
||||||
url = reverse(
|
url = reverse(
|
||||||
"api:builder:builder_id:domains:order", kwargs={"builder_id": builder.id}
|
"api:builder:builder_id:domains:order", kwargs={"builder_id": builder.id}
|
||||||
|
@ -253,8 +314,8 @@ def test_order_domains_domain_not_in_builder(api_client, data_fixture):
|
||||||
def test_order_domains_application_does_not_exist(api_client, data_fixture):
|
def test_order_domains_application_does_not_exist(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
domain_two = data_fixture.create_builder_domain(builder=builder, order=2)
|
domain_two = data_fixture.create_builder_custom_domain(builder=builder, order=2)
|
||||||
|
|
||||||
url = reverse("api:builder:builder_id:domains:order", kwargs={"builder_id": 99999})
|
url = reverse("api:builder:builder_id:domains:order", kwargs={"builder_id": 99999})
|
||||||
response = api_client.post(
|
response = api_client.post(
|
||||||
|
@ -272,7 +333,7 @@ def test_order_domains_application_does_not_exist(api_client, data_fixture):
|
||||||
def test_delete_domain(api_client, data_fixture):
|
def test_delete_domain(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
|
|
||||||
url = reverse("api:builder:domains:item", kwargs={"domain_id": domain.id})
|
url = reverse("api:builder:domains:item", kwargs={"domain_id": domain.id})
|
||||||
response = api_client.delete(
|
response = api_client.delete(
|
||||||
|
@ -288,7 +349,7 @@ def test_delete_domain(api_client, data_fixture):
|
||||||
def test_delete_domain_user_not_in_workspace(api_client, data_fixture):
|
def test_delete_domain_user_not_in_workspace(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
domain = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
|
|
||||||
url = reverse("api:builder:domains:item", kwargs={"domain_id": domain.id})
|
url = reverse("api:builder:domains:item", kwargs={"domain_id": domain.id})
|
||||||
response = api_client.delete(
|
response = api_client.delete(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from baserow.contrib.builder.domains.domain_types import CustomDomainType
|
||||||
from baserow.contrib.builder.domains.exceptions import (
|
from baserow.contrib.builder.domains.exceptions import (
|
||||||
DomainDoesNotExist,
|
DomainDoesNotExist,
|
||||||
DomainNotInBuilder,
|
DomainNotInBuilder,
|
||||||
|
@ -13,7 +14,7 @@ from baserow.core.utils import Progress
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_get_domain(data_fixture):
|
def test_get_domain(data_fixture):
|
||||||
domain = data_fixture.create_builder_domain()
|
domain = data_fixture.create_builder_custom_domain()
|
||||||
assert DomainHandler().get_domain(domain.id).id == domain.id
|
assert DomainHandler().get_domain(domain.id).id == domain.id
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ def test_get_domain_domain_does_not_exist(data_fixture):
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_get_domain_base_queryset(data_fixture, django_assert_num_queries):
|
def test_get_domain_base_queryset(data_fixture, django_assert_num_queries):
|
||||||
domain = data_fixture.create_builder_domain()
|
domain = data_fixture.create_builder_custom_domain()
|
||||||
|
|
||||||
# Without selecting related
|
# Without selecting related
|
||||||
domain = DomainHandler().get_domain(domain.id)
|
domain = DomainHandler().get_domain(domain.id)
|
||||||
|
@ -42,8 +43,8 @@ def test_get_domain_base_queryset(data_fixture, django_assert_num_queries):
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_get_domains(data_fixture):
|
def test_get_domains(data_fixture):
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
domain_two = data_fixture.create_builder_domain(builder=builder)
|
domain_two = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
domains = list(DomainHandler().get_domains(builder))
|
domains = list(DomainHandler().get_domains(builder))
|
||||||
domain_ids = [domain.id for domain in domains]
|
domain_ids = [domain.id for domain in domains]
|
||||||
|
@ -58,7 +59,9 @@ def test_create_domain(data_fixture):
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
expected_order = Domain.get_last_order(builder)
|
expected_order = Domain.get_last_order(builder)
|
||||||
|
|
||||||
domain = DomainHandler().create_domain(builder, "test.com")
|
domain = DomainHandler().create_domain(
|
||||||
|
CustomDomainType(), builder, domain_name="test.com"
|
||||||
|
)
|
||||||
|
|
||||||
assert domain.order == expected_order
|
assert domain.order == expected_order
|
||||||
assert domain.domain_name == "test.com"
|
assert domain.domain_name == "test.com"
|
||||||
|
@ -66,7 +69,7 @@ def test_create_domain(data_fixture):
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_delete_domain(data_fixture):
|
def test_delete_domain(data_fixture):
|
||||||
domain = data_fixture.create_builder_domain()
|
domain = data_fixture.create_builder_custom_domain()
|
||||||
|
|
||||||
DomainHandler().delete_domain(domain)
|
DomainHandler().delete_domain(domain)
|
||||||
|
|
||||||
|
@ -75,7 +78,7 @@ def test_delete_domain(data_fixture):
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_update_domain(data_fixture):
|
def test_update_domain(data_fixture):
|
||||||
domain = data_fixture.create_builder_domain(domain_name="test.com")
|
domain = data_fixture.create_builder_custom_domain(domain_name="test.com")
|
||||||
|
|
||||||
DomainHandler().update_domain(domain, domain_name="new.com")
|
DomainHandler().update_domain(domain, domain_name="new.com")
|
||||||
|
|
||||||
|
@ -87,8 +90,8 @@ def test_update_domain(data_fixture):
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_domains(data_fixture):
|
def test_order_domains(data_fixture):
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
domain_two = data_fixture.create_builder_domain(builder=builder, order=2)
|
domain_two = data_fixture.create_builder_custom_domain(builder=builder, order=2)
|
||||||
|
|
||||||
assert DomainHandler().order_domains(builder, [domain_two.id, domain_one.id]) == [
|
assert DomainHandler().order_domains(builder, [domain_two.id, domain_one.id]) == [
|
||||||
domain_two.id,
|
domain_two.id,
|
||||||
|
@ -105,8 +108,8 @@ def test_order_domains(data_fixture):
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_domains_domain_not_in_builder(data_fixture):
|
def test_order_domains_domain_not_in_builder(data_fixture):
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
domain_two = data_fixture.create_builder_domain(builder=builder, order=2)
|
domain_two = data_fixture.create_builder_custom_domain(builder=builder, order=2)
|
||||||
|
|
||||||
base_qs = Domain.objects.filter(id=domain_two.id)
|
base_qs = Domain.objects.filter(id=domain_two.id)
|
||||||
|
|
||||||
|
@ -120,7 +123,7 @@ def test_order_domains_domain_not_in_builder(data_fixture):
|
||||||
def test_get_public_builder_by_name(data_fixture):
|
def test_get_public_builder_by_name(data_fixture):
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
builder_to = data_fixture.create_builder_application(workspace=None)
|
builder_to = data_fixture.create_builder_application(workspace=None)
|
||||||
domain1 = data_fixture.create_builder_domain(
|
domain1 = data_fixture.create_builder_custom_domain(
|
||||||
builder=builder, published_to=builder_to
|
builder=builder, published_to=builder_to
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -132,7 +135,7 @@ def test_get_public_builder_by_name(data_fixture):
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_get_published_builder_by_missing_domain_name(data_fixture):
|
def test_get_published_builder_by_missing_domain_name(data_fixture):
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
domain1 = data_fixture.create_builder_domain(builder=builder)
|
domain1 = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
with pytest.raises(BuilderDoesNotExist):
|
with pytest.raises(BuilderDoesNotExist):
|
||||||
DomainHandler().get_public_builder_by_domain_name(domain1.domain_name)
|
DomainHandler().get_public_builder_by_domain_name(domain1.domain_name)
|
||||||
|
@ -142,7 +145,7 @@ def test_get_published_builder_by_missing_domain_name(data_fixture):
|
||||||
def test_get_published_builder_for_trashed_builder(data_fixture):
|
def test_get_published_builder_for_trashed_builder(data_fixture):
|
||||||
builder = data_fixture.create_builder_application(trashed=True)
|
builder = data_fixture.create_builder_application(trashed=True)
|
||||||
builder_to = data_fixture.create_builder_application(workspace=None)
|
builder_to = data_fixture.create_builder_application(workspace=None)
|
||||||
domain1 = data_fixture.create_builder_domain(
|
domain1 = data_fixture.create_builder_custom_domain(
|
||||||
builder=builder, published_to=builder_to
|
builder=builder, published_to=builder_to
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -151,7 +154,7 @@ def test_get_published_builder_for_trashed_builder(data_fixture):
|
||||||
|
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
builder_to = data_fixture.create_builder_application(workspace=None)
|
builder_to = data_fixture.create_builder_application(workspace=None)
|
||||||
domain1 = data_fixture.create_builder_domain(
|
domain1 = data_fixture.create_builder_custom_domain(
|
||||||
builder=builder, published_to=builder_to, trashed=True
|
builder=builder, published_to=builder_to, trashed=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -163,7 +166,7 @@ def test_get_published_builder_for_trashed_builder(data_fixture):
|
||||||
def test_domain_publishing(data_fixture):
|
def test_domain_publishing(data_fixture):
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
|
|
||||||
domain1 = data_fixture.create_builder_domain(builder=builder)
|
domain1 = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
page1 = data_fixture.create_builder_page(builder=builder)
|
page1 = data_fixture.create_builder_page(builder=builder)
|
||||||
page2 = data_fixture.create_builder_page(builder=builder)
|
page2 = data_fixture.create_builder_page(builder=builder)
|
||||||
|
|
|
@ -22,10 +22,10 @@ def test_allow_public_builder_manager_type(data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
builder_to = data_fixture.create_builder_application(workspace=None)
|
builder_to = data_fixture.create_builder_application(workspace=None)
|
||||||
domain1 = data_fixture.create_builder_domain(
|
domain1 = data_fixture.create_builder_custom_domain(
|
||||||
builder=builder, published_to=builder_to
|
builder=builder, published_to=builder_to
|
||||||
)
|
)
|
||||||
domain2 = data_fixture.create_builder_domain(builder=builder)
|
domain2 = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
public_page = data_fixture.create_builder_page(builder=builder_to)
|
public_page = data_fixture.create_builder_page(builder=builder_to)
|
||||||
non_public_page = data_fixture.create_builder_page(builder=builder)
|
non_public_page = data_fixture.create_builder_page(builder=builder)
|
||||||
|
|
|
@ -2,6 +2,7 @@ from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from baserow.contrib.builder.domains.domain_types import CustomDomainType
|
||||||
from baserow.contrib.builder.domains.exceptions import DomainNotInBuilder
|
from baserow.contrib.builder.domains.exceptions import DomainNotInBuilder
|
||||||
from baserow.contrib.builder.domains.models import Domain
|
from baserow.contrib.builder.domains.models import Domain
|
||||||
from baserow.contrib.builder.domains.service import DomainService
|
from baserow.contrib.builder.domains.service import DomainService
|
||||||
|
@ -15,7 +16,9 @@ def test_domain_created_signal_sent(domain_created_mock, data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
|
|
||||||
domain = DomainService().create_domain(user, builder, "test")
|
domain = DomainService().create_domain(
|
||||||
|
user, CustomDomainType(), builder, domain_name="test"
|
||||||
|
)
|
||||||
|
|
||||||
assert domain_created_mock.called_with(domain=domain, user=user)
|
assert domain_created_mock.called_with(domain=domain, user=user)
|
||||||
|
|
||||||
|
@ -26,7 +29,9 @@ def test_create_domain_user_not_in_workspace(data_fixture):
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
|
|
||||||
with pytest.raises(UserNotInWorkspace):
|
with pytest.raises(UserNotInWorkspace):
|
||||||
DomainService().create_domain(user, builder, "test")
|
DomainService().create_domain(
|
||||||
|
user, CustomDomainType, builder, domain_name="test"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@patch("baserow.contrib.builder.domains.service.domain_deleted")
|
@patch("baserow.contrib.builder.domains.service.domain_deleted")
|
||||||
|
@ -34,7 +39,7 @@ def test_create_domain_user_not_in_workspace(data_fixture):
|
||||||
def test_domain_deleted_signal_sent(domain_deleted_mock, data_fixture):
|
def test_domain_deleted_signal_sent(domain_deleted_mock, data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain = data_fixture.create_builder_domain(builder=builder)
|
domain = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
DomainService().delete_domain(user, domain)
|
DomainService().delete_domain(user, domain)
|
||||||
|
|
||||||
|
@ -49,7 +54,9 @@ def test_delete_domain_user_not_in_workspace(data_fixture):
|
||||||
user_unrelated = data_fixture.create_user()
|
user_unrelated = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
|
|
||||||
domain = DomainService().create_domain(user, builder, "test")
|
domain = DomainService().create_domain(
|
||||||
|
user, CustomDomainType(), builder, domain_name="test"
|
||||||
|
)
|
||||||
|
|
||||||
with pytest.raises(UserNotInWorkspace):
|
with pytest.raises(UserNotInWorkspace):
|
||||||
DomainService().delete_domain(user_unrelated, domain)
|
DomainService().delete_domain(user_unrelated, domain)
|
||||||
|
@ -61,7 +68,7 @@ def test_delete_domain_user_not_in_workspace(data_fixture):
|
||||||
def test_get_domain_user_not_in_workspace(data_fixture):
|
def test_get_domain_user_not_in_workspace(data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
domain = data_fixture.create_builder_domain(builder=builder)
|
domain = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
with pytest.raises(UserNotInWorkspace):
|
with pytest.raises(UserNotInWorkspace):
|
||||||
DomainService().get_domain(user, domain.id)
|
DomainService().get_domain(user, domain.id)
|
||||||
|
@ -71,7 +78,7 @@ def test_get_domain_user_not_in_workspace(data_fixture):
|
||||||
def test_get_domains_user_not_in_workspace(data_fixture):
|
def test_get_domains_user_not_in_workspace(data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
domain = data_fixture.create_builder_domain(builder=builder)
|
domain = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
with pytest.raises(UserNotInWorkspace):
|
with pytest.raises(UserNotInWorkspace):
|
||||||
DomainService().get_domains(user, builder)
|
DomainService().get_domains(user, builder)
|
||||||
|
@ -81,8 +88,8 @@ def test_get_domains_user_not_in_workspace(data_fixture):
|
||||||
def test_get_domains_partial_permissions(data_fixture, stub_check_permissions):
|
def test_get_domains_partial_permissions(data_fixture, stub_check_permissions):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain_with_access = data_fixture.create_builder_domain(builder=builder)
|
domain_with_access = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
domain_without_access = data_fixture.create_builder_domain(builder=builder)
|
domain_without_access = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
def exclude_domain_without_access(
|
def exclude_domain_without_access(
|
||||||
actor,
|
actor,
|
||||||
|
@ -107,7 +114,7 @@ def test_get_domains_partial_permissions(data_fixture, stub_check_permissions):
|
||||||
def test_domain_updated_signal_sent(domain_updated_mock, data_fixture):
|
def test_domain_updated_signal_sent(domain_updated_mock, data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain = data_fixture.create_builder_domain(builder=builder)
|
domain = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
DomainService().update_domain(user, domain, domain_name="new.com")
|
DomainService().update_domain(user, domain, domain_name="new.com")
|
||||||
|
|
||||||
|
@ -118,7 +125,7 @@ def test_domain_updated_signal_sent(domain_updated_mock, data_fixture):
|
||||||
def test_update_domain_user_not_in_workspace(data_fixture):
|
def test_update_domain_user_not_in_workspace(data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
domain = data_fixture.create_builder_domain(builder=builder)
|
domain = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
with pytest.raises(UserNotInWorkspace):
|
with pytest.raises(UserNotInWorkspace):
|
||||||
DomainService().update_domain(user, domain, domain_name="test.com")
|
DomainService().update_domain(user, domain, domain_name="test.com")
|
||||||
|
@ -128,7 +135,7 @@ def test_update_domain_user_not_in_workspace(data_fixture):
|
||||||
def test_update_domain_invalid_values(data_fixture):
|
def test_update_domain_invalid_values(data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain = data_fixture.create_builder_domain(builder=builder)
|
domain = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
domain_updated = DomainService().update_domain(user, domain, nonsense="hello")
|
domain_updated = DomainService().update_domain(user, domain, nonsense="hello")
|
||||||
|
|
||||||
|
@ -140,8 +147,8 @@ def test_update_domain_invalid_values(data_fixture):
|
||||||
def test_domains_reordered_signal_sent(domains_reordered_mock, data_fixture):
|
def test_domains_reordered_signal_sent(domains_reordered_mock, data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
domain_two = data_fixture.create_builder_domain(builder=builder, order=2)
|
domain_two = data_fixture.create_builder_custom_domain(builder=builder, order=2)
|
||||||
|
|
||||||
full_order = DomainService().order_domains(
|
full_order = DomainService().order_domains(
|
||||||
user, builder, [domain_two.id, domain_one.id]
|
user, builder, [domain_two.id, domain_one.id]
|
||||||
|
@ -156,8 +163,8 @@ def test_domains_reordered_signal_sent(domains_reordered_mock, data_fixture):
|
||||||
def test_order_domains_user_not_in_workspace(data_fixture):
|
def test_order_domains_user_not_in_workspace(data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
domain_two = data_fixture.create_builder_domain(builder=builder, order=2)
|
domain_two = data_fixture.create_builder_custom_domain(builder=builder, order=2)
|
||||||
|
|
||||||
with pytest.raises(UserNotInWorkspace):
|
with pytest.raises(UserNotInWorkspace):
|
||||||
DomainService().order_domains(user, builder, [domain_two.id, domain_one.id])
|
DomainService().order_domains(user, builder, [domain_two.id, domain_one.id])
|
||||||
|
@ -167,8 +174,8 @@ def test_order_domains_user_not_in_workspace(data_fixture):
|
||||||
def test_order_domains_domain_not_in_builder(data_fixture):
|
def test_order_domains_domain_not_in_builder(data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain_one = data_fixture.create_builder_domain(builder=builder, order=1)
|
domain_one = data_fixture.create_builder_custom_domain(builder=builder, order=1)
|
||||||
domain_two = data_fixture.create_builder_domain(order=2)
|
domain_two = data_fixture.create_builder_custom_domain(order=2)
|
||||||
|
|
||||||
with pytest.raises(DomainNotInBuilder):
|
with pytest.raises(DomainNotInBuilder):
|
||||||
DomainService().order_domains(user, builder, [domain_two.id, domain_one.id])
|
DomainService().order_domains(user, builder, [domain_two.id, domain_one.id])
|
||||||
|
@ -179,7 +186,7 @@ def test_get_published_builder_by_domain_name(data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
builder_to = data_fixture.create_builder_application(workspace=None)
|
builder_to = data_fixture.create_builder_application(workspace=None)
|
||||||
domain1 = data_fixture.create_builder_domain(
|
domain1 = data_fixture.create_builder_custom_domain(
|
||||||
builder=builder, published_to=builder_to
|
builder=builder, published_to=builder_to
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -197,7 +204,7 @@ def test_get_published_builder_by_domain_name_unauthorized(
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
builder_to = data_fixture.create_builder_application(workspace=None)
|
builder_to = data_fixture.create_builder_application(workspace=None)
|
||||||
domain1 = data_fixture.create_builder_domain(
|
domain1 = data_fixture.create_builder_custom_domain(
|
||||||
builder=builder, published_to=builder_to
|
builder=builder, published_to=builder_to
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -213,7 +220,7 @@ def test_async_publish_domain(mock_run_async_job, data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
|
|
||||||
domain1 = data_fixture.create_builder_domain(builder=builder)
|
domain1 = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
job = DomainService().async_publish(user, domain1)
|
job = DomainService().async_publish(user, domain1)
|
||||||
|
|
||||||
|
@ -225,7 +232,7 @@ def test_async_publish_domain(mock_run_async_job, data_fixture):
|
||||||
@pytest.mark.django_db(transaction=True)
|
@pytest.mark.django_db(transaction=True)
|
||||||
def test_async_publish_domain_no_permission(data_fixture, stub_check_permissions):
|
def test_async_publish_domain_no_permission(data_fixture, stub_check_permissions):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
domain1 = data_fixture.create_builder_domain()
|
domain1 = data_fixture.create_builder_custom_domain()
|
||||||
|
|
||||||
with stub_check_permissions(raise_permission_denied=True), pytest.raises(
|
with stub_check_permissions(raise_permission_denied=True), pytest.raises(
|
||||||
PermissionException
|
PermissionException
|
||||||
|
@ -239,7 +246,7 @@ def test_publish_domain(domain_updated_mock, data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
|
|
||||||
domain1 = data_fixture.create_builder_domain(builder=builder)
|
domain1 = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
progress = Progress(100)
|
progress = Progress(100)
|
||||||
domain = DomainService().publish(user, domain1, progress)
|
domain = DomainService().publish(user, domain1, progress)
|
||||||
|
@ -252,7 +259,7 @@ def test_publish_domain_unauthorized(data_fixture, stub_check_permissions):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
|
|
||||||
domain1 = data_fixture.create_builder_domain(builder=builder)
|
domain1 = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
progress = Progress(100)
|
progress = Progress(100)
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ from baserow.core.jobs.handler import JobHandler
|
||||||
def test_publish_domain_job_type(data_fixture):
|
def test_publish_domain_job_type(data_fixture):
|
||||||
user = data_fixture.create_user()
|
user = data_fixture.create_user()
|
||||||
builder = data_fixture.create_builder_application(user=user)
|
builder = data_fixture.create_builder_application(user=user)
|
||||||
domain1 = data_fixture.create_builder_domain(builder=builder)
|
domain1 = data_fixture.create_builder_custom_domain(builder=builder)
|
||||||
|
|
||||||
publish_domain_job = JobHandler().create_and_start_job(
|
publish_domain_job = JobHandler().create_and_start_job(
|
||||||
user,
|
user,
|
||||||
|
|
|
@ -410,7 +410,7 @@ def test_builder_application_import(data_fixture):
|
||||||
def test_delete_builder_application_with_published_builder(data_fixture):
|
def test_delete_builder_application_with_published_builder(data_fixture):
|
||||||
builder = data_fixture.create_builder_application()
|
builder = data_fixture.create_builder_application()
|
||||||
builder_to = data_fixture.create_builder_application(workspace=None)
|
builder_to = data_fixture.create_builder_application(workspace=None)
|
||||||
domain1 = data_fixture.create_builder_domain(
|
domain1 = data_fixture.create_builder_custom_domain(
|
||||||
builder=builder, published_to=builder_to
|
builder=builder, published_to=builder_to
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,7 @@ x-backend-variables: &backend-variables
|
||||||
BASEROW_DISABLE_LOCKED_MIGRATIONS:
|
BASEROW_DISABLE_LOCKED_MIGRATIONS:
|
||||||
BASEROW_USE_PG_FULLTEXT_SEARCH:
|
BASEROW_USE_PG_FULLTEXT_SEARCH:
|
||||||
BASEROW_AUTO_VACUUM:
|
BASEROW_AUTO_VACUUM:
|
||||||
|
BASEROW_BUILDER_DOMAINS:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# A caddy reverse proxy sitting in-front of all the services. Responsible for routing
|
# A caddy reverse proxy sitting in-front of all the services. Responsible for routing
|
||||||
|
@ -206,6 +207,7 @@ services:
|
||||||
BASEROW_USE_PG_FULLTEXT_SEARCH:
|
BASEROW_USE_PG_FULLTEXT_SEARCH:
|
||||||
BASEROW_UNIQUE_ROW_VALUES_SIZE_LIMIT:
|
BASEROW_UNIQUE_ROW_VALUES_SIZE_LIMIT:
|
||||||
BASEROW_ROW_PAGE_SIZE_LIMIT:
|
BASEROW_ROW_PAGE_SIZE_LIMIT:
|
||||||
|
BASEROW_BUILDER_DOMAINS:
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
networks:
|
networks:
|
||||||
|
|
|
@ -158,6 +158,7 @@ x-backend-variables: &backend-variables
|
||||||
BASEROW_DISABLE_LOCKED_MIGRATIONS:
|
BASEROW_DISABLE_LOCKED_MIGRATIONS:
|
||||||
BASEROW_USE_PG_FULLTEXT_SEARCH:
|
BASEROW_USE_PG_FULLTEXT_SEARCH:
|
||||||
BASEROW_AUTO_VACUUM:
|
BASEROW_AUTO_VACUUM:
|
||||||
|
BASEROW_BUILDER_DOMAINS:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
|
@ -201,6 +202,7 @@ services:
|
||||||
BASEROW_USE_PG_FULLTEXT_SEARCH:
|
BASEROW_USE_PG_FULLTEXT_SEARCH:
|
||||||
BASEROW_UNIQUE_ROW_VALUES_SIZE_LIMIT:
|
BASEROW_UNIQUE_ROW_VALUES_SIZE_LIMIT:
|
||||||
BASEROW_ROW_PAGE_SIZE_LIMIT:
|
BASEROW_ROW_PAGE_SIZE_LIMIT:
|
||||||
|
BASEROW_BUILDER_DOMAINS:
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
networks:
|
networks:
|
||||||
|
|
|
@ -157,6 +157,7 @@ x-backend-variables: &backend-variables
|
||||||
BASEROW_DISABLE_LOCKED_MIGRATIONS:
|
BASEROW_DISABLE_LOCKED_MIGRATIONS:
|
||||||
BASEROW_USE_PG_FULLTEXT_SEARCH:
|
BASEROW_USE_PG_FULLTEXT_SEARCH:
|
||||||
BASEROW_AUTO_VACUUM:
|
BASEROW_AUTO_VACUUM:
|
||||||
|
BASEROW_BUILDER_DOMAINS:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# A caddy reverse proxy sitting in-front of all the services. Responsible for routing
|
# A caddy reverse proxy sitting in-front of all the services. Responsible for routing
|
||||||
|
@ -220,6 +221,7 @@ services:
|
||||||
POSTHOG_HOST:
|
POSTHOG_HOST:
|
||||||
BASEROW_UNIQUE_ROW_VALUES_SIZE_LIMIT:
|
BASEROW_UNIQUE_ROW_VALUES_SIZE_LIMIT:
|
||||||
BASEROW_ROW_PAGE_SIZE_LIMIT:
|
BASEROW_ROW_PAGE_SIZE_LIMIT:
|
||||||
|
BASEROW_BUILDER_DOMAINS:
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
networks:
|
networks:
|
||||||
|
|
|
@ -139,6 +139,12 @@ The installation methods referred to in the variable descriptions are:
|
||||||
| BASEROW\_INITIAL\_CREATE\_SYNC\_TABLE\_DATA\_LIMIT | The maximum number of rows you can import in a synchronous way | 5000 |
|
| BASEROW\_INITIAL\_CREATE\_SYNC\_TABLE\_DATA\_LIMIT | The maximum number of rows you can import in a synchronous way | 5000 |
|
||||||
| BASEROW\_MAX\_ROW\_REPORT\_ERROR\_COUNT | The maximum row error count tolerated before a file import fails. Before this max error count the import will continue and the non failing rows will be imported and after it, no rows are imported at all. | 30 |
|
| BASEROW\_MAX\_ROW\_REPORT\_ERROR\_COUNT | The maximum row error count tolerated before a file import fails. Before this max error count the import will continue and the non failing rows will be imported and after it, no rows are imported at all. | 30 |
|
||||||
|
|
||||||
|
### Backend Application Builder Configuration
|
||||||
|
| Name | Description | Defaults |
|
||||||
|
|---------------------------|--------------------------------------------------------------------------------------------------------------------------|------------------------|
|
||||||
|
| BASEROW\_BUILDER\_DOMAINS | A comma separated list of domain names that can be used as the domains to create sub domains in the application builder. | |
|
||||||
|
|
||||||
|
|
||||||
### User file upload Configuration
|
### User file upload Configuration
|
||||||
|
|
||||||
Baserow needs somewhere to store the following types of files:
|
Baserow needs somewhere to store the following types of files:
|
||||||
|
@ -242,6 +248,8 @@ domain than your Baserow, you need to make sure CORS is configured correctly.
|
||||||
| BASEROW_MAX_SNAPSHOTS_PER_GROUP | Controls how many application snapshots can be created per workspace. | -1 (unlimited) |
|
| BASEROW_MAX_SNAPSHOTS_PER_GROUP | Controls how many application snapshots can be created per workspace. | -1 (unlimited) |
|
||||||
| BASEROW\_USE\_PG\_FULLTEXT\_SEARCH | By default, Baserow will use Postgres full-text as its search backend. If the product is installed on a system with limited disk space, and less accurate results / degraded search performance is acceptable, then switch this setting off by setting it to false. | true |
|
| BASEROW\_USE\_PG\_FULLTEXT\_SEARCH | By default, Baserow will use Postgres full-text as its search backend. If the product is installed on a system with limited disk space, and less accurate results / degraded search performance is acceptable, then switch this setting off by setting it to false. | true |
|
||||||
| BASEROW\_UNIQUE\_ROW\_VALUES\_SIZE\_LIMIT | Sets the limit for the automatic detection of multiselect options when converting a text field to a multiselect field. Increase the value to detect more options automatically, but consider performance implications. | 100 |
|
| BASEROW\_UNIQUE\_ROW\_VALUES\_SIZE\_LIMIT | Sets the limit for the automatic detection of multiselect options when converting a text field to a multiselect field. Increase the value to detect more options automatically, but consider performance implications. | 100 |
|
||||||
|
| BASEROW\_BUILDER\_DOMAINS | A comma separated list of domain names that can be used as the domains to create sub domains in the application builder. | |
|
||||||
|
|
||||||
|
|
||||||
### SSO Configuration
|
### SSO Configuration
|
||||||
| Name | Description | Defaults |
|
| Name | Description | Defaults |
|
||||||
|
|
|
@ -29,26 +29,25 @@
|
||||||
</div>
|
</div>
|
||||||
<template v-if="serverErrors.domain_name">
|
<template v-if="serverErrors.domain_name">
|
||||||
<div v-if="serverErrors.domain_name.code === 'invalid'" class="error">
|
<div v-if="serverErrors.domain_name.code === 'invalid'" class="error">
|
||||||
{{ $t('customDomainForm.invalidDomain') }}
|
{{ $t('domainForm.invalidDomain') }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="serverErrors.domain_name.code === 'unique'" class="error">
|
<div v-if="serverErrors.domain_name.code === 'unique'" class="error">
|
||||||
{{ $t('customDomainForm.notUniqueDomain') }}
|
{{ $t('domainForm.notUniqueDomain') }}
|
||||||
</div></template
|
</div>
|
||||||
>
|
</template>
|
||||||
</FormElement>
|
</FormElement>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import form from '@baserow/modules/core/mixins/form'
|
|
||||||
import { required, maxLength } from 'vuelidate/lib/validators'
|
import { required, maxLength } from 'vuelidate/lib/validators'
|
||||||
|
import domainForm from '@baserow/modules/builder/mixins/domainForm'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DomainForm',
|
name: 'CustomDomainForm',
|
||||||
mixins: [form],
|
mixins: [domainForm],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
serverErrors: { domain_name: null },
|
|
||||||
values: {
|
values: {
|
||||||
domain_name: '',
|
domain_name: '',
|
||||||
},
|
},
|
||||||
|
@ -64,22 +63,5 @@ export default {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
handleServerError(error) {
|
|
||||||
if (error.handler.code !== 'ERROR_REQUEST_BODY_VALIDATION') return false
|
|
||||||
|
|
||||||
this.serverErrors = Object.fromEntries(
|
|
||||||
Object.entries(error.handler.detail || {}).map(([key, value]) => [
|
|
||||||
key,
|
|
||||||
value[0],
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
hasError() {
|
|
||||||
return !this.isFormValid() || this.serverErrors.domain_name !== null
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,15 +4,15 @@
|
||||||
<Error :error="error"></Error>
|
<Error :error="error"></Error>
|
||||||
<FormElement class="control">
|
<FormElement class="control">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
v-model="selectedDomainType"
|
v-model="selectedDomain"
|
||||||
:show-search="false"
|
:show-search="false"
|
||||||
class="domain-settings__domain-type"
|
class="domain-settings__domain-type"
|
||||||
>
|
>
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
v-for="domainType in domainTypes"
|
v-for="option in options"
|
||||||
:key="domainType.getType()"
|
:key="option.value.domain"
|
||||||
:name="domainType.name"
|
:name="option.name"
|
||||||
:value="domainType.getType()"
|
:value="option.value"
|
||||||
/>
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</FormElement>
|
</FormElement>
|
||||||
|
@ -20,7 +20,9 @@
|
||||||
:is="currentDomainType.formComponent"
|
:is="currentDomainType.formComponent"
|
||||||
ref="domainForm"
|
ref="domainForm"
|
||||||
:builder="builder"
|
:builder="builder"
|
||||||
|
:domain="selectedDomain.domain"
|
||||||
@submitted="createDomain($event)"
|
@submitted="createDomain($event)"
|
||||||
|
@error="formHasError = $event"
|
||||||
/>
|
/>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<a @click="hideForm">
|
<a @click="hideForm">
|
||||||
|
@ -29,7 +31,7 @@
|
||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
class="button button--large"
|
class="button button--large"
|
||||||
:disabled="createLoading || formHasError()"
|
:disabled="createLoading || formHasError"
|
||||||
:class="{ 'button--loading': createLoading }"
|
:class="{ 'button--loading': createLoading }"
|
||||||
@click="onSubmit"
|
@click="onSubmit"
|
||||||
>
|
>
|
||||||
|
@ -58,17 +60,29 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedDomainType: 'custom',
|
selectedDomain: { type: 'custom', domain: 'custom' },
|
||||||
createLoading: false,
|
createLoading: false,
|
||||||
|
formHasError: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
domainTypes() {
|
domainTypes() {
|
||||||
return this.$registry.getAll('domain')
|
return Object.values(this.$registry.getAll('domain')) || []
|
||||||
|
},
|
||||||
|
selectedDomainType() {
|
||||||
|
return this.selectedDomain.type
|
||||||
},
|
},
|
||||||
currentDomainType() {
|
currentDomainType() {
|
||||||
return this.$registry.get('domain', this.selectedDomainType)
|
return this.$registry.get('domain', this.selectedDomainType)
|
||||||
},
|
},
|
||||||
|
options() {
|
||||||
|
return this.domainTypes.map((domainType) => domainType.options).flat()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
selectedDomainType() {
|
||||||
|
this.$refs?.domainForm?.reset()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
|
@ -92,13 +106,6 @@ export default {
|
||||||
}
|
}
|
||||||
this.createLoading = false
|
this.createLoading = false
|
||||||
},
|
},
|
||||||
formHasError() {
|
|
||||||
if (this.$refs.domainForm) {
|
|
||||||
return this.$refs.domainForm.hasError()
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleAnyError(error) {
|
handleAnyError(error) {
|
||||||
if (
|
if (
|
||||||
!this.$refs.domainForm ||
|
!this.$refs.domainForm ||
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{{ $t('subDomainDetails.text') }}
|
||||||
|
</p>
|
||||||
|
<div class="actions actions--right">
|
||||||
|
<a
|
||||||
|
class="button button--error"
|
||||||
|
:class="{ 'button--loading': domain._.loading }"
|
||||||
|
@click="$emit('delete')"
|
||||||
|
>
|
||||||
|
{{ $t('action.delete') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'SubDomainDetails',
|
||||||
|
props: {
|
||||||
|
domain: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,59 @@
|
||||||
|
<template>
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<FormElement :error="fieldHasErrors('domain_name')" class="control">
|
||||||
|
<FormInput
|
||||||
|
v-model="domainPrefix"
|
||||||
|
:label="$t('subDomainForm.domainNameLabel')"
|
||||||
|
:error="
|
||||||
|
$v.values.domain_name.$dirty && !$v.values.domain_name.required
|
||||||
|
? $t('error.requiredField')
|
||||||
|
: $v.values.domain_name.$dirty && !$v.values.domain_name.maxLength
|
||||||
|
? $t('error.maxLength', { max: 255 })
|
||||||
|
: serverErrors.domain_name &&
|
||||||
|
serverErrors.domain_name.code === 'invalid'
|
||||||
|
? $t('domainForm.invalidDomain')
|
||||||
|
: serverErrors.domain_name &&
|
||||||
|
serverErrors.domain_name.code === 'unique'
|
||||||
|
? $t('domainForm.notUniqueDomain')
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
@input="serverErrors.domain_name = null"
|
||||||
|
>
|
||||||
|
<template #suffix> .{{ domain }} </template>
|
||||||
|
</FormInput>
|
||||||
|
</FormElement>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { maxLength, required } from 'vuelidate/lib/validators'
|
||||||
|
import domainForm from '@baserow/modules/builder/mixins/domainForm'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SubDomainForm',
|
||||||
|
mixins: [domainForm],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
domainPrefix: '',
|
||||||
|
values: {
|
||||||
|
domain_name: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
domainPrefix(value) {
|
||||||
|
this.values.domain_name = `${value}.${this.domain}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validations() {
|
||||||
|
return {
|
||||||
|
values: {
|
||||||
|
domain_name: {
|
||||||
|
required,
|
||||||
|
maxLength: maxLength(255),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,6 +1,8 @@
|
||||||
import { Registerable } from '@baserow/modules/core/registry'
|
import { Registerable } from '@baserow/modules/core/registry'
|
||||||
import CustomDomainDetails from '@baserow/modules/builder/components/domain/CustomDomainDetails'
|
import CustomDomainDetails from '@baserow/modules/builder/components/domain/CustomDomainDetails'
|
||||||
import CustomDomainForm from '@baserow/modules/builder/components/domain/CustomDomainForm'
|
import CustomDomainForm from '@baserow/modules/builder/components/domain/CustomDomainForm'
|
||||||
|
import SubDomainForm from '@baserow/modules/builder/components/domain/SubDomainForm'
|
||||||
|
import SubDomainDetails from '@baserow/modules/builder/components/domain/SubDomainDetails'
|
||||||
|
|
||||||
export class DomainType extends Registerable {
|
export class DomainType extends Registerable {
|
||||||
get name() {
|
get name() {
|
||||||
|
@ -10,6 +12,14 @@ export class DomainType extends Registerable {
|
||||||
get detailsComponent() {
|
get detailsComponent() {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get formComponent() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CustomDomainType extends DomainType {
|
export class CustomDomainType extends DomainType {
|
||||||
|
@ -28,4 +38,44 @@ export class CustomDomainType extends DomainType {
|
||||||
get formComponent() {
|
get formComponent() {
|
||||||
return CustomDomainForm
|
return CustomDomainForm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: this.app.i18n.t('domainTypes.customName'),
|
||||||
|
value: {
|
||||||
|
type: this.getType(),
|
||||||
|
domain: 'custom',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SubDomainType extends DomainType {
|
||||||
|
static getType() {
|
||||||
|
return 'sub_domain'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.app.i18n.t('domainTypes.subDomainName')
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return this.app.$config.BASEROW_BUILDER_DOMAINS.map((domain) => ({
|
||||||
|
name: this.app.i18n.t('domainTypes.subDomain', { domain }),
|
||||||
|
value: {
|
||||||
|
type: this.getType(),
|
||||||
|
domain,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
get detailsComponent() {
|
||||||
|
return SubDomainDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
get formComponent() {
|
||||||
|
return SubDomainForm
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,17 +155,27 @@
|
||||||
"hostHeader": "Host",
|
"hostHeader": "Host",
|
||||||
"valueHeader": "Value"
|
"valueHeader": "Value"
|
||||||
},
|
},
|
||||||
|
"domainForm": {
|
||||||
|
"invalidDomain": "The provided domain name is invalid",
|
||||||
|
"notUniqueDomain": "The provided domain is already used"
|
||||||
|
},
|
||||||
"customDomainForm": {
|
"customDomainForm": {
|
||||||
"domainNameLabel": "Domain name",
|
"domainNameLabel": "Domain name"
|
||||||
"invalidDomain": "The provided domain name is invalid",
|
},
|
||||||
"notUniqueDomain": "The provided domain is already used"
|
"subDomainForm": {
|
||||||
|
"domainNameLabel": "Domain name"
|
||||||
|
},
|
||||||
|
"subDomainDetails": {
|
||||||
|
"text": "The DNS settings of the domain have already been configured and checked. It works without making any additional changes."
|
||||||
},
|
},
|
||||||
"domainCard": {
|
"domainCard": {
|
||||||
"refresh": "Refresh settings",
|
"refresh": "Refresh settings",
|
||||||
"detailLabel": "Show details"
|
"detailLabel": "Show details"
|
||||||
},
|
},
|
||||||
"domainTypes": {
|
"domainTypes": {
|
||||||
"customName": "Custom domain"
|
"customName": "Custom domain",
|
||||||
|
"subDomainName": "Subdomain",
|
||||||
|
"subDomain": "Subdomain of {domain}"
|
||||||
},
|
},
|
||||||
"linkElement": {
|
"linkElement": {
|
||||||
"noValue": "Unnamed..."
|
"noValue": "Unnamed..."
|
||||||
|
|
46
web-frontend/modules/builder/mixins/domainForm.js
Normal file
46
web-frontend/modules/builder/mixins/domainForm.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import form from '@baserow/modules/core/mixins/form'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [form],
|
||||||
|
props: {
|
||||||
|
domain: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
serverErrors: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasServerErrors() {
|
||||||
|
return Object.values(this.serverErrors).some((value) => value !== null)
|
||||||
|
},
|
||||||
|
hasError() {
|
||||||
|
return !this.isFormValid() || this.hasServerErrors
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
hasError: {
|
||||||
|
handler(value) {
|
||||||
|
this.$emit('error', value)
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleServerError(error) {
|
||||||
|
if (error.handler.code !== 'ERROR_REQUEST_BODY_VALIDATION') return false
|
||||||
|
|
||||||
|
this.serverErrors = Object.fromEntries(
|
||||||
|
Object.entries(error.handler.detail || {}).map(([key, value]) => [
|
||||||
|
key,
|
||||||
|
value[0],
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
|
@ -49,7 +49,10 @@ import {
|
||||||
VisibilityPageSidePanelType,
|
VisibilityPageSidePanelType,
|
||||||
StylePageSidePanelType,
|
StylePageSidePanelType,
|
||||||
} from '@baserow/modules/builder/pageSidePanelTypes'
|
} from '@baserow/modules/builder/pageSidePanelTypes'
|
||||||
import { CustomDomainType } from '@baserow/modules/builder/domainTypes'
|
import {
|
||||||
|
CustomDomainType,
|
||||||
|
SubDomainType,
|
||||||
|
} from '@baserow/modules/builder/domainTypes'
|
||||||
import { PagePageSettingsType } from '@baserow/modules/builder/pageSettingsTypes'
|
import { PagePageSettingsType } from '@baserow/modules/builder/pageSettingsTypes'
|
||||||
import {
|
import {
|
||||||
TextPathParamType,
|
TextPathParamType,
|
||||||
|
@ -158,6 +161,7 @@ export default (context) => {
|
||||||
app.$registry.register('pageSidePanel', new EventsPageSidePanelType(context))
|
app.$registry.register('pageSidePanel', new EventsPageSidePanelType(context))
|
||||||
|
|
||||||
app.$registry.register('domain', new CustomDomainType(context))
|
app.$registry.register('domain', new CustomDomainType(context))
|
||||||
|
app.$registry.register('domain', new SubDomainType(context))
|
||||||
|
|
||||||
app.$registry.register('pageSettings', new PagePageSettingsType(context))
|
app.$registry.register('pageSettings', new PagePageSettingsType(context))
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
export default (client) => {
|
export default (client) => {
|
||||||
return {
|
return {
|
||||||
async fetchAll(builderId) {
|
fetchAll(builderId) {
|
||||||
// TODO Manually add the domain type while the backend doesn't support it.
|
return client.get(`builder/${builderId}/domains/`)
|
||||||
const result = await client.get(`builder/${builderId}/domains/`)
|
|
||||||
result.data = result.data.map((domain) => ({ type: 'custom', ...domain }))
|
|
||||||
return result
|
|
||||||
},
|
},
|
||||||
async create(builderId, { type, ...data }) {
|
create(builderId, data) {
|
||||||
// TODO For now we manage the type manually.
|
return client.post(`builder/${builderId}/domains/`, data)
|
||||||
const result = await client.post(`builder/${builderId}/domains/`, data)
|
|
||||||
result.data.type = 'custom'
|
|
||||||
return result
|
|
||||||
},
|
},
|
||||||
delete(domainId) {
|
delete(domainId) {
|
||||||
return client.delete(`builder/domains/${domainId}/`)
|
return client.delete(`builder/domains/${domainId}/`)
|
||||||
|
|
|
@ -54,10 +54,10 @@ const actions = {
|
||||||
|
|
||||||
commit('SET_ITEMS', { domains })
|
commit('SET_ITEMS', { domains })
|
||||||
},
|
},
|
||||||
async create({ commit }, { builderId, ...data }) {
|
async create({ commit }, { builderId, type, ...data }) {
|
||||||
const { data: domain } = await DomainService(this.$client).create(
|
const { data: domain } = await DomainService(this.$client).create(
|
||||||
builderId,
|
builderId,
|
||||||
data
|
{ type, ...data }
|
||||||
)
|
)
|
||||||
|
|
||||||
commit('ADD_ITEM', { domain })
|
commit('ADD_ITEM', { domain })
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
.domain-settings__domain-type {
|
.domain-settings__domain-type {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 30%;
|
width: fit-content;
|
||||||
|
|
||||||
|
.dropdown__items {
|
||||||
|
width: min-content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.domains-settings__loading {
|
.domains-settings__loading {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.form-input {
|
.form-input {
|
||||||
display: block;
|
display: flex;
|
||||||
border: 1px solid $color-neutral-400;
|
border: 1px solid $color-neutral-400;
|
||||||
position: relative;
|
position: relative;
|
||||||
@include rounded($rounded);
|
@include rounded($rounded);
|
||||||
|
@ -96,3 +96,14 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-input__suffix {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: fit-content;
|
||||||
|
border-left: 1px solid $color-neutral-400;
|
||||||
|
appearance: none;
|
||||||
|
padding: 0 12px;
|
||||||
|
outline: none;
|
||||||
|
line-height: 100%;
|
||||||
|
}
|
||||||
|
|
|
@ -43,6 +43,11 @@
|
||||||
class="form-input__icon fas"
|
class="form-input__icon fas"
|
||||||
:class="[`fa-${icon}`]"
|
:class="[`fa-${icon}`]"
|
||||||
/>
|
/>
|
||||||
|
<div class="form-input__suffix disabled">
|
||||||
|
<div>
|
||||||
|
<slot name="suffix"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasError" class="error">
|
<div v-if="hasError" class="error">
|
||||||
{{ error }}
|
{{ error }}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
} from '@baserow/modules/core/utils/dom'
|
} from '@baserow/modules/core/utils/dom'
|
||||||
|
|
||||||
import dropdownHelpers from './dropdownHelpers'
|
import dropdownHelpers from './dropdownHelpers'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [dropdownHelpers],
|
mixins: [dropdownHelpers],
|
||||||
|
@ -325,7 +326,7 @@ export default {
|
||||||
getSelectedProperty(value, property) {
|
getSelectedProperty(value, property) {
|
||||||
for (const i in this.getDropdownItemComponents()) {
|
for (const i in this.getDropdownItemComponents()) {
|
||||||
const item = this.getDropdownItemComponents()[i]
|
const item = this.getDropdownItemComponents()[i]
|
||||||
if (item.value === value) {
|
if (_.isEqual(item.value, value)) {
|
||||||
return item[property]
|
return item[property]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,7 +339,7 @@ export default {
|
||||||
hasValue() {
|
hasValue() {
|
||||||
for (const i in this.getDropdownItemComponents()) {
|
for (const i in this.getDropdownItemComponents()) {
|
||||||
const item = this.getDropdownItemComponents()[i]
|
const item = this.getDropdownItemComponents()[i]
|
||||||
if (item.value === this.value) {
|
if (_.isEqual(item.value, this.value)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,7 +367,9 @@ export default {
|
||||||
)
|
)
|
||||||
|
|
||||||
const isArrowUp = event.key === 'ArrowUp'
|
const isArrowUp = event.key === 'ArrowUp'
|
||||||
let index = children.findIndex((item) => item.value === this.hover)
|
let index = children.findIndex((item) =>
|
||||||
|
_.isEqual(item.value, this.hover)
|
||||||
|
)
|
||||||
index = isArrowUp ? index - 1 : index + 1
|
index = isArrowUp ? index - 1 : index + 1
|
||||||
|
|
||||||
// Check if the new index is within the allowed range.
|
// Check if the new index is within the allowed range.
|
||||||
|
|
|
@ -91,6 +91,9 @@ export default function CoreModule(options) {
|
||||||
process.env.BASEROW_UNIQUE_ROW_VALUES_SIZE_LIMIT ?? 100,
|
process.env.BASEROW_UNIQUE_ROW_VALUES_SIZE_LIMIT ?? 100,
|
||||||
BASEROW_ROW_PAGE_SIZE_LIMIT:
|
BASEROW_ROW_PAGE_SIZE_LIMIT:
|
||||||
parseInt(process.env.BASEROW_ROW_PAGE_SIZE_LIMIT) ?? 200,
|
parseInt(process.env.BASEROW_ROW_PAGE_SIZE_LIMIT) ?? 200,
|
||||||
|
BASEROW_BUILDER_DOMAINS: process.env.BASEROW_BUILDER_DOMAINS
|
||||||
|
? process.env.BASEROW_BUILDER_DOMAINS.split(',')
|
||||||
|
: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
const locales = [
|
const locales = [
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue