src/app/features/reporting/report-config.ts
Reports handles by the {@class DataAggregationService}
No results matching.
Properties |
|
Methods |
|
| assertValid |
assertValid()
|
|
Inherited from
Entity
|
|
Checks if the entity is valid and if the check fails, throws an error explaining the failed check.
Returns :
void
|
| Public copy | ||||||||||
copy(newId: string | boolean)
|
||||||||||
|
Inherited from
Entity
|
||||||||||
|
Deep copy of the entity. The resulting entity will be of the same type as this (taking into account subclassing). All schema field values that are objects or arrays are deep-cloned to avoid shared mutable state between original and copy.
Parameters :
Returns :
unknown
|
| Static createPrefixedId | ||||||||||||
createPrefixedId(type: string, id: string)
|
||||||||||||
|
Inherited from
Entity
|
||||||||||||
|
Create a prefixed id by adding the type prefix if it isn't already part of the given id.
Parameters :
Returns :
string
|
| Static extractEntityIdFromId | ||||||||
extractEntityIdFromId(id: string)
|
||||||||
|
Inherited from
Entity
|
||||||||
|
Extract entityId without prefix.
Parameters :
Returns :
string
|
| Static extractTypeFromId | ||||||||
extractTypeFromId(id: string)
|
||||||||
|
Inherited from
Entity
|
||||||||
|
Extract the ENTITY_TYPE from an id.
Parameters :
Returns :
string
|
| Public getColor |
getColor()
|
|
Inherited from
Entity
|
|
Used by some generic UI components to set the color for the entity instance. Override this method as needed.
Returns :
string
|
| Static getColorWithConditions | ||||||
getColorWithConditions(entity: Entity)
|
||||||
|
Inherited from
Entity
|
||||||
|
Static method to evaluate conditional colors for an entity based on ColorMapping configuration.
Parameters :
Returns :
string
|
| getConstructor |
getConstructor()
|
|
Inherited from
Entity
|
|
Get the class (Entity or the actual subclass of the instance) to call static methods on the correct class considering inheritance
Returns :
EntityConstructor<unknown>
|
| Public getId | ||||||||
getId(withoutPrefix: unknown)
|
||||||||
|
Inherited from
Entity
|
||||||||
|
Returns the id of this entity. Note that an id is final and can't be changed after the object has been instantiated, hence there is no
Parameters :
Returns :
string
the unique id of this entity |
| getSchema |
getSchema()
|
|
Inherited from
Entity
|
|
Get the entity schema of this class
Returns :
EntitySchema
|
| Public getType |
getType()
|
|
Inherited from
Entity
|
|
Returns the type which is used to categorize this entity in the database. Important: Do not overwrite this method! Types are handled internally.
Returns :
string
the entity's type (which is the class name). |
| Public getWarningLevel |
getWarningLevel()
|
|
Inherited from
Entity
|
|
Override getWarningLevel() to define when the entity is in a critical condition and should be color-coded and highlighted in generic components of the UI.
Returns :
WarningLevel
|
| mode |
mode:
|
Type : unknown
|
| reportDefinition |
reportDefinition:
|
Type : Aggregation[]
|
| aggregationDefinition |
|
(will be removed completely after server-side migration) (sql v1 only) the definition to calculate the report |
aggregationDefinition:
|
Type : string
|
| Optional |
|
(sql v1 only) the definition to calculate the report |
| description |
description:
|
Type : string
|
| Optional |
|
longer description documenting the purpose and usage of this report |
| icon |
icon:
|
Type : IconName
|
Default value: "chart-line"
|
| isInternalEntity |
isInternalEntity:
|
Type : unknown
|
Default value: true
|
| label |
label:
|
Type : unknown
|
Default value: $localize`:ReportConfig:Report`
|
| labelPlural |
labelPlural:
|
Type : unknown
|
Default value: $localize`:ReportConfig:Reports`
|
| route |
route:
|
Type : string
|
Default value: "admin/report-config"
|
| title |
title:
|
Type : string
|
|
human-readable title of the report |
| toStringAttributes |
toStringAttributes:
|
Type : []
|
Default value: ["title"]
|
| version |
|
(will be removed completely after server-side migration) Omitted for canonical configs (backend normalizes legacy v1 docs on read). |
version:
|
Type : number
|
| Optional |
|
Omitted for canonical configs (backend normalizes legacy v1 docs on read). |
| _isCustomizedType |
| todo: This property is no longer used and will be removed in future versions. |
_isCustomizedType:
|
Type : boolean
|
| Optional |
|
True if this type's schema has been customized dynamically from the config. |
| _rev |
_rev:
|
Type : string
|
|
internal database doc revision, used to detect conflicts by PouchDB/CouchDB |
| anonymized |
anonymized:
|
Type : boolean
|
|
Whether this entity has been anonymized and therefore cannot be re-activated. |
| color |
color:
|
Type : string | ColorMapping[]
|
|
color used for to highlight this entity type across the app. Can be either:
|
| created |
created:
|
Type : UpdateMetadata
|
| DATABASE |
DATABASE:
|
Type : string
|
Default value: "app"
|
|
The database where these entities are stored. |
| enableUserAccounts |
enableUserAccounts:
|
Type : boolean
|
| Optional |
|
Whether to enable user account creation for this entity type. When true, the UI will allow management of user accounts associated with this entity. |
| ENTITY_TYPE |
ENTITY_TYPE:
|
Type : string
|
Default value: "Entity"
|
|
The entity's type.
In classes extending Entity this is usually overridden by the class annotation |
| hasPII |
hasPII:
|
Type : boolean
|
Default value: false
|
|
whether this entity type can contain "personally identifiable information" (PII) and therefore should follow strict data protection requirements and offer a function to anonymize records. |
| inactive |
inactive:
|
Type : boolean
|
| schema |
schema:
|
Type : EntitySchema
|
|
EntitySchema defining property transformations from/to the database.
This is auto-generated from the property annotations see /additional-documentation/how-to-guides/create-a-new-entity-type.html |
| toBlockDetailsAttributes |
toBlockDetailsAttributes:
|
Type : EntityBlockConfig
|
| Optional |
|
Defining which attributes will be displayed in a tooltip on hover when the record is displayed as an entity-block. |
| updated |
updated:
|
Type : UpdateMetadata
|
import { Entity, EntityConstructor } from "../../core/entity/model/entity";
import { DatabaseEntity } from "../../core/entity/database-entity.decorator";
import { Aggregation } from "./data-aggregation.service";
import { ExportColumnConfig } from "../../core/export/data-transformation-service/export-column-config";
import { DatabaseField } from "../../core/entity/database-field.decorator";
import { LongTextDatatype } from "../../core/basic-datatypes/string/long-text.datatype";
import { IconName } from "@fortawesome/fontawesome-svg-core";
/**
* A report can be accessed by users to generate aggregated statistics or customized exports calculated from available data.
* "read" permission for a ReportConfig entity is also used to control which users can generate the report's results.
*
* This is the class which is saved to the database.
* However, when using this in code, use the {@link ReportEntity} instead which provides better type safety.
*/
@DatabaseEntity("ReportConfig")
class ReportConfig extends Entity {
static override isInternalEntity = true;
static override readonly label = $localize`:ReportConfig:Report`;
static override readonly labelPlural = $localize`:ReportConfig:Reports`;
static override readonly toStringAttributes = ["title"];
static override readonly route = "admin/report-config";
static override readonly icon: IconName = "chart-line";
/** human-readable title of the report */
@DatabaseField({
label: $localize`:ReportConfig:Title`,
validators: { required: true },
})
title: string;
/** longer description documenting the purpose and usage of this report */
@DatabaseField({
label: $localize`:ReportConfig:Description`,
description: $localize`:ReportConfig:Document the purpose and usage of this report. This is also shown to users above the results when the report is run.`,
dataType: LongTextDatatype.dataType,
})
description?: string;
/**
* (optional) mode of export.
* The {@link ReportEntity} holds the restriction on valid report modes.
* Default is "reporting"
*/
@DatabaseField({
label: $localize`:ReportConfig:Mode`,
description: $localize`:ReportConfig:How the report is calculated: "reporting" (in-browser aggregations), "exporting" (data export) or "sql" (server-side SQL queries). Defaults to "reporting".`,
editComponent: "EditReportMode",
validators: { required: true },
})
mode?: string;
/**
* @deprecated (will be removed completely after server-side migration)
* Omitted for canonical configs (backend normalizes legacy v1 docs on read).
*/
@DatabaseField() version?: number;
/**
* @deprecated (will be removed completely after server-side migration)
* (sql v1 only) list of arguments needed for the sql query. Placeholder "?" will be replaced.
*/
@DatabaseField() neededArgs?: string[] = [];
/**
* @deprecated Consolidated into {@link reportDefinition} by the one-time CLI migration
* (consolidate-report-definition), which copies this into `reportDefinition` without deleting
* it. Kept during the coexistence period so legacy docs still load and old code keeps working;
* a follow-up migration removes it once every environment runs the new code.
*/
@DatabaseField() aggregationDefinitions?: any[];
/**
* @deprecated (will be removed completely after server-side migration)
* (sql v1 only) the definition to calculate the report
*/
@DatabaseField() aggregationDefinition?: string;
/**
* (sql v2 only) transformations that are applied to input variables (e.g. startDate, endDate)
* example: {startDate: ["SQL_FROM_DATE"], endDate: ["SQL_TO_DATE"]}
*/
@DatabaseField({
label: $localize`:ReportConfig:Use report period (start & end date)`,
description: $localize`:ReportConfig:When you use time filters in your report, users see a date range selector to choose the start and end date for the report when calculating results.`,
editComponent: "EditReportPeriodToggle",
})
transformations: {
[key: string]: string[];
};
/**
* The single definition of what the report calculates. Its shape depends on {@link mode}:
* - "sql": {@link ReportDefinitionDto}[] — SQL queries and optional groups.
* - "reporting": {@link Aggregation}[] — in-browser aggregation definitions.
* - "exporting": {@link ExportColumnConfig}[] — export column definitions.
*
* Consolidated from the former `aggregationDefinitions` so all report modes share one field.
*/
@DatabaseField({
label: $localize`:ReportConfig:Report definition`,
description: $localize`:ReportConfig:The definition of what the report calculates: SQL queries for "sql" mode, or aggregation/export definitions for "reporting"/"exporting" mode.`,
// Mode-aware: structured syntax-highlighting SQL editor for "sql" mode, raw JSON editor otherwise.
editComponent: "EditReportDefinition",
})
reportDefinition:
ReportDefinitionDto[] | Aggregation[] | ExportColumnConfig[];
}
export interface ReportDefinitionDto {
/** an SQL query */
query?: string;
/** title (human-readable) for a set of hierarchically grouped sub-items */
groupTitle?: string;
/** hierarchical child items, building a recursive set of report groups display in an indented way */
items?: ReportDefinitionDto[];
}
/**
* Union type to enable type safety for report configs.
* Use this instead of the {@class ReportConfig}
*/
export type ReportEntity = AggregationReport | ExportingReport | SqlReport;
/**
* This allows the `ReportEntity` to also be used as a constructor or in the `EntityMapper`
*/
export const ReportEntity = ReportConfig as EntityConstructor<ReportEntity>;
/**
* Reports handles by the {@class DataAggregationService}
*/
export interface AggregationReport extends ReportConfig {
mode: "reporting";
reportDefinition: Aggregation[];
}
/**
* Reports handles by the {@class DataTransformationService}
*/
export interface ExportingReport extends ReportConfig {
/**
* If no mode is set, it will default to 'exporting'
*/
mode?: "exporting";
reportDefinition: ExportColumnConfig[];
}
/**
* Reports handles by the {@class SqlReportService}
*/
export interface SqlReport extends ReportConfig {
mode: "sql";
/**
* @deprecated (will be removed completely after server-side migration)
* Legacy version field. Omitted in canonical configs; backend normalizes on read.
*/
version?: number;
/**
* @deprecated (will be removed completely after server-side migration)
* (v1 only) a valid SQL SELECT statement, can contain "?" or "$name" placeholders
*/
aggregationDefinition?: string;
/**
* @deprecated (will be removed completely after server-side migration)
* (v1 only) list of argument names passed into the sql statement
*/
neededArgs?: string[];
/**
* see ReportConfig docs
*/
transformations: {
[key: string]: string[];
};
/**
* see ReportConfig docs
*/
reportDefinition: ReportDefinitionDto[];
}
/**
* Whether a report's results should be rendered as a hierarchical group/count
* table (`sql-v2-table`) rather than a flat tabular table (`object-table`).
*
* Derived purely from the canonical config structure (no version flag):
* a report is hierarchical when its `reportDefinition` contains a group
* (`groupTitle`) or more than one top-level item; otherwise it is tabular.
* Legacy v1 configs (normalized to a single ungrouped query) resolve to tabular.
*/
export function isHierarchicalReport(
report: ReportEntity | undefined,
): boolean {
const reportDefinition = report?.reportDefinition as
ReportDefinitionDto[] | undefined;
if (!reportDefinition?.length) {
return false;
}
return (
reportDefinition.length > 1 ||
reportDefinition.some((item) => !!item.groupTitle)
);
}
/**
* Whether running this report offers a date-range (start & end date) input, derived from the
* date placeholders used in its queries so it stays in sync with the actual report definition:
* - "sql": the query uses the `$startDate` / `$endDate` placeholders.
* - "reporting"/"exporting": the in-browser query uses positional `?` placeholders (date args).
*/
export function reportUsesDateRange(
report: { mode?: string; reportDefinition?: unknown } | undefined,
): boolean {
if (!report) {
return false;
}
const queries = collectQueryStrings(report.reportDefinition);
if (report.mode === "sql") {
return queries.some((query) => /\$startDate|\$endDate/.test(query));
}
return queries.some((query) => query.includes("?"));
}
/** Recursively collect every `query` string value from a report definition tree. */
function collectQueryStrings(node: unknown): string[] {
if (Array.isArray(node)) {
return node.flatMap(collectQueryStrings);
}
if (node && typeof node === "object") {
const out: string[] = [];
for (const [key, value] of Object.entries(node)) {
if (key === "query" && typeof value === "string") {
out.push(value);
} else {
out.push(...collectQueryStrings(value));
}
}
return out;
}
return [];
}