Skip to content

Commit 6235b04

Browse files
authored
feat: support for sql reports without date range (#2502)
1 parent 84ccc22 commit 6235b04

File tree

8 files changed

+137
-33
lines changed

8 files changed

+137
-33
lines changed

src/app/core/entity/database-field.decorator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { EntitySchema } from "./schema/entity-schema";
1010
* @param propertySchema (optional) SchemaField definition that configures additional options regarding this field
1111
*/
1212
export function DatabaseField(propertySchema: EntitySchemaField = {}) {
13-
return (target, propertyName: string) => {
13+
return (target: any, propertyName: string) => {
1414
// Retrieve datatype from TypeScript type definition
1515
if (propertySchema.dataType === undefined) {
1616
const type = Reflect.getMetadata("design:type", target, propertyName);

src/app/features/reporting/report-config.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,16 @@ class ReportConfig extends Entity {
2323
*/
2424
@DatabaseField() mode?: string;
2525

26+
/**
27+
* (sql only) list of arguments needed for the sql query. Placeholder "?" will be replaced.
28+
*/
29+
@DatabaseField() neededArgs?: string[] = [];
30+
2631
/** the definitions to calculate the report's aggregations */
2732
@DatabaseField() aggregationDefinitions: any[] = [];
33+
34+
/** (sql only) the definition to calculate the report */
35+
@DatabaseField() aggregationDefinition: string | undefined = undefined;
2836
}
2937

3038
/**
@@ -62,7 +70,12 @@ export interface ExportingReport extends ReportConfig {
6270
export interface SqlReport extends ReportConfig {
6371
mode: "sql";
6472
/**
65-
* Array of valid SQL SELECT statements
73+
* a valid SQL SELECT statements, can contain "?" placeholder for arguments
74+
*/
75+
aggregationDefinition: string;
76+
77+
/**
78+
* a list of arguments, passed into the sql statement
6679
*/
67-
aggregationDefinitions: string[];
80+
neededArgs: string[];
6881
}

src/app/features/reporting/reporting/reporting.component.spec.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,7 @@ describe("ReportingComponent", () => {
4444
status: "FINISHED_SUCCESS",
4545
startDate: "2024-06-07T09:26:56.414",
4646
endDate: "2024-06-09T09:26:57.431",
47-
args: new Map<String, String>([
48-
["from", "2024-01-01T00:00:00.000"],
49-
["to", "2024-01-01T23:59:59.999"],
50-
]),
47+
args: { from: "2024-01-01T00:00:00.000", to: "2024-01-01T23:59:59.999" },
5148
outcome: {
5249
result_hash: "000",
5350
},

src/app/features/reporting/reporting/select-report/select-report.component.html

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<div *ngIf="reports?.length > 0" class="work-panel">
1313
<div class="flex-row flex-wrap gap-regular">
14-
<mat-form-field>
14+
<mat-form-field class="flex-grow">
1515
<mat-label i18n>Select Report</mat-label>
1616
<mat-select
1717
[(ngModel)]="selectedReport"
@@ -24,7 +24,7 @@
2424
</mat-select>
2525
</mat-form-field>
2626

27-
<mat-form-field>
27+
<mat-form-field *ngIf="isDateRangeReport">
2828
<mat-label i18n>Enter a date range</mat-label>
2929
<mat-date-range-input [rangePicker]="picker" [disabled]="loading">
3030
<input
@@ -56,14 +56,12 @@
5656
mat-stroked-button
5757
color="accent"
5858
class="primary-button"
59-
[disabled]="!selectedReport || (!fromDate && !toDate) || loading"
60-
(click)="
61-
calculateClick.emit({
62-
report: selectedReport,
63-
from: fromDate,
64-
to: toDate,
65-
})
59+
[disabled]="
60+
!selectedReport ||
61+
(isDateRangeReport && !fromDate && !toDate) ||
62+
loading
6663
"
64+
(click)="calculate()"
6765
i18n="Calculate the results for a report"
6866
angulartics2On="click"
6967
angularticsCategory="Reporting"

src/app/features/reporting/reporting/select-report/select-report.component.spec.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,73 @@ describe("SelectReportComponent", () => {
3131

3232
expect(component.selectedReport).toBe(report);
3333
});
34+
35+
it("should display date range filter when report mode is reporting", () => {
36+
const report = new ReportEntity();
37+
report.mode = "reporting";
38+
component.reports = [report];
39+
40+
component.ngOnChanges({ reports: undefined });
41+
42+
expect(component.selectedReport).toBe(report);
43+
expect(component.isDateRangeReport).toBeTrue();
44+
});
45+
46+
it("should display date range filter when sql report supports it", () => {
47+
const report = new ReportEntity();
48+
report.mode = "sql";
49+
report.neededArgs = ["from", "to"];
50+
component.reports = [report];
51+
52+
component.ngOnChanges({ reports: undefined });
53+
54+
expect(component.selectedReport).toBe(report);
55+
expect(component.isDateRangeReport).toBeTrue();
56+
});
57+
58+
it("should hide date range filter when sql report does not have these args", () => {
59+
const report = new ReportEntity();
60+
report.mode = "sql";
61+
component.reports = [report];
62+
63+
component.ngOnChanges({ reports: undefined });
64+
65+
expect(component.selectedReport).toBe(report);
66+
expect(component.isDateRangeReport).toBeFalse();
67+
});
68+
69+
it("should reset dates before calculation when sql report is not a DateRangeReport", () => {
70+
const report = new ReportEntity();
71+
report.mode = "sql";
72+
component.reports = [report];
73+
74+
component.ngOnChanges({ reports: undefined });
75+
component.fromDate = new Date();
76+
component.toDate = new Date();
77+
78+
component.calculate();
79+
80+
expect(component.selectedReport).toBe(report);
81+
expect(component.isDateRangeReport).toBeFalse();
82+
expect(component.fromDate).toBeUndefined();
83+
expect(component.toDate).toBeUndefined();
84+
});
85+
86+
it("should not reset dates before calculation when sql report is a DateRangeReport", () => {
87+
const report = new ReportEntity();
88+
report.mode = "sql";
89+
component.reports = [report];
90+
report.neededArgs = ["from", "to"];
91+
92+
component.ngOnChanges({ reports: undefined });
93+
component.fromDate = new Date();
94+
component.toDate = new Date();
95+
96+
component.calculate();
97+
98+
expect(component.selectedReport).toBe(report);
99+
expect(component.isDateRangeReport).toBeTrue();
100+
expect(component.fromDate).toBeDefined();
101+
expect(component.toDate).toBeDefined();
102+
});
34103
});

src/app/features/reporting/reporting/select-report/select-report.component.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
Output,
77
SimpleChanges,
88
} from "@angular/core";
9-
import { NgForOf, NgIf } from "@angular/common";
9+
import { JsonPipe, NgForOf, NgIf } from "@angular/common";
1010
import { MatButtonModule } from "@angular/material/button";
1111
import { MatFormFieldModule } from "@angular/material/form-field";
1212
import { MatSelectModule } from "@angular/material/select";
@@ -36,6 +36,7 @@ import { ReportEntity } from "../../report-config";
3636
FontAwesomeModule,
3737
MatProgressBarModule,
3838
MatTooltipModule,
39+
JsonPipe,
3940
],
4041
standalone: true,
4142
})
@@ -49,22 +50,49 @@ export class SelectReportComponent implements OnChanges {
4950
selectedReport: ReportEntity;
5051
fromDate: Date;
5152
toDate: Date;
53+
/** whether the currently selected report includes filter parameters for a "from" - "to" date range */
54+
isDateRangeReport: boolean;
5255

5356
ngOnChanges(changes: SimpleChanges): void {
5457
if (changes.hasOwnProperty("reports")) {
5558
if (this.reports?.length === 1) {
5659
this.selectedReport = this.reports[0];
60+
this.checkDateRangeReport();
5761
}
5862
}
5963
}
6064

65+
calculate(): void {
66+
if (!this.isDateRangeReport) {
67+
this.fromDate = undefined;
68+
this.toDate = undefined;
69+
}
70+
71+
this.calculateClick.emit({
72+
report: this.selectedReport,
73+
from: this.fromDate,
74+
to: this.toDate,
75+
});
76+
}
77+
6178
reportChange() {
6279
this.dataChanged.emit();
80+
this.checkDateRangeReport();
6381
}
6482

6583
dateChange() {
6684
this.dataChanged.emit();
6785
}
86+
87+
private checkDateRangeReport(): void {
88+
if (this.selectedReport.mode !== "sql") {
89+
this.isDateRangeReport = true;
90+
} else {
91+
this.isDateRangeReport =
92+
this.selectedReport.neededArgs.indexOf("from") !== -1 ||
93+
this.selectedReport.neededArgs.indexOf("to") !== -1;
94+
}
95+
}
6896
}
6997

