Skip to content

Sync from gdc-ui/sdk hash:b42305b#7345

Merged
yenkins-admin merged 2 commits into
masterfrom
sync-from-gdc-ui-28914312940
Jul 8, 2026
Merged

Sync from gdc-ui/sdk hash:b42305b#7345
yenkins-admin merged 2 commits into
masterfrom
sync-from-gdc-ui-28914312940

Conversation

@yenkins

@yenkins yenkins commented Jul 8, 2026

Copy link
Copy Markdown

Automated sync of gdc-ui → gooddata-ui-sdk Source hash: b42305b

Summary by CodeRabbit

  • New Features

    • Added in-catalog metric creation, editing, duplication, and deletion in the Analytics Catalog.
    • Introduced metric editor support with validation, autocomplete, and YAML-based editing.
    • Added a new metric-related feature flag for controlled rollout.
  • Bug Fixes

    • Improved handling of backend error messages and duplicate-item conflicts.
    • Strengthened recording/redaction behavior to better protect sensitive data.
  • Chores

    • Updated package and release versions across the workspace.

@yenkins yenkins added the sync PR is created from gdc-ui label Jul 8, 2026

@yenkins-admin yenkins-admin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-approved by sync workflow

@yenkins-admin yenkins-admin enabled auto-merge July 8, 2026 03:16
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds an as-code metric editor to the Analytics Catalog (feature-flagged), including YAML schema/validation/serialization, a metric mutation adapter, create/edit/delete dialogs, and UI wiring. It also adds secret-redaction hooks to goodmock recording utilities, and bumps package versions across the monorepo.

Changes

Analytics Catalog Metric As-Code Editor

Layer / File(s) Summary
Feature flag definitions
libs/sdk-backend-tiger/src/backend/uiFeatures.ts, libs/sdk-backend-tiger/src/backend/features/feature.ts, libs/sdk-model/api/sdk-model.api.md, libs/sdk-model/src/settings/settings.ts
Adds enableAnalyticalCatalogMetricEditor flag to Tiger feature mapping and IFeatureFlags.
Metric YAML domain logic
libs/sdk-ui-catalog/src/metric/metricSchema.ts, metricConverter.ts, metricSerialization.ts, metricValidation.ts, metricCopy.ts, tests/*
Zod schema, YAML↔definition conversion, serialization, validation, and duplicate/copy helpers with tests.
Mutation adapter & query API
libs/sdk-ui-catalog/src/catalogItem/query.ts, metric/metricMutationPort.ts, MetricMutationContext.tsx, package.json
Measure CRUD query helpers, IMetricMutationPort adapter, and React context/hook.
Feature gating
libs/sdk-ui-catalog/src/metric/gate.ts
Hooks gating metric editor visibility by feature flag and permission.
YAML editor & linting
MetricYamlEditor.tsx, metricYamlLinter.ts, metricCompletions.ts, utils/yamlSyntaxLinter.ts, parameter/yamlLinter.ts
Shared YAML syntax linter, metric-specific linter, and schema autocompletion.
Create/edit/delete dialogs
MetricDialog.tsx, MetricCreateDialog.tsx, MetricEditDialog.tsx, MetricDeleteDialog.tsx, MetricDetailActions.tsx, tests/*
Dialog components implementing metric create/edit/duplicate/delete flows.
Catalog/Header/Detail UI wiring
AnalyticsCatalogDetail.tsx, Catalog.tsx, catalogDetail/*, header/CreateObjectButton.tsx, main/Main.tsx, parameter/ParameterDialog.tsx, localization/en-US.json, styles/scss/*, tests
Wires providers, feature gates, and menu/dialog flows into catalog UI; restyles as-code dialogs; adds localization strings.

Estimated code review effort: 4 (Complex) | ~60 minutes

Goodmock Secret Redaction

Layer / File(s) Summary
Mapping and options types
libs/sdk-e2e-utils/src/goodmock.ts, src/index.ts, src/playwright.ts, api/sdk-e2e-utils.api.md
Adds ISecretMapping, ILeakPattern, ISnapshotAndSaveRecordingOptions, updates IGoodmockMapping.
Snapshot flow and redaction
goodmock.ts, playwright.ts, changelog entries
Single-snapshot flow, resolveSettings redaction, secret/leak-pattern guards, consumer sanitizeMappings hook.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Version Bump and NOTICE Update

Layer / File(s) Summary
Version metadata bumps
NOTICE, common/config/rush/version-policies.json, **/package.json, libs/sdk-code-convertors/python/pyproject.toml
Bumps all package versions to 11.46.0-alpha.4 and updates NOTICE generation metadata.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CreateObjectButton
  participant MetricCreateDialog
  participant MetricDialog
  participant MetricMutationPort
  participant AnalyticalBackend

  User->>CreateObjectButton: Select "Metric"
  CreateObjectButton->>MetricCreateDialog: open dialog
  MetricCreateDialog->>MetricDialog: render with initialMetric YAML
  User->>MetricDialog: edit YAML, submit
  MetricDialog->>MetricCreateDialog: validated definition
  MetricCreateDialog->>MetricMutationPort: create(definition)
  MetricMutationPort->>AnalyticalBackend: createMeasure()
  AnalyticalBackend-->>MetricMutationPort: saved measure
  MetricMutationPort-->>MetricCreateDialog: created item
  MetricCreateDialog->>CreateObjectButton: onCreated / refetch
