@gooddata/sdk-ui-catalog 11.50.0-alpha.2 → 11.50.0-alpha.3

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 (105) hide show
  1. package/esm/AnalyticsCatalogDetail.d.ts.map +1 -1
  2. package/esm/AnalyticsCatalogDetail.js +1 -3
  3. package/esm/asCode/AsCodeCreateDialog.d.ts +3 -7
  4. package/esm/asCode/AsCodeCreateDialog.d.ts.map +1 -1
  5. package/esm/asCode/AsCodeCreateDialog.js +13 -23
  6. package/esm/asCode/AsCodeDeleteDialog.d.ts +1 -6
  7. package/esm/asCode/AsCodeDeleteDialog.d.ts.map +1 -1
  8. package/esm/asCode/AsCodeDeleteDialog.js +11 -20
  9. package/esm/asCode/AsCodeDetailActions.d.ts +2 -11
  10. package/esm/asCode/AsCodeDetailActions.d.ts.map +1 -1
  11. package/esm/asCode/AsCodeDetailActions.js +16 -22
  12. package/esm/asCode/AsCodeDialog.d.ts +12 -17
  13. package/esm/asCode/AsCodeDialog.d.ts.map +1 -1
  14. package/esm/asCode/AsCodeDialog.js +59 -27
  15. package/esm/asCode/AsCodeEditDialog.d.ts +3 -8
  16. package/esm/asCode/AsCodeEditDialog.d.ts.map +1 -1
  17. package/esm/asCode/AsCodeEditDialog.js +4 -21
  18. package/esm/asCode/descriptor.d.ts +78 -104
  19. package/esm/asCode/descriptor.d.ts.map +1 -1
  20. package/esm/asCode/descriptor.js +13 -4
  21. package/esm/asCode/serverIdentity.d.ts +7 -0
  22. package/esm/asCode/serverIdentity.d.ts.map +1 -0
  23. package/esm/asCode/serverIdentity.js +2 -0
  24. package/esm/asCode/tests/withMutationPort.d.ts +4 -0
  25. package/esm/asCode/tests/withMutationPort.d.ts.map +1 -0
  26. package/esm/asCode/tests/withMutationPort.js +5 -0
  27. package/esm/asCode/useAsCodeBase.d.ts +5 -8
  28. package/esm/asCode/useAsCodeBase.d.ts.map +1 -1
  29. package/esm/asCode/useAsCodeBase.js +20 -18
  30. package/esm/asCode/useAsCodeLoadFailure.d.ts +8 -0
  31. package/esm/asCode/useAsCodeLoadFailure.d.ts.map +1 -0
  32. package/esm/asCode/useAsCodeLoadFailure.js +21 -0
  33. package/esm/asCode/useMutationPort.d.ts +4 -0
  34. package/esm/asCode/useMutationPort.d.ts.map +1 -0
  35. package/esm/asCode/useMutationPort.js +9 -0
  36. package/esm/asCode/validateYaml.d.ts +16 -12
  37. package/esm/asCode/validateYaml.d.ts.map +1 -1
  38. package/esm/asCode/validateYaml.js +8 -9
  39. package/esm/asCodeRegistry.d.ts +5 -14
  40. package/esm/asCodeRegistry.d.ts.map +1 -1
  41. package/esm/asCodeRegistry.js +29 -15
  42. package/esm/catalog/Catalog.d.ts.map +1 -1
  43. package/esm/catalog/Catalog.js +2 -8
  44. package/esm/catalogDetail/CatalogDetailActionBar.d.ts.map +1 -1
  45. package/esm/catalogDetail/CatalogDetailActionBar.js +4 -8
  46. package/esm/catalogDetail/CatalogDetailActions.d.ts.map +1 -1
  47. package/esm/catalogDetail/CatalogDetailActions.js +2 -6
  48. package/esm/catalogDetail/openHandlerEvent.d.ts +6 -0
  49. package/esm/catalogDetail/openHandlerEvent.d.ts.map +1 -0
  50. package/esm/catalogDetail/openHandlerEvent.js +10 -0
  51. package/esm/header/CreateObjectButton.d.ts +1 -3
  52. package/esm/header/CreateObjectButton.d.ts.map +1 -1
  53. package/esm/header/CreateObjectButton.js +10 -22
  54. package/esm/index.d.ts +2 -0
  55. package/esm/index.d.ts.map +1 -1
  56. package/esm/index.js +5 -0
  57. package/esm/insight/gate.d.ts +4 -0
  58. package/esm/insight/gate.d.ts.map +1 -0
  59. package/esm/insight/gate.js +4 -0
  60. package/esm/insight/insightCodecContext.d.ts +22 -0
  61. package/esm/insight/insightCodecContext.d.ts.map +1 -0
  62. package/esm/insight/insightCodecContext.js +30 -0
  63. package/esm/insight/insightCopy.d.ts +4 -0
  64. package/esm/insight/insightCopy.d.ts.map +1 -0
  65. package/esm/insight/insightCopy.js +25 -0
  66. package/esm/insight/insightDescriptor.d.ts +7 -0
  67. package/esm/insight/insightDescriptor.d.ts.map +1 -0
  68. package/esm/insight/insightDescriptor.js +69 -0
  69. package/esm/insight/insightMutationPort.d.ts +13 -0
  70. package/esm/insight/insightMutationPort.d.ts.map +1 -0
  71. package/esm/insight/insightMutationPort.js +59 -0
  72. package/esm/localization/bundles/en-US.localization-bundle.d.ts +72 -0
  73. package/esm/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
  74. package/esm/localization/bundles/en-US.localization-bundle.js +72 -0
  75. package/esm/metric/gate.d.ts +1 -6
  76. package/esm/metric/gate.d.ts.map +1 -1
  77. package/esm/metric/gate.js +1 -9
  78. package/esm/metric/metricConverter.d.ts +8 -17
  79. package/esm/metric/metricConverter.d.ts.map +1 -1
  80. package/esm/metric/metricConverter.js +10 -24
  81. package/esm/metric/metricDescriptor.d.ts.map +1 -1
  82. package/esm/metric/metricDescriptor.js +19 -8
  83. package/esm/metric/metricMutationPort.d.ts +5 -3
  84. package/esm/metric/metricMutationPort.d.ts.map +1 -1
  85. package/esm/metric/metricMutationPort.js +21 -14
  86. package/esm/metric/metricValidation.d.ts.map +1 -1
  87. package/esm/metric/metricValidation.js +7 -4
  88. package/esm/metric/tests/metricMutationPort.test.utils.d.ts +1 -4
  89. package/esm/metric/tests/metricMutationPort.test.utils.d.ts.map +1 -1
  90. package/esm/metric/tests/metricMutationPort.test.utils.js +1 -19
  91. package/esm/parameter/gate.d.ts +1 -5
  92. package/esm/parameter/gate.d.ts.map +1 -1
  93. package/esm/parameter/gate.js +1 -8
  94. package/esm/parameter/parameterDescriptor.d.ts.map +1 -1
  95. package/esm/parameter/parameterDescriptor.js +22 -13
  96. package/esm/parameter/parameterValidation.d.ts.map +1 -1
  97. package/esm/parameter/parameterValidation.js +11 -23
  98. package/esm/sdk-ui-catalog.d.ts +49 -0
  99. package/package.json +15 -15
  100. package/esm/asCode/AsCodeMutationContext.d.ts +0 -24
  101. package/esm/asCode/AsCodeMutationContext.d.ts.map +0 -1
  102. package/esm/asCode/AsCodeMutationContext.js +0 -40
  103. package/esm/asCode/gate.d.ts +0 -7
  104. package/esm/asCode/gate.d.ts.map +0 -1
  105. package/esm/asCode/gate.js +0 -11
