From 623c9ab663ba61695a78ee3d940438b307b18006 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 2 Oct 2023 14:27:35 -0700 Subject: [PATCH] adding tests for processing aggregation parameters. make sure frontend code uses new aggregation parameter structure. --- .../database/sqlite_repository_query_test.go | 51 ++++++++++++++++++- .../models/widget/dashboard-widget-query.ts | 11 ++-- .../report-labs/report-labs.component.ts | 8 ++- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/backend/pkg/database/sqlite_repository_query_test.go b/backend/pkg/database/sqlite_repository_query_test.go index 6bbb1d005..add11cb5e 100644 --- a/backend/pkg/database/sqlite_repository_query_test.go +++ b/backend/pkg/database/sqlite_repository_query_test.go @@ -204,6 +204,55 @@ func TestSearchCodeToFromClause(t *testing.T) { } +//Aggregation tests + +// mimic tests from https://hl7.org/fhir/r4/search.html#token +func TestProcessAggregationParameter(t *testing.T) { + //setup + t.Parallel() + var processSearchParameterTests = []struct { + aggregationFieldWithFn models.QueryResourceAggregation // input + searchParameterLookup map[string]string // input (allowed search parameters) + expected AggregationParameter + expectedError bool // expected result + }{ + //primitive types + {models.QueryResourceAggregation{Field: "test"}, map[string]string{"test": "keyword"}, AggregationParameter{SearchParameter: SearchParameter{Type: "keyword", Name: "test", Modifier: ""}}, false}, + {models.QueryResourceAggregation{Field: "test"}, map[string]string{"test": "number"}, AggregationParameter{SearchParameter: SearchParameter{Type: "number", Name: "test", Modifier: ""}}, false}, + {models.QueryResourceAggregation{Field: "test"}, map[string]string{"test": "uri"}, AggregationParameter{SearchParameter: SearchParameter{Type: "uri", Name: "test", Modifier: ""}}, false}, + {models.QueryResourceAggregation{Field: "test"}, map[string]string{"test": "date"}, AggregationParameter{SearchParameter: SearchParameter{Type: "date", Name: "test", Modifier: ""}}, false}, + + {models.QueryResourceAggregation{Field: "test:hello"}, map[string]string{"test": "keyword"}, AggregationParameter{SearchParameter: SearchParameter{Type: "date", Name: "test", Modifier: ""}}, true}, //cannot have a modifier + {models.QueryResourceAggregation{Field: "test:hello"}, map[string]string{"test": "number"}, AggregationParameter{SearchParameter: SearchParameter{Type: "date", Name: "test", Modifier: ""}}, true}, //cannot have a modifier + {models.QueryResourceAggregation{Field: "test:hello"}, map[string]string{"test": "uri"}, AggregationParameter{SearchParameter: SearchParameter{Type: "date", Name: "test", Modifier: ""}}, true}, //cannot have a modifier + {models.QueryResourceAggregation{Field: "test:hello"}, map[string]string{"test": "date"}, AggregationParameter{SearchParameter: SearchParameter{Type: "date", Name: "test", Modifier: ""}}, true}, //cannot have a modifier + + //complex types + {models.QueryResourceAggregation{Field: "test"}, map[string]string{"test": "reference"}, AggregationParameter{SearchParameter: SearchParameter{Type: "reference", Name: "test", Modifier: ""}}, true}, //complex types should throw an error when missing modifier + {models.QueryResourceAggregation{Field: "test"}, map[string]string{"test": "string"}, AggregationParameter{SearchParameter: SearchParameter{Type: "string", Name: "test", Modifier: ""}}, true}, //complex types should throw an error when missing modifier + {models.QueryResourceAggregation{Field: "test"}, map[string]string{"test": "quantity"}, AggregationParameter{SearchParameter: SearchParameter{Type: "quantity", Name: "test", Modifier: ""}}, true}, //complex types should throw an error when missing modifier + + {models.QueryResourceAggregation{Field: "test:hello"}, map[string]string{"test": "reference"}, AggregationParameter{SearchParameter: SearchParameter{Type: "reference", Name: "test", Modifier: "hello"}}, false}, + {models.QueryResourceAggregation{Field: "test:hello"}, map[string]string{"test": "string"}, AggregationParameter{SearchParameter: SearchParameter{Type: "string", Name: "test", Modifier: "hello"}}, false}, + {models.QueryResourceAggregation{Field: "test:hello"}, map[string]string{"test": "quantity"}, AggregationParameter{SearchParameter: SearchParameter{Type: "quantity", Name: "test", Modifier: "hello"}}, false}, + + //token type + {models.QueryResourceAggregation{Field: "code"}, map[string]string{"code": "token"}, AggregationParameter{SearchParameter: SearchParameter{Type: "token", Name: "code", Modifier: ""}}, false}, + {models.QueryResourceAggregation{Field: "code:code"}, map[string]string{"code": "token"}, AggregationParameter{SearchParameter: SearchParameter{Type: "token", Name: "code", Modifier: "code"}}, false}, + } + + //test && assert + for ndx, tt := range processSearchParameterTests { + actual, actualErr := ProcessAggregationParameter(tt.aggregationFieldWithFn, tt.searchParameterLookup) + if tt.expectedError { + require.Error(t, actualErr, "Expected error but got none for processAggregationParameterTests[%d] %s", ndx, tt.aggregationFieldWithFn) + } else { + require.NoError(t, actualErr, "Expected no error but got one for processAggregationParameterTests[%d] %s", ndx, tt.aggregationFieldWithFn) + require.Equal(t, tt.expected, actual) + } + } +} + func (suite *RepositoryTestSuite) TestQueryResources_SQL() { //setup fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) @@ -245,7 +294,7 @@ func (suite *RepositoryTestSuite) TestQueryResources_SQL() { "SELECT fhir.*", "FROM fhir_observation as fhir, json_each(fhir.code) as codeJson", "WHERE ((codeJson.value ->> '$.code' = ?)) AND (user_id = ?) GROUP BY `fhir`.`id`", - "ORDER BY fhir.sort_date ASC"}, " ")) + "ORDER BY fhir.sort_date DESC"}, " ")) require.Equal(suite.T(), sqlParams, []interface{}{ "test_code", "00000000-0000-0000-0000-000000000000", }) diff --git a/frontend/src/app/models/widget/dashboard-widget-query.ts b/frontend/src/app/models/widget/dashboard-widget-query.ts index a47803f05..d9d0a11f7 100644 --- a/frontend/src/app/models/widget/dashboard-widget-query.ts +++ b/frontend/src/app/models/widget/dashboard-widget-query.ts @@ -8,10 +8,15 @@ export class DashboardWidgetQuery { //https://lodash.com/docs/4.17.15#unionBy aggregations?: { - count_by?: string, //alias for groupBy and orderBy - group_by?: string, - order_by?: string, + count_by?: DashboardWidgetQueryAggregation, //alias for groupBy and orderBy + group_by?: DashboardWidgetQueryAggregation, + order_by?: DashboardWidgetQueryAggregation, } // aggregation_params?: string[] // aggregation_type?: 'countBy' | 'groupBy' | 'orderBy' // | 'minBy' | 'maxBy' | 'sumBy' // 'orderBy' | 'sortBy' | } + +export class DashboardWidgetQueryAggregation { + field: string + fn?: string +} diff --git a/frontend/src/app/pages/report-labs/report-labs.component.ts b/frontend/src/app/pages/report-labs/report-labs.component.ts index 6f449ed28..29252173a 100644 --- a/frontend/src/app/pages/report-labs/report-labs.component.ts +++ b/frontend/src/app/pages/report-labs/report-labs.component.ts @@ -43,7 +43,13 @@ export class ReportLabsComponent implements OnInit { from: "Observation", where: {}, aggregations: { - order_by: "code:code" + order_by: { + field: "sort_date", + fn: "max" + }, + group_by: { + field: "code", + } } }).subscribe(results => { console.log("OBSERVATIONS GROUPED", results)