@@ -55,6 +55,7 @@ import { axiosTablesRetryConfig } from '../../src/lib/openops-tables/requests-he
5555import {
5656 addRow ,
5757 batchDeleteRows ,
58+ batchTableAggregations ,
5859 deleteRow ,
5960 getRowByPrimaryKeyValue ,
6061 getRows ,
@@ -356,6 +357,156 @@ describe('batchDeleteRows', () => {
356357 } ) ;
357358} ) ;
358359
360+ describe ( 'batchTableAggregations' , ( ) => {
361+ beforeEach ( ( ) => {
362+ jest . clearAllMocks ( ) ;
363+ } ) ;
364+
365+ test ( 'posts to correct url with count aggregation' , async ( ) => {
366+ makeOpenOpsTablesPostMock . mockResolvedValue ( { '1' : { count : 5 } } ) ;
367+ createAxiosHeadersMock . mockReturnValue ( 'some header' ) ;
368+
369+ const result = await batchTableAggregations ( {
370+ tokenOrResolver : 'token' ,
371+ tableIds : [ 1 ] ,
372+ aggregations : [ { type : 'count' } ] ,
373+ } ) ;
374+
375+ expect ( result ) . toStrictEqual ( { '1' : { count : 5 } } ) ;
376+ expect ( acquireMock ) . toBeCalledTimes ( 1 ) ;
377+ expect ( releaseMock ) . toBeCalledTimes ( 1 ) ;
378+ expect ( makeOpenOpsTablesPostMock ) . toBeCalledTimes ( 1 ) ;
379+ expect ( makeOpenOpsTablesPostMock ) . toHaveBeenCalledWith (
380+ 'api/database/rows/batch-aggregations/' ,
381+ { table_ids : [ 1 ] , filters : [ ] , aggregations : [ { type : 'count' } ] } ,
382+ 'some header' ,
383+ ) ;
384+ expect ( createAxiosHeadersMock ) . toHaveBeenCalledWith ( 'token' ) ;
385+ } ) ;
386+
387+ test ( 'posts to correct url with sum aggregation' , async ( ) => {
388+ makeOpenOpsTablesPostMock . mockResolvedValue ( {
389+ '2' : { 'sum__Estimated savings USD per month' : 27880 } ,
390+ } ) ;
391+ createAxiosHeadersMock . mockReturnValue ( 'some header' ) ;
392+
393+ const result = await batchTableAggregations ( {
394+ tokenOrResolver : 'token' ,
395+ tableIds : [ 2 ] ,
396+ aggregations : [ { type : 'sum' , field : 'Estimated savings USD per month' } ] ,
397+ } ) ;
398+
399+ expect ( result ) . toStrictEqual ( {
400+ '2' : { 'sum__Estimated savings USD per month' : 27880 } ,
401+ } ) ;
402+ expect ( makeOpenOpsTablesPostMock ) . toHaveBeenCalledWith (
403+ 'api/database/rows/batch-aggregations/' ,
404+ {
405+ table_ids : [ 2 ] ,
406+ filters : [ ] ,
407+ aggregations : [
408+ { type : 'sum' , field : 'Estimated savings USD per month' } ,
409+ ] ,
410+ } ,
411+ 'some header' ,
412+ ) ;
413+ } ) ;
414+
415+ test ( 'posts multiple aggregations for multiple tables' , async ( ) => {
416+ makeOpenOpsTablesPostMock . mockResolvedValue ( {
417+ '1' : { count : 10 , sum__Cost : 500 } ,
418+ '2' : { count : 3 , sum__Cost : 120 } ,
419+ } ) ;
420+ createAxiosHeadersMock . mockReturnValue ( 'some header' ) ;
421+
422+ const result = await batchTableAggregations ( {
423+ tokenOrResolver : 'token' ,
424+ tableIds : [ 1 , 2 ] ,
425+ aggregations : [ { type : 'count' } , { type : 'sum' , field : 'Cost' } ] ,
426+ } ) ;
427+
428+ expect ( result ) . toStrictEqual ( {
429+ '1' : { count : 10 , sum__Cost : 500 } ,
430+ '2' : { count : 3 , sum__Cost : 120 } ,
431+ } ) ;
432+ expect ( makeOpenOpsTablesPostMock ) . toHaveBeenCalledWith (
433+ 'api/database/rows/batch-aggregations/' ,
434+ {
435+ table_ids : [ 1 , 2 ] ,
436+ filters : [ ] ,
437+ aggregations : [ { type : 'count' } , { type : 'sum' , field : 'Cost' } ] ,
438+ } ,
439+ 'some header' ,
440+ ) ;
441+ } ) ;
442+
443+ test ( 'passes filters in request body' , async ( ) => {
444+ makeOpenOpsTablesPostMock . mockResolvedValue ( { '1' : { count : 2 } } ) ;
445+ createAxiosHeadersMock . mockReturnValue ( 'some header' ) ;
446+
447+ await batchTableAggregations ( {
448+ tokenOrResolver : 'token' ,
449+ tableIds : [ 1 ] ,
450+ filters : [
451+ {
452+ fieldName : 'Status' ,
453+ type : 'not_in' ,
454+ value : [ 'Resolved' , 'Dismissed' ] ,
455+ } ,
456+ ] ,
457+ aggregations : [ { type : 'count' } ] ,
458+ } ) ;
459+
460+ expect ( makeOpenOpsTablesPostMock ) . toHaveBeenCalledWith (
461+ 'api/database/rows/batch-aggregations/' ,
462+ {
463+ table_ids : [ 1 ] ,
464+ filters : [
465+ { field : 'Status' , type : 'not_in' , value : [ 'Resolved' , 'Dismissed' ] } ,
466+ ] ,
467+ aggregations : [ { type : 'count' } ] ,
468+ } ,
469+ 'some header' ,
470+ ) ;
471+ } ) ;
472+
473+ test ( 'defaults filters to empty array when not provided' , async ( ) => {
474+ makeOpenOpsTablesPostMock . mockResolvedValue ( { '1' : { count : 0 } } ) ;
475+ createAxiosHeadersMock . mockReturnValue ( 'some header' ) ;
476+
477+ await batchTableAggregations ( {
478+ tokenOrResolver : 'token' ,
479+ tableIds : [ 1 ] ,
480+ aggregations : [ { type : 'count' } ] ,
481+ } ) ;
482+
483+ expect ( makeOpenOpsTablesPostMock ) . toHaveBeenCalledWith (
484+ 'api/database/rows/batch-aggregations/' ,
485+ expect . objectContaining ( { filters : [ ] } ) ,
486+ 'some header' ,
487+ ) ;
488+ } ) ;
489+
490+ test ( 'logs error and rethrows when post fails' , async ( ) => {
491+ const error = new Error ( 'network error' ) ;
492+ makeOpenOpsTablesPostMock . mockRejectedValue ( error ) ;
493+ createAxiosHeadersMock . mockReturnValue ( 'some header' ) ;
494+
495+ await expect (
496+ batchTableAggregations ( {
497+ tokenOrResolver : 'token' ,
498+ tableIds : [ 1 ] ,
499+ aggregations : [ { type : 'count' } ] ,
500+ } ) ,
501+ ) . rejects . toThrow ( 'network error' ) ;
502+
503+ expect ( logger . error ) . toHaveBeenCalledWith (
504+ 'Error while posting batch table aggregations:' ,
505+ expect . objectContaining ( { error, tableIds : [ 1 ] } ) ,
506+ ) ;
507+ } ) ;
508+ } ) ;
509+
359510describe ( 'getRowByPrimaryKeyValue' , ( ) => {
360511 beforeEach ( ( ) => {
361512 jest . clearAllMocks ( ) ;
0 commit comments