Loading
sequenceDiagram
  participant Test
  participant snapshotAndSaveRecording
  participant Goodmock
  participant Consumer

  Test->>snapshotAndSaveRecording: options (secretMappings, leakPatterns, sanitizeMappings)
  snapshotAndSaveRecording->>Goodmock: take snapshot (repeatsAsScenarios)
  Goodmock-->>snapshotAndSaveRecording: mappings
  snapshotAndSaveRecording->>snapshotAndSaveRecording: redact resolveSettings tokens
  snapshotAndSaveRecording->>Consumer: sanitizeMappings(mappings)
  Consumer-->>snapshotAndSaveRecording: sanitized mappings
  snapshotAndSaveRecording->>snapshotAndSaveRecording: mask secrets & check leakPatterns
  snapshotAndSaveRecording-->>Test: write mapping file
Loading

Poem

A rabbit hopped through YAML fields so neat,
Duplicating metrics with paws light and fleet 🐇
Secrets tucked away, no tokens in sight,
Versions all bumped up, alpha-four, just right,
Thump-thump goes my heart at this catalog delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is far shorter than the required template and omits the mandated repository notice and Bear/rel/9.9 instructions. Expand the description to include the full template text, especially the gdc-ui submission notice and the rel/9.9 Bear exception commands.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states this is a sync from gdc-ui and includes the source hash, matching the main change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@yenkins-admin yenkins-admin merged commit eb275c4 into master Jul 8, 2026
15 of 16 checks passed
@yenkins-admin yenkins-admin deleted the sync-from-gdc-ui-28914312940 branch July 8, 2026 03:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (7)
libs/sdk-e2e-utils/src/goodmock.ts (1)

362-384: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated escaped-secret/masked-pattern computation across the masking and leak-guard loops.

escapedSecret and maskedForm are independently rebuilt in both the masking loop (365-373) and the leak-guard loop (375-384). Precomputing once per secret and reusing would avoid the two implementations silently diverging if one is edited without the other.

