From 7e4643ab277fa92c54df807f0b1663ac29bdffbc Mon Sep 17 00:00:00 2001 From: Kristina Date: Thu, 7 May 2026 07:55:06 -0500 Subject: [PATCH] Correlations: Allow target UID to be nullable for PATCH removal (#124166) * backend changes * frontend changes --- apps/correlations/kinds/correlations.cue | 2 +- .../correlation/v0alpha1/correlation_spec_gen.go | 13 +++++++------ apps/correlations/pkg/apis/correlation_manifest.go | 2 +- apps/correlations/pkg/app/app.go | 2 ++ .../correlation/v0alpha1/types.spec.gen.ts | 3 ++- .../rtkq/correlations/v0alpha1/endpoints.gen.ts | 3 ++- .../src/apis/correlations.grafana.app-v0alpha1.json | 8 +++++++- .../correlations.grafana.app-v0alpha1.json | 8 +++++++- public/app/features/correlations/utils.test.ts | 4 ++-- public/app/features/correlations/utils.ts | 7 +++++++ 10 files changed, 38 insertions(+), 14 deletions(-) diff --git a/apps/correlations/kinds/correlations.cue b/apps/correlations/kinds/correlations.cue index 7c99a78dbf6..b3f3f7f5322 100644 --- a/apps/correlations/kinds/correlations.cue +++ b/apps/correlations/kinds/correlations.cue @@ -13,7 +13,7 @@ correlationsv0alpha1: { spec: { type: CorrelationType source: DataSourceRef - target?: DataSourceRef + target?: DataSourceRef | null // null is for PATCH/edit when we want to clear the value description?: string label: string config: ConfigSpec diff --git a/apps/correlations/pkg/apis/correlation/v0alpha1/correlation_spec_gen.go b/apps/correlations/pkg/apis/correlation/v0alpha1/correlation_spec_gen.go index 6ac54aa717c..37f4e02489e 100644 --- a/apps/correlations/pkg/apis/correlation/v0alpha1/correlation_spec_gen.go +++ b/apps/correlations/pkg/apis/correlation/v0alpha1/correlation_spec_gen.go @@ -79,12 +79,13 @@ func (CorrelationTransformationSpec) OpenAPIModelName() string { // +k8s:openapi-gen=true type CorrelationSpec struct { - Type CorrelationCorrelationType `json:"type"` - Source CorrelationDataSourceRef `json:"source"` - Target *CorrelationDataSourceRef `json:"target,omitempty"` - Description *string `json:"description,omitempty"` - Label string `json:"label"` - Config CorrelationConfigSpec `json:"config"` + Type CorrelationCorrelationType `json:"type"` + Source CorrelationDataSourceRef `json:"source"` + // null is for PATCH/edit when we want to clear the value + Target *CorrelationDataSourceRef `json:"target,omitempty"` + Description *string `json:"description,omitempty"` + Label string `json:"label"` + Config CorrelationConfigSpec `json:"config"` } // NewCorrelationSpec creates a new CorrelationSpec object. diff --git a/apps/correlations/pkg/apis/correlation_manifest.go b/apps/correlations/pkg/apis/correlation_manifest.go index 1af213d6b85..ab790fb5d3e 100644 --- a/apps/correlations/pkg/apis/correlation_manifest.go +++ b/apps/correlations/pkg/apis/correlation_manifest.go @@ -20,7 +20,7 @@ import ( ) var ( - rawSchemaCorrelationv0alpha1 = []byte(`{"ConfigSpec":{"additionalProperties":false,"description":"there was a deprecated field here called type, we will need to move that for conversion and provisioning","properties":{"field":{"type":"string"},"target":{"$ref":"#/components/schemas/TargetSpec"},"transformations":{"items":{"$ref":"#/components/schemas/TransformationSpec"},"type":"array"}},"required":["field","target"],"type":"object"},"Correlation":{"properties":{"spec":{"$ref":"#/components/schemas/spec"}},"required":["spec"]},"CorrelationType":{"enum":["query","external"],"type":"string"},"DataSourceRef":{"additionalProperties":false,"properties":{"group":{"description":"same as pluginId","type":"string"},"name":{"description":"same as grafana uid","type":"string"}},"required":["group","name"],"type":"object"},"TargetSpec":{"additionalProperties":true,"type":"object"},"TransformationSpec":{"additionalProperties":false,"properties":{"expression":{"type":"string"},"field":{"type":"string"},"mapValue":{"type":"string"},"type":{"enum":["regex","logfmt"],"type":"string"}},"required":["type"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"config":{"$ref":"#/components/schemas/ConfigSpec"},"description":{"type":"string"},"label":{"type":"string"},"source":{"$ref":"#/components/schemas/DataSourceRef"},"target":{"$ref":"#/components/schemas/DataSourceRef"},"type":{"$ref":"#/components/schemas/CorrelationType"}},"required":["type","source","label","config"],"type":"object"}}`) + rawSchemaCorrelationv0alpha1 = []byte(`{"ConfigSpec":{"additionalProperties":false,"description":"there was a deprecated field here called type, we will need to move that for conversion and provisioning","properties":{"field":{"type":"string"},"target":{"$ref":"#/components/schemas/TargetSpec"},"transformations":{"items":{"$ref":"#/components/schemas/TransformationSpec"},"type":"array"}},"required":["field","target"],"type":"object"},"Correlation":{"properties":{"spec":{"$ref":"#/components/schemas/spec"}},"required":["spec"]},"CorrelationType":{"enum":["query","external"],"type":"string"},"DataSourceRef":{"additionalProperties":false,"properties":{"group":{"description":"same as pluginId","type":"string"},"name":{"description":"same as grafana uid","type":"string"}},"required":["group","name"],"type":"object"},"TargetSpec":{"additionalProperties":true,"type":"object"},"TransformationSpec":{"additionalProperties":false,"properties":{"expression":{"type":"string"},"field":{"type":"string"},"mapValue":{"type":"string"},"type":{"enum":["regex","logfmt"],"type":"string"}},"required":["type"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"config":{"$ref":"#/components/schemas/ConfigSpec"},"description":{"type":"string"},"label":{"type":"string"},"source":{"$ref":"#/components/schemas/DataSourceRef"},"target":{"allOf":[{"$ref":"#/components/schemas/DataSourceRef"}],"description":"null is for PATCH/edit when we want to clear the value","nullable":true},"type":{"$ref":"#/components/schemas/CorrelationType"}},"required":["type","source","label","config"],"type":"object"}}`) versionSchemaCorrelationv0alpha1 app.VersionSchema _ = json.Unmarshal(rawSchemaCorrelationv0alpha1, &versionSchemaCorrelationv0alpha1) ) diff --git a/apps/correlations/pkg/app/app.go b/apps/correlations/pkg/app/app.go index bbb13bed3f0..514586c9144 100644 --- a/apps/correlations/pkg/app/app.go +++ b/apps/correlations/pkg/app/app.go @@ -86,6 +86,8 @@ func DataSourceMutator() *simple.Mutator { c.Labels[TargetRefLabelKey] = fmt.Sprintf("%s.%s", c.Spec.Target.Group, c.Spec.Target.Name) + } else { + delete(c.Labels, TargetRefLabelKey) } return &app.MutatingResponse{UpdatedObject: c}, nil diff --git a/apps/correlations/plugin/src/generated/correlation/v0alpha1/types.spec.gen.ts b/apps/correlations/plugin/src/generated/correlation/v0alpha1/types.spec.gen.ts index 07e003f66ae..f849ee20b6d 100644 --- a/apps/correlations/plugin/src/generated/correlation/v0alpha1/types.spec.gen.ts +++ b/apps/correlations/plugin/src/generated/correlation/v0alpha1/types.spec.gen.ts @@ -49,7 +49,8 @@ export const defaultTransformationSpec = (): TransformationSpec => ({ export interface Spec { type: CorrelationType; source: DataSourceRef; - target?: DataSourceRef; + // null is for PATCH/edit when we want to clear the value + target?: DataSourceRef | null; description?: string; label: string; config: ConfigSpec; diff --git a/packages/grafana-api-clients/src/clients/rtkq/correlations/v0alpha1/endpoints.gen.ts b/packages/grafana-api-clients/src/clients/rtkq/correlations/v0alpha1/endpoints.gen.ts index 9ce2d63e700..69e2a648a6d 100644 --- a/packages/grafana-api-clients/src/clients/rtkq/correlations/v0alpha1/endpoints.gen.ts +++ b/packages/grafana-api-clients/src/clients/rtkq/correlations/v0alpha1/endpoints.gen.ts @@ -380,7 +380,8 @@ export type CorrelationSpec = { description?: string; label: string; source: CorrelationDataSourceRef; - target?: CorrelationDataSourceRef; + /** null is for PATCH/edit when we want to clear the value */ + target?: CorrelationDataSourceRef | null; type: CorrelationCorrelationType; }; export type Correlation = { diff --git a/packages/grafana-openapi/src/apis/correlations.grafana.app-v0alpha1.json b/packages/grafana-openapi/src/apis/correlations.grafana.app-v0alpha1.json index 2a68ed63fde..9baf72006e6 100644 --- a/packages/grafana-openapi/src/apis/correlations.grafana.app-v0alpha1.json +++ b/packages/grafana-openapi/src/apis/correlations.grafana.app-v0alpha1.json @@ -742,7 +742,13 @@ "$ref": "#/components/schemas/CorrelationDataSourceRef" }, "target": { - "$ref": "#/components/schemas/CorrelationDataSourceRef" + "description": "null is for PATCH/edit when we want to clear the value", + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/CorrelationDataSourceRef" + } + ] }, "type": { "$ref": "#/components/schemas/CorrelationCorrelationType" diff --git a/pkg/tests/apis/openapi_snapshots/correlations.grafana.app-v0alpha1.json b/pkg/tests/apis/openapi_snapshots/correlations.grafana.app-v0alpha1.json index bdadfa3af8e..677999262a2 100644 --- a/pkg/tests/apis/openapi_snapshots/correlations.grafana.app-v0alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/correlations.grafana.app-v0alpha1.json @@ -798,7 +798,13 @@ "$ref": "#/components/schemas/com.github.grafana.grafana.apps.correlations.pkg.apis.correlation.v0alpha1.CorrelationDataSourceRef" }, "target": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.correlations.pkg.apis.correlation.v0alpha1.CorrelationDataSourceRef" + "description": "null is for PATCH/edit when we want to clear the value", + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.correlations.pkg.apis.correlation.v0alpha1.CorrelationDataSourceRef" + } + ] }, "type": { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.correlations.pkg.apis.correlation.v0alpha1.CorrelationCorrelationType" diff --git a/public/app/features/correlations/utils.test.ts b/public/app/features/correlations/utils.test.ts index 935a088185e..7dc360bec2f 100644 --- a/public/app/features/correlations/utils.test.ts +++ b/public/app/features/correlations/utils.test.ts @@ -141,7 +141,7 @@ describe('correlations utils', () => { config.featureToggles.lokiLogsDataplane = originalDataplaneState; }); - it('generates a partial spec with config only when nothing is edited', () => { + it('generates a partial spec with config and nulled target only when nothing is edited and the correlation is external', () => { const correlation: Correlation = { uid: 'test', sourceUID: 'test', @@ -152,7 +152,7 @@ describe('correlations utils', () => { }; const editForm: EditFormDTO = { ...correlation, label: correlation.label! }; const partialSpec = generatePartialEditSpec(editForm, correlation); - expect(partialSpec).toStrictEqual({ config: { field: 'test', target: { url: 'test' } } }); + expect(partialSpec).toStrictEqual({ config: { field: 'test', target: { url: 'test' } }, target: null }); }); it('generates a partial spec as expected when things are edited', () => { diff --git a/public/app/features/correlations/utils.ts b/public/app/features/correlations/utils.ts index 78ffbd92375..501818b6645 100644 --- a/public/app/features/correlations/utils.ts +++ b/public/app/features/correlations/utils.ts @@ -150,6 +150,13 @@ export const generateDefaultLabel = async (sourcePane: ExploreItemState, targetP export const generatePartialEditSpec = (data: EditFormDTO, correlation: Correlation): Partial => { let partialSpec: Partial = {}; + + // we will want to clear any target data if the correlation is being updated to external + // null sent in a PATCH will delete the property + if (data.type === 'external') { + partialSpec.target = null; + } + if (data.label !== correlation.label) { partialSpec.label = data.label; }