@gooddata/sdk-code-convertors 11.36.0-alpha.3 → 11.36.0-alpha.6
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/from/declarativeDashboardToYaml.js +26 -4
- package/esm/sdk-code-convertors.d.ts +5 -3
- package/esm/to/yamlDashboardToDeclarative.d.ts +1 -1
- package/esm/to/yamlDashboardToDeclarative.js +95 -24
- package/esm/types.d.ts +2 -1
- package/esm/utils/errors.d.ts +2 -1
- package/esm/utils/errors.js +3 -0
- package/package.json +7 -7
|
@@ -25,6 +25,20 @@ export function declarativeDashboardToYaml(entities, dashboard, filterContexts,
|
|
|
25
25
|
});
|
|
26
26
|
// Add intro comment to the document
|
|
27
27
|
doc.commentBefore = DASHBOARD_COMMENT;
|
|
28
|
+
// Infer the YAML model version from the declarative structure:
|
|
29
|
+
// - V3 (tabs are the sole source of content) when tabs are present and no root-level
|
|
30
|
+
// layout / filter configs / parameters exist.
|
|
31
|
+
// - V2 (legacy) otherwise — covers both root-only dashboards and tab dashboards that
|
|
32
|
+
// still carry mirrored root content for backward compatibility.
|
|
33
|
+
const hasContentTabs = (content.tabs?.length ?? 0) > 0;
|
|
34
|
+
const hasRootContent = Boolean(content.layout ||
|
|
35
|
+
content.dateFilterConfig ||
|
|
36
|
+
(content.dateFilterConfigs && content.dateFilterConfigs.length > 0) ||
|
|
37
|
+
(content.attributeFilterConfigs && content.attributeFilterConfigs.length > 0) ||
|
|
38
|
+
(content.measureValueFilterConfigs && content.measureValueFilterConfigs.length > 0) ||
|
|
39
|
+
(content.parameters && content.parameters.length > 0));
|
|
40
|
+
const yamlVersion = hasContentTabs && !hasRootContent ? "3" : "2";
|
|
41
|
+
doc.add(entryWithSpace("version", yamlVersion));
|
|
28
42
|
// Add optional meta fields
|
|
29
43
|
fillOptionalMetaFields(doc, dashboard);
|
|
30
44
|
//settings
|
|
@@ -40,10 +54,6 @@ export function declarativeDashboardToYaml(entities, dashboard, filterContexts,
|
|
|
40
54
|
if (content.disableFilterViews) {
|
|
41
55
|
doc.add(entryWithSpace("filter_views", false));
|
|
42
56
|
}
|
|
43
|
-
// Add enable_section_headers
|
|
44
|
-
if (content.layout?.configuration?.sections?.enableHeader) {
|
|
45
|
-
doc.add(entryWithSpace("enable_section_headers", content.layout?.configuration?.sections?.enableHeader));
|
|
46
|
-
}
|
|
47
57
|
// Add dashboard tabs
|
|
48
58
|
// Note: If there's only 1 tab without a title (implicit first tab visible in edit mode),
|
|
49
59
|
// treat as "no tabs" and use sections format to preserve previous YAML structure
|
|
@@ -51,12 +61,20 @@ export function declarativeDashboardToYaml(entities, dashboard, filterContexts,
|
|
|
51
61
|
const hasSingleTabWithTitle = content.tabs?.length === 1 && content.tabs[0].title;
|
|
52
62
|
const shouldUseTabs = hasMultipleTabs || hasSingleTabWithTitle;
|
|
53
63
|
if (content.tabs && shouldUseTabs) {
|
|
64
|
+
// Add enable_section_headers
|
|
65
|
+
if (content.tabs[0].layout?.configuration?.sections?.enableHeader) {
|
|
66
|
+
doc.add(entryWithSpace("enable_section_headers", content.tabs[0].layout?.configuration?.sections?.enableHeader));
|
|
67
|
+
}
|
|
54
68
|
const tabs = declarativeTabsToYaml(content.tabs, filterContexts, entities, updateErrorContext(errorContext, { path: ["tabs"] }));
|
|
55
69
|
if (tabs) {
|
|
56
70
|
doc.add(entryWithSpace("tabs", tabs));
|
|
57
71
|
}
|
|
58
72
|
}
|
|
59
73
|
else if (content.tabs?.length === 1) {
|
|
74
|
+
// Add enable_section_headers
|
|
75
|
+
if (content.tabs[0].layout?.configuration?.sections?.enableHeader) {
|
|
76
|
+
doc.add(entryWithSpace("enable_section_headers", content.tabs[0].layout?.configuration?.sections?.enableHeader));
|
|
77
|
+
}
|
|
60
78
|
// Dashboard with implicit single tab - create root yaml structure like without tabs
|
|
61
79
|
// but read all metadata from a single tab
|
|
62
80
|
const tab = content.tabs[0];
|
|
@@ -76,6 +94,10 @@ export function declarativeDashboardToYaml(entities, dashboard, filterContexts,
|
|
|
76
94
|
}
|
|
77
95
|
else {
|
|
78
96
|
// Dashboard without tabs - add sections and filters
|
|
97
|
+
// Add enable_section_headers
|
|
98
|
+
if (content.layout?.configuration?.sections?.enableHeader) {
|
|
99
|
+
doc.add(entryWithSpace("enable_section_headers", content.layout?.configuration?.sections?.enableHeader));
|
|
100
|
+
}
|
|
79
101
|
const section = declarativeSectionsToYaml(content.layout, entities, updateErrorContext(errorContext, { path: ["layout"] }));
|
|
80
102
|
if (section) {
|
|
81
103
|
doc.add(entryWithSpace("sections", section));
|
|
@@ -923,7 +923,8 @@ export declare enum CoreErrorCode {
|
|
|
923
923
|
OnlyOneAttributeItemAllowed = "core.onlyOneAttributeItemAllowed",
|
|
924
924
|
MultipleCommonDateFilters = "core.multipleCommonDateFilters",
|
|
925
925
|
DuplicateFilterLocalIdentifier = "core.duplicateFilterLocalIdentifier",
|
|
926
|
-
DuplicateTabIdentifier = "core.duplicateTabIdentifier"
|
|
926
|
+
DuplicateTabIdentifier = "core.duplicateTabIdentifier",
|
|
927
|
+
TabsAndRootContentMutuallyExclusive = "core.tabsAndRootContentMutuallyExclusive"
|
|
927
928
|
}
|
|
928
929
|
|
|
929
930
|
/**
|
|
@@ -1208,7 +1209,8 @@ export declare type ExportEntities = Array<{
|
|
|
1208
1209
|
data: Dataset | DateDataset | Metric | Visualisation | Dashboard | Plugin_2 | AttributeHierarchy;
|
|
1209
1210
|
declarative?: DeclarativeDataset | DeclarativeDateDataset | DeclarativeMetric | DeclarativeVisualizationObject | DeclarativeDashboardPlugin | DeclarativeAttributeHierarchy | {
|
|
1210
1211
|
dashboard: DeclarativeAnalyticalDashboard;
|
|
1211
|
-
filterContext
|
|
1212
|
+
filterContext?: DeclarativeFilterContext;
|
|
1213
|
+
tabFilterContexts?: DeclarativeFilterContext[];
|
|
1212
1214
|
};
|
|
1213
1215
|
}>;
|
|
1214
1216
|
|
|
@@ -2783,7 +2785,7 @@ export declare function yamlBucketsToDeclarative(entities: ExportEntities, input
|
|
|
2783
2785
|
/** @public */
|
|
2784
2786
|
export declare function yamlDashboardToDeclarative(entities: ExportEntities, input: Dashboard): {
|
|
2785
2787
|
dashboard: DeclarativeAnalyticalDashboard;
|
|
2786
|
-
filterContext
|
|
2788
|
+
filterContext?: DeclarativeFilterContext;
|
|
2787
2789
|
tabFilterContexts?: DeclarativeFilterContext[];
|
|
2788
2790
|
};
|
|
2789
2791
|
|
|
@@ -20,7 +20,7 @@ export type EmptyValueHandling = NonNullable<IDashboardDateFilter["dateFilter"][
|
|
|
20
20
|
/** @public */
|
|
21
21
|
export declare function yamlDashboardToDeclarative(entities: ExportEntities, input: Dashboard): {
|
|
22
22
|
dashboard: DeclarativeAnalyticalDashboard;
|
|
23
|
-
filterContext
|
|
23
|
+
filterContext?: DeclarativeFilterContext;
|
|
24
24
|
tabFilterContexts?: DeclarativeFilterContext[];
|
|
25
25
|
};
|
|
26
26
|
/** @internal */
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { v4 as uuidV4 } from "uuid";
|
|
3
3
|
import { VisualisationsTypes } from "../conts.js";
|
|
4
4
|
import { parseUrlTarget } from "../utils/customUrl.js";
|
|
5
|
+
import { CoreErrorCode, newError } from "../utils/errors.js";
|
|
5
6
|
import { yamlConditionToMatch } from "../utils/filterUtils.js";
|
|
6
7
|
import { convertGranularity } from "../utils/granularityUtils.js";
|
|
7
8
|
import { toDeclarativePermissions } from "../utils/permissionUtils.js";
|
|
@@ -52,11 +53,11 @@ function yamlTabsToDeclarative(entities, tabs, enableSectionHeaders) {
|
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
/**
|
|
55
|
-
*
|
|
56
|
+
* V2 (legacy): tabs present in YAML — mirror tabs[0] content into root-level properties
|
|
57
|
+
* for backward compatibility with consumers that read root layout/filters.
|
|
56
58
|
*/
|
|
57
|
-
function
|
|
59
|
+
function processDashboardWithTabsV2(entities, input) {
|
|
58
60
|
const tabsResult = yamlTabsToDeclarative(entities, input.tabs, input.enable_section_headers);
|
|
59
|
-
// Use first tab as default for backwards compatibility
|
|
60
61
|
const defaultTabIndex = 0;
|
|
61
62
|
const defaultTab = tabsResult.tabs[defaultTabIndex];
|
|
62
63
|
return {
|
|
@@ -71,10 +72,12 @@ function processDashboardWithTabs(entities, input) {
|
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
74
|
/**
|
|
74
|
-
*
|
|
75
|
+
* V2 (legacy): no tabs in YAML — wrap root content in a synthetic single tab AND keep
|
|
76
|
+
* the same content at root level. Produces the duplicated declarative shape the older
|
|
77
|
+
* SDK readers expect.
|
|
75
78
|
*/
|
|
76
|
-
function
|
|
77
|
-
const { filterContext, tabFilterContexts: _tabFilterContexts, ...rest } =
|
|
79
|
+
function processDashboardWithoutTabsV2(entities, input) {
|
|
80
|
+
const { filterContext, tabFilterContexts: _tabFilterContexts, ...rest } = processDashboardWithTabsV2(entities, {
|
|
78
81
|
...input,
|
|
79
82
|
tabs: [
|
|
80
83
|
{
|
|
@@ -100,27 +103,95 @@ function processDashboardWithoutTabs(entities, input) {
|
|
|
100
103
|
tabFilterContexts: [dashboardFilterContext],
|
|
101
104
|
};
|
|
102
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* V3 (clean): tabs present in YAML — tabs are the sole source of layout and filter content.
|
|
108
|
+
* No root-level mirroring. Root filter context reference is left empty; per-tab filter
|
|
109
|
+
* context refs are the only source.
|
|
110
|
+
*/
|
|
111
|
+
function processDashboardWithTabsV3(entities, input) {
|
|
112
|
+
const tabsResult = yamlTabsToDeclarative(entities, input.tabs, input.enable_section_headers);
|
|
113
|
+
return {
|
|
114
|
+
layout: undefined,
|
|
115
|
+
filterContext: undefined,
|
|
116
|
+
dateFilterConfig: undefined,
|
|
117
|
+
dateFilterConfigs: undefined,
|
|
118
|
+
attributeFilterConfigs: undefined,
|
|
119
|
+
measureValueFilterConfigs: undefined,
|
|
120
|
+
tabs: tabsResult.tabs,
|
|
121
|
+
tabFilterContexts: tabsResult.filterContexts,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* V3 (clean): no tabs in YAML — wrap root content in a synthetic single tab. No content
|
|
126
|
+
* is left at root level in the resulting declarative model.
|
|
127
|
+
*/
|
|
128
|
+
function processDashboardWithoutTabsV3(entities, input) {
|
|
129
|
+
const tabsResult = yamlTabsToDeclarative(entities, [
|
|
130
|
+
{
|
|
131
|
+
id: "defaultTabId",
|
|
132
|
+
title: "",
|
|
133
|
+
filters: input.filters,
|
|
134
|
+
sections: input.sections ?? [],
|
|
135
|
+
},
|
|
136
|
+
], input.enable_section_headers);
|
|
137
|
+
return {
|
|
138
|
+
layout: undefined,
|
|
139
|
+
filterContext: undefined,
|
|
140
|
+
dateFilterConfig: undefined,
|
|
141
|
+
dateFilterConfigs: undefined,
|
|
142
|
+
attributeFilterConfigs: undefined,
|
|
143
|
+
measureValueFilterConfigs: undefined,
|
|
144
|
+
tabs: tabsResult.tabs,
|
|
145
|
+
tabFilterContexts: tabsResult.filterContexts,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
103
148
|
/** @public */
|
|
104
149
|
export function yamlDashboardToDeclarative(entities, input) {
|
|
105
|
-
|
|
106
|
-
const
|
|
150
|
+
const hasTabs = Boolean(input.tabs && input.tabs.length > 0);
|
|
151
|
+
const hasRootContent = Boolean((input.sections && input.sections.length > 0) ||
|
|
152
|
+
(input.filters && Object.keys(input.filters).length > 0));
|
|
153
|
+
if (hasTabs && hasRootContent) {
|
|
154
|
+
throw newError(CoreErrorCode.TabsAndRootContentMutuallyExclusive, [input.id]);
|
|
155
|
+
}
|
|
156
|
+
const yamlVersion = input.version ?? "2";
|
|
157
|
+
// Process dashboard based on YAML version and whether it has tabs.
|
|
158
|
+
// V2 mirrors content into root for backward compat; V3 keeps content only inside tabs.
|
|
159
|
+
const { layout, filterContext, dateFilterConfig, dateFilterConfigs, attributeFilterConfigs, measureValueFilterConfigs, tabs, tabFilterContexts, } = yamlVersion === "3"
|
|
160
|
+
? hasTabs
|
|
161
|
+
? processDashboardWithTabsV3(entities, input)
|
|
162
|
+
: processDashboardWithoutTabsV3(entities, input)
|
|
163
|
+
: hasTabs
|
|
164
|
+
? processDashboardWithTabsV2(entities, input)
|
|
165
|
+
: processDashboardWithoutTabsV2(entities, input);
|
|
107
166
|
const plugins = yamlPluginsToDeclarative(input.plugins);
|
|
108
167
|
const [, permissions] = toDeclarativePermissions(input.permissions);
|
|
109
|
-
const content =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
168
|
+
const content = yamlVersion === "3"
|
|
169
|
+
? {
|
|
170
|
+
version: "2",
|
|
171
|
+
plugins,
|
|
172
|
+
tabs,
|
|
173
|
+
...(input.cross_filtering === false ? { disableCrossFiltering: true } : {}),
|
|
174
|
+
...(input.user_filters_save === false ? { disableUserFilterSave: true } : {}),
|
|
175
|
+
...(input.user_filters_reset === false ? { disableUserFilterReset: true } : {}),
|
|
176
|
+
...(input.filter_views === false ? { disableFilterViews: true } : {}),
|
|
177
|
+
}
|
|
178
|
+
: {
|
|
179
|
+
version: "2",
|
|
180
|
+
layout,
|
|
181
|
+
plugins,
|
|
182
|
+
attributeFilterConfigs,
|
|
183
|
+
measureValueFilterConfigs,
|
|
184
|
+
dateFilterConfig,
|
|
185
|
+
dateFilterConfigs,
|
|
186
|
+
...(tabs ? { tabs } : {}),
|
|
187
|
+
...(input.cross_filtering === false ? { disableCrossFiltering: true } : {}),
|
|
188
|
+
...(input.user_filters_save === false ? { disableUserFilterSave: true } : {}),
|
|
189
|
+
...(input.user_filters_reset === false ? { disableUserFilterReset: true } : {}),
|
|
190
|
+
...(input.filter_views === false ? { disableFilterViews: true } : {}),
|
|
191
|
+
filterContextRef: filterContext
|
|
192
|
+
? createIdentifier(filterContext.id, { forceType: "filterContext" })
|
|
193
|
+
: undefined,
|
|
194
|
+
};
|
|
124
195
|
const dashboard = {
|
|
125
196
|
id: input.id,
|
|
126
197
|
title: input.title ?? convertIdToTitle(input.id),
|
|
@@ -504,7 +575,7 @@ function yamlIgnoredFilterToDeclarative(input) {
|
|
|
504
575
|
if (ref?.type === "metric") {
|
|
505
576
|
return {
|
|
506
577
|
type: "measureValueFilterReference",
|
|
507
|
-
measure: createIdentifier(input),
|
|
578
|
+
measure: createIdentifier(input, { forceMetric: true }),
|
|
508
579
|
};
|
|
509
580
|
}
|
|
510
581
|
return null;
|
package/esm/types.d.ts
CHANGED
|
@@ -16,7 +16,8 @@ export type ExportEntities = Array<{
|
|
|
16
16
|
data: Dataset | DateDataset | Metric | Visualisation | Dashboard | Plugin | AttributeHierarchy;
|
|
17
17
|
declarative?: DeclarativeDataset | DeclarativeDateDataset | DeclarativeMetric | DeclarativeVisualizationObject | DeclarativeDashboardPlugin | DeclarativeAttributeHierarchy | {
|
|
18
18
|
dashboard: DeclarativeAnalyticalDashboard;
|
|
19
|
-
filterContext
|
|
19
|
+
filterContext?: DeclarativeFilterContext;
|
|
20
|
+
tabFilterContexts?: DeclarativeFilterContext[];
|
|
20
21
|
};
|
|
21
22
|
}>;
|
|
22
23
|
/** @public */
|
package/esm/utils/errors.d.ts
CHANGED
|
@@ -13,7 +13,8 @@ export declare enum CoreErrorCode {
|
|
|
13
13
|
OnlyOneAttributeItemAllowed = "core.onlyOneAttributeItemAllowed",
|
|
14
14
|
MultipleCommonDateFilters = "core.multipleCommonDateFilters",
|
|
15
15
|
DuplicateFilterLocalIdentifier = "core.duplicateFilterLocalIdentifier",
|
|
16
|
-
DuplicateTabIdentifier = "core.duplicateTabIdentifier"
|
|
16
|
+
DuplicateTabIdentifier = "core.duplicateTabIdentifier",
|
|
17
|
+
TabsAndRootContentMutuallyExclusive = "core.tabsAndRootContentMutuallyExclusive"
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
19
20
|
* @public
|
package/esm/utils/errors.js
CHANGED
|
@@ -18,6 +18,7 @@ var CoreErrorCode;
|
|
|
18
18
|
CoreErrorCode["MultipleCommonDateFilters"] = "core.multipleCommonDateFilters";
|
|
19
19
|
CoreErrorCode["DuplicateFilterLocalIdentifier"] = "core.duplicateFilterLocalIdentifier";
|
|
20
20
|
CoreErrorCode["DuplicateTabIdentifier"] = "core.duplicateTabIdentifier";
|
|
21
|
+
CoreErrorCode["TabsAndRootContentMutuallyExclusive"] = "core.tabsAndRootContentMutuallyExclusive";
|
|
21
22
|
})(CoreErrorCode || (CoreErrorCode = {}));
|
|
22
23
|
/**
|
|
23
24
|
* @public
|
|
@@ -35,6 +36,7 @@ export const CoreErrorMessages = {
|
|
|
35
36
|
[CoreErrorCode.MultipleCommonDateFilters]: `Multiple usage of common date filters in dashboard. Define exactly one date filter that has no date dataset defined.`,
|
|
36
37
|
[CoreErrorCode.DuplicateFilterLocalIdentifier]: `Duplicate filter local identifier "{0}". Each filter must have a unique local identifier across a dashboard tab, including filters in groups.`,
|
|
37
38
|
[CoreErrorCode.DuplicateTabIdentifier]: `Duplicate tab identifier "{0}". Each tab must have a unique identifier within the dashboard.`,
|
|
39
|
+
[CoreErrorCode.TabsAndRootContentMutuallyExclusive]: `Dashboard "{0}" defines both "tabs" and root-level "sections" or "filters". These are mutually exclusive — keep all content inside tabs or at the root, not both.`,
|
|
38
40
|
};
|
|
39
41
|
/**
|
|
40
42
|
* @public
|
|
@@ -52,6 +54,7 @@ export const CoreErrorTypes = {
|
|
|
52
54
|
[CoreErrorCode.MultipleCommonDateFilters]: "MultipleCommonDateFilters",
|
|
53
55
|
[CoreErrorCode.DuplicateFilterLocalIdentifier]: "DuplicateFilterLocalIdentifier",
|
|
54
56
|
[CoreErrorCode.DuplicateTabIdentifier]: "DuplicateTabIdentifier",
|
|
57
|
+
[CoreErrorCode.TabsAndRootContentMutuallyExclusive]: "TabsAndRootContentMutuallyExclusive",
|
|
55
58
|
};
|
|
56
59
|
/**
|
|
57
60
|
* Update error context with new values
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddata/sdk-code-convertors",
|
|
3
|
-
"version": "11.36.0-alpha.
|
|
3
|
+
"version": "11.36.0-alpha.6",
|
|
4
4
|
"description": "GoodData AAC declarative converters",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "GoodData",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"uuid": "11.1.0",
|
|
28
|
-
"yaml": "
|
|
29
|
-
"@gooddata/api-client-tiger": "11.36.0-alpha.
|
|
30
|
-
"@gooddata/sdk-
|
|
31
|
-
"@gooddata/sdk-
|
|
28
|
+
"yaml": "2.8.3",
|
|
29
|
+
"@gooddata/api-client-tiger": "11.36.0-alpha.6",
|
|
30
|
+
"@gooddata/sdk-model": "11.36.0-alpha.6",
|
|
31
|
+
"@gooddata/sdk-code-schemas": "11.36.0-alpha.6"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@microsoft/api-extractor": "^7.55.2",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"oxlint-tsgolint": "0.11.4",
|
|
51
51
|
"typescript": "5.9.3",
|
|
52
52
|
"vitest": "4.1.0",
|
|
53
|
-
"@gooddata/
|
|
54
|
-
"@gooddata/
|
|
53
|
+
"@gooddata/oxlint-config": "11.36.0-alpha.6",
|
|
54
|
+
"@gooddata/eslint-config": "11.36.0-alpha.6"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"_phase:build": "npm run build",
|