7098
interface CalculateReportOptions {

src/app/features/reporting/sql-report/sql-report.service.spec.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe("SqlReportService", () => {
2525
startDate: null,
2626
endDate: null,
2727
status: "PENDING",
28-
args: new Map<String, String>(),
28+
args: {},
2929
outcome: {
3030
result_hash:
3131
"180a94a09c517b24e994aaf8342c58270a775f953eb32af78f06f1c8f61e37b9",
@@ -39,10 +39,7 @@ describe("SqlReportService", () => {
3939
status: "FINISHED_SUCCESS",
4040
startDate: "2024-06-07T09:26:56.414",
4141
endDate: "2024-06-09T09:26:57.431",
42-
args: new Map<String, String>([
43-
["from", "2024-01-01T00:00:00.000"],
44-
["to", "2024-01-01T23:59:59.999"],
45-
]),
42+
args: { from: "2024-01-01T00:00:00.000", to: "2024-01-01T23:59:59.999" },
4643
outcome: {
4744
result_hash: "000",
4845
},
@@ -55,10 +52,7 @@ describe("SqlReportService", () => {
5552
status: "FINISHED_SUCCESS",
5653
startDate: "2024-06-07T09:26:56.414",
5754
endDate: "2024-06-07T09:26:57.431",
58-
args: new Map<String, String>([
59-
["from", "2024-01-01T00:00:00.000"],
60-
["to", "2024-01-01T23:59:59.999"],
61-
]),
55+
args: { from: "2024-01-01T00:00:00.000", to: "2024-01-01T23:59:59.999" },
6256
outcome: {
6357
result_hash: "000",
6458
},

src/app/features/reporting/sql-report/sql-report.service.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export interface ReportCalculation {
2323
};
2424
startDate: string | null;
2525
endDate: string | null;
26-
args: Map<String, String>;
26+
args: { [key: string]: string };
2727
status: "PENDING" | "RUNNING" | "FINISHED_SUCCESS" | "FINISHED_ERROR";
2828
outcome: {
2929
result_hash: string;
@@ -98,17 +98,22 @@ export class SqlReportService {
9898
from: Date,
9999
to: Date,
100100
): Observable<ReportData> {
101+
let params = {};
102+
if (from && to) {
103+
params = {
104+
from: moment(from).format("YYYY-MM-DD"),
105+
to: moment(to).format("YYYY-MM-DD"),
106+
};
107+
}
108+
101109
return this.http
102110
.post<{
103111
id: string;
104112
}>(
105113
`${SqlReportService.QUERY_PROXY}/api/v1/reporting/report-calculation/report/${reportId}`,
106114
{},
107115
{
108-
params: {
109-
from: moment(from).format("YYYY-MM-DD"),
110-
to: moment(to).format("YYYY-MM-DD"),
111-
},
116+
params: params,
112117
},
113118
)
114119
.pipe(
@@ -166,8 +171,8 @@ export class SqlReportService {
166171
from: Date,
167172
to: Date,
168173
): boolean {
169-
let argFrom = value.args.get("from");
170-
let argTo = value.args.get("to");
174+
let argFrom = value.args["from"];
175+
let argTo = value.args["to"];
171176

172177
if (!argFrom || !argTo) {
173178
return false;

0 commit comments

Comments
 (0)