@camunda/ccma-shared-types 4.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/_tests/approvals/expected-value.spec.d.ts +1 -0
  2. package/dist/_tests/charts/chart-query.spec.d.ts +1 -0
  3. package/dist/_tests/dashboards/definition.spec.d.ts +1 -0
  4. package/dist/_tests/types/PreprodClusterTtl.type.spec.d.ts +1 -0
  5. package/dist/_tests/utils/elasticsearchPlanPath.utils.spec.d.ts +1 -0
  6. package/dist/approvals/dns-subdomain.d.ts +12 -0
  7. package/dist/approvals/dns-subdomain.js +16 -0
  8. package/dist/approvals/dns-subdomain.js.map +7 -0
  9. package/dist/approvals/expected-value.d.ts +33 -0
  10. package/dist/approvals/expected-value.js +26 -0
  11. package/dist/approvals/expected-value.js.map +7 -0
  12. package/dist/approvals/use-cases.const.d.ts +65 -51
  13. package/dist/approvals/use-cases.const.js +40 -9
  14. package/dist/approvals/use-cases.const.js.map +2 -2
  15. package/dist/charts/chart-metrics.d.ts +35 -0
  16. package/dist/charts/chart-metrics.js +188 -0
  17. package/dist/charts/chart-metrics.js.map +7 -0
  18. package/dist/charts/chart-query.d.ts +55 -0
  19. package/dist/charts/chart-query.js +376 -0
  20. package/dist/charts/chart-query.js.map +7 -0
  21. package/dist/charts/chart-spec.d.ts +103 -0
  22. package/dist/charts/chart-spec.js +79 -0
  23. package/dist/charts/chart-spec.js.map +7 -0
  24. package/dist/charts/index.d.ts +3 -0
  25. package/dist/charts/index.js +59 -0
  26. package/dist/charts/index.js.map +7 -0
  27. package/dist/dashboards/definition.d.ts +86 -0
  28. package/dist/dashboards/definition.js +137 -0
  29. package/dist/dashboards/definition.js.map +7 -0
  30. package/dist/dtos/Annotation.dto.d.ts +1 -1
  31. package/dist/dtos/Annotation.dto.js +10 -2
  32. package/dist/dtos/Annotation.dto.js.map +2 -2
  33. package/dist/dtos/ControlV2.dto.d.ts +27 -0
  34. package/dist/dtos/Dashboard.dto.d.ts +336 -0
  35. package/dist/dtos/Dashboard.dto.js +70 -0
  36. package/dist/dtos/Dashboard.dto.js.map +7 -0
  37. package/dist/dtos/ExternalWebapp.dto.d.ts +12 -0
  38. package/dist/dtos/FeatureFlags.dto.d.ts +85 -58
  39. package/dist/dtos/FeatureFlags.dto.js +0 -10
  40. package/dist/dtos/FeatureFlags.dto.js.map +3 -3
  41. package/dist/dtos/Hdp.dto.d.ts +70 -0
  42. package/dist/dtos/Organization.dto.d.ts +10 -0
  43. package/dist/dtos/Organization.dto.js.map +1 -1
  44. package/dist/dtos/SalesPlan.dto.d.ts +25 -1
  45. package/dist/dtos/SalesPlan.dto.js +19 -6
  46. package/dist/dtos/SalesPlan.dto.js.map +2 -2
  47. package/dist/enums/SystemAuditEntity.enum.d.ts +4 -2
  48. package/dist/enums/SystemAuditEntity.enum.js +2 -0
  49. package/dist/enums/SystemAuditEntity.enum.js.map +2 -2
  50. package/dist/index.d.ts +17 -10
  51. package/dist/index.js +163 -24
  52. package/dist/index.js.map +2 -2
  53. package/dist/permissions/accounts.permissions.d.ts +7 -28
  54. package/dist/permissions/accounts.permissions.js +7 -27
  55. package/dist/permissions/accounts.permissions.js.map +2 -2
  56. package/dist/permissions/combo.js +2 -3
  57. package/dist/permissions/combo.js.map +2 -2
  58. package/dist/search/compiler.d.ts +20 -0
  59. package/dist/search/compiler.js +48 -11
  60. package/dist/search/compiler.js.map +2 -2
  61. package/dist/search/index.d.ts +1 -1
  62. package/dist/search/index.js.map +2 -2
  63. package/dist/search/parser.d.ts +3 -0
  64. package/dist/search/parser.js +3 -1
  65. package/dist/search/parser.js.map +2 -2
  66. package/dist/types/ClusterPlanFields.type.js +74 -74
  67. package/dist/types/ClusterPlanFields.type.js.map +2 -2
  68. package/dist/types/PreprodClusterTtl.type.d.ts +33 -0
  69. package/dist/types/PreprodClusterTtl.type.js +37 -0
  70. package/dist/types/PreprodClusterTtl.type.js.map +7 -0
  71. package/dist/types/hdp.types.d.ts +6 -0
  72. package/dist/types/hdp.types.js +3 -1
  73. package/dist/types/hdp.types.js.map +2 -2
  74. package/dist/utils/elasticsearchPlanPath.utils.d.ts +18 -0
  75. package/dist/utils/elasticsearchPlanPath.utils.js +23 -0
  76. package/dist/utils/elasticsearchPlanPath.utils.js.map +7 -0
  77. package/dist/utils/qa-user.utils.d.ts +14 -0
  78. package/dist/utils/qa-user.utils.js +14 -0
  79. package/dist/utils/qa-user.utils.js.map +7 -0
  80. package/dist/utils/qa-user.utils.spec.d.ts +1 -0
  81. package/package.json +1 -1
  82. package/dist/dtos/FeatureFlagOverride.dto.d.ts +0 -226
  83. package/dist/dtos/FeatureFlagOverride.dto.js +0 -38
  84. package/dist/dtos/FeatureFlagOverride.dto.js.map +0 -7
  85. /package/dist/_tests/{dtos/FeatureFlagOverride.dto.spec.d.ts → approvals/dns-subdomain.spec.d.ts} +0 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/charts/chart-metrics.ts"],
