mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-15 01:28:30 +00:00
Polymorphic applications request serializers
This commit is contained in:
parent
e74cfc67cf
commit
8886632a15
28 changed files with 252 additions and 190 deletions
backend
src/baserow
api
compat/api
contrib
core
ws
tests/baserow
api/applications
compat/api/applications
contrib/database
core
changelog/entries/unreleased/breaking_change
enterprise/backend/tests/baserow_enterprise_tests
|
@ -16,3 +16,9 @@ ERROR_APPLICATION_OPERATION_NOT_SUPPORTED = (
|
|||
HTTP_400_BAD_REQUEST,
|
||||
"The application does not support this operation.",
|
||||
)
|
||||
|
||||
ERROR_APPLICATION_TYPE_DOES_NOT_EXIST = (
|
||||
"ERROR_APPLICATION_TYPE_DOES_NOT_EXIST",
|
||||
HTTP_400_BAD_REQUEST,
|
||||
"{e}",
|
||||
)
|
||||
|
|
|
@ -4,6 +4,7 @@ from drf_spectacular.openapi import OpenApiTypes
|
|||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from baserow.api.polymorphic import PolymorphicSerializer
|
||||
from baserow.api.workspaces.serializers import WorkspaceSerializer
|
||||
from baserow.core.db import specific_iterator
|
||||
from baserow.core.models import Application
|
||||
|
@ -40,15 +41,13 @@ class ApplicationSerializer(serializers.ModelSerializer):
|
|||
return application_type_registry.get_by_model(instance.specific_class).type
|
||||
|
||||
|
||||
class SpecificApplicationSerializer(ApplicationSerializer):
|
||||
def to_representation(self, instance):
|
||||
specific_instance = instance.specific
|
||||
return get_application_serializer(
|
||||
specific_instance, context=self.context
|
||||
).to_representation(specific_instance)
|
||||
class PolymorphicApplicationResponseSerializer(PolymorphicSerializer):
|
||||
base_class = ApplicationSerializer
|
||||
registry = application_type_registry
|
||||
request = False
|
||||
|
||||
|
||||
class ApplicationCreateSerializer(serializers.ModelSerializer):
|
||||
class BaseApplicationCreatePolymorphicSerializer(serializers.ModelSerializer):
|
||||
type = serializers.ChoiceField(
|
||||
choices=lazy(application_type_registry.get_types, list)()
|
||||
)
|
||||
|
@ -59,12 +58,24 @@ class ApplicationCreateSerializer(serializers.ModelSerializer):
|
|||
fields = ("name", "type", "init_with_data")
|
||||
|
||||
|
||||
class ApplicationUpdateSerializer(serializers.ModelSerializer):
|
||||
class PolymorphicApplicationCreateSerializer(PolymorphicSerializer):
|
||||
base_class = BaseApplicationCreatePolymorphicSerializer
|
||||
registry = application_type_registry
|
||||
request = True
|
||||
|
||||
|
||||
class BaseApplicationUpdatePolymorphicSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Application
|
||||
fields = ("name",)
|
||||
|
||||
|
||||
class PolymorphicApplicationUpdateSerializer(PolymorphicSerializer):
|
||||
base_class = BaseApplicationUpdatePolymorphicSerializer
|
||||
registry = application_type_registry
|
||||
request = True
|
||||
|
||||
|
||||
class OrderApplicationsSerializer(serializers.Serializer):
|
||||
application_ids = serializers.ListField(
|
||||
child=serializers.IntegerField(),
|
||||
|
@ -72,31 +83,6 @@ class OrderApplicationsSerializer(serializers.Serializer):
|
|||
)
|
||||
|
||||
|
||||
def get_application_serializer(instance, **kwargs):
|
||||
"""
|
||||
Returns an instantiated serializer based on the instance class type. Custom
|
||||
serializers can be defined per application type. This function will return the one
|
||||
that is set else it will return the default one.
|
||||
|
||||
:param instance: The instance where a serializer is needed for.
|
||||
:type instance: Application
|
||||
:return: An instantiated serializer for the instance.
|
||||
:rtype: ApplicationSerializer
|
||||
"""
|
||||
|
||||
application = application_type_registry.get_by_model(instance.specific_class)
|
||||
serializer_class = application.instance_serializer_class
|
||||
|
||||
if not serializer_class:
|
||||
serializer_class = ApplicationSerializer
|
||||
|
||||
context = kwargs.pop("context", {})
|
||||
|
||||
context["application"] = application
|
||||
|
||||
return serializer_class(instance, context=context, **kwargs)
|
||||
|
||||
|
||||
class InstallTemplateJobApplicationsSerializer(serializers.JSONField):
|
||||
def to_representation(self, value):
|
||||
application_ids = super().to_representation(value)
|
||||
|
@ -109,4 +95,6 @@ class InstallTemplateJobApplicationsSerializer(serializers.JSONField):
|
|||
pk__in=application_ids, workspace__trashed=False
|
||||
)
|
||||
)
|
||||
return [get_application_serializer(app).data for app in applications]
|
||||
return [
|
||||
PolymorphicApplicationResponseSerializer(app).data for app in applications
|
||||
]
|
||||
|
|
|
@ -11,6 +11,7 @@ from rest_framework.views import APIView
|
|||
from baserow.api.applications.errors import (
|
||||
ERROR_APPLICATION_DOES_NOT_EXIST,
|
||||
ERROR_APPLICATION_NOT_IN_GROUP,
|
||||
ERROR_APPLICATION_TYPE_DOES_NOT_EXIST,
|
||||
)
|
||||
from baserow.api.decorators import map_exceptions, validate_body
|
||||
from baserow.api.errors import ERROR_GROUP_DOES_NOT_EXIST, ERROR_USER_NOT_IN_GROUP
|
||||
|
@ -22,7 +23,7 @@ from baserow.api.schemas import (
|
|||
get_error_schema,
|
||||
)
|
||||
from baserow.api.trash.errors import ERROR_CANNOT_DELETE_ALREADY_DELETED_ITEM
|
||||
from baserow.api.utils import DiscriminatorMappingSerializer
|
||||
from baserow.api.utils import validate_data
|
||||
from baserow.core.action.registries import action_type_registry
|
||||
from baserow.core.actions import (
|
||||
CreateApplicationActionType,
|
||||
|
@ -34,6 +35,7 @@ from baserow.core.db import specific_iterator
|
|||
from baserow.core.exceptions import (
|
||||
ApplicationDoesNotExist,
|
||||
ApplicationNotInWorkspace,
|
||||
ApplicationTypeDoesNotExist,
|
||||
UserNotInWorkspace,
|
||||
WorkspaceDoesNotExist,
|
||||
)
|
||||
|
@ -51,20 +53,12 @@ from baserow.core.registries import application_type_registry
|
|||
from baserow.core.trash.exceptions import CannotDeleteAlreadyDeletedItem
|
||||
|
||||
from .serializers import (
|
||||
ApplicationCreateSerializer,
|
||||
ApplicationSerializer,
|
||||
ApplicationUpdateSerializer,
|
||||
OrderApplicationsSerializer,
|
||||
get_application_serializer,
|
||||
PolymorphicApplicationCreateSerializer,
|
||||
PolymorphicApplicationResponseSerializer,
|
||||
PolymorphicApplicationUpdateSerializer,
|
||||
)
|
||||
|
||||
application_type_serializers = {
|
||||
application_type.type: (
|
||||
application_type.instance_serializer_class or ApplicationSerializer
|
||||
)
|
||||
for application_type in application_type_registry.registry.values()
|
||||
}
|
||||
|
||||
DuplicateApplicationJobTypeSerializer = job_type_registry.get(
|
||||
DuplicateApplicationJobType.type
|
||||
).get_serializer_class(
|
||||
|
@ -86,9 +80,7 @@ class AllApplicationsView(APIView):
|
|||
"workspaces that the user has access to are going to be listed here."
|
||||
),
|
||||
responses={
|
||||
200: DiscriminatorMappingSerializer(
|
||||
"Applications", application_type_serializers, many=True
|
||||
),
|
||||
200: PolymorphicApplicationResponseSerializer(many=True),
|
||||
400: get_error_schema(["ERROR_USER_NOT_IN_GROUP"]),
|
||||
},
|
||||
)
|
||||
|
@ -130,8 +122,8 @@ class AllApplicationsView(APIView):
|
|||
)
|
||||
|
||||
data = [
|
||||
get_application_serializer(
|
||||
application, context={"request": request, "application": application}
|
||||
PolymorphicApplicationResponseSerializer(
|
||||
application, context={"request": request}
|
||||
).data
|
||||
for application in applications
|
||||
]
|
||||
|
@ -167,9 +159,7 @@ class ApplicationsView(APIView):
|
|||
"type. An application always belongs to a single workspace."
|
||||
),
|
||||
responses={
|
||||
200: DiscriminatorMappingSerializer(
|
||||
"Applications", application_type_serializers, many=True
|
||||
),
|
||||
200: PolymorphicApplicationResponseSerializer(many=True),
|
||||
400: get_error_schema(["ERROR_USER_NOT_IN_GROUP"]),
|
||||
404: get_error_schema(["ERROR_GROUP_DOES_NOT_EXIST"]),
|
||||
},
|
||||
|
@ -221,7 +211,9 @@ class ApplicationsView(APIView):
|
|||
)
|
||||
|
||||
data = [
|
||||
get_application_serializer(application, context={"request": request}).data
|
||||
PolymorphicApplicationResponseSerializer(
|
||||
application, context={"request": request}
|
||||
).data
|
||||
for application in applications
|
||||
]
|
||||
return Response(data)
|
||||
|
@ -246,11 +238,9 @@ class ApplicationsView(APIView):
|
|||
"`workspace_id` parameter. If the authorized user does not belong to the workspace "
|
||||
"an error will be returned."
|
||||
),
|
||||
request=ApplicationCreateSerializer,
|
||||
request=PolymorphicApplicationCreateSerializer,
|
||||
responses={
|
||||
200: DiscriminatorMappingSerializer(
|
||||
"Applications", application_type_serializers
|
||||
),
|
||||
200: PolymorphicApplicationResponseSerializer,
|
||||
400: get_error_schema(
|
||||
["ERROR_USER_NOT_IN_GROUP", "ERROR_REQUEST_BODY_VALIDATION"]
|
||||
),
|
||||
|
@ -258,13 +248,14 @@ class ApplicationsView(APIView):
|
|||
},
|
||||
)
|
||||
@transaction.atomic
|
||||
@validate_body(ApplicationCreateSerializer)
|
||||
@map_exceptions(
|
||||
{
|
||||
WorkspaceDoesNotExist: ERROR_GROUP_DOES_NOT_EXIST,
|
||||
UserNotInWorkspace: ERROR_USER_NOT_IN_GROUP,
|
||||
ApplicationTypeDoesNotExist: ERROR_APPLICATION_TYPE_DOES_NOT_EXIST,
|
||||
}
|
||||
)
|
||||
@validate_body(PolymorphicApplicationCreateSerializer)
|
||||
def post(self, request, data, workspace_id):
|
||||
"""Creates a new application for a user."""
|
||||
|
||||
|
@ -280,13 +271,14 @@ class ApplicationsView(APIView):
|
|||
application = action_type_registry.get_by_type(CreateApplicationActionType).do(
|
||||
request.user,
|
||||
workspace,
|
||||
data["type"],
|
||||
name=data["name"],
|
||||
init_with_data=data["init_with_data"],
|
||||
application_type=data.pop("type"),
|
||||
**data,
|
||||
)
|
||||
|
||||
return Response(
|
||||
get_application_serializer(application, context={"request": request}).data
|
||||
PolymorphicApplicationResponseSerializer(
|
||||
application, context={"request": request}
|
||||
).data
|
||||
)
|
||||
|
||||
|
||||
|
@ -309,11 +301,9 @@ class ApplicationView(APIView):
|
|||
"application's workspace. The properties that belong to the application can "
|
||||
"differ per type."
|
||||
),
|
||||
request=ApplicationCreateSerializer,
|
||||
request=PolymorphicApplicationCreateSerializer,
|
||||
responses={
|
||||
200: DiscriminatorMappingSerializer(
|
||||
"Applications", application_type_serializers
|
||||
),
|
||||
200: PolymorphicApplicationResponseSerializer,
|
||||
400: get_error_schema(
|
||||
["ERROR_USER_NOT_IN_GROUP", "ERROR_REQUEST_BODY_VALIDATION"]
|
||||
),
|
||||
|
@ -334,7 +324,9 @@ class ApplicationView(APIView):
|
|||
)
|
||||
|
||||
return Response(
|
||||
get_application_serializer(application, context={"request": request}).data
|
||||
PolymorphicApplicationResponseSerializer(
|
||||
application, context={"request": request}
|
||||
).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
|
@ -356,11 +348,9 @@ class ApplicationView(APIView):
|
|||
"workspace. It is not possible to change the type, but properties like the "
|
||||
"name can be changed."
|
||||
),
|
||||
request=ApplicationUpdateSerializer,
|
||||
request=PolymorphicApplicationUpdateSerializer,
|
||||
responses={
|
||||
200: DiscriminatorMappingSerializer(
|
||||
"Applications", application_type_serializers
|
||||
),
|
||||
200: PolymorphicApplicationResponseSerializer,
|
||||
400: get_error_schema(
|
||||
["ERROR_USER_NOT_IN_GROUP", "ERROR_REQUEST_BODY_VALIDATION"]
|
||||
),
|
||||
|
@ -368,14 +358,14 @@ class ApplicationView(APIView):
|
|||
},
|
||||
)
|
||||
@transaction.atomic
|
||||
@validate_body(ApplicationUpdateSerializer)
|
||||
@map_exceptions(
|
||||
{
|
||||
ApplicationDoesNotExist: ERROR_APPLICATION_DOES_NOT_EXIST,
|
||||
UserNotInWorkspace: ERROR_USER_NOT_IN_GROUP,
|
||||
ApplicationTypeDoesNotExist: ERROR_APPLICATION_TYPE_DOES_NOT_EXIST,
|
||||
}
|
||||
)
|
||||
def patch(self, request, data, application_id):
|
||||
def patch(self, request, application_id):
|
||||
"""Updates the application if the user belongs to the workspace."""
|
||||
|
||||
application = (
|
||||
|
@ -387,12 +377,27 @@ class ApplicationView(APIView):
|
|||
.specific
|
||||
)
|
||||
|
||||
# We validate the data in the method here so that we can
|
||||
# pass the application instance directly into the serializer.
|
||||
# This ensures the `PolymorphicSerializer` can correctly determine
|
||||
# the type of the instance, otherwise PATCH requests would need to
|
||||
# include the `type` field in the request body.
|
||||
data = validate_data(
|
||||
PolymorphicApplicationUpdateSerializer,
|
||||
request.data,
|
||||
partial=True,
|
||||
return_validated=True,
|
||||
instance=application,
|
||||
)
|
||||
|
||||
application = action_type_registry.get_by_type(UpdateApplicationActionType).do(
|
||||
request.user, application, name=data["name"]
|
||||
request.user, application, **data
|
||||
)
|
||||
|
||||
return Response(
|
||||
get_application_serializer(application, context={"request": request}).data
|
||||
PolymorphicApplicationResponseSerializer(
|
||||
application, context={"request": request}
|
||||
).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
|
|
|
@ -104,6 +104,7 @@ class PolymorphicSerializer(serializers.Serializer):
|
|||
instance_type.model_class,
|
||||
base_class=self.base_class,
|
||||
request=self.request,
|
||||
context=self.context,
|
||||
)
|
||||
|
||||
ret = serializer.to_representation(instance)
|
||||
|
@ -121,6 +122,7 @@ class PolymorphicSerializer(serializers.Serializer):
|
|||
instance_type.model_class,
|
||||
base_class=self.base_class,
|
||||
request=self.request,
|
||||
context=self.context,
|
||||
)
|
||||
|
||||
return serializer.to_internal_value(data)
|
||||
|
@ -132,6 +134,7 @@ class PolymorphicSerializer(serializers.Serializer):
|
|||
instance_type.model_class,
|
||||
base_class=self.base_class,
|
||||
request=self.request,
|
||||
context=self.context,
|
||||
)
|
||||
|
||||
return serializer.create(validated_data)
|
||||
|
@ -147,6 +150,7 @@ class PolymorphicSerializer(serializers.Serializer):
|
|||
instance_type.model_class,
|
||||
base_class=self.base_class,
|
||||
request=self.request,
|
||||
context=self.context,
|
||||
)
|
||||
|
||||
return serializer.update(instance, validated_data)
|
||||
|
@ -164,6 +168,8 @@ class PolymorphicSerializer(serializers.Serializer):
|
|||
instance_type.model_class,
|
||||
base_class=self.base_class,
|
||||
request=self.request,
|
||||
context=self.context,
|
||||
data=self.data,
|
||||
)
|
||||
except serializers.ValidationError:
|
||||
child_valid = False
|
||||
|
@ -186,6 +192,7 @@ class PolymorphicSerializer(serializers.Serializer):
|
|||
instance_type.model_class,
|
||||
base_class=self.base_class,
|
||||
request=self.request,
|
||||
context=self.context,
|
||||
)
|
||||
|
||||
validated_data = serializer.run_validation(data)
|
||||
|
|
|
@ -7,8 +7,9 @@ from rest_framework.permissions import AllowAny, IsAuthenticated
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from baserow.api.applications.serializers import get_application_serializer
|
||||
from baserow.api.applications.views import application_type_serializers
|
||||
from baserow.api.applications.serializers import (
|
||||
PolymorphicApplicationResponseSerializer,
|
||||
)
|
||||
from baserow.api.decorators import map_exceptions
|
||||
from baserow.api.errors import ERROR_GROUP_DOES_NOT_EXIST, ERROR_USER_NOT_IN_GROUP
|
||||
from baserow.api.jobs.errors import ERROR_MAX_JOB_COUNT_EXCEEDED
|
||||
|
@ -19,7 +20,6 @@ from baserow.api.schemas import (
|
|||
get_error_schema,
|
||||
)
|
||||
from baserow.api.templates.serializers import TemplateCategoriesSerializer
|
||||
from baserow.api.utils import DiscriminatorMappingSerializer
|
||||
from baserow.core.action.registries import action_type_registry
|
||||
from baserow.core.actions import InstallTemplateActionType
|
||||
from baserow.core.exceptions import (
|
||||
|
@ -97,9 +97,7 @@ class InstallTemplateView(APIView):
|
|||
),
|
||||
request=None,
|
||||
responses={
|
||||
200: DiscriminatorMappingSerializer(
|
||||
"Applications", application_type_serializers, many=True
|
||||
),
|
||||
200: PolymorphicApplicationResponseSerializer(many=True),
|
||||
400: get_error_schema(
|
||||
["ERROR_USER_NOT_IN_GROUP", "ERROR_TEMPLATE_FILE_DOES_NOT_EXIST"]
|
||||
),
|
||||
|
@ -128,7 +126,7 @@ class InstallTemplateView(APIView):
|
|||
).do(request.user, workspace, template)
|
||||
|
||||
data = [
|
||||
get_application_serializer(application).data
|
||||
PolymorphicApplicationResponseSerializer(application).data
|
||||
for application in installed_applications
|
||||
]
|
||||
return Response(data)
|
||||
|
|
|
@ -173,6 +173,7 @@ def validate_data(
|
|||
exception_to_raise: Type[Exception] = RequestBodyValidationException,
|
||||
many: bool = False,
|
||||
return_validated: bool = False,
|
||||
instance=None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Validates the provided data via the provided serializer class. If the data doesn't
|
||||
|
@ -186,10 +187,11 @@ def validate_data(
|
|||
invalid.
|
||||
:param many: Indicates whether the serializer should be constructed as a list.
|
||||
:param return_validated: Returns validated_data from DRF serializer
|
||||
:param instance: The instance that is being updated.
|
||||
:return: The data after being validated by the serializer.
|
||||
"""
|
||||
|
||||
serializer = serializer_class(data=data, partial=partial, many=many)
|
||||
serializer = serializer_class(instance, data=data, partial=partial, many=many)
|
||||
if not serializer.is_valid():
|
||||
detail = serialize_validation_errors_recursive(serializer.errors)
|
||||
raise exception_to_raise(detail)
|
||||
|
|
|
@ -5,9 +5,9 @@ from drf_spectacular.utils import extend_schema
|
|||
|
||||
from baserow.api.applications.errors import ERROR_APPLICATION_NOT_IN_GROUP
|
||||
from baserow.api.applications.serializers import (
|
||||
ApplicationCreateSerializer,
|
||||
ApplicationSerializer,
|
||||
OrderApplicationsSerializer,
|
||||
PolymorphicApplicationCreateSerializer,
|
||||
PolymorphicApplicationResponseSerializer,
|
||||
)
|
||||
from baserow.api.applications.views import ApplicationsView, OrderApplicationsView
|
||||
from baserow.api.decorators import map_exceptions, validate_body
|
||||
|
@ -17,7 +17,6 @@ from baserow.api.schemas import (
|
|||
CLIENT_UNDO_REDO_ACTION_GROUP_ID_SCHEMA_PARAMETER,
|
||||
get_error_schema,
|
||||
)
|
||||
from baserow.api.utils import DiscriminatorMappingSerializer
|
||||
from baserow.compat.api.conf import (
|
||||
APPLICATION_DEPRECATION_PREFIXES as DEPRECATION_PREFIXES,
|
||||
)
|
||||
|
@ -26,14 +25,6 @@ from baserow.core.exceptions import (
|
|||
UserNotInWorkspace,
|
||||
WorkspaceDoesNotExist,
|
||||
)
|
||||
from baserow.core.registries import application_type_registry
|
||||
|
||||
application_type_serializers = {
|
||||
application_type.type: (
|
||||
application_type.instance_serializer_class or ApplicationSerializer
|
||||
)
|
||||
for application_type in application_type_registry.registry.values()
|
||||
}
|
||||
|
||||
|
||||
class ApplicationsCompatView(ApplicationsView):
|
||||
|
@ -59,9 +50,7 @@ class ApplicationsCompatView(ApplicationsView):
|
|||
"belongs to a single group."
|
||||
),
|
||||
responses={
|
||||
200: DiscriminatorMappingSerializer(
|
||||
"Applications", application_type_serializers, many=True
|
||||
),
|
||||
200: PolymorphicApplicationResponseSerializer(many=True),
|
||||
400: get_error_schema(["ERROR_USER_NOT_IN_GROUP"]),
|
||||
404: get_error_schema(["ERROR_GROUP_DOES_NOT_EXIST"]),
|
||||
},
|
||||
|
@ -103,11 +92,9 @@ class ApplicationsCompatView(ApplicationsView):
|
|||
"parameter. If the authorized user does not belong to the group an "
|
||||
"error will be returned."
|
||||
),
|
||||
request=ApplicationCreateSerializer,
|
||||
request=PolymorphicApplicationCreateSerializer,
|
||||
responses={
|
||||
200: DiscriminatorMappingSerializer(
|
||||
"Applications", application_type_serializers
|
||||
),
|
||||
200: PolymorphicApplicationResponseSerializer(),
|
||||
400: get_error_schema(
|
||||
["ERROR_USER_NOT_IN_GROUP", "ERROR_REQUEST_BODY_VALIDATION"]
|
||||
),
|
||||
|
@ -115,7 +102,7 @@ class ApplicationsCompatView(ApplicationsView):
|
|||
},
|
||||
)
|
||||
@transaction.atomic
|
||||
@validate_body(ApplicationCreateSerializer)
|
||||
@validate_body(PolymorphicApplicationCreateSerializer)
|
||||
@map_exceptions(
|
||||
{
|
||||
WorkspaceDoesNotExist: ERROR_GROUP_DOES_NOT_EXIST,
|
||||
|
|
|
@ -3,7 +3,9 @@ from django.db import transaction
|
|||
from drf_spectacular.openapi import OpenApiParameter, OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from baserow.api.applications.views import application_type_serializers
|
||||
from baserow.api.applications.serializers import (
|
||||
PolymorphicApplicationResponseSerializer,
|
||||
)
|
||||
from baserow.api.decorators import map_exceptions
|
||||
from baserow.api.errors import ERROR_GROUP_DOES_NOT_EXIST, ERROR_USER_NOT_IN_GROUP
|
||||
from baserow.api.jobs.errors import ERROR_MAX_JOB_COUNT_EXCEEDED
|
||||
|
@ -23,7 +25,6 @@ from baserow.api.templates.views import (
|
|||
InstallTemplateView,
|
||||
TemplatesView,
|
||||
)
|
||||
from baserow.api.utils import DiscriminatorMappingSerializer
|
||||
from baserow.compat.api.conf import (
|
||||
TEMPLATES_DEPRECATION_PREFIXES as DEPRECATION_PREFIXES,
|
||||
)
|
||||
|
@ -86,9 +87,7 @@ class InstallTemplateCompatView(InstallTemplateView):
|
|||
),
|
||||
request=None,
|
||||
responses={
|
||||
200: DiscriminatorMappingSerializer(
|
||||
"Applications", application_type_serializers, many=True
|
||||
),
|
||||
200: PolymorphicApplicationResponseSerializer(many=True),
|
||||
400: get_error_schema(
|
||||
["ERROR_USER_NOT_IN_GROUP", "ERROR_TEMPLATE_FILE_DOES_NOT_EXIST"]
|
||||
),
|
||||
|
|
|
@ -3,7 +3,6 @@ from typing import List
|
|||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from baserow.api.applications.serializers import ApplicationSerializer
|
||||
from baserow.api.user_sources.serializers import PolymorphicUserSourceSerializer
|
||||
from baserow.contrib.builder.api.pages.serializers import PageSerializer
|
||||
from baserow.contrib.builder.api.theme.serializers import (
|
||||
|
@ -16,7 +15,7 @@ from baserow.core.handler import CoreHandler
|
|||
from baserow.core.user_sources.operations import ListUserSourcesApplicationOperationType
|
||||
|
||||
|
||||
class BuilderSerializer(ApplicationSerializer):
|
||||
class BuilderSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
The builder serializer.
|
||||
|
||||
|
@ -37,9 +36,10 @@ class BuilderSerializer(ApplicationSerializer):
|
|||
"the theme settings."
|
||||
)
|
||||
|
||||
class Meta(ApplicationSerializer.Meta):
|
||||
class Meta:
|
||||
model = Builder
|
||||
ref_name = "BuilderApplication"
|
||||
fields = ApplicationSerializer.Meta.fields + ("pages", "user_sources", "theme")
|
||||
fields = ("id", "name", "pages", "theme", "user_sources")
|
||||
|
||||
@extend_schema_field(PageSerializer(many=True))
|
||||
def get_pages(self, instance: Builder) -> List:
|
||||
|
|
|
@ -25,21 +25,29 @@ from baserow.core.user_sources.handler import UserSourceHandler
|
|||
from baserow.core.utils import ChildProgressBuilder
|
||||
|
||||
|
||||
# This lazy loads the serializer, which is needed because the `BuilderSerializer`
|
||||
# needs to decorate the `get_theme` with the `extend_schema_field` using a
|
||||
# generated serializer that needs the registry to be populated.
|
||||
def lazy_get_instance_serializer_class():
|
||||
from baserow.contrib.builder.api.serializers import BuilderSerializer
|
||||
|
||||
return BuilderSerializer
|
||||
|
||||
|
||||
class BuilderApplicationType(ApplicationType):
|
||||
type = "builder"
|
||||
model_class = Builder
|
||||
supports_actions = False
|
||||
supports_integrations = True
|
||||
supports_user_sources = True
|
||||
|
||||
# This lazy loads the serializer, which is needed because the `BuilderSerializer`
|
||||
# needs to decorate the `get_theme` with the `extend_schema_field` using a
|
||||
# generated serializer that needs the registry to be populated.
|
||||
@property
|
||||
def instance_serializer_class(self):
|
||||
from baserow.contrib.builder.api.serializers import BuilderSerializer
|
||||
|
||||
return BuilderSerializer
|
||||
serializer_field_names = [
|
||||
"name",
|
||||
"pages",
|
||||
"user_sources",
|
||||
"theme",
|
||||
]
|
||||
request_serializer_field_names = []
|
||||
serializer_mixins = [lazy_get_instance_serializer_class]
|
||||
|
||||
def get_api_urls(self):
|
||||
from .api import urls as api_urls
|
||||
|
|
|
@ -3,22 +3,25 @@ from typing import List
|
|||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from baserow.api.applications.serializers import ApplicationSerializer
|
||||
from baserow.contrib.database.api.tables.serializers import TableSerializer
|
||||
from baserow.contrib.database.models import Database
|
||||
from baserow.contrib.database.operations import ListTablesDatabaseTableOperationType
|
||||
from baserow.core.handler import CoreHandler
|
||||
|
||||
|
||||
class DatabaseSerializer(ApplicationSerializer):
|
||||
class DatabaseSerializer(serializers.ModelSerializer):
|
||||
tables = serializers.SerializerMethodField(
|
||||
help_text="This field is specific to the `database` application and contains "
|
||||
"an array of tables that are in the database."
|
||||
)
|
||||
|
||||
class Meta(ApplicationSerializer.Meta):
|
||||
ref_name = "DatabaseApplication"
|
||||
fields = ApplicationSerializer.Meta.fields + ("tables",)
|
||||
class Meta:
|
||||
model = Database
|
||||
fields = (
|
||||
"id",
|
||||
"name",
|
||||
"tables",
|
||||
)
|
||||
|
||||
@extend_schema_field(TableSerializer(many=True))
|
||||
def get_tables(self, instance: Database) -> List:
|
||||
|
|
|
@ -45,7 +45,12 @@ from .table.models import Table
|
|||
class DatabaseApplicationType(ApplicationType):
|
||||
type = "database"
|
||||
model_class = Database
|
||||
serializer_mixins = [DatabaseSerializer]
|
||||
instance_serializer_class = DatabaseSerializer
|
||||
serializer_field_names = ["tables"]
|
||||
# Mark the request serializer field names as empty, otherwise
|
||||
# the polymorphic request serializer will try and serialize tables.
|
||||
request_serializer_field_names = []
|
||||
|
||||
def pre_delete(self, database):
|
||||
"""
|
||||
|
|
|
@ -390,12 +390,7 @@ class CreateApplicationActionType(UndoableActionType):
|
|||
|
||||
@classmethod
|
||||
def do(
|
||||
cls,
|
||||
user: AbstractUser,
|
||||
workspace: Workspace,
|
||||
application_type: str,
|
||||
name: str,
|
||||
init_with_data: bool = False,
|
||||
cls, user: AbstractUser, workspace: Workspace, application_type: str, **kwargs
|
||||
) -> Any:
|
||||
"""
|
||||
Creates a new application based on the provided type. See
|
||||
|
@ -405,14 +400,13 @@ class CreateApplicationActionType(UndoableActionType):
|
|||
:param user: The user creating the application.
|
||||
:param workspace: The workspace to create the application in.
|
||||
:param application_type: The type of application to create.
|
||||
:param name: The name of the new application.
|
||||
:param init_with_data: Whether the application should be initialized with
|
||||
some default data. Defaults to False.
|
||||
:param kwargs: Additional parameters to pass to the application creation.
|
||||
:return: The created Application model instance.
|
||||
"""
|
||||
|
||||
init_with_data = kwargs.get("init_with_data", False)
|
||||
application = CoreHandler().create_application(
|
||||
user, workspace, application_type, name=name, init_with_data=init_with_data
|
||||
user, workspace, application_type, **kwargs
|
||||
)
|
||||
|
||||
application_type = application_type_registry.get_by_model(
|
||||
|
@ -548,7 +542,7 @@ class UpdateApplicationActionType(UndoableActionType):
|
|||
original_application_name: str
|
||||
|
||||
@classmethod
|
||||
def do(cls, user: AbstractUser, application: Application, name: str) -> Application:
|
||||
def do(cls, user: AbstractUser, application: Application, **kwargs) -> Application:
|
||||
"""
|
||||
Updates an existing application instance.
|
||||
See baserow.core.handler.CoreHandler.update_application for further details.
|
||||
|
@ -556,30 +550,34 @@ class UpdateApplicationActionType(UndoableActionType):
|
|||
|
||||
:param user: The user on whose behalf the application is updated.
|
||||
:param application: The application instance that needs to be updated.
|
||||
:param name: The new name of the application.
|
||||
:param kwargs: Additional parameters to pass to the application update.
|
||||
:raises ValueError: If one of the provided parameters is invalid.
|
||||
:return: The updated application instance.
|
||||
"""
|
||||
|
||||
original_name = application.name
|
||||
|
||||
application = CoreHandler().update_application(user, application, name)
|
||||
application = CoreHandler().update_application(user, application, **kwargs)
|
||||
application_type = application_type_registry.get_by_model(
|
||||
application.specific_class
|
||||
)
|
||||
workspace = application.workspace
|
||||
|
||||
params = cls.Params(
|
||||
workspace.id,
|
||||
workspace.name,
|
||||
application_type.type,
|
||||
application.id,
|
||||
name,
|
||||
original_name,
|
||||
)
|
||||
cls.register_action(
|
||||
user, params, scope=cls.scope(workspace.id), workspace=workspace
|
||||
)
|
||||
# Only register an action if this application type supports actions.
|
||||
# At the moment, the builder application doesn't use actions and need
|
||||
# to bypass registering.
|
||||
if application_type.supports_actions:
|
||||
params = cls.Params(
|
||||
workspace.id,
|
||||
workspace.name,
|
||||
application_type.type,
|
||||
application.id,
|
||||
kwargs["name"],
|
||||
original_name,
|
||||
)
|
||||
cls.register_action(
|
||||
user, params, scope=cls.scope(workspace.id), workspace=workspace
|
||||
)
|
||||
|
||||
return application
|
||||
|
||||
|
@ -591,13 +589,15 @@ class UpdateApplicationActionType(UndoableActionType):
|
|||
def undo(cls, user: AbstractUser, params: Params, action_being_undone: Action):
|
||||
application = CoreHandler().get_application(params.application_id).specific
|
||||
CoreHandler().update_application(
|
||||
user, application, params.original_application_name
|
||||
user, application, name=params.original_application_name
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def redo(cls, user: AbstractUser, params: Params, action_being_redone: Action):
|
||||
application = CoreHandler().get_application(params.application_id).specific
|
||||
CoreHandler().update_application(user, application, params.application_name)
|
||||
CoreHandler().update_application(
|
||||
user, application, name=params.application_name
|
||||
)
|
||||
|
||||
|
||||
class DuplicateApplicationActionType(UndoableActionType):
|
||||
|
|
|
@ -110,6 +110,7 @@ from .types import (
|
|||
from .utils import (
|
||||
ChildProgressBuilder,
|
||||
atomic_if_not_already,
|
||||
extract_allowed,
|
||||
find_unused_name,
|
||||
set_allowed_attrs,
|
||||
)
|
||||
|
@ -122,6 +123,9 @@ tracer = trace.get_tracer(__name__)
|
|||
|
||||
|
||||
class CoreHandler(metaclass=baserow_trace_methods(tracer)):
|
||||
default_create_allowed_fields = ["name", "init_with_data"]
|
||||
default_update_allowed_fields = ["name"]
|
||||
|
||||
def get_settings(self):
|
||||
"""
|
||||
Returns a settings model instance containing all the admin configured settings.
|
||||
|
@ -1306,8 +1310,8 @@ class CoreHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
user: AbstractUser,
|
||||
workspace: Workspace,
|
||||
type_name: str,
|
||||
name: str,
|
||||
init_with_data: bool = False,
|
||||
**kwargs,
|
||||
) -> Application:
|
||||
"""
|
||||
Creates a new application based on the provided type.
|
||||
|
@ -1316,9 +1320,9 @@ class CoreHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
:param workspace: The workspace that the application instance belongs to.
|
||||
:param type_name: The type name of the application. ApplicationType can be
|
||||
registered via the ApplicationTypeRegistry.
|
||||
:param name: The name of the application.
|
||||
:param init_with_data: Whether the application should be initialized with
|
||||
some default data. Defaults to False.
|
||||
:param kwargs: Additional parameters to pass to the application creation.
|
||||
:return: The created application instance.
|
||||
"""
|
||||
|
||||
|
@ -1330,8 +1334,11 @@ class CoreHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
)
|
||||
|
||||
application_type = application_type_registry.get(type_name)
|
||||
allowed_values = extract_allowed(
|
||||
kwargs, self.default_create_allowed_fields + application_type.allowed_fields
|
||||
)
|
||||
application = application_type.create_application(
|
||||
user, workspace, name, init_with_data
|
||||
user, workspace, init_with_data=init_with_data, **allowed_values
|
||||
)
|
||||
|
||||
application_created.send(self, application=application, user=user)
|
||||
|
@ -1356,14 +1363,14 @@ class CoreHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
)
|
||||
|
||||
def update_application(
|
||||
self, user: AbstractUser, application: Application, name: str
|
||||
self, user: AbstractUser, application: Application, **kwargs
|
||||
) -> Application:
|
||||
"""
|
||||
Updates an existing application instance.
|
||||
|
||||
:param user: The user on whose behalf the application is updated.
|
||||
:param application: The application instance that needs to be updated.
|
||||
:param name: The new name of the application.
|
||||
:param kwargs: Additional parameters to pass to the application update.
|
||||
:return: The updated application instance.
|
||||
"""
|
||||
|
||||
|
@ -1374,7 +1381,14 @@ class CoreHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
context=application,
|
||||
)
|
||||
|
||||
application.name = name
|
||||
application_type = application_type_registry.get_by_model(application)
|
||||
allowed_updates = extract_allowed(
|
||||
kwargs, self.default_update_allowed_fields + application_type.allowed_fields
|
||||
)
|
||||
|
||||
for key, value in allowed_updates.items():
|
||||
setattr(application, key, value)
|
||||
|
||||
application.save()
|
||||
|
||||
application_updated.send(self, application=application, user=user)
|
||||
|
|
|
@ -7,7 +7,7 @@ from rest_framework.exceptions import ValidationError
|
|||
|
||||
from baserow.api.applications.serializers import (
|
||||
InstallTemplateJobApplicationsSerializer,
|
||||
SpecificApplicationSerializer,
|
||||
PolymorphicApplicationResponseSerializer,
|
||||
)
|
||||
from baserow.api.errors import (
|
||||
ERROR_GROUP_DOES_NOT_EXIST,
|
||||
|
@ -65,8 +65,12 @@ class DuplicateApplicationJobType(JobType):
|
|||
|
||||
serializer_field_names = ["original_application", "duplicated_application"]
|
||||
serializer_field_overrides = {
|
||||
"original_application": SpecificApplicationSerializer(read_only=True),
|
||||
"duplicated_application": SpecificApplicationSerializer(read_only=True),
|
||||
"original_application": PolymorphicApplicationResponseSerializer(
|
||||
read_only=True
|
||||
),
|
||||
"duplicated_application": PolymorphicApplicationResponseSerializer(
|
||||
read_only=True
|
||||
),
|
||||
}
|
||||
|
||||
def transaction_atomic_context(self, job: "DuplicateApplicationJob"):
|
||||
|
|
|
@ -32,6 +32,8 @@ from .export_serialized import CoreExportSerializedStructure
|
|||
from .registry import (
|
||||
APIUrlsInstanceMixin,
|
||||
APIUrlsRegistryMixin,
|
||||
CustomFieldsInstanceMixin,
|
||||
CustomFieldsRegistryMixin,
|
||||
Instance,
|
||||
ModelInstanceMixin,
|
||||
ModelRegistryMixin,
|
||||
|
@ -211,6 +213,7 @@ class PluginRegistry(APIUrlsRegistryMixin, Registry):
|
|||
class ApplicationType(
|
||||
APIUrlsInstanceMixin,
|
||||
ModelInstanceMixin["Application"],
|
||||
CustomFieldsInstanceMixin,
|
||||
Instance,
|
||||
):
|
||||
"""
|
||||
|
@ -280,25 +283,24 @@ class ApplicationType(
|
|||
)
|
||||
|
||||
def create_application(
|
||||
self, user, workspace: "Workspace", name: str, init_with_data: bool = False
|
||||
self, user, workspace: "Workspace", init_with_data: bool = False, **kwargs
|
||||
) -> "Application":
|
||||
"""
|
||||
Creates a new application instance of this type and returns it.
|
||||
|
||||
:param user: The user that is creating the application.
|
||||
:param workspace: The workspace that the application will be created in.
|
||||
:param name: The name of the application.
|
||||
:param init_with_data: Whether the application should be created with some
|
||||
initial data. Defaults to False.
|
||||
:param kwargs: Additional parameters to pass to the application creation,
|
||||
these values have already been validated by the view and are allowed.
|
||||
:return: The newly created application instance.
|
||||
"""
|
||||
|
||||
model = self.model_class
|
||||
last_order = model.get_last_order(workspace)
|
||||
|
||||
instance = model.objects.create(
|
||||
workspace=workspace, order=last_order, name=name
|
||||
)
|
||||
instance = model.objects.create(workspace=workspace, order=last_order, **kwargs)
|
||||
if init_with_data:
|
||||
self.init_application(user, instance)
|
||||
return instance
|
||||
|
@ -486,6 +488,7 @@ class ApplicationTypeRegistry(
|
|||
APIUrlsRegistryMixin,
|
||||
ModelRegistryMixin[ApplicationSubClassInstance, ApplicationType],
|
||||
Registry[ApplicationType],
|
||||
CustomFieldsRegistryMixin,
|
||||
):
|
||||
"""
|
||||
With the application registry it is possible to register new applications. An
|
||||
|
|
|
@ -2,6 +2,7 @@ import contextlib
|
|||
import typing
|
||||
from abc import ABC, abstractmethod
|
||||
from functools import lru_cache
|
||||
from types import FunctionType
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
|
@ -171,11 +172,22 @@ class CustomFieldsInstanceMixin:
|
|||
request_serializer, extra_params, **kwargs
|
||||
)
|
||||
|
||||
# Build a list of serializers, using two methods:
|
||||
# 1) Serializers can provide a function (note: we can't test with callable()
|
||||
# as serializers are callable) which lazy loads a serializer mixin, or
|
||||
# 2) Serializers can provide a serializer mixin directly.
|
||||
dynamic_serializer_mixins = []
|
||||
for serializer_mixin in self.serializer_mixins:
|
||||
if isinstance(serializer_mixin, FunctionType):
|
||||
dynamic_serializer_mixins.append(serializer_mixin())
|
||||
else:
|
||||
dynamic_serializer_mixins.append(serializer_mixin)
|
||||
|
||||
return get_serializer_class(
|
||||
self.model_class,
|
||||
field_names,
|
||||
field_overrides=field_overrides,
|
||||
base_mixins=self.serializer_mixins,
|
||||
base_mixins=dynamic_serializer_mixins,
|
||||
meta_extra_kwargs=self.serializer_extra_kwargs,
|
||||
meta_ref_name=meta_ref_name,
|
||||
base_class=base_class,
|
||||
|
|
|
@ -4,7 +4,7 @@ from django.dispatch import receiver
|
|||
|
||||
from baserow.api.applications.serializers import (
|
||||
ApplicationSerializer,
|
||||
get_application_serializer,
|
||||
PolymorphicApplicationResponseSerializer,
|
||||
)
|
||||
from baserow.api.user.serializers import PublicUserSerializer
|
||||
from baserow.api.workspaces.invitations.serializers import (
|
||||
|
@ -222,7 +222,7 @@ def workspace_restored(sender, workspace_user, user, **kwargs):
|
|||
)
|
||||
applications_qs = specific_iterator(applications_qs)
|
||||
applications = [
|
||||
get_application_serializer(
|
||||
PolymorphicApplicationResponseSerializer(
|
||||
application, context={"user": workspace_user.user}
|
||||
).data
|
||||
for application in applications_qs
|
||||
|
|
|
@ -303,7 +303,9 @@ def broadcast_application_created(
|
|||
:return:
|
||||
"""
|
||||
|
||||
from baserow.api.applications.serializers import get_application_serializer
|
||||
from baserow.api.applications.serializers import (
|
||||
PolymorphicApplicationResponseSerializer,
|
||||
)
|
||||
from baserow.core.handler import CoreHandler
|
||||
from baserow.core.models import Application, WorkspaceUser
|
||||
from baserow.core.operations import ReadApplicationOperationType
|
||||
|
@ -332,7 +334,7 @@ def broadcast_application_created(
|
|||
payload_map = {}
|
||||
for user_id in user_ids:
|
||||
user = users_in_workspace_id_map[user_id]
|
||||
application_serialized = get_application_serializer(
|
||||
application_serialized = PolymorphicApplicationResponseSerializer(
|
||||
application, context={"user": user}
|
||||
).data
|
||||
|
||||
|
|
|
@ -208,8 +208,10 @@ def test_create_application(api_client, data_fixture):
|
|||
)
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_400_BAD_REQUEST
|
||||
assert response_json["error"] == "ERROR_REQUEST_BODY_VALIDATION"
|
||||
assert response_json["detail"]["type"][0]["code"] == "invalid_choice"
|
||||
assert response_json["error"] == "ERROR_APPLICATION_TYPE_DOES_NOT_EXIST"
|
||||
assert (
|
||||
response_json["detail"] == "The application type NOT_EXISTING does not exist."
|
||||
)
|
||||
|
||||
response = api_client.post(
|
||||
reverse("api:applications:list", kwargs={"workspace_id": 99999}),
|
||||
|
|
|
@ -186,8 +186,10 @@ def test_create_application(api_client, data_fixture, group_compat_timebomb):
|
|||
)
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_400_BAD_REQUEST
|
||||
assert response_json["error"] == "ERROR_REQUEST_BODY_VALIDATION"
|
||||
assert response_json["detail"]["type"][0]["code"] == "invalid_choice"
|
||||
assert response_json["error"] == "ERROR_APPLICATION_TYPE_DOES_NOT_EXIST"
|
||||
assert (
|
||||
response_json["detail"] == "The application type NOT_EXISTING does not exist."
|
||||
)
|
||||
|
||||
response = api_client.post(
|
||||
reverse("api:applications_compat:list", kwargs={"group_id": 99999}),
|
||||
|
|
|
@ -134,12 +134,12 @@ def test_create_application_and_init_with_data(data_fixture):
|
|||
user = data_fixture.create_user()
|
||||
workspace = data_fixture.create_workspace(user=user)
|
||||
database_1 = core_handler.create_application(
|
||||
user, workspace, "database", "Database 1"
|
||||
user, workspace, "database", name="Database 1"
|
||||
)
|
||||
assert Table.objects.filter(database=database_1).count() == 0
|
||||
|
||||
database_2 = core_handler.create_application(
|
||||
user, workspace, "database", "Database 2", init_with_data=True
|
||||
user, workspace, "database", True, name="Database 2"
|
||||
)
|
||||
assert Table.objects.filter(database=database_2).count() == 1
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ def test_can_undo_update_application(data_fixture, django_assert_num_queries):
|
|||
)
|
||||
|
||||
action_type_registry.get_by_type(UpdateApplicationActionType).do(
|
||||
user, application, application_name_new
|
||||
user, application, name=application_name_new
|
||||
)
|
||||
|
||||
assert Application.objects.get(pk=application.id).name == application_name_new
|
||||
|
@ -187,7 +187,7 @@ def test_can_undo_redo_update_application(data_fixture, django_assert_num_querie
|
|||
)
|
||||
|
||||
action_type_registry.get_by_type(UpdateApplicationActionType).do(
|
||||
user, application, application_name_new
|
||||
user, application, name=application_name_new
|
||||
)
|
||||
|
||||
assert Application.objects.get(pk=application.id).name == application_name_new
|
||||
|
|
|
@ -778,16 +778,16 @@ def test_undo_redo_action_group_with_interleaved_actions(data_fixture):
|
|||
|
||||
def _interleave_actions():
|
||||
user_1_app = action_type_registry.get_by_type(CreateApplicationActionType).do(
|
||||
user_1, workspace=workspace, application_type="database", name="u1_a1"
|
||||
user_1, workspace, application_type="database", name="u1_a1"
|
||||
)
|
||||
user_2_app = action_type_registry.get_by_type(CreateApplicationActionType).do(
|
||||
user_2, workspace=workspace, application_type="database", name="u2_a1"
|
||||
user_2, workspace, application_type="database", name="u2_a1"
|
||||
)
|
||||
action_type_registry.get_by_type(UpdateApplicationActionType).do(
|
||||
user_1, application=user_1_app, name="u1_a2"
|
||||
user_1, user_1_app, name="u1_a2"
|
||||
)
|
||||
action_type_registry.get_by_type(UpdateApplicationActionType).do(
|
||||
user_2, application=user_2_app, name="u2_a2"
|
||||
user_2, user_2_app, name="u2_a2"
|
||||
)
|
||||
|
||||
_interleave_actions()
|
||||
|
|
|
@ -52,7 +52,7 @@ def test_can_submit_duplicate_application_job(data_fixture):
|
|||
application_name = "My Application"
|
||||
|
||||
application = CoreHandler().create_application(
|
||||
user, workspace, application_type, application_name
|
||||
user, workspace, application_type, name=application_name
|
||||
)
|
||||
|
||||
assert Application.objects.count() == 1
|
||||
|
@ -110,7 +110,7 @@ def test_can_undo_duplicate_application_job(data_fixture):
|
|||
application_name = "My Application"
|
||||
|
||||
application = CoreHandler().create_application(
|
||||
user, workspace, application_type, application_name
|
||||
user, workspace, application_type, name=application_name
|
||||
)
|
||||
|
||||
JobHandler().create_and_start_job(
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"type": "breaking_change",
|
||||
"message": "Made the application request serializers polymorphic.",
|
||||
"issue_number": null,
|
||||
"bullet_points": [
|
||||
"Breaking change: when an application is created or updated via the API with an invalid `type` we will now return an `ERROR_APPLICATION_TYPE_DOES_NOT_EXIST` error code, rather than `ERROR_REQUEST_BODY_VALIDATION`."
|
||||
],
|
||||
"created_at": "2024-03-15"
|
||||
}
|
|
@ -232,10 +232,14 @@ def test_audit_log_export_workspace_csv_correctly(
|
|||
workspace = enterprise_data_fixture.create_workspace(user=user)
|
||||
|
||||
with freeze_time("2023-01-01 12:00:00"):
|
||||
app_1 = CreateApplicationActionType.do(user, workspace, "database", "App 1")
|
||||
app_1 = CreateApplicationActionType.do(
|
||||
user, workspace, "database", name="App 1"
|
||||
)
|
||||
|
||||
with freeze_time("2023-01-01 12:00:10"):
|
||||
app_2 = CreateApplicationActionType.do(user, workspace, "database", "App 2")
|
||||
app_2 = CreateApplicationActionType.do(
|
||||
user, workspace, "database", name="App 2"
|
||||
)
|
||||
|
||||
csv_settings = {
|
||||
"csv_column_separator": ",",
|
||||
|
|
|
@ -2,7 +2,9 @@ import pytest
|
|||
from asgiref.sync import sync_to_async
|
||||
from channels.testing import WebsocketCommunicator
|
||||
|
||||
from baserow.api.applications.serializers import get_application_serializer
|
||||
from baserow.api.applications.serializers import (
|
||||
PolymorphicApplicationResponseSerializer,
|
||||
)
|
||||
from baserow.config.asgi import application
|
||||
from baserow.core.handler import CoreHandler
|
||||
from baserow.core.trash.handler import TrashHandler
|
||||
|
@ -158,6 +160,6 @@ async def test_workspace_restored_applications_arent_leaked(data_fixture):
|
|||
workspace_restored_message = await get_message(communicator, "group_restored")
|
||||
assert workspace_restored_message is not None
|
||||
assert workspace_restored_message["applications"] == [
|
||||
get_application_serializer(database).data
|
||||
PolymorphicApplicationResponseSerializer(database).data
|
||||
]
|
||||
await communicator.disconnect()
|
||||
|
|
Loading…
Add table
Reference in a new issue