F: Improve updating timestamps
Some checks failed
Frontend CI / build (22.x) (push) Waiting to run
Frontend CI / notify-failure (push) Blocked by required conditions
Backend CI / build (3.12) (push) Has been cancelled
Backend CI / notify-failure (push) Has been cancelled

This commit is contained in:
Ivan 2025-07-23 16:13:29 +03:00
parent 65eacba03c
commit 35faeb580b
26 changed files with 89 additions and 49 deletions

View File

@ -105,7 +105,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
tags=['OSS'],
request=s.LayoutSerializer,
responses={
c.HTTP_200_OK: None,
c.HTTP_200_OK: s.OperationSchemaSerializer,
c.HTTP_403_FORBIDDEN: None,
c.HTTP_404_NOT_FOUND: None
}
@ -115,8 +115,11 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
''' Endpoint: Update schema layout. '''
serializer = s.LayoutSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
m.OperationSchema(self.get_object()).update_layout(serializer.validated_data['data'])
return Response(status=c.HTTP_200_OK)
oss = m.OperationSchema(self.get_object())
with transaction.atomic():
oss.update_layout(serializer.validated_data['data'])
oss.save(update_fields=['time_update'])
return Response(status=c.HTTP_200_OK, data=s.OperationSchemaSerializer(oss.model).data)
@extend_schema(
summary='create block',
@ -161,6 +164,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
for operation in children_operations:
operation.parent = new_block
m.Operation.objects.bulk_update(children_operations, ['parent'])
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_201_CREATED,
@ -202,6 +206,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
if 'parent' in serializer.validated_data['item_data']:
block.parent = serializer.validated_data['item_data']['parent']
block.save(update_fields=['title', 'description', 'parent'])
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data
@ -234,6 +239,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
with transaction.atomic():
oss.delete_block(block)
oss.update_layout(layout)
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
@ -269,6 +275,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
for block in serializer.validated_data['blocks']:
block.parent = serializer.validated_data['destination']
block.save(update_fields=['parent'])
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
@ -311,6 +318,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
})
oss.update_layout(layout)
oss.create_input(new_operation)
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_201_CREATED,
@ -364,6 +372,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
new_operation.result = _create_clone(prototype, new_operation, oss.model)
new_operation.save(update_fields=["result"])
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_201_CREATED,
data={
@ -410,6 +420,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
oss.set_substitutions(new_operation.pk, serializer.validated_data['substitutions'])
oss.execute_operation(new_operation)
oss.update_layout(layout)
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_201_CREATED,
@ -465,6 +476,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
oss.set_arguments(operation.pk, serializer.validated_data['arguments'])
if 'substitutions' in serializer.validated_data:
oss.set_substitutions(operation.pk, serializer.validated_data['substitutions'])
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data
@ -505,6 +518,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
elif old_schema.is_synced(oss.model):
old_schema.visible = True
old_schema.save(update_fields=['visible'])
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data
@ -544,6 +559,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
with transaction.atomic():
oss.update_layout(serializer.validated_data['layout'])
schema = oss.create_input(operation)
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
@ -595,6 +611,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
old_schema.save(update_fields=['visible'])
oss.update_layout(serializer.validated_data['layout'])
oss.set_input(target_operation.pk, schema)
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,
data=s.OperationSchemaSerializer(oss.model).data
@ -634,6 +652,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
with transaction.atomic():
oss.update_layout(serializer.validated_data['layout'])
oss.execute_operation(operation)
oss.save(update_fields=['time_update'])
return Response(
status=c.HTTP_200_OK,

View File

@ -9,11 +9,11 @@ export function useUpdateTimestamp() {
const client = useQueryClient();
const libraryKey = useLibraryListKey();
return {
updateTimestamp: (target: number) =>
updateTimestamp: (target: number, timestamp?: string) =>
client.setQueryData(
libraryKey, //
(prev: RO<ILibraryItem[]> | undefined) =>
prev?.map(item => (item.id === target ? { ...item, time_update: Date() } : item))
prev?.map(item => (item.id === target ? { ...item, time_update: timestamp ?? Date() } : item))
)
};
}

View File

@ -18,6 +18,7 @@ import {
type IOperationCreatedResponse,
type IOperationSchemaDTO,
type IOssLayout,
type IOssLayoutDTO,
type IRelocateConstituentsDTO,
type ITargetOperation,
type IUpdateBlockDTO,
@ -49,7 +50,7 @@ export const ossApi = {
},
updateLayout: ({ itemID, data, isSilent }: { itemID: number; data: IOssLayout; isSilent?: boolean }) =>
axiosPatch({
axiosPatch<IOssLayoutDTO, IOperationSchemaDTO>({
endpoint: `/api/oss/${itemID}/update-layout`,
request: {
data: { data: data },

View File

@ -28,6 +28,9 @@ export type IOperationSchemaDTO = z.infer<typeof schemaOperationSchema>;
/** Represents {@link IOperationSchema} layout. */
export type IOssLayout = z.infer<typeof schemaOssLayout>;
/** Represents {@link IOperationSchema} layout for data transfer. */
export type IOssLayoutDTO = z.infer<typeof schemaOssLayoutData>;
/** Represents {@link IBlock} data, used in Create action. */
export type ICreateBlockDTO = z.infer<typeof schemaCreateBlock>;
@ -136,6 +139,10 @@ export const schemaNodePosition = schemaPosition.extend({
export const schemaOssLayout = z.array(schemaNodePosition);
export const schemaOssLayoutData = z.strictObject({
data: schemaOssLayout
});
export const schemaOperationSchema = schemaLibraryItem.extend({
editors: z.number().array(),
operations: z.array(schemaOperation),

View File

@ -13,9 +13,9 @@ export const useCreateBlock = () => {
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-block'],
mutationFn: ossApi.createBlock,
onSuccess: response => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
updateTimestamp(response.oss.id);
onSuccess: data => {
updateTimestamp(data.oss.id, data.oss.time_update);
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
},
onError: () => client.invalidateQueries()
});

View File

@ -1,5 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
import { KEYS } from '@/backend/configuration';
import { ossApi } from './api';
@ -7,10 +9,12 @@ import { type ITargetOperation } from './types';
export const useCreateInput = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-input'],
mutationFn: ossApi.createInput,
onSuccess: async data => {
updateTimestamp(data.oss.id, data.oss.time_update);
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
await Promise.allSettled([
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),

View File

@ -13,9 +13,9 @@ export const useCreateSchema = () => {
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-schema'],
mutationFn: ossApi.createSchema,
onSuccess: response => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
updateTimestamp(response.oss.id);
onSuccess: data => {
updateTimestamp(data.oss.id, data.oss.time_update);
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
},
onError: () => client.invalidateQueries()
});

View File

@ -13,9 +13,9 @@ export const useCreateSynthesis = () => {
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-synthesis'],
mutationFn: ossApi.createSynthesis,
onSuccess: response => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
updateTimestamp(response.oss.id);
onSuccess: data => {
updateTimestamp(data.oss.id, data.oss.time_update);
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
},
onError: () => client.invalidateQueries()
});

View File

@ -1,5 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
import { KEYS } from '@/backend/configuration';
import { ossApi } from './api';
@ -7,10 +9,12 @@ import { type IDeleteBlockDTO } from './types';
export const useDeleteBlock = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-block'],
mutationFn: ossApi.deleteBlock,
onSuccess: async data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
await Promise.allSettled([
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),

View File

@ -1,5 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
import { KEYS } from '@/backend/configuration';
import { ossApi } from './api';
@ -7,10 +9,12 @@ import { type IDeleteOperationDTO } from './types';
export const useDeleteOperation = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-operation'],
mutationFn: ossApi.deleteOperation,
onSuccess: async data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
await Promise.allSettled([
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),

View File

@ -1,5 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
import { KEYS } from '@/backend/configuration';
import { ossApi } from './api';
@ -7,10 +9,12 @@ import { type ITargetOperation } from './types';
export const useExecuteOperation = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'execute-operation'],
mutationFn: ossApi.executeOperation,
onSuccess: async data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
await Promise.allSettled([
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),

View File

@ -13,9 +13,9 @@ export const useImportSchema = () => {
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'import-schema'],
mutationFn: ossApi.importSchema,
onSuccess: response => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
updateTimestamp(response.oss.id);
onSuccess: data => {
updateTimestamp(data.oss.id, data.oss.time_update);
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
},
onError: () => client.invalidateQueries()
});

View File

@ -1,5 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
import { KEYS } from '@/backend/configuration';
import { ossApi } from './api';
@ -7,10 +9,12 @@ import { type IMoveItemsDTO } from './types';
export const useMoveItems = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'move-items'],
mutationFn: ossApi.moveItems,
onSuccess: async data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
await Promise.allSettled([
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),

View File

@ -1,6 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { type ILibraryItem } from '@/features/library';
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
import { KEYS } from '@/backend/configuration';
@ -9,10 +10,12 @@ import { type IUpdateBlockDTO } from './types';
export const useUpdateBlock = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'update-block'],
mutationFn: ossApi.updateBlock,
onSuccess: (data, variables) => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(KEYS.composite.ossItem({ itemID: data.id }), data);
const schemaID = data.operations.find(item => item.id === variables.data.target)?.result;
if (!schemaID) {

View File

@ -1,5 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
import { KEYS } from '@/backend/configuration';
import { ossApi } from './api';
@ -7,10 +9,12 @@ import { type IUpdateInputDTO } from './types';
export const useUpdateInput = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'update-input'],
mutationFn: ossApi.updateInput,
onSuccess: async data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
await Promise.allSettled([
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),

View File

@ -3,10 +3,9 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
import { KEYS } from '@/backend/configuration';
import { type RO } from '@/utils/meta';
import { ossApi } from './api';
import { type IOperationSchemaDTO, type IOssLayout } from './types';
import { type IOssLayout } from './types';
export const useUpdateLayout = () => {
const client = useQueryClient();
@ -14,18 +13,9 @@ export const useUpdateLayout = () => {
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'update-layout'],
mutationFn: ossApi.updateLayout,
onSuccess: (_, variables) => {
updateTimestamp(variables.itemID);
client.setQueryData(
ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey,
(prev: RO<IOperationSchemaDTO> | undefined) =>
!prev
? prev
: {
...prev,
layout: variables.data
}
);
onSuccess: data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(KEYS.composite.ossItem({ itemID: data.id }), data);
},
onError: () => client.invalidateQueries()
});

View File

@ -1,6 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { type ILibraryItem } from '@/features/library';
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
import { KEYS } from '@/backend/configuration';
@ -9,10 +10,12 @@ import { type IUpdateOperationDTO } from './types';
export const useUpdateOperation = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'update-operation'],
mutationFn: ossApi.updateOperation,
onSuccess: (data, variables) => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(KEYS.composite.ossItem({ itemID: data.id }), data);
const schemaID = data.operations.find(item => item.id === variables.data.target)?.result;
if (!schemaID) {

View File

@ -14,9 +14,8 @@ export const useCreateConstituenta = () => {
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'create-constituenta'],
mutationFn: rsformsApi.createConstituenta,
onSuccess: async data => {
updateTimestamp(data.schema.id, data.schema.time_update);
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
updateTimestamp(data.schema.id);
await Promise.allSettled([
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({

View File

@ -14,9 +14,8 @@ export const useDeleteConstituents = () => {
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'delete-constituents'],
mutationFn: rsformsApi.deleteConstituents,
onSuccess: async data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
updateTimestamp(data.id);
await Promise.allSettled([
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({

View File

@ -14,9 +14,8 @@ export const useInlineSynthesis = () => {
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'inline-synthesis'],
mutationFn: rsformsApi.inlineSynthesis,
onSuccess: async data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
updateTimestamp(data.id);
await Promise.allSettled([
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({

View File

@ -14,8 +14,8 @@ export const useMoveConstituents = () => {
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'move-constituents'],
mutationFn: rsformsApi.moveConstituents,
onSuccess: data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
updateTimestamp(data.id);
},
onError: () => client.invalidateQueries()
});

View File

@ -13,9 +13,8 @@ export const useProduceStructure = () => {
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'produce-structure'],
mutationFn: rsformsApi.produceStructure,
onSuccess: async data => {
updateTimestamp(data.schema.id, data.schema.time_update);
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
updateTimestamp(data.schema.id);
await Promise.allSettled([
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({

View File

@ -13,9 +13,8 @@ export const useResetAliases = () => {
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'reset-aliases'],
mutationFn: rsformsApi.resetAliases,
onSuccess: async data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
updateTimestamp(data.id);
await Promise.allSettled([
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({

View File

@ -13,8 +13,8 @@ export const useRestoreOrder = () => {
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'restore-order'],
mutationFn: rsformsApi.restoreOrder,
onSuccess: data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
updateTimestamp(data.id);
},
onError: () => client.invalidateQueries()
});

View File

@ -14,9 +14,8 @@ export const useSubstituteConstituents = () => {
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'substitute-constituents'],
mutationFn: rsformsApi.substituteConstituents,
onSuccess: async data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
updateTimestamp(data.id);
await Promise.allSettled([
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({

View File

@ -13,10 +13,9 @@ export const useUpdateConstituenta = () => {
const mutation = useMutation({
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'update-constituenta'],
mutationFn: rsformsApi.updateConstituenta,
onSuccess: async (data, _) => {
onSuccess: async data => {
updateTimestamp(data.id, data.time_update);
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
updateTimestamp(data.id);
await Promise.allSettled([
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({