@gooddata/sdk-ui-catalog 11.46.0-alpha.2 → 11.46.0-alpha.4
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.
- package/esm/AnalyticsCatalogDetail.d.ts.map +1 -1
- package/esm/AnalyticsCatalogDetail.js +2 -1
- package/esm/catalog/Catalog.d.ts.map +1 -1
- package/esm/catalog/Catalog.js +5 -2
- package/esm/catalogDetail/CatalogDetailActionBar.d.ts +6 -4
- package/esm/catalogDetail/CatalogDetailActionBar.d.ts.map +1 -1
- package/esm/catalogDetail/CatalogDetailActionBar.js +5 -6
- package/esm/catalogDetail/CatalogDetailActions.d.ts +2 -1
- package/esm/catalogDetail/CatalogDetailActions.d.ts.map +1 -1
- package/esm/catalogDetail/CatalogDetailActions.js +6 -2
- package/esm/catalogDetail/CatalogDetailContent.d.ts.map +1 -1
- package/esm/catalogDetail/CatalogDetailContent.js +3 -1
- package/esm/catalogItem/query.d.ts +7 -2
- package/esm/catalogItem/query.d.ts.map +1 -1
- package/esm/catalogItem/query.js +16 -1
- package/esm/header/CreateObjectButton.d.ts +2 -1
- package/esm/header/CreateObjectButton.d.ts.map +1 -1
- package/esm/header/CreateObjectButton.js +42 -38
- package/esm/localization/bundles/en-US.localization-bundle.d.ts +112 -0
- package/esm/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
- package/esm/localization/bundles/en-US.localization-bundle.js +112 -0
- package/esm/main/Main.d.ts.map +1 -1
- package/esm/main/Main.js +2 -3
- package/esm/metric/MetricCreateDialog.d.ts +11 -0
- package/esm/metric/MetricCreateDialog.d.ts.map +1 -0
- package/esm/metric/MetricCreateDialog.js +55 -0
- package/esm/metric/MetricDeleteDialog.d.ts +9 -0
- package/esm/metric/MetricDeleteDialog.d.ts.map +1 -0
- package/esm/metric/MetricDeleteDialog.js +68 -0
- package/esm/metric/MetricDetailActions.d.ts +23 -0
- package/esm/metric/MetricDetailActions.d.ts.map +1 -0
- package/esm/metric/MetricDetailActions.js +101 -0
- package/esm/metric/MetricDialog.d.ts +14 -0
- package/esm/metric/MetricDialog.d.ts.map +1 -0
- package/esm/metric/MetricDialog.js +100 -0
- package/esm/metric/MetricEditDialog.d.ts +11 -0
- package/esm/metric/MetricEditDialog.d.ts.map +1 -0
- package/esm/metric/MetricEditDialog.js +51 -0
- package/esm/metric/MetricMutationContext.d.ts +16 -0
- package/esm/metric/MetricMutationContext.d.ts.map +1 -0
- package/esm/metric/MetricMutationContext.js +32 -0
- package/esm/metric/MetricYamlEditor.d.ts +8 -0
- package/esm/metric/MetricYamlEditor.d.ts.map +1 -0
- package/esm/metric/MetricYamlEditor.js +20 -0
- package/esm/metric/gate.d.ts +9 -0
- package/esm/metric/gate.d.ts.map +1 -0
- package/esm/metric/gate.js +16 -0
- package/esm/metric/metricCompletions.d.ts +7 -0
- package/esm/metric/metricCompletions.d.ts.map +1 -0
- package/esm/metric/metricCompletions.js +59 -0
- package/esm/metric/metricConverter.d.ts +49 -0
- package/esm/metric/metricConverter.d.ts.map +1 -0
- package/esm/metric/metricConverter.js +101 -0
- package/esm/metric/metricCopy.d.ts +4 -0
- package/esm/metric/metricCopy.d.ts.map +1 -0
- package/esm/metric/metricCopy.js +60 -0
- package/esm/metric/metricMutationPort.d.ts +18 -0
- package/esm/metric/metricMutationPort.d.ts.map +1 -0
- package/esm/metric/metricMutationPort.js +39 -0
- package/esm/metric/metricSchema.d.ts +29 -0
- package/esm/metric/metricSchema.d.ts.map +1 -0
- package/esm/metric/metricSchema.js +30 -0
- package/esm/metric/metricSerialization.d.ts +10 -0
- package/esm/metric/metricSerialization.d.ts.map +1 -0
- package/esm/metric/metricSerialization.js +33 -0
- package/esm/metric/metricValidation.d.ts +15 -0
- package/esm/metric/metricValidation.d.ts.map +1 -0
- package/esm/metric/metricValidation.js +47 -0
- package/esm/metric/metricYamlLinter.d.ts +7 -0
- package/esm/metric/metricYamlLinter.d.ts.map +1 -0
- package/esm/metric/metricYamlLinter.js +13 -0
- package/esm/metric/tests/metricMutationPort.test.utils.d.ts +7 -0
- package/esm/metric/tests/metricMutationPort.test.utils.d.ts.map +1 -0
- package/esm/metric/tests/metricMutationPort.test.utils.js +43 -0
- package/esm/parameter/ParameterDialog.d.ts.map +1 -1
- package/esm/parameter/ParameterDialog.js +7 -7
- package/esm/parameter/ParameterYamlEditor.js +1 -1
- package/esm/parameter/yamlLinter.d.ts +1 -4
- package/esm/parameter/yamlLinter.d.ts.map +1 -1
- package/esm/parameter/yamlLinter.js +3 -24
- package/esm/utils/backendError.d.ts +11 -0
- package/esm/utils/backendError.d.ts.map +1 -0
- package/esm/utils/backendError.js +21 -0
- package/esm/utils/yamlSyntaxLinter.d.ts +11 -0
- package/esm/utils/yamlSyntaxLinter.d.ts.map +1 -0
- package/esm/utils/yamlSyntaxLinter.js +31 -0
- package/package.json +15 -14
- package/styles/css/{parameter.css → codeDialog.css} +26 -18
- package/styles/css/codeDialog.css.map +1 -0
- package/styles/css/detail.css +25 -17
- package/styles/css/detail.css.map +1 -1
- package/styles/css/main.css +25 -17
- package/styles/css/main.css.map +1 -1
- package/styles/scss/{parameter.scss → codeDialog.scss} +17 -8
- package/styles/scss/detail.scss +1 -1
- package/styles/css/parameter.css.map +0 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type PropsWithChildren } from "react";
|
|
2
|
+
import { type IMetricMutationPort } from "./metricMutationPort.js";
|
|
3
|
+
type ProviderProps = PropsWithChildren<{
|
|
4
|
+
port?: IMetricMutationPort;
|
|
5
|
+
}>;
|
|
6
|
+
/**
|
|
7
|
+
* Must be placed inside `BackendProvider` and `WorkspaceProvider`.
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export declare function MetricMutationProvider({ children, port }: ProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
/**
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export declare function useMetricMutation(): IMetricMutationPort;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=MetricMutationContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetricMutationContext.d.ts","sourceRoot":"","sources":["../../src/metric/MetricMutationContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAAsC,MAAM,OAAO,CAAC;AAInF,OAAO,EAAE,KAAK,mBAAmB,EAA+B,MAAM,yBAAyB,CAAC;AAIhG,KAAK,aAAa,GAAG,iBAAiB,CAAC;IACnC,IAAI,CAAC,EAAE,mBAAmB,CAAC;CAC9B,CAAC,CAAC;AAEH;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,aAAa,2CAKvE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,mBAAmB,CAMvD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { createContext, useContext, useMemo } from "react";
|
|
4
|
+
import { useBackendStrict, useWorkspaceStrict } from "@gooddata/sdk-ui";
|
|
5
|
+
import { createMetricMutationAdapter } from "./metricMutationPort.js";
|
|
6
|
+
const MetricMutationContext = createContext(null);
|
|
7
|
+
/**
|
|
8
|
+
* Must be placed inside `BackendProvider` and `WorkspaceProvider`.
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export function MetricMutationProvider({ children, port }) {
|
|
12
|
+
if (port) {
|
|
13
|
+
return _jsx(MetricMutationContext.Provider, { value: port, children: children });
|
|
14
|
+
}
|
|
15
|
+
return _jsx(DefaultMetricMutationProvider, { children: children });
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
export function useMetricMutation() {
|
|
21
|
+
const mutation = useContext(MetricMutationContext);
|
|
22
|
+
if (!mutation) {
|
|
23
|
+
throw new Error("useMetricMutation must be used within MetricMutationProvider");
|
|
24
|
+
}
|
|
25
|
+
return mutation;
|
|
26
|
+
}
|
|
27
|
+
function DefaultMetricMutationProvider({ children }) {
|
|
28
|
+
const backend = useBackendStrict();
|
|
29
|
+
const workspace = useWorkspaceStrict();
|
|
30
|
+
const mutation = useMemo(() => createMetricMutationAdapter(backend, workspace), [backend, workspace]);
|
|
31
|
+
return _jsx(MetricMutationContext.Provider, { value: mutation, children: children });
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type Props = {
|
|
2
|
+
initialValue: string;
|
|
3
|
+
onChange?: (value: string) => void;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function MetricYamlEditor({ initialValue, onChange, disabled }: Props): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=MetricYamlEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetricYamlEditor.d.ts","sourceRoot":"","sources":["../../src/metric/MetricYamlEditor.tsx"],"names":[],"mappings":"AAcA,KAAK,KAAK,GAAG;IACT,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAgB,EAAE,EAAE,KAAK,2CA2BnF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { yaml } from "@codemirror/lang-yaml";
|
|
5
|
+
import { lintGutter } from "@codemirror/lint";
|
|
6
|
+
import { lineNumbers } from "@codemirror/view";
|
|
7
|
+
import { useIntl } from "react-intl";
|
|
8
|
+
import { SyntaxHighlightingInput } from "@gooddata/sdk-ui-kit";
|
|
9
|
+
import { metricSchemaCompletions } from "./metricCompletions.js";
|
|
10
|
+
import { createMetricYamlLinter } from "./metricYamlLinter.js";
|
|
11
|
+
export function MetricYamlEditor({ initialValue, onChange, disabled = false }) {
|
|
12
|
+
const intl = useIntl();
|
|
13
|
+
const [value, setValue] = useState(initialValue);
|
|
14
|
+
const extensions = useMemo(() => [yaml(), lineNumbers(), createMetricYamlLinter(intl), lintGutter()], [intl]);
|
|
15
|
+
const handleChange = useCallback((nextValue) => {
|
|
16
|
+
setValue(nextValue);
|
|
17
|
+
onChange?.(nextValue);
|
|
18
|
+
}, [onChange]);
|
|
19
|
+
return (_jsx(SyntaxHighlightingInput, { className: "gd-ascode-dialog-editor-input", value: value, onChange: handleChange, extensions: extensions, onCompletion: metricSchemaCompletions, disabled: disabled }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catalog inline metric editor feature gate.
|
|
3
|
+
*/
|
|
4
|
+
export declare function useIsCatalogMetricEditorEnabled(): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Whether the current user can create and edit metrics inline in the catalog.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useCanManageMetric(): boolean;
|
|
9
|
+
//# sourceMappingURL=gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/metric/gate.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,wBAAgB,+BAA+B,IAAI,OAAO,CAEzD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAI5C"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { useFeatureFlag, useWorkspacePermission } from "../permission/PermissionsContext.js";
|
|
3
|
+
/**
|
|
4
|
+
* Catalog inline metric editor feature gate.
|
|
5
|
+
*/
|
|
6
|
+
export function useIsCatalogMetricEditorEnabled() {
|
|
7
|
+
return useFeatureFlag("enableAnalyticalCatalogMetricEditor");
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Whether the current user can create and edit metrics inline in the catalog.
|
|
11
|
+
*/
|
|
12
|
+
export function useCanManageMetric() {
|
|
13
|
+
const isMetricEditorEnabled = useIsCatalogMetricEditorEnabled();
|
|
14
|
+
const canManageProject = useWorkspacePermission("canManageProject");
|
|
15
|
+
return isMetricEditorEnabled && canManageProject;
|
|
16
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CompletionContext, CompletionResult } from "@codemirror/autocomplete";
|
|
2
|
+
/**
|
|
3
|
+
* CodeMirror completion source that suggests allowed YAML property names
|
|
4
|
+
* based on the metric zod schema and cursor position in the Lezer tree.
|
|
5
|
+
*/
|
|
6
|
+
export declare function metricSchemaCompletions(context: CompletionContext): CompletionResult | null;
|
|
7
|
+
//# sourceMappingURL=metricCompletions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricCompletions.d.ts","sourceRoot":"","sources":["../../src/metric/metricCompletions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAcpF;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,IAAI,CA2B3F"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { syntaxTree } from "@codemirror/language";
|
|
3
|
+
import { METRIC_SCHEMA_KEYS } from "./metricSchema.js";
|
|
4
|
+
/** Pre-built completion options from the schema key map. */
|
|
5
|
+
const completionOptions = Object.fromEntries(Object.entries(METRIC_SCHEMA_KEYS).map(([parent, keys]) => [
|
|
6
|
+
parent,
|
|
7
|
+
keys.map((label) => ({ label, type: "property" })),
|
|
8
|
+
]));
|
|
9
|
+
/**
|
|
10
|
+
* CodeMirror completion source that suggests allowed YAML property names
|
|
11
|
+
* based on the metric zod schema and cursor position in the Lezer tree.
|
|
12
|
+
*/
|
|
13
|
+
export function metricSchemaCompletions(context) {
|
|
14
|
+
const line = context.state.doc.lineAt(context.pos);
|
|
15
|
+
const before = line.text.slice(0, context.pos - line.from);
|
|
16
|
+
// Only trigger when typing a key name at the start of a line
|
|
17
|
+
const match = before.match(/^(\s*)(\w*)$/);
|
|
18
|
+
if (!match) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
// The metric schema is flat: property keys exist only at the top-level mapping, at zero
|
|
22
|
+
// indentation. An indented cursor is inside a value such as the `tags` sequence, where the
|
|
23
|
+
// top-level key suggestions must not be offered.
|
|
24
|
+
if (match[1].length > 0) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const word = match[2];
|
|
28
|
+
const from = context.pos - word.length;
|
|
29
|
+
const parentKey = getParentKey(context.state, context.pos);
|
|
30
|
+
const options = completionOptions[parentKey];
|
|
31
|
+
if (!options) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return { from, options };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Walk up the Lezer YAML tree from cursor to find which key's BlockMapping
|
|
38
|
+
* we're inside. Returns "" for the top-level mapping.
|
|
39
|
+
*/
|
|
40
|
+
function getParentKey(state, pos) {
|
|
41
|
+
const tree = syntaxTree(state);
|
|
42
|
+
let node = tree.resolveInner(pos, -1).parent;
|
|
43
|
+
// Find the BlockMapping we're inside
|
|
44
|
+
while (node) {
|
|
45
|
+
if (node.name === "BlockMapping") {
|
|
46
|
+
// If this BlockMapping is the value of a Pair, return the Pair's key
|
|
47
|
+
const pair = node.parent;
|
|
48
|
+
if (pair?.name === "Pair") {
|
|
49
|
+
const keyNode = pair.getChild("Key");
|
|
50
|
+
if (keyNode) {
|
|
51
|
+
return state.sliceDoc(keyNode.from, keyNode.to).trim();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
node = node.parent;
|
|
57
|
+
}
|
|
58
|
+
return "";
|
|
59
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { v1 } from "@gooddata/sdk-code-schemas";
|
|
2
|
+
import type { IMeasureMetadataObject, IMeasureMetadataObjectDefinition } from "@gooddata/sdk-model";
|
|
3
|
+
import type { MetricSchema } from "./metricSchema.js";
|
|
4
|
+
/**
|
|
5
|
+
* Canonical AAC metric object with an optional `id`.
|
|
6
|
+
*
|
|
7
|
+
* The `id` is dropped when a metric is created or duplicated so the server derives one; a loaded
|
|
8
|
+
* metric always carries it. Field names otherwise mirror `@gooddata/sdk-code-schemas` `v1.Metric`.
|
|
9
|
+
*/
|
|
10
|
+
export type MetricYaml = Omit<v1.Metric, "id"> & {
|
|
11
|
+
id?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Maps a measure definition (or a loaded measure, which is a superset) to the canonical AAC metric
|
|
15
|
+
* object for YAML serialization.
|
|
16
|
+
*
|
|
17
|
+
* Optional meta fields are omitted when empty so the rendered YAML stays terse, matching the
|
|
18
|
+
* analytics-as-code tooling output; a hidden metric is expressed as `show_in_ai_results: false`.
|
|
19
|
+
* The `id` is omitted when the definition has none (e.g. seeding the create dialog from a duplicate)
|
|
20
|
+
* so the server derives a fresh one; a loaded measure always carries it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function definitionToMetricYaml(definition: IMeasureMetadataObjectDefinition): MetricYaml;
|
|
23
|
+
/**
|
|
24
|
+
* Maps a validated AAC metric object to a backend measure definition.
|
|
25
|
+
*
|
|
26
|
+
* `show_in_ai_results` takes precedence over the deprecated `is_hidden` when both are present.
|
|
27
|
+
*/
|
|
28
|
+
export declare function metricYamlToDefinition(metric: MetricSchema): IMeasureMetadataObjectDefinition;
|
|
29
|
+
/**
|
|
30
|
+
* Overlays the fields the as-code YAML owns onto a base measure, keeping the base's identity and the
|
|
31
|
+
* fields the YAML cannot express (`metricType`, `isHiddenFromKda`, ...).
|
|
32
|
+
*
|
|
33
|
+
* Used wherever YAML edits are committed onto an existing measure: the update flow overlays onto the
|
|
34
|
+
* loaded metric, and duplicating from the edit dialog seeds the copy from the metric being edited.
|
|
35
|
+
* Duplicating from the detail panel instead needs YAML-authoritative identity — see
|
|
36
|
+
* {@link mergeCopiedMetricDefinition}.
|
|
37
|
+
*/
|
|
38
|
+
export declare function overlayMetricYamlFields(base: IMeasureMetadataObject, yamlDefinition: IMeasureMetadataObjectDefinition): IMeasureMetadataObject;
|
|
39
|
+
/**
|
|
40
|
+
* Builds the definition to persist when duplicating a metric, layering the author's YAML edits over
|
|
41
|
+
* the copied source.
|
|
42
|
+
*
|
|
43
|
+
* The source carries fields the YAML cannot express (`metricType`, `isHiddenFromKda`), which survive
|
|
44
|
+
* because only the YAML-owned fields are overlaid on top. Identity stays YAML-authoritative: the
|
|
45
|
+
* parsed `id` wins, and its absence lets the server derive a fresh one, so the copied source's id is
|
|
46
|
+
* intentionally dropped from the base first.
|
|
47
|
+
*/
|
|
48
|
+
export declare function mergeCopiedMetricDefinition(copiedMetric: IMeasureMetadataObjectDefinition, yamlDefinition: IMeasureMetadataObjectDefinition): IMeasureMetadataObjectDefinition;
|
|
49
|
+
//# sourceMappingURL=metricConverter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricConverter.d.ts","sourceRoot":"","sources":["../../src/metric/metricConverter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,KAAK,EAAE,sBAAsB,EAAE,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAEpG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,gCAAgC,GAAG,UAAU,CAW/F;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,GAAG,gCAAgC,CAY7F;AAsCD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACnC,IAAI,EAAE,sBAAsB,EAC5B,cAAc,EAAE,gCAAgC,GACjD,sBAAsB,CAKxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACvC,YAAY,EAAE,gCAAgC,EAC9C,cAAc,EAAE,gCAAgC,GACjD,gCAAgC,CAOlC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
/**
|
|
3
|
+
* Maps a measure definition (or a loaded measure, which is a superset) to the canonical AAC metric
|
|
4
|
+
* object for YAML serialization.
|
|
5
|
+
*
|
|
6
|
+
* Optional meta fields are omitted when empty so the rendered YAML stays terse, matching the
|
|
7
|
+
* analytics-as-code tooling output; a hidden metric is expressed as `show_in_ai_results: false`.
|
|
8
|
+
* The `id` is omitted when the definition has none (e.g. seeding the create dialog from a duplicate)
|
|
9
|
+
* so the server derives a fresh one; a loaded measure always carries it.
|
|
10
|
+
*/
|
|
11
|
+
export function definitionToMetricYaml(definition) {
|
|
12
|
+
return {
|
|
13
|
+
type: "metric",
|
|
14
|
+
...(definition.id === undefined ? {} : { id: definition.id }),
|
|
15
|
+
title: definition.title,
|
|
16
|
+
...(definition.description ? { description: definition.description } : {}),
|
|
17
|
+
...(definition.tags && definition.tags.length > 0 ? { tags: definition.tags } : {}),
|
|
18
|
+
maql: definition.expression,
|
|
19
|
+
...(definition.format ? { format: definition.format } : {}),
|
|
20
|
+
...(definition.isHidden === true ? { show_in_ai_results: false } : {}),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Maps a validated AAC metric object to a backend measure definition.
|
|
25
|
+
*
|
|
26
|
+
* `show_in_ai_results` takes precedence over the deprecated `is_hidden` when both are present.
|
|
27
|
+
*/
|
|
28
|
+
export function metricYamlToDefinition(metric) {
|
|
29
|
+
const isHidden = resolveIsHidden(metric);
|
|
30
|
+
return {
|
|
31
|
+
type: "measure",
|
|
32
|
+
...(metric.id === undefined ? {} : { id: metric.id }),
|
|
33
|
+
title: metric.title ?? "",
|
|
34
|
+
description: metric.description ?? "",
|
|
35
|
+
tags: metric.tags ?? [],
|
|
36
|
+
expression: metric.maql,
|
|
37
|
+
format: metric.format ?? "",
|
|
38
|
+
...(isHidden === undefined ? {} : { isHidden }),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function resolveIsHidden(metric) {
|
|
42
|
+
if (metric.show_in_ai_results !== undefined) {
|
|
43
|
+
return metric.show_in_ai_results === false;
|
|
44
|
+
}
|
|
45
|
+
if (metric.is_hidden !== undefined) {
|
|
46
|
+
return metric.is_hidden;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The fields the as-code metric YAML owns, extracted from a parsed definition and normalized for
|
|
52
|
+
* persistence. Every other field on a measure — `metricType`, `isHiddenFromKda`, and a loaded
|
|
53
|
+
* measure's identity (`ref`, `uri`) — lies outside the YAML projection and is preserved by layering
|
|
54
|
+
* these fields over a base object rather than replacing it.
|
|
55
|
+
*
|
|
56
|
+
* `isHidden` is always returned (normalized to `false` when absent), never omitted: it must
|
|
57
|
+
* overwrite the base so that clearing `show_in_ai_results` unhides a previously hidden metric. Were
|
|
58
|
+
* it omitted when undefined, the base's stale `isHidden` would leak through the overlay.
|
|
59
|
+
*/
|
|
60
|
+
function pickMetricYamlFields(definition) {
|
|
61
|
+
return {
|
|
62
|
+
title: definition.title ?? "",
|
|
63
|
+
description: definition.description ?? "",
|
|
64
|
+
tags: definition.tags ?? [],
|
|
65
|
+
expression: definition.expression,
|
|
66
|
+
format: definition.format ?? "",
|
|
67
|
+
isHidden: definition.isHidden ?? false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Overlays the fields the as-code YAML owns onto a base measure, keeping the base's identity and the
|
|
72
|
+
* fields the YAML cannot express (`metricType`, `isHiddenFromKda`, ...).
|
|
73
|
+
*
|
|
74
|
+
* Used wherever YAML edits are committed onto an existing measure: the update flow overlays onto the
|
|
75
|
+
* loaded metric, and duplicating from the edit dialog seeds the copy from the metric being edited.
|
|
76
|
+
* Duplicating from the detail panel instead needs YAML-authoritative identity — see
|
|
77
|
+
* {@link mergeCopiedMetricDefinition}.
|
|
78
|
+
*/
|
|
79
|
+
export function overlayMetricYamlFields(base, yamlDefinition) {
|
|
80
|
+
return {
|
|
81
|
+
...base,
|
|
82
|
+
...pickMetricYamlFields(yamlDefinition),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Builds the definition to persist when duplicating a metric, layering the author's YAML edits over
|
|
87
|
+
* the copied source.
|
|
88
|
+
*
|
|
89
|
+
* The source carries fields the YAML cannot express (`metricType`, `isHiddenFromKda`), which survive
|
|
90
|
+
* because only the YAML-owned fields are overlaid on top. Identity stays YAML-authoritative: the
|
|
91
|
+
* parsed `id` wins, and its absence lets the server derive a fresh one, so the copied source's id is
|
|
92
|
+
* intentionally dropped from the base first.
|
|
93
|
+
*/
|
|
94
|
+
export function mergeCopiedMetricDefinition(copiedMetric, yamlDefinition) {
|
|
95
|
+
const { id: _copiedId, ...carriedOver } = copiedMetric;
|
|
96
|
+
return {
|
|
97
|
+
...carriedOver,
|
|
98
|
+
...pickMetricYamlFields(yamlDefinition),
|
|
99
|
+
...(yamlDefinition.id === undefined ? {} : { id: yamlDefinition.id }),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IMeasureMetadataObjectDefinition } from "@gooddata/sdk-model";
|
|
2
|
+
export declare function createCopiedMetric(metric: IMeasureMetadataObjectDefinition): IMeasureMetadataObjectDefinition;
|
|
3
|
+
export declare function isDuplicateIdError(error: unknown): boolean;
|
|
4
|
+
//# sourceMappingURL=metricCopy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricCopy.d.ts","sourceRoot":"","sources":["../../src/metric/metricCopy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAK5E,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,gCAAgC,GACzC,gCAAgC,CAkBlC;AAwCD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE1D"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { isUnexpectedResponseError } from "@gooddata/sdk-backend-spi";
|
|
3
|
+
const canonicalUuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4
|
+
const trailingCopySuffixRegex = /^(.*) \((\d+)\)$/;
|
|
5
|
+
export function createCopiedMetric(metric) {
|
|
6
|
+
const title = getCopiedTitle(metric.title);
|
|
7
|
+
const id = shouldOmitCopiedId(metric.id) ? undefined : getCopiedId(title);
|
|
8
|
+
// Whitelist only definition-owned fields; the source may be a loaded measure carrying
|
|
9
|
+
// server-managed fields (uri, ref, timestamps, ...) that must not leak into the definition.
|
|
10
|
+
// metricType and isHiddenFromKda are author-owned semantics, so they carry over to the copy.
|
|
11
|
+
return {
|
|
12
|
+
type: "measure",
|
|
13
|
+
...(id === undefined ? {} : { id }),
|
|
14
|
+
...(title === undefined ? {} : { title }),
|
|
15
|
+
description: metric.description,
|
|
16
|
+
tags: metric.tags,
|
|
17
|
+
expression: metric.expression,
|
|
18
|
+
format: metric.format,
|
|
19
|
+
...(metric.metricType === undefined ? {} : { metricType: metric.metricType }),
|
|
20
|
+
...(metric.isHidden === undefined ? {} : { isHidden: metric.isHidden }),
|
|
21
|
+
...(metric.isHiddenFromKda === undefined ? {} : { isHiddenFromKda: metric.isHiddenFromKda }),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function getCopiedTitle(title) {
|
|
25
|
+
if (title === undefined) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
if (title === "") {
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
const match = title.match(trailingCopySuffixRegex);
|
|
32
|
+
if (match) {
|
|
33
|
+
const [, prefix, suffix] = match;
|
|
34
|
+
return `${prefix} (${Number(suffix) + 1})`;
|
|
35
|
+
}
|
|
36
|
+
return `${title} (2)`;
|
|
37
|
+
}
|
|
38
|
+
// A UUID id was server-generated, so the copy omits its id and lets the server generate a fresh one.
|
|
39
|
+
// A human-authored id (e.g. "revenue.total") is instead re-derived from the copied title to stay
|
|
40
|
+
// readable; a resulting collision is handled by the create 409 retry-without-id.
|
|
41
|
+
function shouldOmitCopiedId(id) {
|
|
42
|
+
return id === undefined || isCanonicalUuid(id);
|
|
43
|
+
}
|
|
44
|
+
function getCopiedId(title) {
|
|
45
|
+
if (!title) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const normalizedTitle = title
|
|
49
|
+
.normalize("NFKD")
|
|
50
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
51
|
+
.replace(/\W/g, "_")
|
|
52
|
+
.toLowerCase();
|
|
53
|
+
return normalizedTitle || undefined;
|
|
54
|
+
}
|
|
55
|
+
function isCanonicalUuid(value) {
|
|
56
|
+
return canonicalUuidRegex.test(value);
|
|
57
|
+
}
|
|
58
|
+
export function isDuplicateIdError(error) {
|
|
59
|
+
return isUnexpectedResponseError(error) && error.httpStatus === 409;
|
|
60
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IAnalyticalBackend, IMeasureReferencing } from "@gooddata/sdk-backend-spi";
|
|
2
|
+
import { type IMeasureMetadataObject, type IMeasureMetadataObjectDefinition } from "@gooddata/sdk-model";
|
|
3
|
+
import type { ICatalogItemMeasure, ICatalogItemRef } from "../catalogItem/types.js";
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export interface IMetricMutationPort {
|
|
8
|
+
create(definition: IMeasureMetadataObjectDefinition): Promise<ICatalogItemMeasure>;
|
|
9
|
+
update(existing: IMeasureMetadataObject, definition: IMeasureMetadataObjectDefinition): Promise<ICatalogItemMeasure>;
|
|
10
|
+
delete(ref: ICatalogItemRef): Promise<void>;
|
|
11
|
+
load(ref: ICatalogItemRef): Promise<IMeasureMetadataObject>;
|
|
12
|
+
getReferencingObjects(ref: ICatalogItemRef): Promise<IMeasureReferencing>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export declare function createMetricMutationAdapter(backend: IAnalyticalBackend, workspace: string): IMetricMutationPort;
|
|
18
|
+
//# sourceMappingURL=metricMutationPort.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricMutationPort.d.ts","sourceRoot":"","sources":["../../src/metric/metricMutationPort.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,EACH,KAAK,sBAAsB,EAC3B,KAAK,gCAAgC,EAExC,MAAM,qBAAqB,CAAC;AAU7B,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAIpF;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,MAAM,CAAC,UAAU,EAAE,gCAAgC,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnF,MAAM,CACF,QAAQ,EAAE,sBAAsB,EAChC,UAAU,EAAE,gCAAgC,GAC7C,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAChC,MAAM,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC5D,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC7E;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACvC,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,GAClB,mBAAmB,CA4BrB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { idRef, } from "@gooddata/sdk-model";
|
|
3
|
+
import { convertMeasureToCatalogItem } from "../catalogItem/converter.js";
|
|
4
|
+
import { createMeasureCatalogItem, deleteMeasureCatalogItem, getMeasureCatalogItem, getMeasureReferencingObjectsCatalogItem, updateMeasureCatalogItem, } from "../catalogItem/query.js";
|
|
5
|
+
import { overlayMetricYamlFields } from "./metricConverter.js";
|
|
6
|
+
/**
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export function createMetricMutationAdapter(backend, workspace) {
|
|
10
|
+
return {
|
|
11
|
+
async create(definition) {
|
|
12
|
+
const savedMeasure = await createMeasureCatalogItem(backend, workspace, definition);
|
|
13
|
+
return convertMeasureToCatalogItem(savedMeasure);
|
|
14
|
+
},
|
|
15
|
+
async update(existing, definition) {
|
|
16
|
+
const savedMeasure = await updateMeasureCatalogItem(backend, workspace, mergeMeasureDefinition(existing, definition));
|
|
17
|
+
return convertMeasureToCatalogItem(savedMeasure);
|
|
18
|
+
},
|
|
19
|
+
async delete(ref) {
|
|
20
|
+
await deleteMeasureCatalogItem(backend, workspace, idRef(ref.identifier, "measure"));
|
|
21
|
+
},
|
|
22
|
+
async load(ref) {
|
|
23
|
+
return getMeasureCatalogItem(backend, workspace, idRef(ref.identifier, "measure"));
|
|
24
|
+
},
|
|
25
|
+
async getReferencingObjects(ref) {
|
|
26
|
+
return getMeasureReferencingObjectsCatalogItem(backend, workspace, idRef(ref.identifier, "measure"));
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Overlays the fields the as-code YAML owns onto the loaded metric.
|
|
32
|
+
*
|
|
33
|
+
* The measures update endpoint is a full replace, so fields absent from the YAML
|
|
34
|
+
* (`metricType`, `isHiddenFromKda`, identity, ...) must be carried over from the loaded metric to
|
|
35
|
+
* avoid wiping them; `overlayMetricYamlFields` preserves them via the base spread.
|
|
36
|
+
*/
|
|
37
|
+
function mergeMeasureDefinition(existing, definition) {
|
|
38
|
+
return overlayMetricYamlFields(existing, definition);
|
|
39
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as z from "zod/mini";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for the analytics-as-code (AAC) metric YAML shape.
|
|
4
|
+
*
|
|
5
|
+
* Field names and structure mirror the canonical AAC metric definition
|
|
6
|
+
* (`@gooddata/sdk-code-schemas` `v1.Metric`) so YAML authored here is interchangeable
|
|
7
|
+
* with metrics produced by the analytics-as-code tooling. `id` is optional because the
|
|
8
|
+
* server derives one from the title when it is omitted on create.
|
|
9
|
+
*/
|
|
10
|
+
export declare const metricSchema: z.ZodMiniObject<{
|
|
11
|
+
type: z.ZodMiniDefault<z.ZodMiniLiteral<"metric">>;
|
|
12
|
+
id: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
13
|
+
title: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
14
|
+
description: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
15
|
+
tags: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
|
|
16
|
+
maql: z.ZodMiniString<string>;
|
|
17
|
+
format: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
18
|
+
show_in_ai_results: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
19
|
+
is_hidden: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
export type MetricSchemaInput = z.input<typeof metricSchema>;
|
|
22
|
+
export type MetricSchema = z.infer<typeof metricSchema>;
|
|
23
|
+
/**
|
|
24
|
+
* Allowed YAML property names keyed by parent mapping, used to power editor autocompletion.
|
|
25
|
+
* The metric schema is flat, so the only entry is the top-level mapping (keyed by `""`), whose
|
|
26
|
+
* value is the object's property names read from zod's public `shape`.
|
|
27
|
+
*/
|
|
28
|
+
export declare const METRIC_SCHEMA_KEYS: Record<string, string[]>;
|
|
29
|
+
//# sourceMappingURL=metricSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricSchema.d.ts","sourceRoot":"","sources":["../../src/metric/metricSchema.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAE9B;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;kBAWvB,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAC7D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAEvD,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import * as z from "zod/mini";
|
|
3
|
+
/**
|
|
4
|
+
* Zod schema for the analytics-as-code (AAC) metric YAML shape.
|
|
5
|
+
*
|
|
6
|
+
* Field names and structure mirror the canonical AAC metric definition
|
|
7
|
+
* (`@gooddata/sdk-code-schemas` `v1.Metric`) so YAML authored here is interchangeable
|
|
8
|
+
* with metrics produced by the analytics-as-code tooling. `id` is optional because the
|
|
9
|
+
* server derives one from the title when it is omitted on create.
|
|
10
|
+
*/
|
|
11
|
+
export const metricSchema = z.strictObject({
|
|
12
|
+
type: z._default(z.literal("metric"), "metric"),
|
|
13
|
+
id: z.optional(z.string()),
|
|
14
|
+
title: z.optional(z.string()),
|
|
15
|
+
description: z.optional(z.string()),
|
|
16
|
+
tags: z.optional(z.array(z.string())),
|
|
17
|
+
maql: z.string(),
|
|
18
|
+
format: z.optional(z.string()),
|
|
19
|
+
show_in_ai_results: z.optional(z.boolean()),
|
|
20
|
+
// Deprecated predecessor of `show_in_ai_results`; accepted so AAC exports still parse.
|
|
21
|
+
is_hidden: z.optional(z.boolean()),
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Allowed YAML property names keyed by parent mapping, used to power editor autocompletion.
|
|
25
|
+
* The metric schema is flat, so the only entry is the top-level mapping (keyed by `""`), whose
|
|
26
|
+
* value is the object's property names read from zod's public `shape`.
|
|
27
|
+
*/
|
|
28
|
+
export const METRIC_SCHEMA_KEYS = {
|
|
29
|
+
"": Object.keys(metricSchema.shape),
|
|
30
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MetricYaml } from "./metricConverter.js";
|
|
2
|
+
/**
|
|
3
|
+
* Serializes a canonical AAC metric object to YAML.
|
|
4
|
+
*
|
|
5
|
+
* A blank line is placed before `title` and `maql` to visually group identity, metadata,
|
|
6
|
+
* and definition, matching the analytics-as-code tooling layout. The `id` line is omitted when
|
|
7
|
+
* the metric has none so the server derives one on create.
|
|
8
|
+
*/
|
|
9
|
+
export declare function serializeMetricToYaml(metric: MetricYaml): string;
|
|
10
|
+
//# sourceMappingURL=metricSerialization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricSerialization.d.ts","sourceRoot":"","sources":["../../src/metric/metricSerialization.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAsBhE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { Document, Pair, Scalar } from "yaml";
|
|
3
|
+
/**
|
|
4
|
+
* Serializes a canonical AAC metric object to YAML.
|
|
5
|
+
*
|
|
6
|
+
* A blank line is placed before `title` and `maql` to visually group identity, metadata,
|
|
7
|
+
* and definition, matching the analytics-as-code tooling layout. The `id` line is omitted when
|
|
8
|
+
* the metric has none so the server derives one on create.
|
|
9
|
+
*/
|
|
10
|
+
export function serializeMetricToYaml(metric) {
|
|
11
|
+
const doc = new Document(metric.id === undefined ? { type: "metric" } : { type: "metric", id: metric.id });
|
|
12
|
+
doc.add(spacedPair("title", metric.title ?? ""));
|
|
13
|
+
if (metric.description) {
|
|
14
|
+
doc.add(doc.createPair("description", metric.description));
|
|
15
|
+
}
|
|
16
|
+
if (metric.tags && metric.tags.length > 0) {
|
|
17
|
+
doc.add(doc.createPair("tags", metric.tags));
|
|
18
|
+
}
|
|
19
|
+
doc.add(spacedPair("maql", metric.maql ?? ""));
|
|
20
|
+
if (metric.format) {
|
|
21
|
+
doc.add(doc.createPair("format", metric.format));
|
|
22
|
+
}
|
|
23
|
+
if (metric.show_in_ai_results === false) {
|
|
24
|
+
doc.add(doc.createPair("show_in_ai_results", false));
|
|
25
|
+
}
|
|
26
|
+
return doc.toString({ lineWidth: 0 }).trimEnd();
|
|
27
|
+
}
|
|
28
|
+
/** Builds a map entry preceded by a blank line. */
|
|
29
|
+
function spacedPair(key, value) {
|
|
30
|
+
const keyScalar = new Scalar(key);
|
|
31
|
+
keyScalar.spaceBefore = true;
|
|
32
|
+
return new Pair(keyScalar, value);
|
|
33
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IMeasureMetadataObjectDefinition } from "@gooddata/sdk-model";
|
|
2
|
+
export type MetricValidationResult = {
|
|
3
|
+
isValid: true;
|
|
4
|
+
measure: IMeasureMetadataObjectDefinition;
|
|
5
|
+
} | {
|
|
6
|
+
isValid: false;
|
|
7
|
+
errorCode: MetricValidationErrorCode;
|
|
8
|
+
};
|
|
9
|
+
export type MetricValidationErrorCode = "empty" | "syntax" | "invalidStructure" | "idImmutable" | "missingMaql" | "invalidTags";
|
|
10
|
+
type ValidateMetricYamlOptions = {
|
|
11
|
+
fixedIdentifier?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function validateMetricYaml(value: string, options?: ValidateMetricYamlOptions): MetricValidationResult;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=metricValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricValidation.d.ts","sourceRoot":"","sources":["../../src/metric/metricValidation.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAK5E,MAAM,MAAM,sBAAsB,GAC5B;IACI,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,gCAAgC,CAAC;CAC7C,GACD;IACI,OAAO,EAAE,KAAK,CAAC;IACf,SAAS,EAAE,yBAAyB,CAAC;CACxC,CAAC;AAER,MAAM,MAAM,yBAAyB,GAC/B,OAAO,GACP,QAAQ,GACR,kBAAkB,GAClB,aAAa,GACb,aAAa,GACb,aAAa,CAAC;AAEpB,KAAK,yBAAyB,GAAG;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,yBAA8B,GACxC,sBAAsB,CAyBxB"}
|