@@ -863,6 +863,78 @@ export declare const en_US: {
863
863
  text: string;
864
864
  crowdinContext: string;
865
865
  };
866
+ "analyticsCatalog.visualization.dialog.create.title": {
867
+ text: string;
868
+ crowdinContext: string;
869
+ };
870
+ "analyticsCatalog.visualization.dialog.edit.title": {
871
+ text: string;
872
+ crowdinContext: string;
873
+ };
874
+ "analyticsCatalog.visualization.dialog.sectionHeader": {
875
+ text: string;
876
+ crowdinContext: string;
877
+ };
878
+ "analyticsCatalog.visualization.dialog.sectionHeader.tooltip": {
879
+ text: string;
880
+ crowdinContext: string;
881
+ };
882
+ "analyticsCatalog.visualization.actions.openInAnalyticalDesigner": {
883
+ text: string;
884
+ crowdinContext: string;
885
+ };
886
+ "analyticsCatalog.visualization.dialog.edit.duplicate": {
887
+ text: string;
888
+ crowdinContext: string;
889
+ };
890
+ "analyticsCatalog.visualization.dialog.help": {
891
+ text: string;
892
+ crowdinContext: string;
893
+ };
894
+ "analyticsCatalog.visualization.dialog.submit.error": {
895
+ text: string;
896
+ crowdinContext: string;
897
+ };
898
+ "analyticsCatalog.visualization.create.defaultTitle": {
899
+ text: string;
900
+ crowdinContext: string;
901
+ };
902
+ "analyticsCatalog.visualization.create.success": {
903
+ text: string;
904
+ crowdinContext: string;
905
+ };
906
+ "analyticsCatalog.visualization.update.success": {
907
+ text: string;
908
+ crowdinContext: string;
909
+ };
910
+ "analyticsCatalog.visualization.load.error": {
911
+ text: string;
912
+ crowdinContext: string;
913
+ };
914
+ "analyticsCatalog.visualization.dialog.delete.title": {
915
+ text: string;
916
+ crowdinContext: string;
917
+ };
918
+ "analyticsCatalog.visualization.dialog.delete.body": {
919
+ text: string;
920
+ crowdinContext: string;
921
+ };
922
+ "analyticsCatalog.visualization.dialog.delete.usageWarning": {
923
+ text: string;
924
+ crowdinContext: string;
925
+ };
926
+ "analyticsCatalog.visualization.dialog.delete.submit": {
927
+ text: string;
928
+ crowdinContext: string;
929
+ };
930
+ "analyticsCatalog.visualization.delete.success": {
931
+ text: string;
932
+ crowdinContext: string;
933
+ };
934
+ "analyticsCatalog.visualization.delete.error": {
935
+ text: string;
936
+ crowdinContext: string;
937
+ };
866
938
  "analyticsCatalog.share.button": {
867
939
  text: string;
868
940
  crowdinContext: string;
@@ -1 +1 @@
1
- {"version":3,"file":"en-US.localization-bundle.d.ts","sourceRoot":"","sources":["../../../src/localization/bundles/en-US.localization-bundle.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAy4BjB,CAAC"}
1
+ {"version":3,"file":"en-US.localization-bundle.d.ts","sourceRoot":"","sources":["../../../src/localization/bundles/en-US.localization-bundle.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAi9BjB,CAAC"}
@@ -865,6 +865,78 @@ export const en_US = {
865
865
  "text": "Metric could not be deleted.",
866
866
  "crowdinContext": "Error toast shown when metric deletion fails."
867
867
  },
868
+ "analyticsCatalog.visualization.dialog.create.title": {
869
+ "text": "Create visualization",
870
+ "crowdinContext": "Title of the dialog for creating a new visualization as code."
871
+ },
872
+ "analyticsCatalog.visualization.dialog.edit.title": {
873
+ "text": "Edit visualization",
874
+ "crowdinContext": "Title of the dialog for editing a visualization as code."
875
+ },
876
+ "analyticsCatalog.visualization.dialog.sectionHeader": {
877
+ "text": "Visualization definition",
878
+ "crowdinContext": "Header above the as-code editor in the visualization dialog."
879
+ },
880
+ "analyticsCatalog.visualization.dialog.sectionHeader.tooltip": {
881
+ "text": "Define the visualization in YAML, including its type, query, and configuration.",
882
+ "crowdinContext": "Tooltip explaining the as-code visualization editor."
883
+ },
884
+ "analyticsCatalog.visualization.actions.openInAnalyticalDesigner": {
885
+ "text": "Open in Analytical Designer",
886
+ "crowdinContext": "Detail-panel menu action that opens the visualization in Analytical Designer (as opposed to the inline as-code editor)."
887
+ },
888
+ "analyticsCatalog.visualization.dialog.edit.duplicate": {
889
+ "text": "Duplicate",
890
+ "crowdinContext": "Button that duplicates the visualization being edited into a new visualization."
891
+ },
892
+ "analyticsCatalog.visualization.dialog.help": {
893
+ "text": "How to write a visualization",
894
+ "crowdinContext": "Help link in the visualization as-code dialog."
895
+ },
896
+ "analyticsCatalog.visualization.dialog.submit.error": {
897
+ "text": "The visualization could not be saved.",
898
+ "crowdinContext": "Generic error shown when saving a visualization fails."
899
+ },
900
+ "analyticsCatalog.visualization.create.defaultTitle": {
901
+ "text": "My visualization",
902
+ "crowdinContext": "Default placeholder title seeded into the new-visualization as-code editor before the user types their own."
903
+ },
904
+ "analyticsCatalog.visualization.create.success": {
905
+ "text": "Visualization created.",
906
+ "crowdinContext": "Toast shown after a visualization is created successfully from the Analytics Catalog."
907
+ },
908
+ "analyticsCatalog.visualization.update.success": {
909
+ "text": "Visualization saved.",
910
+ "crowdinContext": "Toast shown after a visualization is updated successfully from the Analytics Catalog."
911
+ },
912
+ "analyticsCatalog.visualization.load.error": {
913
+ "text": "The visualization could not be loaded.",
914
+ "crowdinContext": "Error toast shown when loading a visualization for editing or duplication fails."
915
+ },
916
+ "analyticsCatalog.visualization.dialog.delete.title": {
917
+ "text": "Delete visualization",
918
+ "crowdinContext": "Title of the confirmation dialog for deleting a visualization."
919
+ },
920
+ "analyticsCatalog.visualization.dialog.delete.body": {
921
+ "text": "Are you sure you want to delete <b>{name}</b>?",
922
+ "crowdinContext": "Body of the confirmation dialog for deleting a visualization. {name} is the visualization title."
923
+ },
924
+ "analyticsCatalog.visualization.dialog.delete.usageWarning": {
925
+ "text": "This visualization is used by {count, plural, one {# dashboard} other {# dashboards}}. Deleting it may break them.",
926
+ "crowdinContext": "Warning shown in the visualization delete dialog when the visualization is referenced by dashboards."
927
+ },
928
+ "analyticsCatalog.visualization.dialog.delete.submit": {
929
+ "text": "Delete",
930
+ "crowdinContext": "Submit button of the delete visualization confirmation dialog."
931
+ },
932
+ "analyticsCatalog.visualization.delete.success": {
933
+ "text": "Visualization deleted.",
934
+ "crowdinContext": "Toast shown after a visualization is deleted successfully from the Analytics Catalog."
935
+ },
936
+ "analyticsCatalog.visualization.delete.error": {
937
+ "text": "Visualization could not be deleted.",
938
+ "crowdinContext": "Error toast shown when visualization deletion fails."
939
+ },
868
940
  "analyticsCatalog.share.button": {
869
941
  "text": "Share",
870
942
  "crowdinContext": "Button label opening the share dialog from the catalog detail page."
@@ -1,8 +1,3 @@
1
- /** The flag gating the in-catalog metric editor. The single source of truth, referenced by both the
2
- * gate below and `metricDescriptor.featureFlag`. */
1
+ /** The flag gating the in-catalog metric editor, referenced by `metricDescriptor.featureFlag`. */
3
2
  export declare const METRIC_EDITOR_FEATURE_FLAG = "enableAnalyticalCatalogMetricEditor";
4
- /**
5
- * Whether the current user can create and edit metrics inline in the catalog.
6
- */
7
- export declare function useCanManageMetric(): boolean;
8
3
  //# sourceMappingURL=gate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/metric/gate.ts"],"names":[],"mappings":"AAIA;qDACqD;AACrD,eAAO,MAAM,0BAA0B,wCAAwC,CAAC;AAEhF;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C"}
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/metric/gate.ts"],"names":[],"mappings":"AAEA,kGAAkG;AAClG,eAAO,MAAM,0BAA0B,wCAAwC,CAAC"}
@@ -1,11 +1,3 @@
1
1
  // (C) 2026 GoodData Corporation
2
- import { useCanManageAsCode } from "../asCode/gate.js";
3
- /** The flag gating the in-catalog metric editor. The single source of truth, referenced by both the
4
- * gate below and `metricDescriptor.featureFlag`. */
2
+ /** The flag gating the in-catalog metric editor, referenced by `metricDescriptor.featureFlag`. */
5
3
  export const METRIC_EDITOR_FEATURE_FLAG = "enableAnalyticalCatalogMetricEditor";
6
- /**
7
- * Whether the current user can create and edit metrics inline in the catalog.
8
- */
9
- export function useCanManageMetric() {
10
- return useCanManageAsCode(METRIC_EDITOR_FEATURE_FLAG);
11
- }
@@ -1,5 +1,5 @@
1
1
  import type { v1 } from "@gooddata/sdk-code-schemas";
2
- import type { IMeasureMetadataObject, IMeasureMetadataObjectDefinition } from "@gooddata/sdk-model";
2
+ import type { IMeasureMetadataObjectDefinition } from "@gooddata/sdk-model";
3
3
  import type { MetricSchema } from "./metricSchema.js";
4
4
  /**
5
5
  * Canonical AAC metric object with an optional `id`.
@@ -27,23 +27,14 @@ export declare function definitionToMetricYaml(definition: IMeasureMetadataObjec
27
27
  */
28
28
  export declare function metricYamlToDefinition(metric: MetricSchema): IMeasureMetadataObjectDefinition;
29
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`, ...).
30
+ * The metric's `reconcile`: layers the author's parsed YAML edits over the base definition — the
31
+ * loaded measure on an edit, the copy source on a duplicate.
32
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
33
+ * The base carries fields the YAML cannot express (`metricType`, `isHiddenFromKda`), which survive
44
34
  * 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.
35
+ * parsed `id` wins, and its absence lets the server derive a fresh one, so the base's id is
36
+ * intentionally dropped first. (On an edit this is moot — validation pins the parsed id to the loaded
37
+ * object's — but on a duplicate it is what frees the copy from the source's identity.)
47
38
  */
48
- export declare function mergeCopiedMetricDefinition(copiedMetric: IMeasureMetadataObjectDefinition, yamlDefinition: IMeasureMetadataObjectDefinition): IMeasureMetadataObjectDefinition;
39
+ export declare function reconcileMetricDefinition(base: IMeasureMetadataObjectDefinition, edited: IMeasureMetadataObjectDefinition): IMeasureMetadataObjectDefinition;
49
40
  //# sourceMappingURL=metricConverter.d.ts.map
@@ -1 +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"}
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,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAE5E,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;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACrC,IAAI,EAAE,gCAAgC,EACtC,MAAM,EAAE,gCAAgC,GACzC,gCAAgC,CAOlC"}
@@ -68,34 +68,20 @@ function pickMetricYamlFields(definition) {
68
68
  };
69
69
  }
70
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`, ...).
71
+ * The metric's `reconcile`: layers the author's parsed YAML edits over the base definition — the
72
+ * loaded measure on an edit, the copy source on a duplicate.
73
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
74
+ * The base carries fields the YAML cannot express (`metricType`, `isHiddenFromKda`), which survive
90
75
  * 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.
76
+ * parsed `id` wins, and its absence lets the server derive a fresh one, so the base's id is
77
+ * intentionally dropped first. (On an edit this is moot — validation pins the parsed id to the loaded
78
+ * object's — but on a duplicate it is what frees the copy from the source's identity.)
93
79
  */
94
- export function mergeCopiedMetricDefinition(copiedMetric, yamlDefinition) {
95
- const { id: _copiedId, ...carriedOver } = copiedMetric;
80
+ export function reconcileMetricDefinition(base, edited) {
81
+ const { id: _baseId, ...carriedOver } = base;
96
82
  return {
97
83
  ...carriedOver,
98
- ...pickMetricYamlFields(yamlDefinition),
99
- ...(yamlDefinition.id === undefined ? {} : { id: yamlDefinition.id }),
84
+ ...pickMetricYamlFields(edited),
85
+ ...(edited.id === undefined ? {} : { id: edited.id }),
100
86
  };
101
87
  }
@@ -1 +1 @@
1
- {"version":3,"file":"metricDescriptor.d.ts","sourceRoot":"","sources":["../../src/metric/metricDescriptor.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAG5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAqEnE;;GAEG;AACH,eAAO,MAAM,gBAAgB,4GAe5B,CAAC"}
1
+ {"version":3,"file":"metricDescriptor.d.ts","sourceRoot":"","sources":["../../src/metric/metricDescriptor.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAQ5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAyEnE;;GAEG;AACH,eAAO,MAAM,gBAAgB,4GAuB5B,CAAC"}
@@ -1,13 +1,13 @@
1
1
  // (C) 2026 GoodData Corporation
2
2
  import { useMemo } from "react";
3
3
  import { defineMessages, useIntl } from "react-intl";
4
- import { defineAsCodeDescriptor } from "../asCode/descriptor.js";
4
+ import { defineAsCodeDescriptor, fixedIdentifierOf, } from "../asCode/descriptor.js";
5
5
  import { ObjectTypes } from "../objectType/constants.js";
6
6
  import { METRIC_EDITOR_FEATURE_FLAG } from "./gate.js";
7
7
  import { metricCompletions } from "./metricCompletions.js";
8
- import { definitionToMetricYaml, mergeCopiedMetricDefinition, metricYamlToDefinition, } from "./metricConverter.js";
8
+ import { definitionToMetricYaml, metricYamlToDefinition, reconcileMetricDefinition, } from "./metricConverter.js";
9
9
  import { createCopiedMetric } from "./metricCopy.js";
10
- import { createMetricMutationAdapter } from "./metricMutationPort.js";
10
+ import { countMetricReferences, createMetricMutationAdapter, loadMetric } from "./metricMutationPort.js";
11
11
  import { serializeMetricToYaml } from "./metricSerialization.js";
12
12
  import { validateMetricYaml } from "./metricValidation.js";
13
13
  const METRIC_DOCS_URL = "https://www.gooddata.ai/docs/cloud/api-and-sdk/vs-code-extension/structures/#metric";
@@ -22,12 +22,14 @@ const messages = defineMessages({
22
22
  sectionHeaderTooltip: { id: "analyticsCatalog.metric.dialog.sectionHeader.tooltip" },
23
23
  help: { id: "analyticsCatalog.metric.dialog.help" },
24
24
  submitError: { id: "analyticsCatalog.metric.dialog.submit.error" },
25
- loadError: { id: "analyticsCatalog.metric.load.error" },
26
25
  deleteTitle: { id: "analyticsCatalog.metric.dialog.delete.title" },
27
26
  deleteBody: { id: "analyticsCatalog.metric.dialog.delete.body" },
28
27
  deleteSubmit: { id: "analyticsCatalog.metric.dialog.delete.submit" },
29
28
  deleteSuccess: { id: "analyticsCatalog.metric.delete.success" },
30
29
  deleteError: { id: "analyticsCatalog.metric.delete.error" },
30
+ });
31
+ const capabilityMessages = defineMessages({
32
+ loadError: { id: "analyticsCatalog.metric.load.error" },
31
33
  deleteUsageWarning: { id: "analyticsCatalog.metric.dialog.delete.usageWarning" },
32
34
  });
33
35
  const actionMessages = defineMessages({
@@ -48,12 +50,13 @@ function useMetricEditing() {
48
50
  syntaxErrorMessage: intl.formatMessage(errorMessages.syntax),
49
51
  // The editor works in the YAML shape, a lossy projection of the measure definition.
50
52
  serialize: (definition) => serializeMetricToYaml(definitionToMetricYaml(definition)),
51
- validate: (value, options) => {
52
- const result = validateMetricYaml(value, options);
53
+ validate: (value, context) => {
54
+ const result = validateMetricYaml(value, { fixedIdentifier: fixedIdentifierOf(context) });
53
55
  return result.isValid
54
56
  ? { isValid: true, definition: result.measure }
55
57
  : { isValid: false, error: intl.formatMessage(errorMessages[result.errorCode]) };
56
58
  },
59
+ reconcile: reconcileMetricDefinition,
57
60
  }), [intl]);
58
61
  }
59
62
  /**
@@ -67,8 +70,16 @@ export const metricDescriptor = defineAsCodeDescriptor({
67
70
  useEditing: useMetricEditing,
68
71
  createMutationPort: createMetricMutationAdapter,
69
72
  emptyDefinition: (defaultTitle) => metricYamlToDefinition({ type: "metric", title: defaultTitle, maql: "SELECT 1" }),
70
- // No editSeed: the full measure is loaded via port.load (the item carries no MAQL).
73
+ seed: { load: loadMetric, loadError: capabilityMessages.loadError },
74
+ referenceCounted: {
75
+ count: countMetricReferences,
76
+ usageWarning: capabilityMessages.deleteUsageWarning,
77
+ },
78
+ // A copied metric derives a human-readable id that can collide on create; identity lets the dialog retry without it.
79
+ identity: {
80
+ read: (definition) => definition.id,
81
+ strip: ({ id: _id, ...definition }) => definition,
82
+ },
71
83
  toCopy: createCopiedMetric,
72
- applyYamlEdits: mergeCopiedMetricDefinition,
73
84
  openAction: actionMessages.openInEditor,
74
85
  });
@@ -3,11 +3,13 @@ import { type IMeasureMetadataObjectDefinition } from "@gooddata/sdk-model";
3
3
  import type { IAsCodeMutationPort } from "../asCode/descriptor.js";
4
4
  import type { ICatalogItemMeasure } from "../catalogItem/types.js";
5
5
  /**
6
- * A metric loads its full measure before editing (the catalog item carries no MAQL) and always
7
- * supports the optional `load`/`getReferencingObjectsCount` capabilities — hence `Required`.
8
6
  * @internal
9
7
  */
10
- export type IMetricMutationPort = Required<IAsCodeMutationPort<IMeasureMetadataObjectDefinition, ICatalogItemMeasure>>;
8
+ export type IMetricMutationPort = IAsCodeMutationPort<IMeasureMetadataObjectDefinition, ICatalogItemMeasure>;
9
+ /** Fetches the full measure for editing (the catalog item carries no MAQL). @internal */
10
+ export declare function loadMetric(backend: IAnalyticalBackend, workspace: string, item: ICatalogItemMeasure): Promise<IMeasureMetadataObjectDefinition>;
11
+ /** Counts the insights and measures referencing a measure. @internal */
12
+ export declare function countMetricReferences(backend: IAnalyticalBackend, workspace: string, item: ICatalogItemMeasure): Promise<number>;
11
13
  /**
12
14
  * @internal
13
15
  */
@@ -1 +1 @@
1
- {"version":3,"file":"metricMutationPort.d.ts","sourceRoot":"","sources":["../../src/metric/metricMutationPort.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,KAAK,gCAAgC,EAAkC,MAAM,qBAAqB,CAAC;AAE5G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AASnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAInE;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CACtC,mBAAmB,CAAC,gCAAgC,EAAE,mBAAmB,CAAC,CAC7E,CAAC;AAEF;;GAEG;AACH,wBAAgB,2BAA2B,CACvC,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,GAClB,mBAAmB,CAoCrB"}
1
+ {"version":3,"file":"metricMutationPort.d.ts","sourceRoot":"","sources":["../../src/metric/metricMutationPort.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAEH,KAAK,gCAAgC,EAGxC,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAUnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEnE;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,gCAAgC,EAAE,mBAAmB,CAAC,CAAC;AAW7G,yFAAyF;AACzF,wBAAgB,UAAU,CACtB,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,mBAAmB,GAC1B,OAAO,CAAC,gCAAgC,CAAC,CAE3C;AAED,wEAAwE;AACxE,wBAAsB,qBAAqB,CACvC,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,mBAAmB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACvC,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,GAClB,mBAAmB,CAsBrB"}
@@ -1,8 +1,21 @@
1
1
  // (C) 2026 GoodData Corporation
2
- import { idRef, isMeasureMetadataObject } from "@gooddata/sdk-model";
2
+ import { idRef, isMeasureMetadataObject, } from "@gooddata/sdk-model";
3
3
  import { convertMeasureToCatalogItem } from "../catalogItem/converter.js";
4
4
  import { createMeasureCatalogItem, deleteMeasureCatalogItem, getMeasureCatalogItem, getMeasureReferencingObjectsCatalogItem, updateMeasureCatalogItem, } from "../catalogItem/query.js";
5
- import { overlayMetricYamlFields } from "./metricConverter.js";
5
+ // `id` is definition-expressible yet server-owned: the update targets by it, so a hand-edited id must not retarget it.
6
+ function pickMetricIdentity(measure) {
7
+ const { id, ref, uri, created, updated, createdBy, updatedBy, certification } = measure;
8
+ return { id, ref, uri, created, updated, createdBy, updatedBy, certification };
9
+ }
10
+ /** Fetches the full measure for editing (the catalog item carries no MAQL). @internal */
11
+ export function loadMetric(backend, workspace, item) {
12
+ return getMeasureCatalogItem(backend, workspace, idRef(item.identifier, "measure"));
13
+ }
14
+ /** Counts the insights and measures referencing a measure. @internal */
15
+ export async function countMetricReferences(backend, workspace, item) {
16
+ const referencing = await getMeasureReferencingObjectsCatalogItem(backend, workspace, idRef(item.identifier, "measure"));
17
+ return (referencing.insights?.length ?? 0) + (referencing.measures?.length ?? 0);
18
+ }
6
19
  /**
7
20
  * @internal
8
21
  */
@@ -13,25 +26,19 @@ export function createMetricMutationAdapter(backend, workspace) {
13
26
  return convertMeasureToCatalogItem(savedMeasure);
14
27
  },
15
28
  async update(base, definition) {
16
- // The measures endpoint is a full replace, so overlay the YAML-owned fields onto the loaded
17
- // measure to carry over what the YAML cannot express (metricType, isHiddenFromKda, identity).
18
- // `base` must be the persisted measure `load` returned — a bare definition (no server-managed
19
- // `ref`) would silently drop those fields, so reject it loudly rather than corrupt the update.
20
29
  if (!isMeasureMetadataObject(base)) {
21
30
  throw new Error("Metric update requires the loaded measure as base, not a bare definition.");
22
31
  }
23
- const savedMeasure = await updateMeasureCatalogItem(backend, workspace, overlayMetricYamlFields(base, definition));
32
+ // Definition is authoritative (already reconciled); base fills only non-definition fields, and the server-identity pick wins.
33
+ const savedMeasure = await updateMeasureCatalogItem(backend, workspace, {
34
+ ...base,
35
+ ...definition,
36
+ ...pickMetricIdentity(base),
37
+ });
24
38
  return convertMeasureToCatalogItem(savedMeasure);
25
39
  },
26
40
  async delete(ref) {
27
41
  await deleteMeasureCatalogItem(backend, workspace, idRef(ref.identifier, "measure"));
28
42
  },
29
- async load(item) {
30
- return getMeasureCatalogItem(backend, workspace, idRef(item.identifier, "measure"));
31
- },
32
- async getReferencingObjectsCount(item) {
33
- const referencing = await getMeasureReferencingObjectsCatalogItem(backend, workspace, idRef(item.identifier, "measure"));
34
- return (referencing.insights?.length ?? 0) + (referencing.measures?.length ?? 0);
35
- },
36
43
  };
37
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"metricValidation.d.ts","sourceRoot":"","sources":["../../src/metric/metricValidation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAO5E,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,CAUxB"}
1
+ {"version":3,"file":"metricValidation.d.ts","sourceRoot":"","sources":["../../src/metric/metricValidation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAO5E,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,CAaxB"}
@@ -6,11 +6,14 @@ export function validateMetricYaml(value, options = {}) {
6
6
  const result = validateYaml(value, {
7
7
  schema: metricSchema,
8
8
  fixedIdentifier: options.fixedIdentifier,
9
- classifyError: classifyMetricError,
10
9
  });
11
- return result.ok
12
- ? { isValid: true, measure: metricYamlToDefinition(result.data) }
13
- : { isValid: false, errorCode: result.errorCode };
10
+ if (result.ok) {
11
+ return { isValid: true, measure: metricYamlToDefinition(result.data) };
12
+ }
13
+ return {
14
+ isValid: false,
15
+ errorCode: result.kind === "schema" ? classifyMetricError(result.error, result.parsed) : result.kind,
16
+ };
14
17
  }
15
18
  function classifyMetricError(error, parsed) {
16
19
  const maqlProvided = typeof parsed === "object" && parsed !== null && parsed.maql !== undefined;
@@ -1,7 +1,4 @@
1
1
  import type { IMetricMutationPort } from "../metricMutationPort.js";
2
- /**
3
- * Test utility. Returns a metric mutation port with vi.fn() stubs.
4
- * @internal
5
- */
2
+ /** @internal */
6
3
  export declare function createTestMetricMutationPort(overrides?: Partial<IMetricMutationPort>): IMetricMutationPort;
7
4
  //# sourceMappingURL=metricMutationPort.test.utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metricMutationPort.test.utils.d.ts","sourceRoot":"","sources":["../../../src/metric/tests/metricMutationPort.test.utils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAiBpE;;;GAGG;AACH,wBAAgB,4BAA4B,CACxC,SAAS,GAAE,OAAO,CAAC,mBAAmB,CAAM,GAC7C,mBAAmB,CAsBrB"}
1
+ {"version":3,"file":"metricMutationPort.test.utils.d.ts","sourceRoot":"","sources":["../../../src/metric/tests/metricMutationPort.test.utils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAiBpE,gBAAgB;AAChB,wBAAgB,4BAA4B,CACxC,SAAS,GAAE,OAAO,CAAC,mBAAmB,CAAM,GAC7C,mBAAmB,CAOrB"}
@@ -14,30 +14,12 @@ const defaultSavedItem = {
14
14
  isEditable: true,
15
15
  format: "#,##0.00",
16
16
  };
17
- /**
18
- * Test utility. Returns a metric mutation port with vi.fn() stubs.
19
- * @internal
20
- */
17
+ /** @internal */
21
18
  export function createTestMetricMutationPort(overrides = {}) {
22
19
  return {
23
20
  create: vi.fn().mockResolvedValue(defaultSavedItem),
24
21
  update: vi.fn().mockResolvedValue(defaultSavedItem),
25
22
  delete: vi.fn().mockResolvedValue(undefined),
26
- load: vi.fn().mockResolvedValue({
27
- id: "test-metric",
28
- uri: "test-metric",
29
- ref: { identifier: "test-metric", type: "measure" },
30
- type: "measure",
31
- title: "Test Metric",
32
- description: "",
33
- tags: [],
34
- production: true,
35
- deprecated: false,
36
- unlisted: false,
37
- expression: "SELECT 1",
38
- format: "#,##0.00",
39
- }),
40
- getReferencingObjectsCount: vi.fn().mockResolvedValue(0),
41
23
  ...overrides,
42
24
  };
43
25
  }
@@ -1,15 +1,11 @@
1
1
  import type { ParameterType } from "@gooddata/sdk-model";
2
- /** The flag gating catalog parameters. The single source of truth, referenced by the gates below and
2
+ /** The flag gating catalog parameters, referenced by the gate below and
3
3
  * `parameterDescriptor.featureFlag`. */
4
4
  export declare const PARAMETER_FEATURE_FLAG = "enableParameters";
5
5
  /**
6
6
  * Catalog parameter feature gate.
7
7
  */
8
8
  export declare function useIsParametersEnabled(): boolean;
9
- /**
10
- * Whether the current user can manage parameters in the catalog.
11
- */
12
- export declare function useCanManageParameter(): boolean;
13
9
  /**
14
10
  * Parameter types authorable in the catalog, gated by `enableStringParameters`.
15
11
  */
@@ -1 +1 @@
1
- {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/parameter/gate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKzD;yCACyC;AACzC,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,aAAa,EAAE,CAM1D"}
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/parameter/gate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD;yCACyC;AACzC,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,aAAa,EAAE,CAM1D"}
@@ -1,8 +1,7 @@
1
1
  // (C) 2026 GoodData Corporation
2
2
  import { useMemo } from "react";
3
- import { useCanManageAsCode } from "../asCode/gate.js";
4
3
  import { useFeatureFlag } from "../permission/PermissionsContext.js";
5
- /** The flag gating catalog parameters. The single source of truth, referenced by the gates below and
4
+ /** The flag gating catalog parameters, referenced by the gate below and
6
5
  * `parameterDescriptor.featureFlag`. */
7
6
  export const PARAMETER_FEATURE_FLAG = "enableParameters";
8
7
  /**
@@ -11,12 +10,6 @@ export const PARAMETER_FEATURE_FLAG = "enableParameters";
11
10
  export function useIsParametersEnabled() {
12
11
  return useFeatureFlag(PARAMETER_FEATURE_FLAG);
13
12
  }
14
- /**
15
- * Whether the current user can manage parameters in the catalog.
16
- */
17
- export function useCanManageParameter() {
18
- return useCanManageAsCode(PARAMETER_FEATURE_FLAG);
19
- }
20
13
  /**
21
14
  * Parameter types authorable in the catalog, gated by `enableStringParameters`.
22
15
  */
@@ -1 +1 @@
1
- {"version":3,"file":"parameterDescriptor.d.ts","sourceRoot":"","sources":["../../src/parameter/parameterDescriptor.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,qBAAqB,CAAC;AAG9E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAqErE;;GAEG;AACH,eAAO,MAAM,mBAAmB,gHA2B9B,CAAC"}
1
+ {"version":3,"file":"parameterDescriptor.d.ts","sourceRoot":"","sources":["../../src/parameter/parameterDescriptor.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,qBAAqB,CAAC;AAQ9E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAwErE;;GAEG;AACH,eAAO,MAAM,mBAAmB,gHAiC9B,CAAC"}
@@ -1,7 +1,7 @@
1
1
  // (C) 2026 GoodData Corporation
2
2
  import { useMemo } from "react";
3
3
  import { defineMessages, useIntl } from "react-intl";
4
- import { defineAsCodeDescriptor } from "../asCode/descriptor.js";
4
+ import { defineAsCodeDescriptor, fixedIdentifierOf, } from "../asCode/descriptor.js";
5
5
  import { ObjectTypes } from "../objectType/constants.js";
6
6
  import { PARAMETER_FEATURE_FLAG, useEnabledParameterTypes } from "./gate.js";
7
7
  import { createParameterCompletions } from "./parameterCompletions.js";
@@ -47,8 +47,11 @@ function useParameterEditing() {
47
47
  syntaxErrorMessage: intl.formatMessage(errorMessages.syntax),
48
48
  // Parameter YAML round-trips 1:1 to its definition, so serialization is a plain stringify.
49
49
  serialize: (definition) => serializeParameterToYaml(definition),
50
- validate: (value, options) => {
51
- const result = validateParameterYaml(value, { enabledTypes, ...options });
50
+ validate: (value, context) => {
51
+ const result = validateParameterYaml(value, {
52
+ enabledTypes,
53
+ fixedIdentifier: fixedIdentifierOf(context),
54
+ });
52
55
  return result.isValid
53
56
  ? { isValid: true, definition: result.parameter }
54
57
  : {
@@ -77,15 +80,21 @@ export const parameterDescriptor = defineAsCodeDescriptor({
77
80
  description: "",
78
81
  definition: { type: "NUMBER", defaultValue: 0 },
79
82
  }),
80
- // No port.load: the catalog item maps field-for-field to a definition, so the editor seeds from it.
81
- editSeed: (item) => ({
82
- id: item.identifier,
83
- type: "parameter",
84
- title: item.title,
85
- description: item.description,
86
- tags: item.tags,
87
- definition: item.definition,
88
- }),
89
- // Source is already a definition, so toCopy only re-derives identity; no applyYamlEdits (YAML round-trips 1:1).
83
+ // Item maps field-for-field to a definition, so no fetch is needed.
84
+ seed: {
85
+ editSeed: (item) => ({
86
+ id: item.identifier,
87
+ type: "parameter",
88
+ title: item.title,
89
+ description: item.description,
90
+ tags: item.tags,
91
+ definition: item.definition,
92
+ }),
93
+ },
94
+ // A copied parameter derives a human-readable id that can collide on create; identity lets the dialog retry without it.
95
+ identity: {
96
+ read: (definition) => definition.id,
97
+ strip: ({ id: _id, ...definition }) => definition,
98
+ },
90
99
  toCopy: createCopiedParameter,
91
100
  });