♻️ Suggested refactor
-    for (const { secret, placeholder } of effectiveSecretMappings) {
-        const escapedSecret = JSON.stringify(secret).slice(1, -1);
-        const escapedPlaceholder = JSON.stringify(placeholder).slice(1, -1);
-        output = output.replaceAll(escapedSecret, escapedPlaceholder);
-        const maskedForm = buildMaskedFormPattern(escapedSecret);
-        if (maskedForm) {
-            output = output.replace(maskedForm, escapedPlaceholder);
-        }
-    }
-    // Leak guard: never write a recording that still contains a real secret (full or masked).
-    for (const { secret } of effectiveSecretMappings) {
-        const escapedSecret = JSON.stringify(secret).slice(1, -1);
-        const maskedForm = buildMaskedFormPattern(escapedSecret);
-        if (output.includes(escapedSecret) || output.includes(secret) || maskedForm?.test(output)) {
+    const secretMasks = effectiveSecretMappings.map(({ secret, placeholder }) => {
+        const escapedSecret = JSON.stringify(secret).slice(1, -1);
+        const escapedPlaceholder = JSON.stringify(placeholder).slice(1, -1);
+        return { secret, escapedSecret, escapedPlaceholder, maskedForm: buildMaskedFormPattern(escapedSecret) };
+    });
+    for (const { escapedSecret, escapedPlaceholder, maskedForm } of secretMasks) {
+        output = output.replaceAll(escapedSecret, escapedPlaceholder);
+        if (maskedForm) {
+            output = output.replace(maskedForm, escapedPlaceholder);
+        }
+    }
+    // Leak guard: never write a recording that still contains a real secret (full or masked).
+    for (const { secret, escapedSecret, maskedForm } of secretMasks) {
+        if (output.includes(escapedSecret) || output.includes(secret) || maskedForm?.test(output)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sdk-e2e-utils/src/goodmock.ts` around lines 362 - 384, The masking logic
in goodmock’s secret sanitization re-computes escapedSecret and maskedForm
separately in the replacement loop and the leak-guard loop, which risks the two
paths drifting apart. Refactor the handling around effectiveSecretMappings in
goodmock so each secret’s escaped secret and masked pattern are computed once
and reused in both replacement and validation, keeping the sanitization and
leak-check behavior consistent.
libs/sdk-ui-catalog/src/metric/MetricYamlEditor.tsx (1)

1-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Near-duplicate of ParameterYamlEditor.tsx.

This component's structure (state, useMemo extensions, handleChange, render) is essentially identical to ParameterYamlEditor.tsx, differing only in the linter/completions imports and CSS class reuse. Consider extracting a shared generic AsCodeYamlEditor that accepts linter/onCompletion as props to avoid two editors drifting out of sync as more as-code editors are added.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sdk-ui-catalog/src/metric/MetricYamlEditor.tsx` around lines 1 - 48, The
MetricYamlEditor component is a near-duplicate of ParameterYamlEditor and should
be refactored to avoid drift as more as-code editors are added. Extract the
shared state, useMemo extensions, handleChange, and SyntaxHighlightingInput
wiring into a generic AsCodeYamlEditor component, and pass the editor-specific
pieces (such as createMetricYamlLinter and metricSchemaCompletions) as props or
config from MetricYamlEditor. Keep MetricYamlEditor as a thin wrapper that
supplies its linter/completions and class name while reusing the shared
implementation.
libs/sdk-ui-catalog/src/metric/MetricDialog.tsx (1)

1-227: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Large structural duplicate of ParameterDialog.tsx.

MetricDialog mirrors ParameterDialog almost line-for-line (state shape, validate/handleSubmit/handleDuplicate/footerLeftRenderer, ConfirmDialog wiring), differing mainly in message ids, docs URL, and the validate/serialize functions it delegates to. This is significant duplicated logic across two feature areas that will need to be kept in sync for every future bugfix (e.g., the submit-disabled logic, error-message mapping, duplicate flow). Consider extracting a shared generic AsCodeDialog parameterized by the validate/serialize functions, docs URL, and message bundle.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sdk-ui-catalog/src/metric/MetricDialog.tsx` around lines 1 - 227,
`MetricDialog` is a near-copy of `ParameterDialog`, with the same state,
submit/duplicate handling, footer rendering, and `ConfirmDialog` wiring repeated
here. Refactor the shared dialog behavior into a reusable `AsCodeDialog` (or
equivalent) and parameterize it with the metric-specific pieces:
validation/serialization functions, docs URL, and message bundle. Keep
`MetricDialog` as a thin wrapper that supplies metric-specific dependencies and
symbols like `handleSubmit`, `handleDuplicate`, `footerLeftRenderer`, and
`MetricYamlEditor`.
libs/sdk-ui-catalog/src/metric/MetricDetailActions.tsx (1)

47-56: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

onOpen type doesn't match the actual event union it can receive.

handleActionsMenuSelect accepts MouseEvent | KeyboardEvent (keyboard-triggered menu selection), but onOpen is typed to only accept MouseEvent, forcing a cast at the call site that misrepresents the runtime type for keyboard-triggered opens.

♻️ Proposed fix
-    onOpen?: (event: MouseEvent, openEvent: OpenHandlerEvent) => void;
+    onOpen?: (event: MouseEvent | KeyboardEvent, openEvent: OpenHandlerEvent) => void;
             if (actionId === "open") {
-                onOpen?.(event as MouseEvent, {
+                onOpen?.(event, {
                     item,
                     workspaceId,
                     newTab: event.metaKey || event.ctrlKey,
                     preventDefault: event.preventDefault.bind(event),
                 });

Also applies to: 114-124

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sdk-ui-catalog/src/metric/MetricDetailActions.tsx` around lines 47 - 56,
Update the MetricDetailActions component API so onOpen in
IMetricDetailActionsProps matches the actual event shape handled by
handleActionsMenuSelect, which can pass either a mouse or keyboard event. Adjust
the onOpen callback typing and any related call sites in MetricDetailActions to
use the shared union type instead of casting, so keyboard-triggered opens are
represented correctly without unsafe assumptions.
libs/sdk-ui-catalog/src/metric/MetricDeleteDialog.tsx (1)

61-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider surfacing the backend-provided delete failure reason.

extractBackendErrorDetail (added in utils/backendError.ts) is used by ParameterDialog.tsx to surface curated backend reasons (e.g., dependency conflicts, limits). Here, any delete failure — including a specific "still referenced" error from the backend — collapses to the same generic deleteError toast, losing actionable detail for the user.

♻️ Illustrative fix (adapt to `useToastMessage` API)
+import { extractBackendErrorDetail } from "../utils/backendError.js";
+
 const handleDelete = useCallback(async () => {
     setIsDeleting(true);
     try {
         await mutation.delete(item);
         onDeleted();
         onClose();
         addSuccess(messages.deleteSuccess);
-    } catch {
-        addError(messages.deleteError);
+    } catch (error) {
+        addError(extractBackendErrorDetail(error) ?? messages.deleteError);
         setIsDeleting(false);
     }
 }, [addError, addSuccess, item, mutation, onClose, onDeleted]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sdk-ui-catalog/src/metric/MetricDeleteDialog.tsx` around lines 61 - 72,
The delete flow in MetricDeleteDialog’s handleDelete swallows the backend error,
so all failures show the same generic toast. Update the catch block to capture
the thrown error from mutation.delete(item) and pass it through
extractBackendErrorDetail (as used in ParameterDialog and utils/backendError.ts)
before calling addError, so backend-specific reasons like “still referenced” are
surfaced to the user. Keep the existing success path unchanged and preserve the
isDeleting reset on failure.
libs/sdk-ui-catalog/src/tests/AnalyticsCatalogDetail.test.tsx (1)

86-99: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add negative-path coverage for the metric editor feature flag.

Only the enabled-flag path is tested. Consider adding a case with enableAnalyticalCatalogMetricEditor: false (or canEdit: false) asserting the metric edit UI does not render, to lock in the gating behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sdk-ui-catalog/src/tests/AnalyticsCatalogDetail.test.tsx` around lines
86 - 99, Add a negative-path test for the metric editor gating in
AnalyticsCatalogDetail.test.tsx: the current test only covers the enabled path
in AnalyticsCatalogDetailContent. Add a case using the same setup but with
enableAnalyticalCatalogMetricEditor disabled (or canEdit set to false) and
assert the edit button/action bar does not render. Keep the coverage alongside
the existing render test so the flag behavior is locked in.
libs/sdk-ui-catalog/src/header/CreateObjectButton.tsx (1)

19-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Logic looks correct; verify test coverage for the new metric-create branch.

The conditional redirect/in-catalog split and handleSelect routing for ObjectTypes.METRIC look correct. The provided CreateObjectButton.test.tsx diff only touches the parameter-creation error path; no test appears to exercise showMetricEditor=true opening MetricCreateDialog or verify handleMetricCreated/refetchObjectType(ObjectTypes.METRIC).

Also applies to: 40-97, 108-115, 160-162

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sdk-ui-catalog/src/header/CreateObjectButton.tsx` at line 19, The new
metric-create branch in CreateObjectButton needs test coverage. Add or update
tests in CreateObjectButton.test.tsx to exercise the showMetricEditor=true path
that renders MetricCreateDialog, and verify the metric flow calls
handleMetricCreated and refetchObjectType(ObjectTypes.METRIC) after creation.
Use the CreateObjectButton, handleSelect, and handleMetricCreated paths to
locate the relevant behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/sdk-e2e-utils/src/goodmock.ts`:
- Around line 310-314: Add the missing no-console suppression before the new
console.warn in goodmock.ts so it matches the existing console.* usages in this
file; update the catch block around sanitizeCredentials / resolveSettings to
include the same // eslint-disable-next-line no-console comment pattern used
elsewhere, keeping the warning call unchanged.
- Around line 233-274: The snapshot flow in snapshotAndSaveRecording parses the
response body without checking whether fetch() returned an error status. Add an
explicit snapshotRes.ok guard immediately after the POST to
/__admin/recordings/snapshot, and surface a clear snapshot-specific error before
calling snapshotRes.json(). Keep the fix localized to snapshotAndSaveRecording
so non-2xx goodmock failures don’t turn into misleading JSON parse exceptions.

In `@libs/sdk-ui-catalog/styles/scss/detail.scss`:
- Line 6: The `@use` import in the stylesheet is using the explicit .scss
extension, which triggers the scss/load-partial-extension lint rule. Update the
import in detail.scss to reference the partial by its basename only, and keep
the change localized to the existing `@use` statement for codeDialog.scss.

---

Nitpick comments:
In `@libs/sdk-e2e-utils/src/goodmock.ts`:
- Around line 362-384: The masking logic in goodmock’s secret sanitization
re-computes escapedSecret and maskedForm separately in the replacement loop and
the leak-guard loop, which risks the two paths drifting apart. Refactor the
handling around effectiveSecretMappings in goodmock so each secret’s escaped
secret and masked pattern are computed once and reused in both replacement and
validation, keeping the sanitization and leak-check behavior consistent.

In `@libs/sdk-ui-catalog/src/header/CreateObjectButton.tsx`:
- Line 19: The new metric-create branch in CreateObjectButton needs test
coverage. Add or update tests in CreateObjectButton.test.tsx to exercise the
showMetricEditor=true path that renders MetricCreateDialog, and verify the
metric flow calls handleMetricCreated and refetchObjectType(ObjectTypes.METRIC)
after creation. Use the CreateObjectButton, handleSelect, and
handleMetricCreated paths to locate the relevant behavior.

In `@libs/sdk-ui-catalog/src/metric/MetricDeleteDialog.tsx`:
- Around line 61-72: The delete flow in MetricDeleteDialog’s handleDelete
swallows the backend error, so all failures show the same generic toast. Update
the catch block to capture the thrown error from mutation.delete(item) and pass
it through extractBackendErrorDetail (as used in ParameterDialog and
utils/backendError.ts) before calling addError, so backend-specific reasons like
“still referenced” are surfaced to the user. Keep the existing success path
unchanged and preserve the isDeleting reset on failure.

In `@libs/sdk-ui-catalog/src/metric/MetricDetailActions.tsx`:
- Around line 47-56: Update the MetricDetailActions component API so onOpen in
IMetricDetailActionsProps matches the actual event shape handled by
handleActionsMenuSelect, which can pass either a mouse or keyboard event. Adjust
the onOpen callback typing and any related call sites in MetricDetailActions to
use the shared union type instead of casting, so keyboard-triggered opens are
represented correctly without unsafe assumptions.

In `@libs/sdk-ui-catalog/src/metric/MetricDialog.tsx`:
- Around line 1-227: `MetricDialog` is a near-copy of `ParameterDialog`, with
the same state, submit/duplicate handling, footer rendering, and `ConfirmDialog`
wiring repeated here. Refactor the shared dialog behavior into a reusable
`AsCodeDialog` (or equivalent) and parameterize it with the metric-specific
pieces: validation/serialization functions, docs URL, and message bundle. Keep
`MetricDialog` as a thin wrapper that supplies metric-specific dependencies and
symbols like `handleSubmit`, `handleDuplicate`, `footerLeftRenderer`, and
`MetricYamlEditor`.

In `@libs/sdk-ui-catalog/src/metric/MetricYamlEditor.tsx`:
- Around line 1-48: The MetricYamlEditor component is a near-duplicate of
ParameterYamlEditor and should be refactored to avoid drift as more as-code
editors are added. Extract the shared state, useMemo extensions, handleChange,
and SyntaxHighlightingInput wiring into a generic AsCodeYamlEditor component,
and pass the editor-specific pieces (such as createMetricYamlLinter and
metricSchemaCompletions) as props or config from MetricYamlEditor. Keep
MetricYamlEditor as a thin wrapper that supplies its linter/completions and
class name while reusing the shared implementation.

In `@libs/sdk-ui-catalog/src/tests/AnalyticsCatalogDetail.test.tsx`:
- Around line 86-99: Add a negative-path test for the metric editor gating in
AnalyticsCatalogDetail.test.tsx: the current test only covers the enabled path
in AnalyticsCatalogDetailContent. Add a case using the same setup but with
enableAnalyticalCatalogMetricEditor disabled (or canEdit set to false) and
assert the edit button/action bar does not render. Keep the coverage alongside
the existing render test so the flag behavior is locked in.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8eb5725c-5b95-48f7-90b9-2b1266801605

📥 Commits

Reviewing files that changed from the base of the PR and between b67c3ed and a756876.

⛔ Files ignored due to path filters (1)
  • common/config/rush/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (126)
  • NOTICE
  • common/changes/@gooddata/sdk-ui-all/c.jma-F1-2657-as-code-metric-editor_2026-07-01-12-04-41.json
  • common/changes/@gooddata/sdk-ui-all/gen-ai-llm-test-secret-redaction_2026-07-06-05-10-18.json
  • common/config/rush/version-policies.json
  • examples/playground/package.json
  • examples/sdk-interactive-examples/examples-template/package.json
  • examples/sdk-interactive-examples/examples/example-attributefilter/package.json
  • examples/sdk-interactive-examples/examples/example-chartconfig/package.json
  • examples/sdk-interactive-examples/examples/example-columnchart/package.json
  • examples/sdk-interactive-examples/examples/example-combochart/package.json
  • examples/sdk-interactive-examples/examples/example-dashboard/package.json
  • examples/sdk-interactive-examples/examples/example-datefilter/package.json
  • examples/sdk-interactive-examples/examples/example-dependentfilters/package.json
  • examples/sdk-interactive-examples/examples/example-execute/package.json
  • examples/sdk-interactive-examples/examples/example-granularity/package.json
  • examples/sdk-interactive-examples/examples/example-headline/package.json
  • examples/sdk-interactive-examples/examples/example-pivottable/package.json
  • examples/sdk-interactive-examples/examples/example-relativedatefilter/package.json
  • examples/sdk-interactive-examples/examples/example-repeater/package.json
  • examples/sdk-interactive-examples/package.json
  • libs/api-client-tiger/package.json
  • libs/sdk-backend-base/package.json
  • libs/sdk-backend-mockingbird/package.json
  • libs/sdk-backend-spi/package.json
  • libs/sdk-backend-tiger/package.json
  • libs/sdk-backend-tiger/src/backend/features/feature.ts
  • libs/sdk-backend-tiger/src/backend/uiFeatures.ts
  • libs/sdk-code-convertors/package.json
  • libs/sdk-code-convertors/python/pyproject.toml
  • libs/sdk-code-schemas/package.json
  • libs/sdk-e2e-utils/api/sdk-e2e-utils.api.md
  • libs/sdk-e2e-utils/package.json
  • libs/sdk-e2e-utils/src/goodmock.ts
  • libs/sdk-e2e-utils/src/index.ts
  • libs/sdk-e2e-utils/src/playwright.ts
  • libs/sdk-embedding/package.json
  • libs/sdk-model/api/sdk-model.api.md
  • libs/sdk-model/package.json
  • libs/sdk-model/src/settings/settings.ts
  • libs/sdk-pluggable-application-model/package.json
  • libs/sdk-ui-all/package.json
  • libs/sdk-ui-application-header/package.json
  • libs/sdk-ui-catalog/package.json
  • libs/sdk-ui-catalog/src/AnalyticsCatalogDetail.tsx
  • libs/sdk-ui-catalog/src/catalog/Catalog.tsx
  • libs/sdk-ui-catalog/src/catalogDetail/CatalogDetailActionBar.tsx
  • libs/sdk-ui-catalog/src/catalogDetail/CatalogDetailActions.tsx
  • libs/sdk-ui-catalog/src/catalogDetail/CatalogDetailContent.tsx
  • libs/sdk-ui-catalog/src/catalogItem/query.ts
  • libs/sdk-ui-catalog/src/header/CreateObjectButton.tsx
  • libs/sdk-ui-catalog/src/header/tests/CreateObjectButton.test.tsx
  • libs/sdk-ui-catalog/src/localization/bundles/en-US.json
  • libs/sdk-ui-catalog/src/main/Main.tsx
  • libs/sdk-ui-catalog/src/metric/MetricCreateDialog.tsx
  • libs/sdk-ui-catalog/src/metric/MetricDeleteDialog.tsx
  • libs/sdk-ui-catalog/src/metric/MetricDetailActions.tsx
  • libs/sdk-ui-catalog/src/metric/MetricDialog.tsx
  • libs/sdk-ui-catalog/src/metric/MetricEditDialog.tsx
  • libs/sdk-ui-catalog/src/metric/MetricMutationContext.tsx
  • libs/sdk-ui-catalog/src/metric/MetricYamlEditor.tsx
  • libs/sdk-ui-catalog/src/metric/gate.ts
  • libs/sdk-ui-catalog/src/metric/metricCompletions.ts
  • libs/sdk-ui-catalog/src/metric/metricConverter.ts
  • libs/sdk-ui-catalog/src/metric/metricCopy.ts
  • libs/sdk-ui-catalog/src/metric/metricMutationPort.ts
  • libs/sdk-ui-catalog/src/metric/metricSchema.ts
  • libs/sdk-ui-catalog/src/metric/metricSerialization.ts
  • libs/sdk-ui-catalog/src/metric/metricValidation.ts
  • libs/sdk-ui-catalog/src/metric/metricYamlLinter.ts
  • libs/sdk-ui-catalog/src/metric/tests/MetricDeleteDialog.test.tsx
  • libs/sdk-ui-catalog/src/metric/tests/MetricDetailActions.test.tsx
  • libs/sdk-ui-catalog/src/metric/tests/metricConverter.test.ts
  • libs/sdk-ui-catalog/src/metric/tests/metricCopy.test.ts
  • libs/sdk-ui-catalog/src/metric/tests/metricMutationPort.test.ts
  • libs/sdk-ui-catalog/src/metric/tests/metricMutationPort.test.utils.ts
  • libs/sdk-ui-catalog/src/metric/tests/metricSchema.test.ts
  • libs/sdk-ui-catalog/src/metric/tests/metricSerialization.test.ts
  • libs/sdk-ui-catalog/src/metric/tests/metricValidation.test.ts
  • libs/sdk-ui-catalog/src/parameter/ParameterDialog.tsx
  • libs/sdk-ui-catalog/src/parameter/ParameterYamlEditor.tsx
  • libs/sdk-ui-catalog/src/parameter/yamlLinter.ts
  • libs/sdk-ui-catalog/src/tests/AnalyticsCatalogDetail.test.tsx
  • libs/sdk-ui-catalog/src/utils/backendError.ts
  • libs/sdk-ui-catalog/src/utils/tests/backendError.test.ts
  • libs/sdk-ui-catalog/src/utils/yamlSyntaxLinter.ts
  • libs/sdk-ui-catalog/styles/scss/codeDialog.scss
  • libs/sdk-ui-catalog/styles/scss/detail.scss
  • libs/sdk-ui-charts/package.json
  • libs/sdk-ui-dashboard/package.json
  • libs/sdk-ui-ext/package.json
  • libs/sdk-ui-filters/package.json
  • libs/sdk-ui-gen-ai/package.json
  • libs/sdk-ui-geo/package.json
  • libs/sdk-ui-kit/package.json
  • libs/sdk-ui-loaders/package.json
  • libs/sdk-ui-pivot/package.json
  • libs/sdk-ui-pluggable-application/package.json
  • libs/sdk-ui-pluggable-host/package.json
  • libs/sdk-ui-semantic-search/package.json
  • libs/sdk-ui-tests-app/package.json
  • libs/sdk-ui-tests-e2e/package.json
  • libs/sdk-ui-tests-reference-workspace/package.json
  • libs/sdk-ui-tests-scenarios/package.json
  • libs/sdk-ui-tests-storybook/package.json
  • libs/sdk-ui-theme-provider/package.json
  • libs/sdk-ui-vis-commons/package.json
  • libs/sdk-ui-web-components/package.json
  • libs/sdk-ui/package.json
  • libs/util/package.json
  • templates/pluggable-module/harness/package.json
  • templates/pluggable-module/module/package.json
  • tools/app-toolkit/package.json
  • tools/applink/package.json
  • tools/catalog-export/package.json
  • tools/create-pluggable-module/package.json
  • tools/dashboard-plugin-template/package.json
  • tools/eslint-config/package.json
  • tools/i18n-toolkit/package.json
  • tools/lint-config/package.json
  • tools/mock-handling/package.json
  • tools/oxlint-config/package.json
  • tools/plugin-toolkit/package.json
  • tools/react-app-template/package.json
  • tools/reference-workspace-mgmt/package.json
  • tools/reference-workspace/package.json
  • tools/stylelint-config/package.json

Comment on lines +233 to +274
* A single snapshot is taken with repeatsAsScenarios enabled: goodmock turns a
* request into a scenario chain only when its responses differ across the
* recording, so stateful sequences are preserved and everything else stays flat.
*
* @param host -
* @param mappingFilePath -
* @param workspaceIdMappings - Optional source/target workspace ID rewrite(s)
* applied before mappings are saved. Accepts a single mapping or an array.
* @param backendHost - Optional real backend URL (e.g. "https://example.gooddata.com").
* When provided together with baseUrl, all occurrences are replaced in the
* saved mappings so that recorded responses (e.g. geo tile URLs) resolve
* correctly during replay.
* @param baseUrl - Optional app base URL (e.g. "http://kpi-dashboards-ui:9500")
* that replaces backendHost references in the saved mappings.
* @param host - Goodmock host:port (e.g. "backend-mock:8080")
* @param mappingFilePath - Absolute path to write the mapping JSON file to
* @param options - See {@link ISnapshotAndSaveRecordingOptions}.
*/
export async function snapshotAndSaveRecording(
host: string,
mappingFilePath: string,
workspaceIdMappings?: IWorkspaceIdMapping | IWorkspaceIdMapping[],
backendHost?: string,
baseUrl?: string,
options: ISnapshotAndSaveRecordingOptions = {},
): Promise<void> {
// Snapshot executionResults with scenarios
const scenariosRes = await fetch(`http://${host}/__admin/recordings/snapshot`, {
const { workspaceIdMappings, backendHost, baseUrl, secretMappings, leakPatterns, sanitizeMappings } =
options;
// Snapshot everything with scenarios enabled. goodmock only emits a scenario
// chain when a given request produced *different* responses across the
// recording; identical repeats collapse to a single mapping. This captures
// stateful sequences (e.g. a GET whose result changes after a POST) that the
// old per-URL dedup path silently dropped, while keeping idempotent traffic flat.
const snapshotRes = await fetch(`http://${host}/__admin/recordings/snapshot`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
repeatsAsScenarios: true,
filters: { urlPattern: ".*executionResults.*" },
persist: false,
...snapshotParams,
}),
});
const scenariosData = (await scenariosRes.json()) as { mappings: IGoodmockMapping[] };
const snapshotData = (await snapshotRes.json()) as { mappings: IGoodmockMapping[] };

// Snapshot everything else without scenarios
const plainRes = await fetch(`http://${host}/__admin/recordings/snapshot`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
repeatsAsScenarios: false,
filters: { urlPattern: "((?!executionResults).)*" },
persist: false,
...snapshotParams,
}),
});
const plainData = (await plainRes.json()) as { mappings: IGoodmockMapping[] };
const mappings = [...snapshotData.mappings];

const mappings = [...plainData.mappings, ...scenariosData.mappings];
// An empty snapshot means the recording journal was lost (e.g. a worker crash re-ran the
// beforeAll reset mid-spec) or the spec made no requests at all. Never overwrite a previous
// recording with an empty one, and fail the run so the broken recording cannot go unnoticed.
if (mappings.length === 0) {
throw new Error(
`Recording snapshot for ${mappingFilePath} is empty — keeping the existing mapping ` +
`file. Investigate (worker crash mid-spec? goodmock proxy misconfigured?) and re-record.`,
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether `snapshotParams` is declared anywhere in this file.
rg -n 'snapshotParams' libs/sdk-e2e-utils/src/goodmock.ts

Repository: gooddata/gooddata-ui-sdk

Length of output: 226


🏁 Script executed:

#!/bin/bash
sed -n '110,140p' libs/sdk-e2e-utils/src/goodmock.ts
printf '\n---\n'
sed -n '233,285p' libs/sdk-e2e-utils/src/goodmock.ts

Repository: gooddata/gooddata-ui-sdk

Length of output: 3821


Handle non-2xx snapshot responses before parsing JSON.

fetch() can return an error response here; check snapshotRes.ok first so goodmock failures surface as a clear snapshot error instead of a JSON parse exception.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sdk-e2e-utils/src/goodmock.ts` around lines 233 - 274, The snapshot flow
in snapshotAndSaveRecording parses the response body without checking whether
fetch() returned an error status. Add an explicit snapshotRes.ok guard
immediately after the POST to /__admin/recordings/snapshot, and surface a clear
snapshot-specific error before calling snapshotRes.json(). Keep the fix
localized to snapshotAndSaveRecording so non-2xx goodmock failures don’t turn
into misleading JSON parse exceptions.

Comment on lines 310 to 314
} catch {
console.warn(
`sanitizeCredentials – resolveSettings body is not valid JSON for url: ${requestUrl}`,
`sanitizeCredentials – resolveSettings body is not valid JSON for: ${requestPath}`,
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing no-console eslint-disable comment on the new console.warn.

Every other console.* call in this file (e.g. lines 222-223, 405-406) is preceded by // eslint-disable-next-line no-console. This new one isn't, which will likely fail lint.

🧹 Suggested fix
             } catch {
+                // eslint-disable-next-line no-console
                 console.warn(
                     `sanitizeCredentials – resolveSettings body is not valid JSON for: ${requestPath}`,
                 );
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch {
console.warn(
`sanitizeCredentials – resolveSettings body is not valid JSON for url: ${requestUrl}`,
`sanitizeCredentials – resolveSettings body is not valid JSON for: ${requestPath}`,
);
}
} catch {
// eslint-disable-next-line no-console
console.warn(
`sanitizeCredentials – resolveSettings body is not valid JSON for: ${requestPath}`,
);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sdk-e2e-utils/src/goodmock.ts` around lines 310 - 314, Add the missing
no-console suppression before the new console.warn in goodmock.ts so it matches
the existing console.* usages in this file; update the catch block around
sanitizeCredentials / resolveSettings to include the same //
eslint-disable-next-line no-console comment pattern used elsewhere, keeping the
warning call unchanged.


@use "./objectType.scss";
@use "./parameter.scss";
@use "./codeDialog.scss";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Drop the .scss extension in the @use path.

Stylelint's scss/load-partial-extension flags this import.

🔧 Proposed fix
-@use "./codeDialog.scss";
+@use "./codeDialog";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@use "./codeDialog.scss";
`@use` "./codeDialog";
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 6-6: Unexpected extension ".scss" in @use (scss/load-partial-extension)

(scss/load-partial-extension)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/sdk-ui-catalog/styles/scss/detail.scss` at line 6, The `@use` import in
the stylesheet is using the explicit .scss extension, which triggers the
scss/load-partial-extension lint rule. Update the import in detail.scss to
reference the partial by its basename only, and keep the change localized to the
existing `@use` statement for codeDialog.scss.

Source: Linters/SAST tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sync PR is created from gdc-ui

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants