mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-08-14 12:50:37 +03:00
F: Improve data validation for user inputs and backend serializers
This commit is contained in:
parent
9964b8df23
commit
ebd1bfbd2c
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
from .basics import AccessPolicySerializer, LocationSerializer, RenameLocationSerializer
|
from .basics import AccessPolicySerializer, LocationSerializer, RenameLocationSerializer
|
||||||
from .data_access import (
|
from .data_access import (
|
||||||
|
LibraryItemBaseNonStrictSerializer,
|
||||||
LibraryItemBaseSerializer,
|
LibraryItemBaseSerializer,
|
||||||
LibraryItemCloneSerializer,
|
LibraryItemCloneSerializer,
|
||||||
LibraryItemDetailsSerializer,
|
LibraryItemDetailsSerializer,
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from shared import messages as msg
|
from shared import messages as msg
|
||||||
|
from shared.serializers import StrictSerializer
|
||||||
|
|
||||||
from ..models import AccessPolicy, validate_location
|
from ..models import AccessPolicy, validate_location
|
||||||
|
|
||||||
|
|
||||||
class LocationSerializer(serializers.Serializer):
|
class LocationSerializer(StrictSerializer):
|
||||||
''' Serializer: Item location. '''
|
''' Serializer: Item location. '''
|
||||||
location = serializers.CharField(max_length=500)
|
location = serializers.CharField(max_length=500)
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ class LocationSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class RenameLocationSerializer(serializers.Serializer):
|
class RenameLocationSerializer(StrictSerializer):
|
||||||
''' Serializer: rename location. '''
|
''' Serializer: rename location. '''
|
||||||
target = serializers.CharField(max_length=500)
|
target = serializers.CharField(max_length=500)
|
||||||
new_location = serializers.CharField(max_length=500)
|
new_location = serializers.CharField(max_length=500)
|
||||||
|
@ -37,7 +38,7 @@ class RenameLocationSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class AccessPolicySerializer(serializers.Serializer):
|
class AccessPolicySerializer(StrictSerializer):
|
||||||
''' Serializer: Constituenta renaming. '''
|
''' Serializer: Constituenta renaming. '''
|
||||||
access_policy = serializers.CharField()
|
access_policy = serializers.CharField()
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,13 @@ from rest_framework import serializers
|
||||||
from rest_framework.serializers import PrimaryKeyRelatedField as PKField
|
from rest_framework.serializers import PrimaryKeyRelatedField as PKField
|
||||||
|
|
||||||
from apps.rsform.models import Constituenta
|
from apps.rsform.models import Constituenta
|
||||||
|
from shared import messages
|
||||||
|
from shared.serializers import StrictModelSerializer, StrictSerializer
|
||||||
|
|
||||||
from ..models import LibraryItem, Version
|
from ..models import LibraryItem, Version
|
||||||
|
|
||||||
|
|
||||||
class LibraryItemBaseSerializer(serializers.ModelSerializer):
|
class LibraryItemBaseSerializer(StrictModelSerializer):
|
||||||
''' Serializer: LibraryItem entry full access. '''
|
''' Serializer: LibraryItem entry full access. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -17,7 +19,16 @@ class LibraryItemBaseSerializer(serializers.ModelSerializer):
|
||||||
read_only_fields = ('id',)
|
read_only_fields = ('id',)
|
||||||
|
|
||||||
|
|
||||||
class LibraryItemReferenceSerializer(serializers.ModelSerializer):
|
class LibraryItemBaseNonStrictSerializer(serializers.ModelSerializer):
|
||||||
|
''' Serializer: LibraryItem entry full access and no strict validation. '''
|
||||||
|
class Meta:
|
||||||
|
''' serializer metadata. '''
|
||||||
|
model = LibraryItem
|
||||||
|
fields = '__all__'
|
||||||
|
read_only_fields = ('id',)
|
||||||
|
|
||||||
|
|
||||||
|
class LibraryItemReferenceSerializer(StrictModelSerializer):
|
||||||
''' Serializer: reference to LibraryItem. '''
|
''' Serializer: reference to LibraryItem. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -25,7 +36,7 @@ class LibraryItemReferenceSerializer(serializers.ModelSerializer):
|
||||||
fields = 'id', 'alias'
|
fields = 'id', 'alias'
|
||||||
|
|
||||||
|
|
||||||
class LibraryItemSerializer(serializers.ModelSerializer):
|
class LibraryItemSerializer(StrictModelSerializer):
|
||||||
''' Serializer: LibraryItem entry limited access. '''
|
''' Serializer: LibraryItem entry limited access. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -34,17 +45,27 @@ class LibraryItemSerializer(serializers.ModelSerializer):
|
||||||
read_only_fields = ('id', 'item_type', 'owner', 'location', 'access_policy')
|
read_only_fields = ('id', 'item_type', 'owner', 'location', 'access_policy')
|
||||||
|
|
||||||
|
|
||||||
class LibraryItemCloneSerializer(serializers.ModelSerializer):
|
class LibraryItemCloneSerializer(StrictSerializer):
|
||||||
''' Serializer: LibraryItem cloning. '''
|
''' Serializer: LibraryItem cloning. '''
|
||||||
items = PKField(many=True, required=False, queryset=Constituenta.objects.all().only('pk'))
|
class ItemCloneData(StrictModelSerializer):
|
||||||
|
''' Serialize: LibraryItem cloning data. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
model = LibraryItem
|
model = LibraryItem
|
||||||
exclude = ['id', 'item_type', 'owner']
|
exclude = ['id', 'item_type', 'owner', 'read_only']
|
||||||
|
|
||||||
|
items = PKField(many=True, queryset=Constituenta.objects.all().only('pk', 'schema_id'))
|
||||||
|
item_data = ItemCloneData()
|
||||||
|
|
||||||
|
def validate_items(self, value):
|
||||||
|
schema = self.context.get('schema')
|
||||||
|
invalid = [item.pk for item in value if item.schema_id != schema.id]
|
||||||
|
if invalid:
|
||||||
|
raise serializers.ValidationError(messages.constituentsInvalid(invalid))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class VersionSerializer(serializers.ModelSerializer):
|
class VersionSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Version data. '''
|
''' Serializer: Version data. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -53,7 +74,7 @@ class VersionSerializer(serializers.ModelSerializer):
|
||||||
read_only_fields = ('id', 'item', 'time_create')
|
read_only_fields = ('id', 'item', 'time_create')
|
||||||
|
|
||||||
|
|
||||||
class VersionInnerSerializer(serializers.ModelSerializer):
|
class VersionInnerSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Version data for list of versions. '''
|
''' Serializer: Version data for list of versions. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -62,7 +83,7 @@ class VersionInnerSerializer(serializers.ModelSerializer):
|
||||||
read_only_fields = ('id', 'item', 'time_create')
|
read_only_fields = ('id', 'item', 'time_create')
|
||||||
|
|
||||||
|
|
||||||
class VersionCreateSerializer(serializers.ModelSerializer):
|
class VersionCreateSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Version create data. '''
|
''' Serializer: Version create data. '''
|
||||||
items = PKField(many=True, required=False, default=None, queryset=Constituenta.objects.all().only('pk'))
|
items = PKField(many=True, required=False, default=None, queryset=Constituenta.objects.all().only('pk'))
|
||||||
|
|
||||||
|
@ -72,7 +93,7 @@ class VersionCreateSerializer(serializers.ModelSerializer):
|
||||||
fields = 'version', 'description', 'items'
|
fields = 'version', 'description', 'items'
|
||||||
|
|
||||||
|
|
||||||
class LibraryItemDetailsSerializer(serializers.ModelSerializer):
|
class LibraryItemDetailsSerializer(StrictModelSerializer):
|
||||||
''' Serializer: LibraryItem detailed data. '''
|
''' Serializer: LibraryItem detailed data. '''
|
||||||
editors = serializers.SerializerMethodField()
|
editors = serializers.SerializerMethodField()
|
||||||
versions = serializers.SerializerMethodField()
|
versions = serializers.SerializerMethodField()
|
||||||
|
@ -90,11 +111,11 @@ class LibraryItemDetailsSerializer(serializers.ModelSerializer):
|
||||||
return [VersionInnerSerializer(item).data for item in instance.getQ_versions().order_by('pk')]
|
return [VersionInnerSerializer(item).data for item in instance.getQ_versions().order_by('pk')]
|
||||||
|
|
||||||
|
|
||||||
class UserTargetSerializer(serializers.Serializer):
|
class UserTargetSerializer(StrictSerializer):
|
||||||
''' Serializer: Target single User. '''
|
''' Serializer: Target single User. '''
|
||||||
user = PKField(many=False, queryset=User.objects.all().only('pk'))
|
user = PKField(many=False, queryset=User.objects.all().only('pk'))
|
||||||
|
|
||||||
|
|
||||||
class UsersListSerializer(serializers.Serializer):
|
class UsersListSerializer(StrictSerializer):
|
||||||
''' Serializer: List of Users. '''
|
''' Serializer: List of Users. '''
|
||||||
users = PKField(many=True, queryset=User.objects.all().only('pk'))
|
users = PKField(many=True, queryset=User.objects.all().only('pk'))
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
''' Utility serializers for REST API schema - SHOULD NOT BE ACCESSED DIRECTLY. '''
|
''' Utility serializers for REST API schema - SHOULD NOT BE ACCESSED DIRECTLY. '''
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from shared.serializers import StrictSerializer
|
||||||
|
|
||||||
class NewVersionResponse(serializers.Serializer):
|
|
||||||
|
class NewVersionResponse(StrictSerializer):
|
||||||
''' Serializer: Create version response. '''
|
''' Serializer: Create version response. '''
|
||||||
version = serializers.IntegerField()
|
version = serializers.IntegerField()
|
||||||
schema = serializers.JSONField()
|
schema = serializers.JSONField()
|
||||||
|
|
|
@ -345,13 +345,12 @@ class TestLibraryViewset(EndpointTester):
|
||||||
term_resolved='люди'
|
term_resolved='люди'
|
||||||
)
|
)
|
||||||
|
|
||||||
data = {'title': 'Title1337'}
|
data = {'item_data': {'title': 'Title1337'}, 'items': []}
|
||||||
self.executeNotFound(data=data, item=self.invalid_item)
|
self.executeNotFound(data=data, item=self.invalid_item)
|
||||||
self.executeCreated(data=data, item=self.unowned.pk)
|
self.executeCreated(data=data, item=self.unowned.pk)
|
||||||
|
|
||||||
data = {'title': 'Title1338'}
|
|
||||||
response = self.executeCreated(data=data, item=self.owned.pk)
|
response = self.executeCreated(data=data, item=self.owned.pk)
|
||||||
self.assertEqual(response.data['title'], data['title'])
|
self.assertEqual(response.data['title'], data['item_data']['title'])
|
||||||
self.assertEqual(len(response.data['items']), 2)
|
self.assertEqual(len(response.data['items']), 2)
|
||||||
self.assertEqual(response.data['items'][0]['alias'], x12.alias)
|
self.assertEqual(response.data['items'][0]['alias'], x12.alias)
|
||||||
self.assertEqual(response.data['items'][0]['term_raw'], x12.term_raw)
|
self.assertEqual(response.data['items'][0]['term_raw'], x12.term_raw)
|
||||||
|
@ -359,14 +358,14 @@ class TestLibraryViewset(EndpointTester):
|
||||||
self.assertEqual(response.data['items'][1]['term_raw'], d2.term_raw)
|
self.assertEqual(response.data['items'][1]['term_raw'], d2.term_raw)
|
||||||
self.assertEqual(response.data['items'][1]['term_resolved'], d2.term_resolved)
|
self.assertEqual(response.data['items'][1]['term_resolved'], d2.term_resolved)
|
||||||
|
|
||||||
data = {'title': 'Title1340', 'items': []}
|
data = {'item_data': {'title': 'Title1340'}, 'items': []}
|
||||||
response = self.executeCreated(data=data, item=self.owned.pk)
|
response = self.executeCreated(data=data, item=self.owned.pk)
|
||||||
self.assertEqual(response.data['title'], data['title'])
|
self.assertEqual(response.data['title'], data['item_data']['title'])
|
||||||
self.assertEqual(len(response.data['items']), 2)
|
self.assertEqual(len(response.data['items']), 2)
|
||||||
|
|
||||||
data = {'title': 'Title1341', 'items': [x12.pk]}
|
data = {'item_data': {'title': 'Title1341'}, 'items': [x12.pk]}
|
||||||
response = self.executeCreated(data=data, item=self.owned.pk)
|
response = self.executeCreated(data=data, item=self.owned.pk)
|
||||||
self.assertEqual(response.data['title'], data['title'])
|
self.assertEqual(response.data['title'], data['item_data']['title'])
|
||||||
self.assertEqual(len(response.data['items']), 1)
|
self.assertEqual(len(response.data['items']), 1)
|
||||||
self.assertEqual(response.data['items'][0]['alias'], x12.alias)
|
self.assertEqual(response.data['items'][0]['alias'], x12.alias)
|
||||||
self.assertEqual(response.data['items'][0]['term_raw'], x12.term_raw)
|
self.assertEqual(response.data['items'][0]['term_raw'], x12.term_raw)
|
||||||
|
|
|
@ -151,22 +151,24 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
||||||
@action(detail=True, methods=['post'], url_path='clone')
|
@action(detail=True, methods=['post'], url_path='clone')
|
||||||
def clone(self, request: Request, pk) -> HttpResponse:
|
def clone(self, request: Request, pk) -> HttpResponse:
|
||||||
''' Endpoint: Create deep copy of library item. '''
|
''' Endpoint: Create deep copy of library item. '''
|
||||||
serializer = s.LibraryItemCloneSerializer(data=request.data)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
item = self._get_item()
|
item = self._get_item()
|
||||||
if item.item_type != m.LibraryItemType.RSFORM:
|
if item.item_type != m.LibraryItemType.RSFORM:
|
||||||
return Response(status=c.HTTP_400_BAD_REQUEST)
|
return Response(status=c.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
serializer = s.LibraryItemCloneSerializer(data=request.data, context={'schema': item})
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
data = serializer.validated_data['item_data']
|
||||||
clone = deepcopy(item)
|
clone = deepcopy(item)
|
||||||
clone.pk = None
|
clone.pk = None
|
||||||
clone.owner = cast(User, self.request.user)
|
clone.owner = cast(User, self.request.user)
|
||||||
clone.title = serializer.validated_data['title']
|
clone.title = data['title']
|
||||||
clone.alias = serializer.validated_data.get('alias', '')
|
clone.alias = data.get('alias', '')
|
||||||
clone.description = serializer.validated_data.get('description', '')
|
clone.description = data.get('description', '')
|
||||||
clone.visible = serializer.validated_data.get('visible', True)
|
clone.visible = data.get('visible', True)
|
||||||
clone.read_only = False
|
clone.read_only = False
|
||||||
clone.access_policy = serializer.validated_data.get('access_policy', m.AccessPolicy.PUBLIC)
|
clone.access_policy = data.get('access_policy', m.AccessPolicy.PUBLIC)
|
||||||
clone.location = serializer.validated_data.get('location', m.LocationHead.USER)
|
clone.location = data.get('location', m.LocationHead.USER)
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
clone.save()
|
clone.save()
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
''' Basic serializers that do not interact with database. '''
|
''' Basic serializers that do not interact with database. '''
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from shared.serializers import StrictSerializer
|
||||||
|
|
||||||
class PositionSerializer(serializers.Serializer):
|
|
||||||
|
class PositionSerializer(StrictSerializer):
|
||||||
''' Serializer: Position data. '''
|
''' Serializer: Position data. '''
|
||||||
x = serializers.FloatField()
|
x = serializers.FloatField()
|
||||||
y = serializers.FloatField()
|
y = serializers.FloatField()
|
||||||
|
@ -10,7 +12,7 @@ class PositionSerializer(serializers.Serializer):
|
||||||
height = serializers.FloatField()
|
height = serializers.FloatField()
|
||||||
|
|
||||||
|
|
||||||
class NodeSerializer(serializers.Serializer):
|
class NodeSerializer(StrictSerializer):
|
||||||
''' Oss node serializer. '''
|
''' Oss node serializer. '''
|
||||||
nodeID = serializers.CharField()
|
nodeID = serializers.CharField()
|
||||||
x = serializers.FloatField()
|
x = serializers.FloatField()
|
||||||
|
@ -19,12 +21,12 @@ class NodeSerializer(serializers.Serializer):
|
||||||
height = serializers.FloatField()
|
height = serializers.FloatField()
|
||||||
|
|
||||||
|
|
||||||
class LayoutSerializer(serializers.Serializer):
|
class LayoutSerializer(StrictSerializer):
|
||||||
''' Serializer: Layout data. '''
|
''' Serializer: Layout data. '''
|
||||||
data = serializers.ListField(child=NodeSerializer()) # type: ignore
|
data = serializers.ListField(child=NodeSerializer()) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class SubstitutionExSerializer(serializers.Serializer):
|
class SubstitutionExSerializer(StrictSerializer):
|
||||||
''' Serializer: Substitution extended data. '''
|
''' Serializer: Substitution extended data. '''
|
||||||
operation = serializers.IntegerField()
|
operation = serializers.IntegerField()
|
||||||
original = serializers.IntegerField()
|
original = serializers.IntegerField()
|
||||||
|
|
|
@ -11,12 +11,13 @@ from apps.library.serializers import LibraryItemDetailsSerializer
|
||||||
from apps.rsform.models import Constituenta
|
from apps.rsform.models import Constituenta
|
||||||
from apps.rsform.serializers import SubstitutionSerializerBase
|
from apps.rsform.serializers import SubstitutionSerializerBase
|
||||||
from shared import messages as msg
|
from shared import messages as msg
|
||||||
|
from shared.serializers import StrictModelSerializer, StrictSerializer
|
||||||
|
|
||||||
from ..models import Argument, Block, Inheritance, Operation, OperationSchema, OperationType
|
from ..models import Argument, Block, Inheritance, Operation, OperationSchema, OperationType
|
||||||
from .basics import NodeSerializer, PositionSerializer, SubstitutionExSerializer
|
from .basics import NodeSerializer, PositionSerializer, SubstitutionExSerializer
|
||||||
|
|
||||||
|
|
||||||
class OperationSerializer(serializers.ModelSerializer):
|
class OperationSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Operation data. '''
|
''' Serializer: Operation data. '''
|
||||||
is_import = serializers.BooleanField(default=False, required=False)
|
is_import = serializers.BooleanField(default=False, required=False)
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ class OperationSerializer(serializers.ModelSerializer):
|
||||||
read_only_fields = ('id', 'oss')
|
read_only_fields = ('id', 'oss')
|
||||||
|
|
||||||
|
|
||||||
class BlockSerializer(serializers.ModelSerializer):
|
class BlockSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Block data. '''
|
''' Serializer: Block data. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -36,7 +37,7 @@ class BlockSerializer(serializers.ModelSerializer):
|
||||||
read_only_fields = ('id', 'oss')
|
read_only_fields = ('id', 'oss')
|
||||||
|
|
||||||
|
|
||||||
class ArgumentSerializer(serializers.ModelSerializer):
|
class ArgumentSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Operation data. '''
|
''' Serializer: Operation data. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -44,9 +45,9 @@ class ArgumentSerializer(serializers.ModelSerializer):
|
||||||
fields = ('operation', 'argument')
|
fields = ('operation', 'argument')
|
||||||
|
|
||||||
|
|
||||||
class CreateBlockSerializer(serializers.Serializer):
|
class CreateBlockSerializer(StrictSerializer):
|
||||||
''' Serializer: Block creation. '''
|
''' Serializer: Block creation. '''
|
||||||
class BlockCreateData(serializers.ModelSerializer):
|
class BlockCreateData(StrictModelSerializer):
|
||||||
''' Serializer: Block creation data. '''
|
''' Serializer: Block creation data. '''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -92,9 +93,9 @@ class CreateBlockSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class UpdateBlockSerializer(serializers.Serializer):
|
class UpdateBlockSerializer(StrictSerializer):
|
||||||
''' Serializer: Block update. '''
|
''' Serializer: Block update. '''
|
||||||
class UpdateBlockData(serializers.ModelSerializer):
|
class UpdateBlockData(StrictModelSerializer):
|
||||||
''' Serializer: Block update data. '''
|
''' Serializer: Block update data. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -129,7 +130,7 @@ class UpdateBlockSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class DeleteBlockSerializer(serializers.Serializer):
|
class DeleteBlockSerializer(StrictSerializer):
|
||||||
''' Serializer: Delete block. '''
|
''' Serializer: Delete block. '''
|
||||||
layout = serializers.ListField(
|
layout = serializers.ListField(
|
||||||
child=NodeSerializer()
|
child=NodeSerializer()
|
||||||
|
@ -146,7 +147,7 @@ class DeleteBlockSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class MoveItemsSerializer(serializers.Serializer):
|
class MoveItemsSerializer(StrictSerializer):
|
||||||
''' Serializer: Move items to another parent. '''
|
''' Serializer: Move items to another parent. '''
|
||||||
layout = serializers.ListField(
|
layout = serializers.ListField(
|
||||||
child=NodeSerializer()
|
child=NodeSerializer()
|
||||||
|
@ -190,7 +191,7 @@ class MoveItemsSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class CreateOperationData(serializers.ModelSerializer):
|
class CreateOperationData(StrictModelSerializer):
|
||||||
''' Serializer: Operation creation data. '''
|
''' Serializer: Operation creation data. '''
|
||||||
alias = serializers.CharField()
|
alias = serializers.CharField()
|
||||||
|
|
||||||
|
@ -200,7 +201,7 @@ class CreateOperationData(serializers.ModelSerializer):
|
||||||
fields = 'alias', 'title', 'description', 'parent'
|
fields = 'alias', 'title', 'description', 'parent'
|
||||||
|
|
||||||
|
|
||||||
class CreateSchemaSerializer(serializers.Serializer):
|
class CreateSchemaSerializer(StrictSerializer):
|
||||||
''' Serializer: Schema creation for new operation. '''
|
''' Serializer: Schema creation for new operation. '''
|
||||||
layout = serializers.ListField(child=NodeSerializer())
|
layout = serializers.ListField(child=NodeSerializer())
|
||||||
item_data = CreateOperationData()
|
item_data = CreateOperationData()
|
||||||
|
@ -216,7 +217,7 @@ class CreateSchemaSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class ImportSchemaSerializer(serializers.Serializer):
|
class ImportSchemaSerializer(StrictSerializer):
|
||||||
''' Serializer: Import schema to new operation. '''
|
''' Serializer: Import schema to new operation. '''
|
||||||
layout = serializers.ListField(child=NodeSerializer())
|
layout = serializers.ListField(child=NodeSerializer())
|
||||||
item_data = CreateOperationData()
|
item_data = CreateOperationData()
|
||||||
|
@ -238,7 +239,7 @@ class ImportSchemaSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class CreateSynthesisSerializer(serializers.Serializer):
|
class CreateSynthesisSerializer(StrictSerializer):
|
||||||
''' Serializer: Synthesis operation creation. '''
|
''' Serializer: Synthesis operation creation. '''
|
||||||
layout = serializers.ListField(child=NodeSerializer())
|
layout = serializers.ListField(child=NodeSerializer())
|
||||||
item_data = CreateOperationData()
|
item_data = CreateOperationData()
|
||||||
|
@ -292,9 +293,9 @@ class CreateSynthesisSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class UpdateOperationSerializer(serializers.Serializer):
|
class UpdateOperationSerializer(StrictSerializer):
|
||||||
''' Serializer: Operation update. '''
|
''' Serializer: Operation update. '''
|
||||||
class UpdateOperationData(serializers.ModelSerializer):
|
class UpdateOperationData(StrictModelSerializer):
|
||||||
''' Serializer: Operation update data. '''
|
''' Serializer: Operation update data. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -369,7 +370,7 @@ class UpdateOperationSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class DeleteOperationSerializer(serializers.Serializer):
|
class DeleteOperationSerializer(StrictSerializer):
|
||||||
''' Serializer: Delete operation. '''
|
''' Serializer: Delete operation. '''
|
||||||
layout = serializers.ListField(
|
layout = serializers.ListField(
|
||||||
child=NodeSerializer()
|
child=NodeSerializer()
|
||||||
|
@ -388,7 +389,7 @@ class DeleteOperationSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class TargetOperationSerializer(serializers.Serializer):
|
class TargetOperationSerializer(StrictSerializer):
|
||||||
''' Serializer: Target single operation. '''
|
''' Serializer: Target single operation. '''
|
||||||
layout = serializers.ListField(
|
layout = serializers.ListField(
|
||||||
child=NodeSerializer()
|
child=NodeSerializer()
|
||||||
|
@ -405,7 +406,7 @@ class TargetOperationSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class SetOperationInputSerializer(serializers.Serializer):
|
class SetOperationInputSerializer(StrictSerializer):
|
||||||
''' Serializer: Set input schema for operation. '''
|
''' Serializer: Set input schema for operation. '''
|
||||||
layout = serializers.ListField(
|
layout = serializers.ListField(
|
||||||
child=NodeSerializer()
|
child=NodeSerializer()
|
||||||
|
@ -432,7 +433,7 @@ class SetOperationInputSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class OperationSchemaSerializer(serializers.ModelSerializer):
|
class OperationSchemaSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Detailed data for OSS. '''
|
''' Serializer: Detailed data for OSS. '''
|
||||||
operations = serializers.ListField(
|
operations = serializers.ListField(
|
||||||
child=OperationSerializer()
|
child=OperationSerializer()
|
||||||
|
@ -489,7 +490,7 @@ class OperationSchemaSerializer(serializers.ModelSerializer):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class RelocateConstituentsSerializer(serializers.Serializer):
|
class RelocateConstituentsSerializer(StrictSerializer):
|
||||||
''' Serializer: Relocate constituents. '''
|
''' Serializer: Relocate constituents. '''
|
||||||
destination = PKField(
|
destination = PKField(
|
||||||
many=False,
|
many=False,
|
||||||
|
|
|
@ -2,29 +2,30 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from apps.library.serializers import LibraryItemSerializer
|
from apps.library.serializers import LibraryItemSerializer
|
||||||
|
from shared.serializers import StrictSerializer
|
||||||
|
|
||||||
from .data_access import OperationSchemaSerializer
|
from .data_access import OperationSchemaSerializer
|
||||||
|
|
||||||
|
|
||||||
class OperationCreatedResponse(serializers.Serializer):
|
class OperationCreatedResponse(StrictSerializer):
|
||||||
''' Serializer: Create operation response. '''
|
''' Serializer: Create operation response. '''
|
||||||
new_operation = serializers.IntegerField()
|
new_operation = serializers.IntegerField()
|
||||||
oss = OperationSchemaSerializer()
|
oss = OperationSchemaSerializer()
|
||||||
|
|
||||||
|
|
||||||
class BlockCreatedResponse(serializers.Serializer):
|
class BlockCreatedResponse(StrictSerializer):
|
||||||
''' Serializer: Create block response. '''
|
''' Serializer: Create block response. '''
|
||||||
new_block = serializers.IntegerField()
|
new_block = serializers.IntegerField()
|
||||||
oss = OperationSchemaSerializer()
|
oss = OperationSchemaSerializer()
|
||||||
|
|
||||||
|
|
||||||
class SchemaCreatedResponse(serializers.Serializer):
|
class SchemaCreatedResponse(StrictSerializer):
|
||||||
''' Serializer: Create RSForm for input operation response. '''
|
''' Serializer: Create RSForm for input operation response. '''
|
||||||
new_schema = LibraryItemSerializer()
|
new_schema = LibraryItemSerializer()
|
||||||
oss = OperationSchemaSerializer()
|
oss = OperationSchemaSerializer()
|
||||||
|
|
||||||
|
|
||||||
class ConstituentaReferenceResponse(serializers.Serializer):
|
class ConstituentaReferenceResponse(StrictSerializer):
|
||||||
''' Serializer: Constituenta reference. '''
|
''' Serializer: Constituenta reference. '''
|
||||||
id = serializers.IntegerField()
|
id = serializers.IntegerField()
|
||||||
schema = serializers.IntegerField()
|
schema = serializers.IntegerField()
|
||||||
|
|
|
@ -3,11 +3,12 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from shared import messages as msg
|
from shared import messages as msg
|
||||||
|
from shared.serializers import StrictModelSerializer
|
||||||
|
|
||||||
from ..models import PromptTemplate
|
from ..models import PromptTemplate
|
||||||
|
|
||||||
|
|
||||||
class PromptTemplateSerializer(serializers.ModelSerializer):
|
class PromptTemplateSerializer(StrictModelSerializer):
|
||||||
'''Serializer for PromptTemplate, enforcing permissions and ownership logic.'''
|
'''Serializer for PromptTemplate, enforcing permissions and ownership logic.'''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -42,7 +43,7 @@ class PromptTemplateSerializer(serializers.ModelSerializer):
|
||||||
return super().update(instance, validated_data)
|
return super().update(instance, validated_data)
|
||||||
|
|
||||||
|
|
||||||
class PromptTemplateListSerializer(serializers.ModelSerializer):
|
class PromptTemplateListSerializer(StrictModelSerializer):
|
||||||
'''Serializer for listing PromptTemplates without the 'text' field.'''
|
'''Serializer for listing PromptTemplates without the 'text' field.'''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
|
|
@ -4,26 +4,28 @@ from typing import cast
|
||||||
from cctext import EntityReference, Reference, ReferenceType, Resolver, SyntacticReference
|
from cctext import EntityReference, Reference, ReferenceType, Resolver, SyntacticReference
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from shared.serializers import StrictSerializer
|
||||||
|
|
||||||
class ExpressionSerializer(serializers.Serializer):
|
|
||||||
|
class ExpressionSerializer(StrictSerializer):
|
||||||
''' Serializer: RSLang expression. '''
|
''' Serializer: RSLang expression. '''
|
||||||
expression = serializers.CharField()
|
expression = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class ConstituentaCheckSerializer(serializers.Serializer):
|
class ConstituentaCheckSerializer(StrictSerializer):
|
||||||
''' Serializer: RSLang expression. '''
|
''' Serializer: RSLang expression. '''
|
||||||
alias = serializers.CharField()
|
alias = serializers.CharField()
|
||||||
definition_formal = serializers.CharField(allow_blank=True)
|
definition_formal = serializers.CharField(allow_blank=True)
|
||||||
cst_type = serializers.CharField()
|
cst_type = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class WordFormSerializer(serializers.Serializer):
|
class WordFormSerializer(StrictSerializer):
|
||||||
''' Serializer: inflect request. '''
|
''' Serializer: inflect request. '''
|
||||||
text = serializers.CharField()
|
text = serializers.CharField()
|
||||||
grams = serializers.CharField()
|
grams = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class MultiFormSerializer(serializers.Serializer):
|
class MultiFormSerializer(StrictSerializer):
|
||||||
''' Serializer: inflect request. '''
|
''' Serializer: inflect request. '''
|
||||||
items = serializers.ListField(
|
items = serializers.ListField(
|
||||||
child=WordFormSerializer()
|
child=WordFormSerializer()
|
||||||
|
@ -41,18 +43,18 @@ class MultiFormSerializer(serializers.Serializer):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class TextSerializer(serializers.Serializer):
|
class TextSerializer(StrictSerializer):
|
||||||
''' Serializer: Text with references. '''
|
''' Serializer: Text with references. '''
|
||||||
text = serializers.CharField()
|
text = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class FunctionArgSerializer(serializers.Serializer):
|
class FunctionArgSerializer(StrictSerializer):
|
||||||
''' Serializer: RSLang function argument type. '''
|
''' Serializer: RSLang function argument type. '''
|
||||||
alias = serializers.CharField()
|
alias = serializers.CharField()
|
||||||
typification = serializers.CharField()
|
typification = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class CstParseSerializer(serializers.Serializer):
|
class CstParseSerializer(StrictSerializer):
|
||||||
''' Serializer: Constituenta parse result. '''
|
''' Serializer: Constituenta parse result. '''
|
||||||
status = serializers.CharField()
|
status = serializers.CharField()
|
||||||
valueClass = serializers.CharField()
|
valueClass = serializers.CharField()
|
||||||
|
@ -63,7 +65,7 @@ class CstParseSerializer(serializers.Serializer):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ErrorDescriptionSerializer(serializers.Serializer):
|
class ErrorDescriptionSerializer(StrictSerializer):
|
||||||
''' Serializer: RSError description. '''
|
''' Serializer: RSError description. '''
|
||||||
errorType = serializers.IntegerField()
|
errorType = serializers.IntegerField()
|
||||||
position = serializers.IntegerField()
|
position = serializers.IntegerField()
|
||||||
|
@ -73,13 +75,13 @@ class ErrorDescriptionSerializer(serializers.Serializer):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class NodeDataSerializer(serializers.Serializer):
|
class NodeDataSerializer(StrictSerializer):
|
||||||
''' Serializer: Node data. '''
|
''' Serializer: Node data. '''
|
||||||
dataType = serializers.CharField()
|
dataType = serializers.CharField()
|
||||||
value = serializers.CharField()
|
value = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class ASTNodeSerializer(serializers.Serializer):
|
class ASTNodeSerializer(StrictSerializer):
|
||||||
''' Serializer: Syntax tree node. '''
|
''' Serializer: Syntax tree node. '''
|
||||||
uid = serializers.IntegerField()
|
uid = serializers.IntegerField()
|
||||||
parent = serializers.IntegerField() # type: ignore
|
parent = serializers.IntegerField() # type: ignore
|
||||||
|
@ -89,7 +91,7 @@ class ASTNodeSerializer(serializers.Serializer):
|
||||||
data = NodeDataSerializer() # type: ignore
|
data = NodeDataSerializer() # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class ExpressionParseSerializer(serializers.Serializer):
|
class ExpressionParseSerializer(StrictSerializer):
|
||||||
''' Serializer: RSlang expression parse result. '''
|
''' Serializer: RSlang expression parse result. '''
|
||||||
parseResult = serializers.BooleanField()
|
parseResult = serializers.BooleanField()
|
||||||
prefixLen = serializers.IntegerField()
|
prefixLen = serializers.IntegerField()
|
||||||
|
@ -108,13 +110,13 @@ class ExpressionParseSerializer(serializers.Serializer):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TextPositionSerializer(serializers.Serializer):
|
class TextPositionSerializer(StrictSerializer):
|
||||||
''' Serializer: Text position. '''
|
''' Serializer: Text position. '''
|
||||||
start = serializers.IntegerField()
|
start = serializers.IntegerField()
|
||||||
finish = serializers.IntegerField()
|
finish = serializers.IntegerField()
|
||||||
|
|
||||||
|
|
||||||
class ReferenceDataSerializer(serializers.Serializer):
|
class ReferenceDataSerializer(StrictSerializer):
|
||||||
''' Serializer: Reference data - Union of all references. '''
|
''' Serializer: Reference data - Union of all references. '''
|
||||||
offset = serializers.IntegerField()
|
offset = serializers.IntegerField()
|
||||||
nominal = serializers.CharField()
|
nominal = serializers.CharField()
|
||||||
|
@ -122,7 +124,7 @@ class ReferenceDataSerializer(serializers.Serializer):
|
||||||
form = serializers.CharField()
|
form = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class ReferenceSerializer(serializers.Serializer):
|
class ReferenceSerializer(StrictSerializer):
|
||||||
''' Serializer: Language reference. '''
|
''' Serializer: Language reference. '''
|
||||||
type = serializers.CharField()
|
type = serializers.CharField()
|
||||||
data = ReferenceDataSerializer() # type: ignore
|
data = ReferenceDataSerializer() # type: ignore
|
||||||
|
@ -130,7 +132,7 @@ class ReferenceSerializer(serializers.Serializer):
|
||||||
pos_output = TextPositionSerializer()
|
pos_output = TextPositionSerializer()
|
||||||
|
|
||||||
|
|
||||||
class InheritanceDataSerializer(serializers.Serializer):
|
class InheritanceDataSerializer(StrictSerializer):
|
||||||
''' Serializer: inheritance data. '''
|
''' Serializer: inheritance data. '''
|
||||||
child = serializers.IntegerField()
|
child = serializers.IntegerField()
|
||||||
child_source = serializers.IntegerField()
|
child_source = serializers.IntegerField()
|
||||||
|
@ -138,7 +140,7 @@ class InheritanceDataSerializer(serializers.Serializer):
|
||||||
parent_source = serializers.IntegerField()
|
parent_source = serializers.IntegerField()
|
||||||
|
|
||||||
|
|
||||||
class ResolverSerializer(serializers.Serializer):
|
class ResolverSerializer(StrictSerializer):
|
||||||
''' Serializer: Resolver results serializer. '''
|
''' Serializer: Resolver results serializer. '''
|
||||||
input = serializers.CharField()
|
input = serializers.CharField()
|
||||||
output = serializers.CharField()
|
output = serializers.CharField()
|
||||||
|
|
|
@ -9,19 +9,20 @@ from rest_framework.serializers import PrimaryKeyRelatedField as PKField
|
||||||
|
|
||||||
from apps.library.models import LibraryItem
|
from apps.library.models import LibraryItem
|
||||||
from apps.library.serializers import (
|
from apps.library.serializers import (
|
||||||
LibraryItemBaseSerializer,
|
LibraryItemBaseNonStrictSerializer,
|
||||||
LibraryItemDetailsSerializer,
|
LibraryItemDetailsSerializer,
|
||||||
LibraryItemReferenceSerializer
|
LibraryItemReferenceSerializer
|
||||||
)
|
)
|
||||||
from apps.oss.models import Inheritance
|
from apps.oss.models import Inheritance
|
||||||
from shared import messages as msg
|
from shared import messages as msg
|
||||||
|
from shared.serializers import StrictModelSerializer, StrictSerializer
|
||||||
|
|
||||||
from ..models import Constituenta, CstType, RSForm
|
from ..models import Constituenta, CstType, RSForm
|
||||||
from .basics import CstParseSerializer, InheritanceDataSerializer
|
from .basics import CstParseSerializer, InheritanceDataSerializer
|
||||||
from .io_pyconcept import PyConceptAdapter
|
from .io_pyconcept import PyConceptAdapter
|
||||||
|
|
||||||
|
|
||||||
class CstBaseSerializer(serializers.ModelSerializer):
|
class CstBaseSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Constituenta all data. '''
|
''' Serializer: Constituenta all data. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -30,7 +31,7 @@ class CstBaseSerializer(serializers.ModelSerializer):
|
||||||
read_only_fields = ('id',)
|
read_only_fields = ('id',)
|
||||||
|
|
||||||
|
|
||||||
class CstInfoSerializer(serializers.ModelSerializer):
|
class CstInfoSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Constituenta public information. '''
|
''' Serializer: Constituenta public information. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -38,9 +39,9 @@ class CstInfoSerializer(serializers.ModelSerializer):
|
||||||
exclude = ('order', 'schema')
|
exclude = ('order', 'schema')
|
||||||
|
|
||||||
|
|
||||||
class CstUpdateSerializer(serializers.Serializer):
|
class CstUpdateSerializer(StrictSerializer):
|
||||||
''' Serializer: Constituenta update. '''
|
''' Serializer: Constituenta update. '''
|
||||||
class ConstituentaUpdateData(serializers.ModelSerializer):
|
class ConstituentaUpdateData(StrictModelSerializer):
|
||||||
''' Serializer: Operation creation data. '''
|
''' Serializer: Operation creation data. '''
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
|
@ -70,7 +71,7 @@ class CstUpdateSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class CstDetailsSerializer(serializers.ModelSerializer):
|
class CstDetailsSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Constituenta data including parse. '''
|
''' Serializer: Constituenta data including parse. '''
|
||||||
parse = CstParseSerializer()
|
parse = CstParseSerializer()
|
||||||
|
|
||||||
|
@ -80,7 +81,7 @@ class CstDetailsSerializer(serializers.ModelSerializer):
|
||||||
exclude = ('order',)
|
exclude = ('order',)
|
||||||
|
|
||||||
|
|
||||||
class CstCreateSerializer(serializers.ModelSerializer):
|
class CstCreateSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Constituenta creation. '''
|
''' Serializer: Constituenta creation. '''
|
||||||
insert_after = PKField(
|
insert_after = PKField(
|
||||||
many=False,
|
many=False,
|
||||||
|
@ -100,7 +101,7 @@ class CstCreateSerializer(serializers.ModelSerializer):
|
||||||
'insert_after', 'term_forms'
|
'insert_after', 'term_forms'
|
||||||
|
|
||||||
|
|
||||||
class RSFormSerializer(serializers.ModelSerializer):
|
class RSFormSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Detailed data for RSForm. '''
|
''' Serializer: Detailed data for RSForm. '''
|
||||||
editors = serializers.ListField(
|
editors = serializers.ListField(
|
||||||
child=serializers.IntegerField()
|
child=serializers.IntegerField()
|
||||||
|
@ -208,7 +209,7 @@ class RSFormSerializer(serializers.ModelSerializer):
|
||||||
validated_data=new_cst.validated_data
|
validated_data=new_cst.validated_data
|
||||||
)
|
)
|
||||||
|
|
||||||
loaded_item = LibraryItemBaseSerializer(data=data)
|
loaded_item = LibraryItemBaseNonStrictSerializer(data=data)
|
||||||
loaded_item.is_valid(raise_exception=True)
|
loaded_item.is_valid(raise_exception=True)
|
||||||
loaded_item.update(
|
loaded_item.update(
|
||||||
instance=cast(LibraryItem, self.instance),
|
instance=cast(LibraryItem, self.instance),
|
||||||
|
@ -216,7 +217,7 @@ class RSFormSerializer(serializers.ModelSerializer):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RSFormParseSerializer(serializers.ModelSerializer):
|
class RSFormParseSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Detailed data for RSForm including parse. '''
|
''' Serializer: Detailed data for RSForm including parse. '''
|
||||||
editors = serializers.ListField(
|
editors = serializers.ListField(
|
||||||
child=serializers.IntegerField()
|
child=serializers.IntegerField()
|
||||||
|
@ -250,7 +251,7 @@ class RSFormParseSerializer(serializers.ModelSerializer):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class CstTargetSerializer(serializers.Serializer):
|
class CstTargetSerializer(StrictSerializer):
|
||||||
''' Serializer: Target single Constituenta. '''
|
''' Serializer: Target single Constituenta. '''
|
||||||
target = PKField(many=False, queryset=Constituenta.objects.all())
|
target = PKField(many=False, queryset=Constituenta.objects.all())
|
||||||
|
|
||||||
|
@ -265,7 +266,7 @@ class CstTargetSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class CstListSerializer(serializers.Serializer):
|
class CstListSerializer(StrictSerializer):
|
||||||
''' Serializer: List of constituents from one origin. '''
|
''' Serializer: List of constituents from one origin. '''
|
||||||
items = PKField(many=True, queryset=Constituenta.objects.all().only('schema_id'))
|
items = PKField(many=True, queryset=Constituenta.objects.all().only('schema_id'))
|
||||||
|
|
||||||
|
@ -287,13 +288,13 @@ class CstMoveSerializer(CstListSerializer):
|
||||||
move_to = serializers.IntegerField()
|
move_to = serializers.IntegerField()
|
||||||
|
|
||||||
|
|
||||||
class SubstitutionSerializerBase(serializers.Serializer):
|
class SubstitutionSerializerBase(StrictSerializer):
|
||||||
''' Serializer: Basic substitution. '''
|
''' Serializer: Basic substitution. '''
|
||||||
original = PKField(many=False, queryset=Constituenta.objects.only('alias', 'schema_id'))
|
original = PKField(many=False, queryset=Constituenta.objects.only('alias', 'schema_id'))
|
||||||
substitution = PKField(many=False, queryset=Constituenta.objects.only('alias', 'schema_id'))
|
substitution = PKField(many=False, queryset=Constituenta.objects.only('alias', 'schema_id'))
|
||||||
|
|
||||||
|
|
||||||
class CstSubstituteSerializer(serializers.Serializer):
|
class CstSubstituteSerializer(StrictSerializer):
|
||||||
''' Serializer: Constituenta substitution. '''
|
''' Serializer: Constituenta substitution. '''
|
||||||
substitutions = serializers.ListField(
|
substitutions = serializers.ListField(
|
||||||
child=SubstitutionSerializerBase(),
|
child=SubstitutionSerializerBase(),
|
||||||
|
@ -326,7 +327,7 @@ class CstSubstituteSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class InlineSynthesisSerializer(serializers.Serializer):
|
class InlineSynthesisSerializer(StrictSerializer):
|
||||||
''' Serializer: Inline synthesis operation input. '''
|
''' Serializer: Inline synthesis operation input. '''
|
||||||
receiver = PKField(many=False, queryset=LibraryItem.objects.all().only('owner_id'))
|
receiver = PKField(many=False, queryset=LibraryItem.objects.all().only('owner_id'))
|
||||||
source = PKField(many=False, queryset=LibraryItem.objects.all().only('owner_id')) # type: ignore
|
source = PKField(many=False, queryset=LibraryItem.objects.all().only('owner_id')) # type: ignore
|
||||||
|
|
|
@ -4,6 +4,7 @@ from rest_framework import serializers
|
||||||
|
|
||||||
from apps.library.models import LibraryItem
|
from apps.library.models import LibraryItem
|
||||||
from shared import messages as msg
|
from shared import messages as msg
|
||||||
|
from shared.serializers import StrictSerializer
|
||||||
|
|
||||||
from ..models import Constituenta, RSForm
|
from ..models import Constituenta, RSForm
|
||||||
from ..utils import fix_old_references
|
from ..utils import fix_old_references
|
||||||
|
@ -15,12 +16,12 @@ _TRS_VERSION = 16
|
||||||
_TRS_HEADER = 'Exteor 4.8.13.1000 - 30/05/2022'
|
_TRS_HEADER = 'Exteor 4.8.13.1000 - 30/05/2022'
|
||||||
|
|
||||||
|
|
||||||
class FileSerializer(serializers.Serializer):
|
class FileSerializer(StrictSerializer):
|
||||||
''' Serializer: File input. '''
|
''' Serializer: File input. '''
|
||||||
file = serializers.FileField(allow_empty_file=False)
|
file = serializers.FileField(allow_empty_file=False)
|
||||||
|
|
||||||
|
|
||||||
class RSFormUploadSerializer(serializers.Serializer):
|
class RSFormUploadSerializer(StrictSerializer):
|
||||||
''' Upload data for RSForm serializer. '''
|
''' Upload data for RSForm serializer. '''
|
||||||
file = serializers.FileField()
|
file = serializers.FileField()
|
||||||
load_metadata = serializers.BooleanField()
|
load_metadata = serializers.BooleanField()
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
''' Utility serializers for REST API schema - SHOULD NOT BE ACCESSED DIRECTLY. '''
|
''' Utility serializers for REST API schema - SHOULD NOT BE ACCESSED DIRECTLY. '''
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from shared.serializers import StrictSerializer
|
||||||
|
|
||||||
from .data_access import RSFormParseSerializer
|
from .data_access import RSFormParseSerializer
|
||||||
|
|
||||||
|
|
||||||
class ResultTextResponse(serializers.Serializer):
|
class ResultTextResponse(StrictSerializer):
|
||||||
''' Serializer: Text result of a function call. '''
|
''' Serializer: Text result of a function call. '''
|
||||||
result = serializers.CharField()
|
result = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class NewCstResponse(serializers.Serializer):
|
class NewCstResponse(StrictSerializer):
|
||||||
''' Serializer: Create cst response. '''
|
''' Serializer: Create cst response. '''
|
||||||
new_cst = serializers.IntegerField()
|
new_cst = serializers.IntegerField()
|
||||||
schema = RSFormParseSerializer()
|
schema = RSFormParseSerializer()
|
||||||
|
|
||||||
|
|
||||||
class NewMultiCstResponse(serializers.Serializer):
|
class NewMultiCstResponse(StrictSerializer):
|
||||||
''' Serializer: Create multiple cst response. '''
|
''' Serializer: Create multiple cst response. '''
|
||||||
cst_list = serializers.ListField(
|
cst_list = serializers.ListField(
|
||||||
child=serializers.IntegerField()
|
child=serializers.IntegerField()
|
||||||
|
|
|
@ -5,18 +5,19 @@ from rest_framework import serializers
|
||||||
|
|
||||||
from apps.library.models import Editor
|
from apps.library.models import Editor
|
||||||
from shared import messages as msg
|
from shared import messages as msg
|
||||||
|
from shared.serializers import StrictModelSerializer, StrictSerializer
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
class NonFieldErrorSerializer(serializers.Serializer):
|
class NonFieldErrorSerializer(StrictSerializer):
|
||||||
''' Serializer: list of non-field errors. '''
|
''' Serializer: list of non-field errors. '''
|
||||||
non_field_errors = serializers.ListField(
|
non_field_errors = serializers.ListField(
|
||||||
child=serializers.CharField()
|
child=serializers.CharField()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class LoginSerializer(serializers.Serializer):
|
class LoginSerializer(StrictSerializer):
|
||||||
''' Serializer: User authentication by login/password. '''
|
''' Serializer: User authentication by login/password. '''
|
||||||
username = serializers.CharField(
|
username = serializers.CharField(
|
||||||
label='Имя пользователя',
|
label='Имя пользователя',
|
||||||
|
@ -54,7 +55,7 @@ class LoginSerializer(serializers.Serializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class AuthSerializer(serializers.Serializer):
|
class AuthSerializer(StrictSerializer):
|
||||||
''' Serializer: Authorization data. '''
|
''' Serializer: Authorization data. '''
|
||||||
id = serializers.IntegerField()
|
id = serializers.IntegerField()
|
||||||
username = serializers.CharField()
|
username = serializers.CharField()
|
||||||
|
@ -77,7 +78,7 @@ class AuthSerializer(serializers.Serializer):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
class UserSerializer(StrictModelSerializer):
|
||||||
''' Serializer: User data. '''
|
''' Serializer: User data. '''
|
||||||
id = serializers.IntegerField(read_only=True)
|
id = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ class UserSerializer(serializers.ModelSerializer):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class UserInfoSerializer(serializers.ModelSerializer):
|
class UserInfoSerializer(StrictModelSerializer):
|
||||||
''' Serializer: User open information. '''
|
''' Serializer: User open information. '''
|
||||||
id = serializers.IntegerField(read_only=True)
|
id = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
|
@ -119,13 +120,13 @@ class UserInfoSerializer(serializers.ModelSerializer):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ChangePasswordSerializer(serializers.Serializer):
|
class ChangePasswordSerializer(StrictSerializer):
|
||||||
''' Serializer: Change password. '''
|
''' Serializer: Change password. '''
|
||||||
old_password = serializers.CharField(required=True)
|
old_password = serializers.CharField(required=True)
|
||||||
new_password = serializers.CharField(required=True)
|
new_password = serializers.CharField(required=True)
|
||||||
|
|
||||||
|
|
||||||
class SignupSerializer(serializers.ModelSerializer):
|
class SignupSerializer(StrictModelSerializer):
|
||||||
''' Serializer: Create user profile. '''
|
''' Serializer: Create user profile. '''
|
||||||
id = serializers.IntegerField(read_only=True)
|
id = serializers.IntegerField(read_only=True)
|
||||||
password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
|
password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
|
||||||
|
|
|
@ -2,6 +2,14 @@
|
||||||
# pylint: skip-file
|
# pylint: skip-file
|
||||||
|
|
||||||
|
|
||||||
|
def fieldNotAllowed():
|
||||||
|
return 'Недопустимое поле'
|
||||||
|
|
||||||
|
|
||||||
|
def constituentsInvalid(constituents: list[int]):
|
||||||
|
return f'некорректные конституенты для схемы: {constituents}'
|
||||||
|
|
||||||
|
|
||||||
def constituentaNotInRSform(title: str):
|
def constituentaNotInRSform(title: str):
|
||||||
return f'Конституента не принадлежит схеме: {title}'
|
return f'Конституента не принадлежит схеме: {title}'
|
||||||
|
|
||||||
|
|
23
rsconcept/backend/shared/serializers.py
Normal file
23
rsconcept/backend/shared/serializers.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
import shared.messages as msg
|
||||||
|
|
||||||
|
|
||||||
|
class StrictSerializer(serializers.Serializer):
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
extra_keys = set(data.keys()) - set(self.fields.keys())
|
||||||
|
if extra_keys:
|
||||||
|
raise serializers.ValidationError({
|
||||||
|
key: msg.fieldNotAllowed() for key in extra_keys
|
||||||
|
})
|
||||||
|
return super().to_internal_value(data)
|
||||||
|
|
||||||
|
|
||||||
|
class StrictModelSerializer(serializers.ModelSerializer):
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
extra_keys = set(data.keys()) - set(self.fields.keys())
|
||||||
|
if extra_keys:
|
||||||
|
raise serializers.ValidationError({
|
||||||
|
key: msg.fieldNotAllowed() for key in extra_keys
|
||||||
|
})
|
||||||
|
return super().to_internal_value(data)
|
|
@ -1,5 +1,8 @@
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { limits } from '@/utils/constants';
|
||||||
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
/** Represents AI prompt. */
|
/** Represents AI prompt. */
|
||||||
export type IPromptTemplate = IPromptTemplateDTO;
|
export type IPromptTemplate = IPromptTemplateDTO;
|
||||||
|
|
||||||
|
@ -28,20 +31,22 @@ export const schemaPromptTemplate = z.strictObject({
|
||||||
is_shared: z.boolean()
|
is_shared: z.boolean()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaCreatePromptTemplate = schemaPromptTemplate.pick({
|
const schemaPromptTemplateInput = schemaPromptTemplate
|
||||||
label: true,
|
.pick({
|
||||||
description: true,
|
is_shared: true,
|
||||||
text: true,
|
owner: true
|
||||||
is_shared: true
|
})
|
||||||
|
.extend({
|
||||||
|
label: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField),
|
||||||
|
description: z.string().max(limits.len_description, errorMsg.descriptionLength),
|
||||||
|
text: z.string().max(limits.len_text, errorMsg.textLength)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaCreatePromptTemplate = schemaPromptTemplateInput.omit({
|
||||||
|
owner: true
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaUpdatePromptTemplate = schemaPromptTemplate.pick({
|
export const schemaUpdatePromptTemplate = schemaPromptTemplateInput;
|
||||||
owner: true,
|
|
||||||
label: true,
|
|
||||||
description: true,
|
|
||||||
text: true,
|
|
||||||
is_shared: true
|
|
||||||
});
|
|
||||||
|
|
||||||
export const schemaPromptTemplateInfo = schemaPromptTemplate.pick({
|
export const schemaPromptTemplateInfo = schemaPromptTemplate.pick({
|
||||||
id: true,
|
id: true,
|
||||||
|
|
|
@ -22,17 +22,23 @@ export function DlgCreatePromptTemplate() {
|
||||||
const { items: templates } = useAvailableTemplatesSuspense();
|
const { items: templates } = useAvailableTemplatesSuspense();
|
||||||
const { user } = useAuthSuspense();
|
const { user } = useAuthSuspense();
|
||||||
|
|
||||||
const { handleSubmit, control, register } = useForm<ICreatePromptTemplateDTO>({
|
const {
|
||||||
|
handleSubmit,
|
||||||
|
control,
|
||||||
|
register,
|
||||||
|
formState: { errors }
|
||||||
|
} = useForm<ICreatePromptTemplateDTO>({
|
||||||
resolver: zodResolver(schemaCreatePromptTemplate),
|
resolver: zodResolver(schemaCreatePromptTemplate),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
label: '',
|
label: '',
|
||||||
description: '',
|
description: '',
|
||||||
text: '',
|
text: '',
|
||||||
is_shared: false
|
is_shared: false
|
||||||
}
|
},
|
||||||
|
mode: 'onChange'
|
||||||
});
|
});
|
||||||
const label = useWatch({ control, name: 'label' });
|
const label = useWatch({ control, name: 'label' });
|
||||||
const isValid = label !== '' && !templates.find(template => template.label === label);
|
const isValid = !!label && !templates.find(template => template.label === label);
|
||||||
|
|
||||||
function onSubmit(data: ICreatePromptTemplateDTO) {
|
function onSubmit(data: ICreatePromptTemplateDTO) {
|
||||||
void createPromptTemplate(data).then(onCreate);
|
void createPromptTemplate(data).then(onCreate);
|
||||||
|
@ -47,8 +53,8 @@ export function DlgCreatePromptTemplate() {
|
||||||
submitInvalidTooltip='Введите уникальное название шаблона'
|
submitInvalidTooltip='Введите уникальное название шаблона'
|
||||||
className='cc-column w-140 max-h-120 py-2 px-6'
|
className='cc-column w-140 max-h-120 py-2 px-6'
|
||||||
>
|
>
|
||||||
<TextInput id='dlg_prompt_label' {...register('label')} label='Название шаблона' />
|
<TextInput id='dlg_prompt_label' {...register('label')} label='Название шаблона' error={errors.label} />
|
||||||
<TextArea id='dlg_prompt_description' {...register('description')} label='Описание' />
|
<TextArea id='dlg_prompt_description' {...register('description')} label='Описание' error={errors.description} />
|
||||||
{user.is_staff ? (
|
{user.is_staff ? (
|
||||||
<Controller
|
<Controller
|
||||||
name='is_shared'
|
name='is_shared'
|
||||||
|
|
|
@ -7,9 +7,6 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useDebounce } from 'use-debounce';
|
import { useDebounce } from 'use-debounce';
|
||||||
|
|
||||||
import { useMutatingPrompts } from '@/features/ai/backend/use-mutating-prompts';
|
|
||||||
import { useUpdatePromptTemplate } from '@/features/ai/backend/use-update-prompt-template';
|
|
||||||
import { generateSample } from '@/features/ai/models/prompting-api';
|
|
||||||
import { useAuthSuspense } from '@/features/auth';
|
import { useAuthSuspense } from '@/features/auth';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/control';
|
import { MiniButton } from '@/components/control';
|
||||||
|
@ -24,6 +21,9 @@ import {
|
||||||
type IUpdatePromptTemplateDTO,
|
type IUpdatePromptTemplateDTO,
|
||||||
schemaUpdatePromptTemplate
|
schemaUpdatePromptTemplate
|
||||||
} from '../../../backend/types';
|
} from '../../../backend/types';
|
||||||
|
import { useMutatingPrompts } from '../../../backend/use-mutating-prompts';
|
||||||
|
import { useUpdatePromptTemplate } from '../../../backend/use-update-prompt-template';
|
||||||
|
import { generateSample } from '../../../models/prompting-api';
|
||||||
|
|
||||||
interface FormPromptTemplateProps {
|
interface FormPromptTemplateProps {
|
||||||
promptTemplate: IPromptTemplate;
|
promptTemplate: IPromptTemplate;
|
||||||
|
@ -55,7 +55,8 @@ export function FormPromptTemplate({ promptTemplate, className, isMutable, toggl
|
||||||
description: promptTemplate.description,
|
description: promptTemplate.description,
|
||||||
text: promptTemplate.text,
|
text: promptTemplate.text,
|
||||||
is_shared: promptTemplate.is_shared
|
is_shared: promptTemplate.is_shared
|
||||||
}
|
},
|
||||||
|
mode: 'onChange'
|
||||||
});
|
});
|
||||||
const text = useWatch({ control, name: 'text' });
|
const text = useWatch({ control, name: 'text' });
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { limits } from '@/utils/constants';
|
||||||
import { errorMsg } from '@/utils/labels';
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
/**
|
/** Represents CurrentUser information. */
|
||||||
* Represents CurrentUser information.
|
|
||||||
*/
|
|
||||||
export interface ICurrentUser {
|
export interface ICurrentUser {
|
||||||
id: number | null;
|
id: number | null;
|
||||||
username: string;
|
username: string;
|
||||||
|
@ -12,27 +11,40 @@ export interface ICurrentUser {
|
||||||
editor: number[];
|
editor: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Represents login data, used to authenticate users. */
|
||||||
* Represents login data, used to authenticate users.
|
|
||||||
*/
|
|
||||||
export const schemaUserLogin = z.strictObject({
|
|
||||||
username: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
password: z.string().nonempty(errorMsg.requiredField)
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents login data, used to authenticate users.
|
|
||||||
*/
|
|
||||||
export type IUserLoginDTO = z.infer<typeof schemaUserLogin>;
|
export type IUserLoginDTO = z.infer<typeof schemaUserLogin>;
|
||||||
|
|
||||||
/**
|
/** Represents data needed to update password for current user. */
|
||||||
* Represents data needed to update password for current user.
|
export type IChangePasswordDTO = z.infer<typeof schemaChangePassword>;
|
||||||
*/
|
|
||||||
|
/** Represents password reset request data. */
|
||||||
|
export interface IRequestPasswordDTO {
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents password reset data. */
|
||||||
|
export interface IResetPasswordDTO {
|
||||||
|
password: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents password token data. */
|
||||||
|
export interface IPasswordTokenDTO {
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========= SCHEMAS ========
|
||||||
|
|
||||||
|
export const schemaUserLogin = z.strictObject({
|
||||||
|
username: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField),
|
||||||
|
password: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField)
|
||||||
|
});
|
||||||
|
|
||||||
export const schemaChangePassword = z
|
export const schemaChangePassword = z
|
||||||
.object({
|
.object({
|
||||||
old_password: z.string().nonempty(errorMsg.requiredField),
|
old_password: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField),
|
||||||
new_password: z.string().nonempty(errorMsg.requiredField),
|
new_password: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField),
|
||||||
new_password2: z.string().nonempty(errorMsg.requiredField)
|
new_password2: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField)
|
||||||
})
|
})
|
||||||
.refine(schema => schema.new_password === schema.new_password2, {
|
.refine(schema => schema.new_password === schema.new_password2, {
|
||||||
path: ['new_password2'],
|
path: ['new_password2'],
|
||||||
|
@ -42,30 +54,3 @@ export const schemaChangePassword = z
|
||||||
path: ['new_password'],
|
path: ['new_password'],
|
||||||
message: errorMsg.passwordsSame
|
message: errorMsg.passwordsSame
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents data needed to update password for current user.
|
|
||||||
*/
|
|
||||||
export type IChangePasswordDTO = z.infer<typeof schemaChangePassword>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents password reset request data.
|
|
||||||
*/
|
|
||||||
export interface IRequestPasswordDTO {
|
|
||||||
email: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents password reset data.
|
|
||||||
*/
|
|
||||||
export interface IResetPasswordDTO {
|
|
||||||
password: string;
|
|
||||||
token: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents password token data.
|
|
||||||
*/
|
|
||||||
export interface IPasswordTokenDTO {
|
|
||||||
token: string;
|
|
||||||
}
|
|
||||||
|
|
|
@ -118,10 +118,10 @@ export const libraryApi = {
|
||||||
successMessage: infoMsg.itemDestroyed
|
successMessage: infoMsg.itemDestroyed
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
cloneItem: (data: ICloneLibraryItemDTO) =>
|
cloneItem: ({ itemID, data }: { itemID: number; data: ICloneLibraryItemDTO }) =>
|
||||||
axiosPost<ICloneLibraryItemDTO, IRSFormDTO>({
|
axiosPost<ICloneLibraryItemDTO, IRSFormDTO>({
|
||||||
schema: schemaRSForm,
|
schema: schemaRSForm,
|
||||||
endpoint: `/api/library/${data.id}/clone`,
|
endpoint: `/api/library/${itemID}/clone`,
|
||||||
request: {
|
request: {
|
||||||
data: data,
|
data: data,
|
||||||
successMessage: newSchema => infoMsg.cloneComplete(newSchema.alias)
|
successMessage: newSchema => infoMsg.cloneComplete(newSchema.alias)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { limits } from '@/utils/constants';
|
||||||
import { errorMsg } from '@/utils/labels';
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
import { validateLocation } from '../models/library-api';
|
import { validateLocation } from '../models/library-api';
|
||||||
|
@ -61,9 +62,11 @@ export const schemaAccessPolicy = z.enum(Object.values(AccessPolicy) as [AccessP
|
||||||
export const schemaLibraryItem = z.strictObject({
|
export const schemaLibraryItem = z.strictObject({
|
||||||
id: z.coerce.number(),
|
id: z.coerce.number(),
|
||||||
item_type: schemaLibraryItemType,
|
item_type: schemaLibraryItemType,
|
||||||
title: z.string(),
|
|
||||||
alias: z.string().nonempty(),
|
alias: z.string().nonempty(),
|
||||||
|
title: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
|
|
||||||
visible: z.boolean(),
|
visible: z.boolean(),
|
||||||
read_only: z.boolean(),
|
read_only: z.boolean(),
|
||||||
location: z.string(),
|
location: z.string(),
|
||||||
|
@ -76,58 +79,51 @@ export const schemaLibraryItem = z.strictObject({
|
||||||
|
|
||||||
export const schemaLibraryItemArray = z.array(schemaLibraryItem);
|
export const schemaLibraryItemArray = z.array(schemaLibraryItem);
|
||||||
|
|
||||||
export const schemaCloneLibraryItem = schemaLibraryItem
|
const schemaInputLibraryItem = schemaLibraryItem
|
||||||
.pick({
|
.pick({
|
||||||
id: true,
|
|
||||||
item_type: true,
|
item_type: true,
|
||||||
title: true,
|
|
||||||
alias: true,
|
|
||||||
description: true,
|
|
||||||
visible: true,
|
visible: true,
|
||||||
read_only: true,
|
read_only: true,
|
||||||
location: true,
|
|
||||||
access_policy: true
|
access_policy: true
|
||||||
})
|
})
|
||||||
.extend({
|
.extend({
|
||||||
title: z.string().nonempty(errorMsg.requiredField),
|
alias: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField),
|
||||||
alias: z.string().nonempty(errorMsg.requiredField),
|
title: z.string().max(limits.len_title, errorMsg.titleLength).nonempty(errorMsg.requiredField),
|
||||||
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
|
description: z.string().max(limits.len_description, errorMsg.descriptionLength),
|
||||||
|
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation })
|
||||||
items: z.array(z.number())
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaCreateLibraryItem = z
|
export const schemaCloneLibraryItem = z.strictObject({
|
||||||
.object({
|
items: z.array(z.number()),
|
||||||
item_type: schemaLibraryItemType,
|
item_data: schemaInputLibraryItem.omit({ item_type: true, read_only: true })
|
||||||
title: z.string().optional(),
|
});
|
||||||
alias: z.string().optional(),
|
|
||||||
description: z.string(),
|
export const schemaCreateLibraryItem = schemaInputLibraryItem
|
||||||
visible: z.boolean(),
|
.extend({
|
||||||
read_only: z.boolean(),
|
alias: z.string().max(limits.len_alias, errorMsg.aliasLength).optional(),
|
||||||
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
|
title: z.string().max(limits.len_title, errorMsg.titleLength).optional(),
|
||||||
access_policy: schemaAccessPolicy,
|
description: z.string().max(limits.len_description, errorMsg.descriptionLength).optional(),
|
||||||
|
|
||||||
file: z.instanceof(File).optional(),
|
file: z.instanceof(File).optional(),
|
||||||
fileName: z.string().optional()
|
fileName: z.string().optional()
|
||||||
})
|
})
|
||||||
.refine(data => !!data.file || !!data.title, {
|
|
||||||
path: ['title'],
|
|
||||||
message: errorMsg.requiredField
|
|
||||||
})
|
|
||||||
.refine(data => !!data.file || !!data.alias, {
|
.refine(data => !!data.file || !!data.alias, {
|
||||||
path: ['alias'],
|
path: ['alias'],
|
||||||
message: errorMsg.requiredField
|
message: errorMsg.requiredField
|
||||||
|
})
|
||||||
|
.refine(data => !!data.file || !!data.title, {
|
||||||
|
path: ['title'],
|
||||||
|
message: errorMsg.requiredField
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaUpdateLibraryItem = z.strictObject({
|
export const schemaUpdateLibraryItem = schemaInputLibraryItem
|
||||||
id: z.number(),
|
.omit({
|
||||||
item_type: schemaLibraryItemType,
|
location: true,
|
||||||
title: z.string().nonempty(errorMsg.requiredField),
|
access_policy: true
|
||||||
alias: z.string().nonempty(errorMsg.requiredField),
|
})
|
||||||
description: z.string(),
|
.extend({
|
||||||
visible: z.boolean(),
|
id: z.number()
|
||||||
read_only: z.boolean()
|
});
|
||||||
});
|
|
||||||
|
|
||||||
export const schemaVersionInfo = z.strictObject({
|
export const schemaVersionInfo = z.strictObject({
|
||||||
id: z.coerce.number(),
|
id: z.coerce.number(),
|
||||||
|
@ -136,18 +132,19 @@ export const schemaVersionInfo = z.strictObject({
|
||||||
time_create: z.string().datetime({ offset: true })
|
time_create: z.string().datetime({ offset: true })
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const schemaVersionInput = z.strictObject({
|
||||||
|
version: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField),
|
||||||
|
description: z.string().max(limits.len_description, errorMsg.descriptionLength)
|
||||||
|
});
|
||||||
|
|
||||||
export const schemaVersionExInfo = schemaVersionInfo.extend({
|
export const schemaVersionExInfo = schemaVersionInfo.extend({
|
||||||
item: z.number()
|
item: z.number()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaUpdateVersion = z.strictObject({
|
export const schemaUpdateVersion = schemaVersionInput.extend({
|
||||||
id: z.number(),
|
id: z.number()
|
||||||
version: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
description: z.string()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaCreateVersion = z.strictObject({
|
export const schemaCreateVersion = schemaVersionInput.extend({
|
||||||
version: z.string(),
|
|
||||||
description: z.string(),
|
|
||||||
items: z.array(z.number())
|
items: z.array(z.number())
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,6 @@ export const useCloneItem = () => {
|
||||||
onError: () => client.invalidateQueries()
|
onError: () => client.invalidateQueries()
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
cloneItem: (data: ICloneLibraryItemDTO) => mutation.mutateAsync(data)
|
cloneItem: (data: { itemID: number; data: ICloneLibraryItemDTO }) => mutation.mutateAsync(data)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ import clsx from 'clsx';
|
||||||
|
|
||||||
import { useAuthSuspense } from '@/features/auth';
|
import { useAuthSuspense } from '@/features/auth';
|
||||||
|
|
||||||
import { TextArea } from '@/components/input';
|
import { ErrorField, TextArea } from '@/components/input';
|
||||||
import { type Styling } from '@/components/props';
|
import { type Styling } from '@/components/props';
|
||||||
|
|
||||||
import { LocationHead } from '../../models/library';
|
import { LocationHead } from '../../models/library';
|
||||||
|
@ -56,8 +56,8 @@ export function PickLocation({
|
||||||
rows={rows}
|
rows={rows}
|
||||||
value={value.substring(3)}
|
value={value.substring(3)}
|
||||||
onChange={event => onChange(combineLocation(value.substring(0, 2), event.target.value))}
|
onChange={event => onChange(combineLocation(value.substring(0, 2), event.target.value))}
|
||||||
error={error}
|
|
||||||
/>
|
/>
|
||||||
|
<ErrorField className='absolute bottom-1 right-4' error={error} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ export function DlgChangeLocation() {
|
||||||
overflowVisible
|
overflowVisible
|
||||||
header='Изменение расположения'
|
header='Изменение расположения'
|
||||||
submitText='Переместить'
|
submitText='Переместить'
|
||||||
submitInvalidTooltip={`Допустимы буквы, цифры, подчерк, пробел и "!". Сегмент пути не может начинаться и заканчиваться пробелом. Общая длина (с корнем) не должна превышать ${limits.location_len}`}
|
submitInvalidTooltip={`Допустимы буквы, цифры, подчерк, пробел и "!". Сегмент пути не может начинаться и заканчиваться пробелом. Общая длина (с корнем) не должна превышать ${limits.len_location}`}
|
||||||
canSubmit={isValid && isDirty}
|
canSubmit={isValid && isDirty}
|
||||||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||||
className='w-130 pb-3 px-6 h-36'
|
className='w-130 pb-3 px-6 h-36'
|
||||||
|
|
|
@ -39,15 +39,14 @@ export function DlgCloneLibraryItem() {
|
||||||
} = useForm<ICloneLibraryItemDTO>({
|
} = useForm<ICloneLibraryItemDTO>({
|
||||||
resolver: zodResolver(schemaCloneLibraryItem),
|
resolver: zodResolver(schemaCloneLibraryItem),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: base.id,
|
item_data: {
|
||||||
item_type: base.item_type,
|
|
||||||
title: cloneTitle(base),
|
title: cloneTitle(base),
|
||||||
alias: base.alias,
|
alias: base.alias,
|
||||||
description: base.description,
|
description: base.description,
|
||||||
visible: true,
|
visible: true,
|
||||||
read_only: false,
|
|
||||||
access_policy: AccessPolicy.PUBLIC,
|
access_policy: AccessPolicy.PUBLIC,
|
||||||
location: initialLocation,
|
location: initialLocation
|
||||||
|
},
|
||||||
items: []
|
items: []
|
||||||
},
|
},
|
||||||
mode: 'onChange',
|
mode: 'onChange',
|
||||||
|
@ -55,7 +54,10 @@ export function DlgCloneLibraryItem() {
|
||||||
});
|
});
|
||||||
|
|
||||||
function onSubmit(data: ICloneLibraryItemDTO) {
|
function onSubmit(data: ICloneLibraryItemDTO) {
|
||||||
return cloneItem(data).then(newSchema => router.pushAsync({ path: urls.schema(newSchema.id), force: true }));
|
return cloneItem({
|
||||||
|
itemID: base.id,
|
||||||
|
data: data
|
||||||
|
}).then(newSchema => router.pushAsync({ path: urls.schema(newSchema.id), force: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -69,18 +71,24 @@ export function DlgCloneLibraryItem() {
|
||||||
<TextInput
|
<TextInput
|
||||||
id='dlg_full_name' //
|
id='dlg_full_name' //
|
||||||
label='Название'
|
label='Название'
|
||||||
{...register('title')}
|
{...register('item_data.title')}
|
||||||
error={errors.title}
|
error={errors.item_data?.title}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='flex justify-between gap-3'>
|
<div className='flex justify-between gap-3'>
|
||||||
<TextInput id='dlg_alias' label='Сокращение' className='w-64' {...register('alias')} error={errors.alias} />
|
<TextInput
|
||||||
|
id='dlg_alias'
|
||||||
|
label='Сокращение'
|
||||||
|
className='w-64'
|
||||||
|
{...register('item_data.alias')}
|
||||||
|
error={errors.item_data?.alias}
|
||||||
|
/>
|
||||||
<div className='flex flex-col gap-2'>
|
<div className='flex flex-col gap-2'>
|
||||||
<Label text='Доступ' className='self-center select-none' />
|
<Label text='Доступ' className='self-center select-none' />
|
||||||
<div className='ml-auto cc-icons'>
|
<div className='ml-auto cc-icons'>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name='access_policy'
|
name='item_data.access_policy'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<SelectAccessPolicy
|
<SelectAccessPolicy
|
||||||
value={field.value} //
|
value={field.value} //
|
||||||
|
@ -91,7 +99,7 @@ export function DlgCloneLibraryItem() {
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name='visible'
|
name='item_data.visible'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title={field.value ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
|
title={field.value ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
|
||||||
|
@ -107,19 +115,24 @@ export function DlgCloneLibraryItem() {
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name='location'
|
name='item_data.location'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<PickLocation
|
<PickLocation
|
||||||
value={field.value} //
|
value={field.value} //
|
||||||
rows={2}
|
rows={2}
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
className={!!errors.location ? '-mb-6' : undefined}
|
error={errors.item_data?.location}
|
||||||
error={errors.location}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextArea id='dlg_comment' {...register('description')} label='Описание' rows={4} error={errors.description} />
|
<TextArea
|
||||||
|
id='dlg_comment'
|
||||||
|
{...register('item_data.description')}
|
||||||
|
label='Описание'
|
||||||
|
rows={4}
|
||||||
|
error={errors.item_data?.description}
|
||||||
|
/>
|
||||||
|
|
||||||
{selected.length > 0 ? (
|
{selected.length > 0 ? (
|
||||||
<Controller
|
<Controller
|
||||||
|
|
|
@ -26,16 +26,22 @@ export function DlgCreateVersion() {
|
||||||
);
|
);
|
||||||
const { createVersion: versionCreate } = useCreateVersion();
|
const { createVersion: versionCreate } = useCreateVersion();
|
||||||
|
|
||||||
const { register, handleSubmit, control } = useForm<ICreateVersionDTO>({
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
control,
|
||||||
|
formState: { errors }
|
||||||
|
} = useForm<ICreateVersionDTO>({
|
||||||
resolver: zodResolver(schemaCreateVersion),
|
resolver: zodResolver(schemaCreateVersion),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
version: versions.length > 0 ? nextVersion(versions[versions.length - 1].version) : '1.0.0',
|
version: versions.length > 0 ? nextVersion(versions[versions.length - 1].version) : '1.0.0',
|
||||||
description: '',
|
description: '',
|
||||||
items: []
|
items: []
|
||||||
}
|
},
|
||||||
|
mode: 'onChange'
|
||||||
});
|
});
|
||||||
const version = useWatch({ control, name: 'version' });
|
const version = useWatch({ control, name: 'version' });
|
||||||
const canSubmit = !versions.find(ver => ver.version === version);
|
const canSubmit = !!version && !versions.find(ver => ver.version === version);
|
||||||
|
|
||||||
function onSubmit(data: ICreateVersionDTO) {
|
function onSubmit(data: ICreateVersionDTO) {
|
||||||
return versionCreate({ itemID, data }).then(onCreate);
|
return versionCreate({ itemID, data }).then(onCreate);
|
||||||
|
@ -50,7 +56,7 @@ export function DlgCreateVersion() {
|
||||||
submitText='Создать'
|
submitText='Создать'
|
||||||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||||
>
|
>
|
||||||
<TextInput id='dlg_version' {...register('version')} dense label='Версия' className='w-64' />
|
<TextInput id='dlg_version' {...register('version')} label='Версия' className='w-64' error={errors.version} />
|
||||||
<TextArea id='dlg_description' {...register('description')} spellCheck label='Описание' rows={3} />
|
<TextArea id='dlg_description' {...register('description')} spellCheck label='Описание' rows={3} />
|
||||||
{selected.length > 0 ? (
|
{selected.length > 0 ? (
|
||||||
<Controller
|
<Controller
|
||||||
|
|
|
@ -53,7 +53,7 @@ export function DlgEditVersions() {
|
||||||
const versionName = useWatch({ control, name: 'version' });
|
const versionName = useWatch({ control, name: 'version' });
|
||||||
|
|
||||||
const isValid = useMemo(
|
const isValid = useMemo(
|
||||||
() => schema.versions.every(ver => ver.id === versionID || ver.version != versionName),
|
() => !!versionName && schema.versions.every(ver => ver.id === versionID || ver.version != versionName),
|
||||||
[schema, versionID, versionName]
|
[schema, versionID, versionName]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ export function nextVersion(version: string): string {
|
||||||
* Validation location against regexp.
|
* Validation location against regexp.
|
||||||
*/
|
*/
|
||||||
export function validateLocation(location: string): boolean {
|
export function validateLocation(location: string): boolean {
|
||||||
return location.length <= limits.location_len && LOCATION_REGEXP.test(location);
|
return location.length <= limits.len_location && LOCATION_REGEXP.test(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { urls, useConceptNavigation } from '@/app';
|
||||||
|
|
||||||
import { Button, MiniButton, SubmitButton } from '@/components/control';
|
import { Button, MiniButton, SubmitButton } from '@/components/control';
|
||||||
import { IconDownload } from '@/components/icons';
|
import { IconDownload } from '@/components/icons';
|
||||||
import { InfoError } from '@/components/info-error';
|
|
||||||
import { Label, TextArea, TextInput } from '@/components/input';
|
import { Label, TextArea, TextInput } from '@/components/input';
|
||||||
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@ import { useLibrarySearchStore } from '../../stores/library-search';
|
||||||
|
|
||||||
export function FormCreateItem() {
|
export function FormCreateItem() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { createItem, isPending, error: serverError, reset: clearServerError } = useCreateItem();
|
const { createItem, isPending, reset: clearServerError } = useCreateItem();
|
||||||
|
|
||||||
const searchLocation = useLibrarySearchStore(state => state.location);
|
const searchLocation = useLibrarySearchStore(state => state.location);
|
||||||
const setSearchLocation = useLibrarySearchStore(state => state.setLocation);
|
const setSearchLocation = useLibrarySearchStore(state => state.setLocation);
|
||||||
|
@ -203,7 +202,6 @@ export function FormCreateItem() {
|
||||||
value={field.value} //
|
value={field.value} //
|
||||||
rows={2}
|
rows={2}
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
className={!!errors.location ? '-mb-6' : undefined}
|
|
||||||
error={errors.location}
|
error={errors.location}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -221,7 +219,6 @@ export function FormCreateItem() {
|
||||||
<SubmitButton text='Создать схему' loading={isPending} className='min-w-40' />
|
<SubmitButton text='Создать схему' loading={isPending} className='min-w-40' />
|
||||||
<Button text='Отмена' className='min-w-40' onClick={() => handleCancel()} />
|
<Button text='Отмена' className='min-w-40' onClick={() => handleCancel()} />
|
||||||
</div>
|
</div>
|
||||||
{serverError ? <InfoError error={serverError} /> : null}
|
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { z } from 'zod';
|
||||||
import { schemaLibraryItem } from '@/features/library/backend/types';
|
import { schemaLibraryItem } from '@/features/library/backend/types';
|
||||||
import { schemaSubstituteConstituents } from '@/features/rsform/backend/types';
|
import { schemaSubstituteConstituents } from '@/features/rsform/backend/types';
|
||||||
|
|
||||||
|
import { limits } from '@/utils/constants';
|
||||||
import { errorMsg } from '@/utils/labels';
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
/** Represents {@link IOperation} type. */
|
/** Represents {@link IOperation} type. */
|
||||||
|
@ -92,11 +93,18 @@ export const schemaOperation = z.strictObject({
|
||||||
result: z.number().nullable()
|
result: z.number().nullable()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaOperationData = schemaOperation.pick({
|
const schemaOperationData = schemaOperation
|
||||||
alias: true,
|
.pick({
|
||||||
title: true,
|
|
||||||
description: true,
|
|
||||||
parent: true
|
parent: true
|
||||||
|
})
|
||||||
|
.extend({
|
||||||
|
alias: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField),
|
||||||
|
title: z.string().max(limits.len_title, errorMsg.titleLength),
|
||||||
|
description: z.string().max(limits.len_description, errorMsg.descriptionLength)
|
||||||
|
});
|
||||||
|
|
||||||
|
const schemaBlockData = schemaOperationData.omit({ alias: true }).extend({
|
||||||
|
title: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaBlock = z.strictObject({
|
export const schemaBlock = z.strictObject({
|
||||||
|
@ -144,11 +152,7 @@ export const schemaOperationSchema = schemaLibraryItem.extend({
|
||||||
|
|
||||||
export const schemaCreateBlock = z.strictObject({
|
export const schemaCreateBlock = z.strictObject({
|
||||||
layout: schemaOssLayout,
|
layout: schemaOssLayout,
|
||||||
item_data: z.strictObject({
|
item_data: schemaBlockData,
|
||||||
title: z.string(),
|
|
||||||
description: z.string(),
|
|
||||||
parent: z.number().nullable()
|
|
||||||
}),
|
|
||||||
position: schemaPosition,
|
position: schemaPosition,
|
||||||
children_operations: z.array(z.number()),
|
children_operations: z.array(z.number()),
|
||||||
children_blocks: z.array(z.number())
|
children_blocks: z.array(z.number())
|
||||||
|
@ -162,11 +166,7 @@ export const schemaBlockCreatedResponse = z.strictObject({
|
||||||
export const schemaUpdateBlock = z.strictObject({
|
export const schemaUpdateBlock = z.strictObject({
|
||||||
target: z.number(),
|
target: z.number(),
|
||||||
layout: schemaOssLayout,
|
layout: schemaOssLayout,
|
||||||
item_data: z.strictObject({
|
item_data: schemaBlockData
|
||||||
title: z.string(),
|
|
||||||
description: z.string(),
|
|
||||||
parent: z.number().nullable()
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaDeleteBlock = z.strictObject({
|
export const schemaDeleteBlock = z.strictObject({
|
||||||
|
@ -191,12 +191,7 @@ export const schemaCreateSynthesis = z.strictObject({
|
||||||
export const schemaImportSchema = z.strictObject({
|
export const schemaImportSchema = z.strictObject({
|
||||||
layout: schemaOssLayout,
|
layout: schemaOssLayout,
|
||||||
item_data: schemaOperationData,
|
item_data: schemaOperationData,
|
||||||
position: z.strictObject({
|
position: schemaPosition,
|
||||||
x: z.number(),
|
|
||||||
y: z.number(),
|
|
||||||
width: z.number(),
|
|
||||||
height: z.number()
|
|
||||||
}),
|
|
||||||
source: z.number(),
|
source: z.number(),
|
||||||
clone_source: z.boolean()
|
clone_source: z.boolean()
|
||||||
});
|
});
|
||||||
|
@ -209,12 +204,7 @@ export const schemaOperationCreatedResponse = z.strictObject({
|
||||||
export const schemaUpdateOperation = z.strictObject({
|
export const schemaUpdateOperation = z.strictObject({
|
||||||
target: z.number(),
|
target: z.number(),
|
||||||
layout: schemaOssLayout,
|
layout: schemaOssLayout,
|
||||||
item_data: z.strictObject({
|
item_data: schemaOperationData,
|
||||||
alias: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
title: z.string(),
|
|
||||||
description: z.string(),
|
|
||||||
parent: z.number().nullable()
|
|
||||||
}),
|
|
||||||
arguments: z.array(z.number()),
|
arguments: z.array(z.number()),
|
||||||
substitutions: z.array(schemaSubstituteConstituents)
|
substitutions: z.array(schemaSubstituteConstituents)
|
||||||
});
|
});
|
||||||
|
|
|
@ -44,7 +44,7 @@ export function EditorOssCard() {
|
||||||
<div
|
<div
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'relative md:w-fit md:max-w-fit max-w-128',
|
'relative md:w-fit md:max-w-fit max-w-136',
|
||||||
'flex px-6 pt-8',
|
'flex px-6 pt-8',
|
||||||
isNarrow && 'flex-col md:items-center'
|
isNarrow && 'flex-col md:items-center'
|
||||||
)}
|
)}
|
||||||
|
@ -65,7 +65,7 @@ export function EditorOssCard() {
|
||||||
|
|
||||||
<OssStats
|
<OssStats
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'w-80 md:w-56 mt-3 md:mt-8 mx-auto md:ml-5 md:mr-0',
|
'w-80 md:w-56 mt-3 md:mt-8 mx-auto md:ml-8 md:mr-0',
|
||||||
'cc-animate-sidebar',
|
'cc-animate-sidebar',
|
||||||
showOSSStats ? 'max-w-full' : 'opacity-0 max-w-0'
|
showOSSStats ? 'max-w-full' : 'opacity-0 max-w-0'
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -41,7 +41,8 @@ export function FormOSS() {
|
||||||
description: schema.description,
|
description: schema.description,
|
||||||
visible: schema.visible,
|
visible: schema.visible,
|
||||||
read_only: schema.read_only
|
read_only: schema.read_only
|
||||||
}
|
},
|
||||||
|
mode: 'onChange'
|
||||||
});
|
});
|
||||||
const visible = useWatch({ control, name: 'visible' });
|
const visible = useWatch({ control, name: 'visible' });
|
||||||
const readOnly = useWatch({ control, name: 'read_only' });
|
const readOnly = useWatch({ control, name: 'read_only' });
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { z } from 'zod';
|
||||||
|
|
||||||
import { schemaLibraryItem, schemaVersionInfo } from '@/features/library/backend/types';
|
import { schemaLibraryItem, schemaVersionInfo } from '@/features/library/backend/types';
|
||||||
|
|
||||||
|
import { limits } from '@/utils/constants';
|
||||||
import { errorMsg } from '@/utils/labels';
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
/** Represents {@link IConstituenta} type. */
|
/** Represents {@link IConstituenta} type. */
|
||||||
|
@ -320,14 +321,14 @@ export const schemaVersionCreatedResponse = z.strictObject({
|
||||||
export const schemaCreateConstituenta = schemaConstituentaBasics
|
export const schemaCreateConstituenta = schemaConstituentaBasics
|
||||||
.pick({
|
.pick({
|
||||||
cst_type: true,
|
cst_type: true,
|
||||||
alias: true,
|
|
||||||
convention: true,
|
|
||||||
definition_formal: true,
|
|
||||||
definition_raw: true,
|
|
||||||
term_raw: true,
|
|
||||||
term_forms: true
|
term_forms: true
|
||||||
})
|
})
|
||||||
.extend({
|
.extend({
|
||||||
|
alias: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField),
|
||||||
|
convention: z.string().max(limits.len_description, errorMsg.descriptionLength),
|
||||||
|
definition_formal: z.string().max(limits.len_description, errorMsg.descriptionLength),
|
||||||
|
definition_raw: z.string().max(limits.len_description, errorMsg.descriptionLength),
|
||||||
|
term_raw: z.string().max(limits.len_description, errorMsg.descriptionLength),
|
||||||
insert_after: z.number().nullable()
|
insert_after: z.number().nullable()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -339,13 +340,20 @@ export const schemaConstituentaCreatedResponse = z.strictObject({
|
||||||
export const schemaUpdateConstituenta = z.strictObject({
|
export const schemaUpdateConstituenta = z.strictObject({
|
||||||
target: z.number(),
|
target: z.number(),
|
||||||
item_data: z.strictObject({
|
item_data: z.strictObject({
|
||||||
alias: z.string().optional(),
|
alias: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField).optional(),
|
||||||
cst_type: schemaCstType.optional(),
|
cst_type: schemaCstType.optional(),
|
||||||
convention: z.string().optional(),
|
convention: z.string().max(limits.len_description, errorMsg.descriptionLength).optional(),
|
||||||
definition_formal: z.string().optional(),
|
definition_formal: z.string().max(limits.len_description, errorMsg.descriptionLength).optional(),
|
||||||
definition_raw: z.string().optional(),
|
definition_raw: z.string().max(limits.len_description, errorMsg.descriptionLength).optional(),
|
||||||
term_raw: z.string().optional(),
|
term_raw: z.string().max(limits.len_description, errorMsg.descriptionLength).optional(),
|
||||||
term_forms: z.array(z.strictObject({ text: z.string(), tags: z.string() })).optional()
|
term_forms: z
|
||||||
|
.array(
|
||||||
|
z.strictObject({
|
||||||
|
text: z.string().max(limits.len_description, errorMsg.descriptionLength),
|
||||||
|
tags: z.string().max(limits.len_alias, errorMsg.aliasLength)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ export function EditorRSFormCard() {
|
||||||
<div
|
<div
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'relative md:w-fit md:max-w-fit max-w-128',
|
'relative md:w-fit md:max-w-fit max-w-136',
|
||||||
'flex px-6 pt-8',
|
'flex px-6 pt-8',
|
||||||
isNarrow && 'flex-col md:items-center'
|
isNarrow && 'flex-col md:items-center'
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -51,7 +51,8 @@ export function FormRSForm() {
|
||||||
description: schema.description,
|
description: schema.description,
|
||||||
visible: schema.visible,
|
visible: schema.visible,
|
||||||
read_only: schema.read_only
|
read_only: schema.read_only
|
||||||
}
|
},
|
||||||
|
mode: 'onChange'
|
||||||
});
|
});
|
||||||
const visible = useWatch({ control, name: 'visible' });
|
const visible = useWatch({ control, name: 'visible' });
|
||||||
const readOnly = useWatch({ control, name: 'read_only' });
|
const readOnly = useWatch({ control, name: 'read_only' });
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { patterns } from '@/utils/constants';
|
import { limits, patterns } from '@/utils/constants';
|
||||||
import { errorMsg } from '@/utils/labels';
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
/** Represents user profile for viewing and editing. */
|
/** Represents user profile for viewing and editing. */
|
||||||
|
@ -29,20 +29,24 @@ export const schemaUserProfile = schemaUser.omit({ is_staff: true });
|
||||||
|
|
||||||
export const schemaUserInfo = schemaUser.omit({ username: true, email: true, is_staff: true });
|
export const schemaUserInfo = schemaUser.omit({ username: true, email: true, is_staff: true });
|
||||||
|
|
||||||
export const schemaUserSignup = z
|
const schemaUserInput = z.strictObject({
|
||||||
.object({
|
username: z
|
||||||
username: z.string().nonempty(errorMsg.requiredField).regex(RegExp(patterns.login), errorMsg.loginFormat),
|
.string()
|
||||||
email: z.string().email(errorMsg.emailField),
|
.nonempty(errorMsg.requiredField)
|
||||||
first_name: z.string(),
|
.regex(RegExp(patterns.login), errorMsg.loginFormat)
|
||||||
last_name: z.string(),
|
.max(limits.len_alias, errorMsg.aliasLength),
|
||||||
|
email: z.string().email(errorMsg.emailField).max(limits.len_email, errorMsg.emailLength),
|
||||||
|
first_name: z.string().max(limits.len_alias, errorMsg.aliasLength),
|
||||||
|
last_name: z.string().max(limits.len_alias, errorMsg.aliasLength)
|
||||||
|
});
|
||||||
|
|
||||||
password: z.string().nonempty(errorMsg.requiredField),
|
export const schemaUserSignup = schemaUserInput
|
||||||
password2: z.string().nonempty(errorMsg.requiredField)
|
.extend({
|
||||||
|
password: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField),
|
||||||
|
password2: z.string().max(limits.len_alias, errorMsg.aliasLength).nonempty(errorMsg.requiredField)
|
||||||
})
|
})
|
||||||
.refine(schema => schema.password === schema.password2, { path: ['password2'], message: errorMsg.passwordsMismatch });
|
.refine(schema => schema.password === schema.password2, { path: ['password2'], message: errorMsg.passwordsMismatch });
|
||||||
|
|
||||||
export const schemaUpdateProfile = z.strictObject({
|
export const schemaUpdateProfile = schemaUserInput.omit({
|
||||||
email: z.string().email(errorMsg.emailField),
|
username: true
|
||||||
first_name: z.string(),
|
|
||||||
last_name: z.string()
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,7 +21,8 @@ export function EditorPassword() {
|
||||||
clearErrors,
|
clearErrors,
|
||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useForm<IChangePasswordDTO>({
|
} = useForm<IChangePasswordDTO>({
|
||||||
resolver: zodResolver(schemaChangePassword)
|
resolver: zodResolver(schemaChangePassword),
|
||||||
|
mode: 'onChange'
|
||||||
});
|
});
|
||||||
|
|
||||||
function resetErrors() {
|
function resetErrors() {
|
||||||
|
|
|
@ -30,7 +30,8 @@ export function EditorProfile() {
|
||||||
first_name: profile.first_name,
|
first_name: profile.first_name,
|
||||||
last_name: profile.last_name,
|
last_name: profile.last_name,
|
||||||
email: profile.email
|
email: profile.email
|
||||||
}
|
},
|
||||||
|
mode: 'onChange'
|
||||||
});
|
});
|
||||||
|
|
||||||
useBlockNavigation(isDirty);
|
useBlockNavigation(isDirty);
|
||||||
|
|
|
@ -4,22 +4,22 @@
|
||||||
|
|
||||||
/** Global application Parameters. The place where magic numbers are put to rest. */
|
/** Global application Parameters. The place where magic numbers are put to rest. */
|
||||||
export const PARAMETER = {
|
export const PARAMETER = {
|
||||||
smallScreen: 640, // == tailwind:sm
|
smallScreen: 640, // Tailwind CSS 'sm' breakpoint for small screens (in pixels)
|
||||||
|
|
||||||
minimalTimeout: 10, // milliseconds delay for fast updates
|
minimalTimeout: 10, // Minimum delay for rapid UI updates (in milliseconds)
|
||||||
refreshTimeout: 100, // milliseconds delay for post-refresh actions
|
refreshTimeout: 100, // Delay after refresh actions to allow UI to settle (in milliseconds)
|
||||||
notificationDelay: 300, // milliseconds delay for notifications
|
notificationDelay: 300, // Duration to display notifications (in milliseconds)
|
||||||
zoomDuration: 500, // milliseconds animation duration
|
zoomDuration: 500, // Duration of zoom animations (in milliseconds)
|
||||||
navigationPopupDelay: 300, // milliseconds delay for navigation popup
|
navigationPopupDelay: 300, // Delay before showing navigation popups (in milliseconds)
|
||||||
|
|
||||||
moveDuration: 500, // milliseconds - duration of move animation
|
moveDuration: 500, // Duration of move animations (in milliseconds)
|
||||||
|
|
||||||
ossImageWidth: 1280, // pixels - size of OSS image
|
ossImageWidth: 1280, // Default width for OSS images (in pixels)
|
||||||
ossImageHeight: 960, // pixels - size of OSS image
|
ossImageHeight: 960, // Default height for OSS images (in pixels)
|
||||||
|
|
||||||
graphHandleSize: 3, // pixels - size of graph connection handle
|
graphHandleSize: 3, // Size of graph connection handles (in pixels)
|
||||||
graphNodePadding: 5, // pixels - padding of graph node
|
graphNodePadding: 5, // Padding inside graph nodes (in pixels)
|
||||||
graphNodeRadius: 20, // pixels - radius of graph node
|
graphNodeRadius: 20, // Radius of graph nodes (in pixels)
|
||||||
|
|
||||||
logicLabel: 'LOGIC',
|
logicLabel: 'LOGIC',
|
||||||
errorNodeLabel: '[ERROR]',
|
errorNodeLabel: '[ERROR]',
|
||||||
|
@ -28,7 +28,12 @@ export const PARAMETER = {
|
||||||
|
|
||||||
/** Numeric limitations. */
|
/** Numeric limitations. */
|
||||||
export const limits = {
|
export const limits = {
|
||||||
location_len: 500
|
len_alias: 255,
|
||||||
|
len_email: 320,
|
||||||
|
len_title: 500,
|
||||||
|
len_location: 500,
|
||||||
|
len_description: 10000,
|
||||||
|
len_text: 20000
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/** Exteor file extension for RSForm. */
|
/** Exteor file extension for RSForm. */
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
* Description is a long description used in tooltips.
|
* Description is a long description used in tooltips.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { limits } from './constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UI info descriptors.
|
* UI info descriptors.
|
||||||
*/
|
*/
|
||||||
|
@ -48,6 +50,11 @@ export const infoMsg = {
|
||||||
*/
|
*/
|
||||||
export const errorMsg = {
|
export const errorMsg = {
|
||||||
astFailed: 'Невозможно построить дерево разбора',
|
astFailed: 'Невозможно построить дерево разбора',
|
||||||
|
aliasLength: `до ${limits.len_alias} символов`,
|
||||||
|
emailLength: `до ${limits.len_email} символов`,
|
||||||
|
titleLength: `до ${limits.len_title} символов`,
|
||||||
|
descriptionLength: `до ${limits.len_description} символов`,
|
||||||
|
textLength: `до ${limits.len_text} символов`,
|
||||||
typeStructureFailed: 'Структура отсутствует',
|
typeStructureFailed: 'Структура отсутствует',
|
||||||
passwordsMismatch: 'Пароли не совпадают',
|
passwordsMismatch: 'Пароли не совпадают',
|
||||||
passwordsSame: 'Пароль совпадает со старым',
|
passwordsSame: 'Пароль совпадает со старым',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user