1- import { CellValueType , FieldType , SortFunc , TimeFormatting } from '@teable/core' ;
1+ import { CellValueType , FieldKeyType , FieldType , SortFunc , TimeFormatting } from '@teable/core' ;
22import {
3- FieldKeyType ,
43 ListTableRecordsQuery ,
54 ListTableRecordsResult ,
5+ UpdateRecordsResult ,
66 v2CoreTokens ,
77} from '@teable/v2-core' ;
88import { beforeEach , describe , expect , it , vi } from 'vitest' ;
@@ -17,8 +17,12 @@ describe('RecordOpenApiV2Service', () => {
1717 const getReadQuerySource = vi . fn ( ) ;
1818 const getFieldsByQuery = vi . fn ( ) ;
1919 const execute = vi . fn ( ) ;
20+ const commandExecute = vi . fn ( ) ;
2021 const resolve = vi . fn ( ) ;
2122 const getContainer = vi . fn ( ) ;
23+ const clsGet = vi . fn ( ) ;
24+ const cacheDel = vi . fn ( ) ;
25+ const cacheSetDetail = vi . fn ( ) ;
2226
2327 let service : RecordOpenApiV2Service ;
2428
@@ -29,12 +33,28 @@ describe('RecordOpenApiV2Service', () => {
2933 if ( token === v2CoreTokens . queryBus ) {
3034 return { execute } ;
3135 }
36+ if ( token === v2CoreTokens . commandBus ) {
37+ return { execute : commandExecute } ;
38+ }
3239 return undefined ;
3340 } ) ;
3441 getContainer . mockResolvedValue ( { resolve } ) ;
3542 createContext . mockResolvedValue ( { } ) ;
43+ clsGet . mockImplementation ( ( key : string ) => {
44+ if ( key === 'user.id' ) {
45+ return `usr${ 'h' . repeat ( 16 ) } ` ;
46+ }
47+ if ( key === 'windowId' ) {
48+ return `win${ 'i' . repeat ( 16 ) } ` ;
49+ }
50+ return undefined ;
51+ } ) ;
3652 getReadQuerySource . mockResolvedValue ( undefined ) ;
3753 getFieldsByQuery . mockResolvedValue ( [ ] ) ;
54+ commandExecute . mockResolvedValue ( {
55+ isErr : ( ) => false ,
56+ value : UpdateRecordsResult . create ( 2 , [ ] ) ,
57+ } ) ;
3858 execute . mockResolvedValue ( {
3959 isErr : ( ) => false ,
4060 value : ListTableRecordsResult . create (
@@ -51,14 +71,13 @@ describe('RecordOpenApiV2Service', () => {
5171 { data : { id : 'rec1111111111111111' , fields : { } } } ,
5272 { data : { id : 'rec2222222222222222' , fields : { } } } ,
5373 ] ) ;
54-
5574 service = new RecordOpenApiV2Service (
5675 { getContainer } as never ,
5776 { createContext } as never ,
5877 { getDocIdsByQuery, getSnapshotBulkWithPermission } as never ,
5978 { } as never ,
60- { } as never ,
61- { get : vi . fn ( ) } as never ,
79+ { get : clsGet } as never ,
80+ { del : cacheDel , setDetail : cacheSetDetail } as never ,
6281 { getFieldsByQuery } as never ,
6382 { getReadQuerySource } as never ,
6483 { } as never ,
@@ -263,4 +282,93 @@ describe('RecordOpenApiV2Service', () => {
263282 } ,
264283 ] ) ;
265284 } ) ;
285+
286+ it ( 'routes explicit batch field updates through native v2 updateRecords' , async ( ) => {
287+ getSnapshotBulkWithPermission . mockResolvedValue ( [
288+ { data : { id : 'rec1111111111111111' , fields : { fldStatus : 'Done' } } } ,
289+ { data : { id : 'rec2222222222222222' , fields : { fldStatus : 'Open' } } } ,
290+ ] ) ;
291+
292+ const result = await service . updateRecords ( `tbl${ 'c' . repeat ( 16 ) } ` , {
293+ fieldKeyType : FieldKeyType . Id ,
294+ records : [
295+ { id : 'rec1111111111111111' , fields : { fldStatus : 'Done' } } ,
296+ { id : 'rec2222222222222222' , fields : { fldStatus : 'Open' } } ,
297+ ] ,
298+ } ) ;
299+
300+ expect ( commandExecute ) . toHaveBeenCalledTimes ( 1 ) ;
301+ expect ( commandExecute . mock . calls [ 0 ] ?. [ 1 ] . records ) . toHaveLength ( 2 ) ;
302+ expect ( commandExecute . mock . calls [ 0 ] ?. [ 1 ] . records ?. [ 0 ] ?. recordId . toString ( ) ) . toBe (
303+ 'rec1111111111111111'
304+ ) ;
305+ expect ( commandExecute . mock . calls [ 0 ] ?. [ 1 ] . records ?. [ 1 ] ?. fieldValues . get ( 'fldStatus' ) ) . toBe (
306+ 'Open'
307+ ) ;
308+ expect ( commandExecute . mock . calls [ 0 ] ?. [ 1 ] . order ) . toBeUndefined ( ) ;
309+ expect ( result ) . toEqual ( [
310+ { id : 'rec1111111111111111' , fields : { fldStatus : 'Done' } } ,
311+ { id : 'rec2222222222222222' , fields : { fldStatus : 'Open' } } ,
312+ ] ) ;
313+ expect ( cacheDel ) . toHaveBeenCalledWith (
314+ `operations:engine:usr${ 'h' . repeat ( 16 ) } :tbl${ 'c' . repeat ( 16 ) } :win${ 'i' . repeat ( 16 ) } `
315+ ) ;
316+ } ) ;
317+
318+ it ( 'passes batch order through native v2 updateRecords' , async ( ) => {
319+ await service . updateRecords ( `tbl${ 'c' . repeat ( 16 ) } ` , {
320+ fieldKeyType : FieldKeyType . Id ,
321+ records : [
322+ { id : 'rec1111111111111111' , fields : { fldStatus : 'Done' } } ,
323+ { id : 'rec2222222222222222' , fields : { fldStatus : 'Open' } } ,
324+ ] ,
325+ order : {
326+ viewId : `viw${ 'c' . repeat ( 16 ) } ` ,
327+ anchorId : 'rec1111111111111111' ,
328+ position : 'after' ,
329+ } ,
330+ } ) ;
331+
332+ expect ( commandExecute ) . toHaveBeenCalledTimes ( 1 ) ;
333+ expect ( commandExecute . mock . calls [ 0 ] ?. [ 1 ] . order ?. viewId . toString ( ) ) . toBe ( `viw${ 'c' . repeat ( 16 ) } ` ) ;
334+ expect ( commandExecute . mock . calls [ 0 ] ?. [ 1 ] . order ?. position ) . toBe ( 'after' ) ;
335+ } ) ;
336+
337+ it ( 'merges duplicate record updates before calling native v2 updateRecords' , async ( ) => {
338+ getSnapshotBulkWithPermission . mockResolvedValue ( [
339+ { data : { id : 'rec1111111111111111' , fields : { fldStatus : 'Done' , fldNote : 'latest' } } } ,
340+ ] ) ;
341+
342+ const result = await service . updateRecords ( `tbl${ 'c' . repeat ( 16 ) } ` , {
343+ fieldKeyType : FieldKeyType . Id ,
344+ records : [
345+ { id : 'rec1111111111111111' , fields : { fldStatus : 'Open' , fldNote : 'first' } } ,
346+ { id : 'rec1111111111111111' , fields : { fldStatus : 'Done' } } ,
347+ { id : 'rec1111111111111111' , fields : { fldNote : 'latest' } } ,
348+ ] ,
349+ } ) ;
350+
351+ expect ( commandExecute ) . toHaveBeenCalledTimes ( 1 ) ;
352+ expect ( commandExecute . mock . calls [ 0 ] ?. [ 1 ] . records ) . toHaveLength ( 1 ) ;
353+ expect ( commandExecute . mock . calls [ 0 ] ?. [ 1 ] . records ?. [ 0 ] ?. recordId . toString ( ) ) . toBe (
354+ 'rec1111111111111111'
355+ ) ;
356+ expect ( commandExecute . mock . calls [ 0 ] ?. [ 1 ] . records ?. [ 0 ] ?. fieldValues . get ( 'fldStatus' ) ) . toBe (
357+ 'Done'
358+ ) ;
359+ expect ( commandExecute . mock . calls [ 0 ] ?. [ 1 ] . records ?. [ 0 ] ?. fieldValues . get ( 'fldNote' ) ) . toBe (
360+ 'latest'
361+ ) ;
362+ expect ( getSnapshotBulkWithPermission ) . toHaveBeenCalledWith (
363+ `tbl${ 'c' . repeat ( 16 ) } ` ,
364+ [ 'rec1111111111111111' ] ,
365+ undefined ,
366+ FieldKeyType . Id ,
367+ undefined ,
368+ true
369+ ) ;
370+ expect ( result ) . toEqual ( [
371+ { id : 'rec1111111111111111' , fields : { fldStatus : 'Done' , fldNote : 'latest' } } ,
372+ ] ) ;
373+ } ) ;
266374} ) ;
0 commit comments