4
+ "sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\nimport type { SearchFieldSpec } from '../search/fields.js'\nimport type { ChartGrain, ChartUnit } from './chart-spec.js'\n\n// ============================================================================\n// The metric catalogue of the chart tiles, without SQL.\n//\n// The frontend reads it for autocomplete, help and validation as you type.\n// accounts-backend pairs each entry with its SQL (`hdp-data.charts.controller`),\n// and a test there makes sure that the two have the same ids.\n// ============================================================================\n\nexport interface ChartDimensionSpec {\n /** As typed after `by`. */\n id: string\n label: string\n description: string\n}\n\nexport interface ChartMetricSpec {\n /** As typed at the start of a query. */\n id: string\n label: string\n description: string\n unit: ChartUnit\n dimensions: readonly ChartDimensionSpec[]\n /** The fields that `where` can use. */\n filters: readonly SearchFieldSpec[]\n grains: readonly ChartGrain[]\n /** The longest window, in days. */\n maxWindowDays: number\n /**\n * How one number comes from a chart, for the `stat` render: first over the\n * series of a bucket, then over the buckets of the window. A counter sums\n * both. A gauge must not: a sum of daily load percentages means nothing.\n */\n stat: { series: 'sum' | 'mean'; window: 'sum' | 'max' | 'mean' }\n}\n\nconst CLUSTER_DIMENSION: ChartDimensionSpec = {\n id: 'cluster',\n label: 'Cluster',\n description: 'The cluster id.',\n}\n\nconst ORG_DIMENSION: ChartDimensionSpec = {\n id: 'org',\n label: 'Organization',\n description: 'The organization id.',\n}\n\n/**\n * The name of the organization of the cluster. It comes from the clusters\n * table of HDP, which holds the prod clusters only, so on another stage every\n * series is `(unknown)`.\n */\nconst ORG_NAME_DIMENSION: ChartDimensionSpec = {\n id: 'orgName',\n label: 'Organization name',\n description:\n 'The name of the organization of the cluster. Prod clusters only: on other stages it is (unknown).',\n}\n\nconst ORG_ID_FILTER: SearchFieldSpec = {\n field: 'orgId',\n label: 'Organization id',\n description: 'Exact organization id.',\n type: 'string',\n capabilities: ['match'],\n example: 'orgId:3f2c\u2026',\n}\n\nconst CLUSTER_ID_FILTER: SearchFieldSpec = {\n field: 'clusterId',\n label: 'Cluster id',\n description: 'Exact cluster id.',\n type: 'string',\n capabilities: ['match'],\n example: 'clusterId:9a1b\u2026',\n}\n\nconst VALUE_METRIC_DIMENSIONS: readonly ChartDimensionSpec[] = [\n CLUSTER_DIMENSION,\n ORG_DIMENSION,\n ORG_NAME_DIMENSION,\n {\n id: 'salesPlan',\n label: 'Sales plan',\n description: 'The sales plan of the organization on that day.',\n },\n {\n id: 'env',\n label: 'Environment',\n description: 'Production or development cluster.',\n },\n]\n\nconst VALUE_METRIC_FILTERS: readonly SearchFieldSpec[] = [\n ORG_ID_FILTER,\n CLUSTER_ID_FILTER,\n {\n field: 'salesPlan',\n label: 'Sales plan',\n description: 'Sales plan name, case-insensitive. Use * as a wildcard.',\n type: 'string',\n capabilities: ['match'],\n example: 'salesPlan:enterprise*',\n },\n {\n field: 'env',\n label: 'Environment',\n description: 'Production or development cluster.',\n type: 'enum',\n capabilities: ['match'],\n enumValues: ['production', 'development'],\n example: 'env:production',\n },\n]\n\nconst ALL_GRAINS: readonly ChartGrain[] = ['day', 'week', 'month']\nconst VALUE_METRIC_GRAINS = ALL_GRAINS\n\n/**\n * About one year, for a metric on a large table: the cluster gauges, which\n * are scraped about once a minute, and the change events of every backend\n * and stage. `over 12m every month` starts on the 1st of a month and reads up\n * to 396 days, so the limit holds that query.\n */\nconst LARGE_TABLE_MAX_WINDOW_DAYS = 400\n\n/** A gauge of the clusters, from the Prometheus metrics in HDP. */\nconst gaugeMetric = (\n id: string,\n label: string,\n unit: ChartMetricSpec['unit'],\n stat: ChartMetricSpec['stat'],\n description: string,\n): ChartMetricSpec => ({\n id,\n label,\n description,\n unit,\n dimensions: [CLUSTER_DIMENSION, ORG_DIMENSION, ORG_NAME_DIMENSION],\n filters: [ORG_ID_FILTER, CLUSTER_ID_FILTER],\n grains: ALL_GRAINS,\n maxWindowDays: LARGE_TABLE_MAX_WINDOW_DAYS,\n stat,\n})\n\n/**\n * About two years. The table holds one row for each cluster, day and metric.\n * `over 24m every month` starts on the 1st of a month and reads up to 761\n * days, so the limit holds that query.\n */\nconst VALUE_METRIC_MAX_WINDOW_DAYS = 762\n\nconst valueMetric = (\n id: string,\n label: string,\n description: string,\n): ChartMetricSpec => ({\n id,\n label,\n description,\n unit: 'count',\n dimensions: VALUE_METRIC_DIMENSIONS,\n filters: VALUE_METRIC_FILTERS,\n grains: VALUE_METRIC_GRAINS,\n maxWindowDays: VALUE_METRIC_MAX_WINDOW_DAYS,\n stat: { series: 'sum', window: 'sum' },\n})\n\nexport const CHART_METRICS: readonly ChartMetricSpec[] = [\n valueMetric(\n 'processInstances',\n 'Process instances',\n 'Process instances that started, from the HDP value metrics. The sum over each bucket.',\n ),\n valueMetric(\n 'decisionInstances',\n 'Decision instances',\n 'Decision instances that ran, from the HDP value metrics. The sum over each bucket.',\n ),\n valueMetric(\n 'taskUsers',\n 'Task users',\n 'Task users, from the HDP value metrics. The sum of the daily values over each bucket, as on the Value metrics tab.',\n ),\n gaugeMetric(\n 'activeTenants',\n 'Active tenants',\n 'count',\n { series: 'sum', window: 'max' },\n 'Active tenants: the peak of each cluster on a day, summed over the clusters. A week or a month shows its highest day. The table holds every stage.',\n ),\n gaugeMetric(\n 'clusterLoad',\n 'Cluster load',\n 'percent',\n { series: 'mean', window: 'mean' },\n 'The load of a cluster, in percent: the mean of a day, then the mean over the clusters and the days of a bucket. The table holds every stage.',\n ),\n gaugeMetric(\n 'diskUsedBytes',\n 'Elasticsearch disk used',\n 'bytes',\n { series: 'sum', window: 'max' },\n 'The used Elasticsearch disk: the peak of each cluster on a day, summed over the clusters. A week or a month shows its highest day. The table holds every stage.',\n ),\n {\n id: 'hostingCost',\n label: 'Hosting cost',\n description:\n 'The loaded hosting cost of the clusters, in US dollars, from the cloud bill. The sum over each bucket. The bill holds every stage.',\n unit: 'usd',\n dimensions: [CLUSTER_DIMENSION, ORG_DIMENSION, ORG_NAME_DIMENSION],\n filters: [ORG_ID_FILTER, CLUSTER_ID_FILTER],\n grains: ALL_GRAINS,\n maxWindowDays: VALUE_METRIC_MAX_WINDOW_DAYS,\n stat: { series: 'sum', window: 'sum' },\n },\n {\n id: 'changeEvents',\n label: 'Change events',\n description:\n 'The change events that the backends of this stage sent to HDP: one for each insert, update or delete of an entity. The count over each bucket.',\n unit: 'count',\n dimensions: [\n { id: 'entity', label: 'Entity', description: 'For example cluster.' },\n {\n id: 'operation',\n label: 'Operation',\n description: 'insert, update or delete.',\n },\n ],\n filters: [\n {\n field: 'entity',\n label: 'Entity',\n description: 'The entity, case-insensitive. Use * as a wildcard.',\n type: 'string',\n capabilities: ['match'],\n example: 'entity:cluster',\n },\n {\n field: 'operation',\n label: 'Operation',\n description: 'The operation.',\n type: 'enum',\n capabilities: ['match'],\n enumValues: ['insert', 'update', 'delete'],\n example: 'operation:delete',\n },\n ],\n grains: ALL_GRAINS,\n maxWindowDays: LARGE_TABLE_MAX_WINDOW_DAYS,\n stat: { series: 'sum', window: 'sum' },\n },\n]\n\n/** Case-insensitive. */\nexport const findChartMetric = (\n id: string,\n metrics: readonly ChartMetricSpec[] = CHART_METRICS,\n): ChartMetricSpec | undefined =>\n metrics.find((metric) => metric.id.toLowerCase() === id.toLowerCase())\n\n/** Case-insensitive. */\nexport const findChartDimension = (\n metric: ChartMetricSpec,\n id: string,\n): ChartDimensionSpec | undefined =>\n metric.dimensions.find(\n (dimension) => dimension.id.toLowerCase() === id.toLowerCase(),\n )\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,55 @@
1
+ import { type ChartMetricSpec } from './chart-metrics.js';
2
+ import { type ChartSpec } from './chart-spec.js';
3
+ export type ChartQueryErrorKind = 'syntax' | 'unknown-metric' | 'unknown-dimension' | 'unknown-field' | 'bad-value';
4
+ export declare class ChartQueryError extends Error {
5
+ readonly kind: ChartQueryErrorKind;
6
+ /** A name to use instead, for "did you mean". */
7
+ readonly suggestion?: string;
8
+ constructor(message: string, kind: ChartQueryErrorKind, suggestion?: string);
9
+ }
10
+ /** Type guard that works across package boundaries. */
11
+ export declare const isChartQueryError: (error: unknown) => error is ChartQueryError;
12
+ /**
13
+ * The answer of `POST /api/dashboards/chart-queries/check`: the canonical spec
14
+ * of a valid query, or what is wrong with a query and how to fix it. A wrong
15
+ * query is an answer and not a failure, so a client reads `valid`.
16
+ */
17
+ export type ChartQueryCheck = {
18
+ valid: true;
19
+ spec: ChartSpec;
20
+ query: string;
21
+ } | {
22
+ valid: false;
23
+ error: {
24
+ kind: ChartQueryErrorKind;
25
+ message: string;
26
+ suggestion?: string;
27
+ };
28
+ };
29
+ export interface ChartQueryOptions {
30
+ metrics?: readonly ChartMetricSpec[];
31
+ /** `YYYY-MM-DD`. The default is today in UTC. */
32
+ today?: string;
33
+ }
34
+ /**
35
+ * Parse and validate a chart query. Gives the canonical {@link ChartSpec}:
36
+ * the ids in the spelling of the catalogue, and the defaults filled in.
37
+ * Throws {@link ChartQueryError}.
38
+ */
39
+ export declare const parseChartQuery: (text: string, options?: ChartQueryOptions) => ChartSpec;
40
+ /**
41
+ * Check a spec against the catalogue and give it in canonical form. The chart
42
+ * route runs this on every request body, so a spec that a tile stored long ago
43
+ * gets the rules of today. Throws {@link ChartQueryError}.
44
+ */
45
+ export declare const validateChartSpec: (spec: ChartSpec, options?: ChartQueryOptions) => ChartSpec;
46
+ /**
47
+ * The text of a spec, in one canonical form. Parsing it again gives the same
48
+ * spec, so the text field and the builder can never disagree.
49
+ */
50
+ export declare const formatChartSpec: (spec: ChartSpec) => string;
51
+ /**
52
+ * Read a spec from JSON, for example a request body or a stored tile. Checks
53
+ * the shape only. Run {@link validateChartSpec} after it.
54
+ */
55
+ export declare const readChartSpec: (value: unknown) => ChartSpec;
@@ -0,0 +1,376 @@
1
+ import { isLuceneQueryError } from "../search/ast.js";
2
+ import { closestField } from "../search/compiler.js";
3
+ import { findSearchField } from "../search/fields.js";
4
+ import { parseLuceneQuery } from "../search/parser.js";
5
+ import {
6
+ CHART_METRICS,
7
+ findChartDimension,
8
+ findChartMetric
9
+ } from "./chart-metrics.js";
10
+ import {
11
+ CHART_DAY_GRAIN_MAX_DAYS,
12
+ CHART_GRAIN_DEFAULT,
13
+ CHART_GRAINS,
14
+ CHART_ID_MAX_LENGTH,
15
+ CHART_QUERY_MAX_LENGTH,
16
+ CHART_TOP_DEFAULT,
17
+ CHART_TOP_MAX,
18
+ CHART_WINDOW_AMOUNT_MAX,
19
+ CHART_WINDOW_DEFAULT,
20
+ CHART_WINDOW_UNITS,
21
+ chartToday,
22
+ chartWindowRange
23
+ } from "./chart-spec.js";
24
+ class ChartQueryError extends Error {
25
+ kind;
26
+ /** A name to use instead, for "did you mean". */
27
+ suggestion;
28
+ constructor(message, kind, suggestion) {
29
+ super(message);
30
+ this.name = "ChartQueryError";
31
+ this.kind = kind;
32
+ this.suggestion = suggestion;
33
+ }
34
+ }
35
+ const isChartQueryError = (error) => error instanceof ChartQueryError || typeof error === "object" && error !== null && error.name === "ChartQueryError";
36
+ const KEYWORDS = ["by", "every", "over", "top", "where"];
37
+ const DURATION = /^(\d{1,4})([dwm])$/i;
38
+ const INTEGER = /^\d{1,4}$/;
39
+ const parseChartQuery = (text, options = {}) => validateChartSpec(readChartQueryText(text), options);
40
+ const readChartQueryText = (text) => {
41
+ if (text.length > CHART_QUERY_MAX_LENGTH) {
42
+ throw new ChartQueryError(
43
+ `The query is too long. The maximum is ${CHART_QUERY_MAX_LENGTH} characters.`,
44
+ "syntax"
45
+ );
46
+ }
47
+ const tokens = [...text.matchAll(/\S+/g)];
48
+ if (tokens.length === 0) {
49
+ throw new ChartQueryError(
50
+ "The query is empty. Start with a metric.",
51
+ "syntax"
52
+ );
53
+ }
54
+ const metric = tokens[0][0];
55
+ if (isKeyword(metric)) {
56
+ throw new ChartQueryError(
57
+ `The query starts with "${metric}". Start with a metric.`,
58
+ "syntax"
59
+ );
60
+ }
61
+ const spec = { metric };
62
+ const seen = /* @__PURE__ */ new Set();
63
+ let index = 1;
64
+ while (index < tokens.length) {
65
+ const word = tokens[index][0];
66
+ const keyword = word.toLowerCase();
67
+ if (!isKeyword(keyword)) {
68
+ const suggestion = closestField(keyword, KEYWORDS);
69
+ throw new ChartQueryError(
70
+ `"${word}" is not a clause.${suggestion ? ` Did you mean "${suggestion}"?` : ""} Use by, every, over, top or where.`,
71
+ "syntax",
72
+ suggestion
73
+ );
74
+ }
75
+ if (seen.has(keyword)) {
76
+ throw new ChartQueryError(
77
+ `"${keyword}" occurs more than one time.`,
78
+ "syntax"
79
+ );
80
+ }
81
+ seen.add(keyword);
82
+ if (keyword === "where") {
83
+ const start = (tokens[index].index ?? 0) + word.length;
84
+ const filter = text.slice(start).trim();
85
+ if (filter.length === 0) {
86
+ throw new ChartQueryError('"where" has no filter after it.', "syntax");
87
+ }
88
+ spec.filter = filter;
89
+ break;
90
+ }
91
+ const value = tokens[index + 1]?.[0];
92
+ if (value === void 0 || isKeyword(value.toLowerCase())) {
93
+ throw new ChartQueryError(`"${keyword}" has no value after it.`, "syntax");
94
+ }
95
+ switch (keyword) {
96
+ case "by":
97
+ spec.by = value;
98
+ break;
99
+ case "every":
100
+ spec.grain = readGrain(value);
101
+ break;
102
+ case "over":
103
+ spec.window = readDuration(value);
104
+ break;
105
+ case "top":
106
+ spec.top = readTop(value);
107
+ break;
108
+ }
109
+ index += 2;
110
+ }
111
+ return {
112
+ ...spec,
113
+ grain: spec.grain ?? CHART_GRAIN_DEFAULT,
114
+ window: spec.window ?? CHART_WINDOW_DEFAULT
115
+ };
116
+ };
117
+ const isKeyword = (word) => KEYWORDS.includes(word);
118
+ const readGrain = (value) => {
119
+ const grain = value.toLowerCase();
120
+ if (!CHART_GRAINS.includes(grain)) {
121
+ throw new ChartQueryError(
122
+ `"every ${value}" is not a bucket. Use day, week or month.`,
123
+ "bad-value"
124
+ );
125
+ }
126
+ return grain;
127
+ };
128
+ const readDuration = (value) => {
129
+ const match = DURATION.exec(value);
130
+ if (!match || Number(match[1]) === 0) {
131
+ throw new ChartQueryError(
132
+ `"over ${value}" is not a duration. Use a number and d, w or m, for example 90d.`,
133
+ "bad-value"
134
+ );
135
+ }
136
+ return {
137
+ amount: Number(match[1]),
138
+ unit: match[2].toLowerCase()
139
+ };
140
+ };
141
+ const readTop = (value) => {
142
+ if (!INTEGER.test(value)) {
143
+ throw new ChartQueryError(`"top ${value}" is not a number.`, "bad-value");
144
+ }
145
+ return Number(value);
146
+ };
147
+ const validateChartSpec = (spec, options = {}) => {
148
+ const metrics = options.metrics ?? CHART_METRICS;
149
+ const metric = findChartMetric(spec.metric, metrics);
150
+ if (!metric) {
151
+ const suggestion = closestField(
152
+ spec.metric,
153
+ metrics.map((entry) => entry.id)
154
+ );
155
+ throw new ChartQueryError(
156
+ `There is no metric "${spec.metric}".${suggestion ? ` Did you mean "${suggestion}"?` : ""}`,
157
+ "unknown-metric",
158
+ suggestion
159
+ );
160
+ }
161
+ let by;
162
+ if (spec.by !== void 0) {
163
+ const dimension = findChartDimension(metric, spec.by);
164
+ if (!dimension) {
165
+ const ids = metric.dimensions.map((entry) => entry.id);
166
+ const suggestion = closestField(spec.by, ids);
167
+ throw new ChartQueryError(
168
+ `${metric.id} has no dimension "${spec.by}". Use one of: ${ids.join(", ")}.`,
169
+ "unknown-dimension",
170
+ suggestion
171
+ );
172
+ }
173
+ by = dimension.id;
174
+ }
175
+ if (!metric.grains.includes(spec.grain)) {
176
+ throw new ChartQueryError(
177
+ `${metric.id} has no bucket "${spec.grain}". Use one of: ${metric.grains.join(", ")}.`,
178
+ "bad-value"
179
+ );
180
+ }
181
+ const today = options.today ?? chartToday();
182
+ const { days } = chartWindowRange(spec.window, spec.grain, today);
183
+ if (days > metric.maxWindowDays) {
184
+ throw new ChartQueryError(
185
+ `"over ${formatDuration(spec.window)}" reads ${days} days. ${metric.id} reads at most ${metric.maxWindowDays} days.`,
186
+ "bad-value"
187
+ );
188
+ }
189
+ if (spec.grain === "day" && days > CHART_DAY_GRAIN_MAX_DAYS) {
190
+ throw new ChartQueryError(
191
+ `"over ${formatDuration(spec.window)}" reads ${days} days. With "every day", the maximum is ${CHART_DAY_GRAIN_MAX_DAYS} days. Use "every week" or "every month".`,
192
+ "bad-value"
193
+ );
194
+ }
195
+ let top;
196
+ if (spec.top !== void 0 && by === void 0) {
197
+ throw new ChartQueryError(
198
+ '"top" needs "by". Without "by", the chart has one series.',
199
+ "syntax"
200
+ );
201
+ }
202
+ if (by !== void 0) {
203
+ top = spec.top ?? CHART_TOP_DEFAULT;
204
+ if (top < 1 || top > CHART_TOP_MAX) {
205
+ throw new ChartQueryError(
206
+ `"top ${top}" is out of range. Use 1 to ${CHART_TOP_MAX}.`,
207
+ "bad-value"
208
+ );
209
+ }
210
+ }
211
+ const filter = spec.filter?.trim() || void 0;
212
+ if (filter !== void 0) {
213
+ checkFilter(filter, metric.filters);
214
+ }
215
+ return {
216
+ metric: metric.id,
217
+ ...by !== void 0 ? { by } : {},
218
+ grain: spec.grain,
219
+ window: { amount: spec.window.amount, unit: spec.window.unit },
220
+ ...filter !== void 0 ? { filter } : {},
221
+ ...top !== void 0 ? { top } : {}
222
+ };
223
+ };
224
+ const checkFilter = (filter, fields) => {
225
+ let ast;
226
+ try {
227
+ ast = parseLuceneQuery(filter);
228
+ } catch (error) {
229
+ if (isLuceneQueryError(error)) {
230
+ throw new ChartQueryError(`In "where": ${error.message}`, "syntax");
231
+ }
232
+ throw error;
233
+ }
234
+ if (ast) {
235
+ checkFilterNode(ast, fields);
236
+ }
237
+ };
238
+ const checkFilterNode = (node, fields) => {
239
+ switch (node.kind) {
240
+ case "and":
241
+ case "or":
242
+ checkFilterNode(node.left, fields);
243
+ checkFilterNode(node.right, fields);
244
+ return;
245
+ case "not":
246
+ checkFilterNode(node.child, fields);
247
+ return;
248
+ case "range": {
249
+ const spec = filterField(node.field, fields);
250
+ if (!spec.capabilities.includes("range")) {
251
+ throw new ChartQueryError(
252
+ `"${spec.field}" does not take a range.`,
253
+ "bad-value"
254
+ );
255
+ }
256
+ return;
257
+ }
258
+ case "term": {
259
+ if (node.field === void 0) {
260
+ throw new ChartQueryError(
261
+ `"${node.value}" has no field. Write field:value, for example ${fields[0]?.example ?? "orgId:\u2026"}.`,
262
+ "syntax"
263
+ );
264
+ }
265
+ const spec = filterField(node.field, fields);
266
+ if (node.operator !== ":" && !spec.capabilities.includes("range")) {
267
+ throw new ChartQueryError(
268
+ `"${spec.field}" takes only ${spec.field}:value.`,
269
+ "bad-value"
270
+ );
271
+ }
272
+ if (node.value.startsWith("$")) {
273
+ throw new ChartQueryError(
274
+ `"${node.value}" is a dashboard variable. Dashboard variables are not available yet.`,
275
+ "bad-value"
276
+ );
277
+ }
278
+ if (spec.type === "enum" && !spec.enumValues?.some(
279
+ (value) => value.toLowerCase() === node.value.toLowerCase()
280
+ )) {
281
+ throw new ChartQueryError(
282
+ `"${spec.field}" takes one of: ${spec.enumValues?.join(", ")}.`,
283
+ "bad-value"
284
+ );
285
+ }
286
+ return;
287
+ }
288
+ }
289
+ };
290
+ const filterField = (name, fields) => {
291
+ const spec = findSearchField(fields, name);
292
+ if (!spec) {
293
+ const suggestion = closestField(
294
+ name,
295
+ fields.map((field) => field.field)
296
+ );
297
+ throw new ChartQueryError(
298
+ `There is no filter field "${name}".${suggestion ? ` Did you mean "${suggestion}"?` : ""}`,
299
+ "unknown-field",
300
+ suggestion
301
+ );
302
+ }
303
+ return spec;
304
+ };
305
+ const formatDuration = (window) => `${window.amount}${window.unit}`;
306
+ const formatChartSpec = (spec) => [
307
+ spec.metric,
308
+ ...spec.by !== void 0 ? ["by", spec.by] : [],
309
+ "every",
310
+ spec.grain,
311
+ "over",
312
+ formatDuration(spec.window),
313
+ ...spec.top !== void 0 ? ["top", String(spec.top)] : [],
314
+ ...spec.filter !== void 0 ? ["where", spec.filter] : []
315
+ ].join(" ");
316
+ const readChartSpec = (value) => {
317
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
318
+ throw new ChartQueryError("The chart query must be an object.", "syntax");
319
+ }
320
+ const record = value;
321
+ const allowed = /* @__PURE__ */ new Set(["metric", "by", "grain", "window", "filter", "top"]);
322
+ for (const key of Object.keys(record)) {
323
+ if (!allowed.has(key)) {
324
+ throw new ChartQueryError(
325
+ `The chart query has an unknown key "${key}".`,
326
+ "syntax"
327
+ );
328
+ }
329
+ }
330
+ const { metric, by, grain, window, filter, top } = record;
331
+ if (typeof metric !== "string" || metric.length === 0 || metric.length > CHART_ID_MAX_LENGTH) {
332
+ throw new ChartQueryError('"metric" must be a string.', "syntax");
333
+ }
334
+ if (by !== void 0 && (typeof by !== "string" || by.length > CHART_ID_MAX_LENGTH)) {
335
+ throw new ChartQueryError('"by" must be a string.', "syntax");
336
+ }
337
+ if (typeof grain !== "string" || !CHART_GRAINS.includes(grain)) {
338
+ throw new ChartQueryError(
339
+ `"grain" must be one of: ${CHART_GRAINS.join(", ")}.`,
340
+ "syntax"
341
+ );
342
+ }
343
+ if (typeof window !== "object" || window === null) {
344
+ throw new ChartQueryError('"window" must be an object.', "syntax");
345
+ }
346
+ const { amount, unit } = window;
347
+ if (typeof amount !== "number" || !Number.isInteger(amount) || amount < 1 || amount > CHART_WINDOW_AMOUNT_MAX || typeof unit !== "string" || !CHART_WINDOW_UNITS.includes(unit) || Object.keys(window).length !== 2) {
348
+ throw new ChartQueryError(
349
+ `"window" must be { amount, unit }, with a whole amount from 1 to ${CHART_WINDOW_AMOUNT_MAX} and a unit of d, w or m.`,
350
+ "syntax"
351
+ );
352
+ }
353
+ if (filter !== void 0 && typeof filter !== "string") {
354
+ throw new ChartQueryError('"filter" must be a string.', "syntax");
355
+ }
356
+ if (top !== void 0 && (typeof top !== "number" || !Number.isInteger(top))) {
357
+ throw new ChartQueryError('"top" must be a whole number.', "syntax");
358
+ }
359
+ return {
360
+ metric,
361
+ ...by !== void 0 ? { by } : {},
362
+ grain,
363
+ window: { amount, unit },
364
+ ...filter !== void 0 ? { filter } : {},
365
+ ...top !== void 0 ? { top } : {}
366
+ };
367
+ };
368
+ export {
369
+ ChartQueryError,
370
+ formatChartSpec,
371
+ isChartQueryError,
372
+ parseChartQuery,
373
+ readChartSpec,
374
+ validateChartSpec
375
+ };
376
+ //# sourceMappingURL=chart-query.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/charts/chart-query.ts"],
4
+ "sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\nimport { isLuceneQueryError, type QueryNode } from '../search/ast.js'\nimport { closestField } from '../search/compiler.js'\nimport { findSearchField, type SearchFieldSpec } from '../search/fields.js'\nimport { parseLuceneQuery } from '../search/parser.js'\nimport {\n CHART_METRICS,\n type ChartMetricSpec,\n findChartDimension,\n findChartMetric,\n} from './chart-metrics.js'\nimport {\n CHART_DAY_GRAIN_MAX_DAYS,\n CHART_GRAIN_DEFAULT,\n CHART_GRAINS,\n CHART_ID_MAX_LENGTH,\n CHART_QUERY_MAX_LENGTH,\n CHART_TOP_DEFAULT,\n CHART_TOP_MAX,\n CHART_WINDOW_AMOUNT_MAX,\n CHART_WINDOW_DEFAULT,\n CHART_WINDOW_UNITS,\n type ChartGrain,\n type ChartSpec,\n type ChartWindow,\n type ChartWindowUnit,\n chartToday,\n chartWindowRange,\n} from './chart-spec.js'\n\n// ============================================================================\n// The chart query language.\n//\n// query = metric clause*\n// clause = \"by\" dimension | \"every\" grain | \"over\" duration\n// | \"top\" integer | \"where\" lucene\n// duration = integer (\"d\" | \"w\" | \"m\")\n//\n// Keywords are case-insensitive, and each clause occurs at most one time.\n// `where` is the last clause: the rest of the text is the Lucene filter of\n// Fleet query, over the filter fields of the metric. The frontend and the\n// backend run the same checks, so a query that the field accepts also runs.\n// ============================================================================\n\nexport type ChartQueryErrorKind =\n | 'syntax'\n | 'unknown-metric'\n | 'unknown-dimension'\n | 'unknown-field'\n | 'bad-value'\n\nexport class ChartQueryError extends Error {\n readonly kind: ChartQueryErrorKind\n /** A name to use instead, for \"did you mean\". */\n readonly suggestion?: string\n\n constructor(message: string, kind: ChartQueryErrorKind, suggestion?: string) {\n super(message)\n this.name = 'ChartQueryError'\n this.kind = kind\n this.suggestion = suggestion\n }\n}\n\n/** Type guard that works across package boundaries. */\nexport const isChartQueryError = (error: unknown): error is ChartQueryError =>\n error instanceof ChartQueryError ||\n (typeof error === 'object' &&\n error !== null &&\n (error as { name?: unknown }).name === 'ChartQueryError')\n\n/**\n * The answer of `POST /api/dashboards/chart-queries/check`: the canonical spec\n * of a valid query, or what is wrong with a query and how to fix it. A wrong\n * query is an answer and not a failure, so a client reads `valid`.\n */\nexport type ChartQueryCheck =\n | { valid: true; spec: ChartSpec; query: string }\n | {\n valid: false\n error: {\n kind: ChartQueryErrorKind\n message: string\n suggestion?: string\n }\n }\n\nconst KEYWORDS = ['by', 'every', 'over', 'top', 'where'] as const\ntype Keyword = (typeof KEYWORDS)[number]\n\nconst DURATION = /^(\\d{1,4})([dwm])$/i\nconst INTEGER = /^\\d{1,4}$/\n\nexport interface ChartQueryOptions {\n metrics?: readonly ChartMetricSpec[]\n /** `YYYY-MM-DD`. The default is today in UTC. */\n today?: string\n}\n\n/**\n * Parse and validate a chart query. Gives the canonical {@link ChartSpec}:\n * the ids in the spelling of the catalogue, and the defaults filled in.\n * Throws {@link ChartQueryError}.\n */\nexport const parseChartQuery = (\n text: string,\n options: ChartQueryOptions = {},\n): ChartSpec => validateChartSpec(readChartQueryText(text), options)\n\n/** The clauses of the text, with no check against the catalogue. */\nconst readChartQueryText = (text: string): ChartSpec => {\n if (text.length > CHART_QUERY_MAX_LENGTH) {\n throw new ChartQueryError(\n `The query is too long. The maximum is ${CHART_QUERY_MAX_LENGTH} characters.`,\n 'syntax',\n )\n }\n const tokens = [...text.matchAll(/\\S+/g)]\n if (tokens.length === 0) {\n throw new ChartQueryError(\n 'The query is empty. Start with a metric.',\n 'syntax',\n )\n }\n const metric = tokens[0][0]\n if (isKeyword(metric)) {\n throw new ChartQueryError(\n `The query starts with \"${metric}\". Start with a metric.`,\n 'syntax',\n )\n }\n\n const spec: Partial<ChartSpec> & { metric: string } = { metric }\n const seen = new Set<Keyword>()\n let index = 1\n while (index < tokens.length) {\n const word = tokens[index][0]\n const keyword = word.toLowerCase()\n if (!isKeyword(keyword)) {\n const suggestion = closestField(keyword, KEYWORDS)\n throw new ChartQueryError(\n `\"${word}\" is not a clause.${suggestion ? ` Did you mean \"${suggestion}\"?` : ''} Use by, every, over, top or where.`,\n 'syntax',\n suggestion,\n )\n }\n if (seen.has(keyword)) {\n throw new ChartQueryError(\n `\"${keyword}\" occurs more than one time.`,\n 'syntax',\n )\n }\n seen.add(keyword)\n\n if (keyword === 'where') {\n const start = (tokens[index].index ?? 0) + word.length\n const filter = text.slice(start).trim()\n if (filter.length === 0) {\n throw new ChartQueryError('\"where\" has no filter after it.', 'syntax')\n }\n spec.filter = filter\n break\n }\n\n const value = tokens[index + 1]?.[0]\n if (value === undefined || isKeyword(value.toLowerCase())) {\n throw new ChartQueryError(`\"${keyword}\" has no value after it.`, 'syntax')\n }\n switch (keyword) {\n case 'by':\n spec.by = value\n break\n case 'every':\n spec.grain = readGrain(value)\n break\n case 'over':\n spec.window = readDuration(value)\n break\n case 'top':\n spec.top = readTop(value)\n break\n }\n index += 2\n }\n\n return {\n ...spec,\n grain: spec.grain ?? CHART_GRAIN_DEFAULT,\n window: spec.window ?? CHART_WINDOW_DEFAULT,\n }\n}\n\nconst isKeyword = (word: string): word is Keyword =>\n (KEYWORDS as readonly string[]).includes(word)\n\nconst readGrain = (value: string): ChartGrain => {\n const grain = value.toLowerCase()\n if (!(CHART_GRAINS as readonly string[]).includes(grain)) {\n throw new ChartQueryError(\n `\"every ${value}\" is not a bucket. Use day, week or month.`,\n 'bad-value',\n )\n }\n return grain as ChartGrain\n}\n\nconst readDuration = (value: string): ChartWindow => {\n const match = DURATION.exec(value)\n if (!match || Number(match[1]) === 0) {\n throw new ChartQueryError(\n `\"over ${value}\" is not a duration. Use a number and d, w or m, for example 90d.`,\n 'bad-value',\n )\n }\n return {\n amount: Number(match[1]),\n unit: match[2].toLowerCase() as ChartWindowUnit,\n }\n}\n\nconst readTop = (value: string): number => {\n if (!INTEGER.test(value)) {\n throw new ChartQueryError(`\"top ${value}\" is not a number.`, 'bad-value')\n }\n return Number(value)\n}\n\n/**\n * Check a spec against the catalogue and give it in canonical form. The chart\n * route runs this on every request body, so a spec that a tile stored long ago\n * gets the rules of today. Throws {@link ChartQueryError}.\n */\nexport const validateChartSpec = (\n spec: ChartSpec,\n options: ChartQueryOptions = {},\n): ChartSpec => {\n const metrics = options.metrics ?? CHART_METRICS\n const metric = findChartMetric(spec.metric, metrics)\n if (!metric) {\n const suggestion = closestField(\n spec.metric,\n metrics.map((entry) => entry.id),\n )\n throw new ChartQueryError(\n `There is no metric \"${spec.metric}\".${suggestion ? ` Did you mean \"${suggestion}\"?` : ''}`,\n 'unknown-metric',\n suggestion,\n )\n }\n\n let by: string | undefined\n if (spec.by !== undefined) {\n const dimension = findChartDimension(metric, spec.by)\n if (!dimension) {\n const ids = metric.dimensions.map((entry) => entry.id)\n const suggestion = closestField(spec.by, ids)\n throw new ChartQueryError(\n `${metric.id} has no dimension \"${spec.by}\". Use one of: ${ids.join(', ')}.`,\n 'unknown-dimension',\n suggestion,\n )\n }\n by = dimension.id\n }\n\n if (!metric.grains.includes(spec.grain)) {\n throw new ChartQueryError(\n `${metric.id} has no bucket \"${spec.grain}\". Use one of: ${metric.grains.join(', ')}.`,\n 'bad-value',\n )\n }\n\n const today = options.today ?? chartToday()\n const { days } = chartWindowRange(spec.window, spec.grain, today)\n if (days > metric.maxWindowDays) {\n throw new ChartQueryError(\n `\"over ${formatDuration(spec.window)}\" reads ${days} days. ${metric.id} reads at most ${metric.maxWindowDays} days.`,\n 'bad-value',\n )\n }\n if (spec.grain === 'day' && days > CHART_DAY_GRAIN_MAX_DAYS) {\n throw new ChartQueryError(\n `\"over ${formatDuration(spec.window)}\" reads ${days} days. With \"every day\", the maximum is ${CHART_DAY_GRAIN_MAX_DAYS} days. Use \"every week\" or \"every month\".`,\n 'bad-value',\n )\n }\n\n let top: number | undefined\n if (spec.top !== undefined && by === undefined) {\n throw new ChartQueryError(\n '\"top\" needs \"by\". Without \"by\", the chart has one series.',\n 'syntax',\n )\n }\n if (by !== undefined) {\n top = spec.top ?? CHART_TOP_DEFAULT\n if (top < 1 || top > CHART_TOP_MAX) {\n throw new ChartQueryError(\n `\"top ${top}\" is out of range. Use 1 to ${CHART_TOP_MAX}.`,\n 'bad-value',\n )\n }\n }\n\n const filter = spec.filter?.trim() || undefined\n if (filter !== undefined) {\n checkFilter(filter, metric.filters)\n }\n\n return {\n metric: metric.id,\n ...(by !== undefined ? { by } : {}),\n grain: spec.grain,\n window: { amount: spec.window.amount, unit: spec.window.unit },\n ...(filter !== undefined ? { filter } : {}),\n ...(top !== undefined ? { top } : {}),\n }\n}\n\nconst checkFilter = (\n filter: string,\n fields: readonly SearchFieldSpec[],\n): void => {\n let ast: QueryNode | undefined\n try {\n ast = parseLuceneQuery(filter)\n } catch (error) {\n if (isLuceneQueryError(error)) {\n throw new ChartQueryError(`In \"where\": ${error.message}`, 'syntax')\n }\n throw error\n }\n if (ast) {\n checkFilterNode(ast, fields)\n }\n}\n\nconst checkFilterNode = (\n node: QueryNode,\n fields: readonly SearchFieldSpec[],\n): void => {\n switch (node.kind) {\n case 'and':\n case 'or':\n checkFilterNode(node.left, fields)\n checkFilterNode(node.right, fields)\n return\n case 'not':\n checkFilterNode(node.child, fields)\n return\n case 'range': {\n const spec = filterField(node.field, fields)\n if (!spec.capabilities.includes('range')) {\n throw new ChartQueryError(\n `\"${spec.field}\" does not take a range.`,\n 'bad-value',\n )\n }\n return\n }\n case 'term': {\n if (node.field === undefined) {\n throw new ChartQueryError(\n `\"${node.value}\" has no field. Write field:value, for example ${fields[0]?.example ?? 'orgId:\u2026'}.`,\n 'syntax',\n )\n }\n const spec = filterField(node.field, fields)\n if (node.operator !== ':' && !spec.capabilities.includes('range')) {\n throw new ChartQueryError(\n `\"${spec.field}\" takes only ${spec.field}:value.`,\n 'bad-value',\n )\n }\n if (node.value.startsWith('$')) {\n throw new ChartQueryError(\n `\"${node.value}\" is a dashboard variable. Dashboard variables are not available yet.`,\n 'bad-value',\n )\n }\n if (\n spec.type === 'enum' &&\n !spec.enumValues?.some(\n (value) => value.toLowerCase() === node.value.toLowerCase(),\n )\n ) {\n throw new ChartQueryError(\n `\"${spec.field}\" takes one of: ${spec.enumValues?.join(', ')}.`,\n 'bad-value',\n )\n }\n return\n }\n }\n}\n\nconst filterField = (\n name: string,\n fields: readonly SearchFieldSpec[],\n): SearchFieldSpec => {\n const spec = findSearchField(fields, name)\n if (!spec) {\n const suggestion = closestField(\n name,\n fields.map((field) => field.field),\n )\n throw new ChartQueryError(\n `There is no filter field \"${name}\".${suggestion ? ` Did you mean \"${suggestion}\"?` : ''}`,\n 'unknown-field',\n suggestion,\n )\n }\n return spec\n}\n\nconst formatDuration = (window: ChartWindow): string =>\n `${window.amount}${window.unit}`\n\n/**\n * The text of a spec, in one canonical form. Parsing it again gives the same\n * spec, so the text field and the builder can never disagree.\n */\nexport const formatChartSpec = (spec: ChartSpec): string =>\n [\n spec.metric,\n ...(spec.by !== undefined ? ['by', spec.by] : []),\n 'every',\n spec.grain,\n 'over',\n formatDuration(spec.window),\n ...(spec.top !== undefined ? ['top', String(spec.top)] : []),\n ...(spec.filter !== undefined ? ['where', spec.filter] : []),\n ].join(' ')\n\n/**\n * Read a spec from JSON, for example a request body or a stored tile. Checks\n * the shape only. Run {@link validateChartSpec} after it.\n */\nexport const readChartSpec = (value: unknown): ChartSpec => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new ChartQueryError('The chart query must be an object.', 'syntax')\n }\n const record = value as Record<string, unknown>\n const allowed = new Set(['metric', 'by', 'grain', 'window', 'filter', 'top'])\n for (const key of Object.keys(record)) {\n if (!allowed.has(key)) {\n throw new ChartQueryError(\n `The chart query has an unknown key \"${key}\".`,\n 'syntax',\n )\n }\n }\n const { metric, by, grain, window, filter, top } = record\n if (\n typeof metric !== 'string' ||\n metric.length === 0 ||\n metric.length > CHART_ID_MAX_LENGTH\n ) {\n throw new ChartQueryError('\"metric\" must be a string.', 'syntax')\n }\n if (\n by !== undefined &&\n (typeof by !== 'string' || by.length > CHART_ID_MAX_LENGTH)\n ) {\n throw new ChartQueryError('\"by\" must be a string.', 'syntax')\n }\n if (\n typeof grain !== 'string' ||\n !(CHART_GRAINS as readonly string[]).includes(grain)\n ) {\n throw new ChartQueryError(\n `\"grain\" must be one of: ${CHART_GRAINS.join(', ')}.`,\n 'syntax',\n )\n }\n if (typeof window !== 'object' || window === null) {\n throw new ChartQueryError('\"window\" must be an object.', 'syntax')\n }\n const { amount, unit } = window as Record<string, unknown>\n if (\n typeof amount !== 'number' ||\n !Number.isInteger(amount) ||\n amount < 1 ||\n amount > CHART_WINDOW_AMOUNT_MAX ||\n typeof unit !== 'string' ||\n !(CHART_WINDOW_UNITS as readonly string[]).includes(unit) ||\n Object.keys(window).length !== 2\n ) {\n throw new ChartQueryError(\n `\"window\" must be { amount, unit }, with a whole amount from 1 to ${CHART_WINDOW_AMOUNT_MAX} and a unit of d, w or m.`,\n 'syntax',\n )\n }\n if (filter !== undefined && typeof filter !== 'string') {\n throw new ChartQueryError('\"filter\" must be a string.', 'syntax')\n }\n if (\n top !== undefined &&\n (typeof top !== 'number' || !Number.isInteger(top))\n ) {\n throw new ChartQueryError('\"top\" must be a whole number.', 'syntax')\n }\n // The checks above narrow nothing for the compiler, thus the casts.\n return {\n metric,\n ...(by !== undefined ? { by: by as string } : {}),\n grain: grain as ChartGrain,\n window: { amount, unit: unit as ChartWindowUnit },\n ...(filter !== undefined ? { filter: filter as string } : {}),\n ...(top !== undefined ? { top: top as number } : {}),\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,103 @@
1
+ /** The time buckets of a chart. A week starts on Monday (ISO). */
2
+ export declare const CHART_GRAINS: readonly ["day", "week", "month"];
3
+ export type ChartGrain = (typeof CHART_GRAINS)[number];
4
+ /** `d` is days, `w` is weeks and `m` is calendar months. */
5
+ export declare const CHART_WINDOW_UNITS: readonly ["d", "w", "m"];
6
+ export type ChartWindowUnit = (typeof CHART_WINDOW_UNITS)[number];
7
+ /** A window that ends today (UTC). "The last 90 days" is `{ amount: 90, unit: 'd' }`. */
8
+ export interface ChartWindow {
9
+ amount: number;
10
+ unit: ChartWindowUnit;
11
+ }
12
+ /**
13
+ * One chart query. A tile stores this and never the data, so the chart
14
+ * follows the window as the days go by.
15
+ */
16
+ export interface ChartSpec {
17
+ /** A metric id of `CHART_METRICS`. */
18
+ metric: string;
19
+ /** A dimension id of the metric. Each value of it is one series. */
20
+ by?: string;
21
+ grain: ChartGrain;
22
+ window: ChartWindow;
23
+ /** Lucene source text over the filter fields of the metric. */
24
+ filter?: string;
25
+ /** The number of series to show. Only with `by`. */
26
+ top?: number;
27
+ }
28
+ export declare const CHART_GRAIN_DEFAULT: ChartGrain;
29
+ export declare const CHART_WINDOW_DEFAULT: ChartWindow;
30
+ export declare const CHART_TOP_DEFAULT = 10;
31
+ export declare const CHART_TOP_MAX = 20;
32
+ /** The longest window, in days, for `every day`. */
33
+ export declare const CHART_DAY_GRAIN_MAX_DAYS = 366;
34
+ /**
35
+ * The longest text of a whole chart query. The filter has its own limit,
36
+ * `CHART_FILTER_MAX_LENGTH`.
37
+ */
38
+ export declare const CHART_QUERY_MAX_LENGTH = 700;
39
+ /**
40
+ * The longest filter of a chart query. It is the limit of the Lucene parser,
41
+ * which checks every filter on every run.
42
+ */
43
+ export declare const CHART_FILTER_MAX_LENGTH = 512;
44
+ /** The longest metric id or dimension id that a stored chart query holds. */
45
+ export declare const CHART_ID_MAX_LENGTH = 64;
46
+ /** The largest amount of a window, in its unit. */
47
+ export declare const CHART_WINDOW_AMOUNT_MAX = 9999;
48
+ /**
49
+ * The series key of the sum of every series that is not in the top N. When a
50
+ * real value of the dimension has this key, the key gets a `*`, so the keys
51
+ * of a result stay unique.
52
+ */
53
+ export declare const CHART_OTHER_SERIES_KEY = "(other)";
54
+ /**
55
+ * The series key of the rows with no value for the `by` dimension. It gets a
56
+ * `*` the same way as `CHART_OTHER_SERIES_KEY`.
57
+ */
58
+ export declare const CHART_UNKNOWN_SERIES_KEY = "(unknown)";
59
+ export interface ChartPoint {
60
+ /** The first day of the bucket, `YYYY-MM-DD`. */
61
+ ts: string;
62
+ value: number;
63
+ }
64
+ export interface ChartSeries {
65
+ key: string;
66
+ points: ChartPoint[];
67
+ }
68
+ /** `percent` is 0 to 100. */
69
+ export type ChartUnit = 'count' | 'bytes' | 'usd' | 'percent';
70
+ /** The answer of `POST /api/hdp/charts/query`. */
71
+ export interface ChartResult {
72
+ metric: string;
73
+ unit: ChartUnit;
74
+ grain: ChartGrain;
75
+ /** The first day that the query reads, `YYYY-MM-DD`, inclusive. */
76
+ from: string;
77
+ /** The last day that the query reads, `YYYY-MM-DD`, inclusive. */
78
+ to: string;
79
+ /**
80
+ * Sorted by the total of the series, the largest first. `(other)` is last.
81
+ * The keys are unique.
82
+ */
83
+ series: ChartSeries[];
84
+ /** True when the top N left out series. Their sum is the `(other)` series. */
85
+ truncated: boolean;
86
+ bytesProcessed: number;
87
+ /** When the result came from the cache: the time of the query, ISO. */
88
+ cachedAt?: string;
89
+ }
90
+ /** The first day of the bucket of `day`, as BigQuery's `DATE_TRUNC` gives it. */
91
+ export declare const chartBucketStart: (day: string, grain: ChartGrain) => string;
92
+ /**
93
+ * The days that a chart reads. The window ends on `today` and goes back by its
94
+ * amount. The start moves back to the start of its bucket, so the first
95
+ * bucket of the chart is complete.
96
+ */
97
+ export declare const chartWindowRange: (window: ChartWindow, grain: ChartGrain, today: string) => {
98
+ from: string;
99
+ to: string;
100
+ days: number;
101
+ };
102
+ /** Today in UTC, `YYYY-MM-DD`. */
103
+ export declare const chartToday: (now?: Date) => string;