From c51c901779e7cc1a60c1757475713a2354c9fb3c Mon Sep 17 00:00:00 2001 From: Sandor Karpf Date: Mon, 8 Dec 2025 18:57:33 +0100 Subject: [PATCH 1/5] fix: Typescript type and Zod parser were not the same. Signed-off-by: Sandor Karpf --- .changeset/green-pears-jog.md | 5 + plugins/catalog-react/report-alpha.api.md | 4 +- .../createEntityPredicateSchema.test.ts | 156 ++++++++++-------- .../src/alpha/predicates/types.ts | 4 +- 4 files changed, 96 insertions(+), 73 deletions(-) create mode 100644 .changeset/green-pears-jog.md diff --git a/.changeset/green-pears-jog.md b/.changeset/green-pears-jog.md new file mode 100644 index 0000000000..112bff3947 --- /dev/null +++ b/.changeset/green-pears-jog.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +$contains may have string value in an entity filter. Typescript type and Zod parser were not the same. diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md index 4e55240955..44e8c29d30 100644 --- a/plugins/catalog-react/report-alpha.api.md +++ b/plugins/catalog-react/report-alpha.api.md @@ -478,6 +478,7 @@ export const EntityIconLinkBlueprint: ExtensionBlueprint<{ export type EntityPredicate = | EntityPredicateExpression | EntityPredicatePrimitive + | EntityPredicatePrimitive[] | { $all: EntityPredicate[]; } @@ -506,6 +507,7 @@ export function entityPredicateToFilterFunction( // @alpha (undocumented) export type EntityPredicateValue = | EntityPredicatePrimitive + | EntityPredicatePrimitive[] | { $exists: boolean; } @@ -513,7 +515,7 @@ export type EntityPredicateValue = $in: EntityPredicatePrimitive[]; } | { - $contains: EntityPredicateExpression; + $contains: EntityPredicate; }; // @alpha diff --git a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts index 4fdf921c1a..24d75c76fa 100644 --- a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts +++ b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts @@ -16,84 +16,98 @@ import { z } from 'zod'; import { createEntityPredicateSchema } from './createEntityPredicateSchema'; +import { EntityPredicate } from './types'; describe('createEntityPredicateSchema', () => { const schema = createEntityPredicateSchema(z); - it.each([ - 'string', - '', - [], - 1, - { kind: 'component', 'spec.type': 'service' }, - { 'metadata.tags': { $in: ['java'] } }, - { - $all: [ - { 'metadata.tags': { $contains: 'java' } }, - { 'metadata.tags': { $contains: 'spring' } }, - ], - }, - { 'metadata.tags': ['java', 'spring'] }, - { 'metadata.tags': { $in: ['go'] } }, - { 'metadata.tags.0': 'java' }, - { $not: { 'metadata.tags': { $in: ['java'] } } }, - { - $any: [{ kind: 'component', 'spec.type': 'service' }, { kind: 'group' }], - }, - { - relations: { - $contains: { type: 'ownedBy', targetRef: 'group:default/g' }, + describe('valid predicates', () => { + const predicates: EntityPredicate[] = [ + 'string', + '', + [], + 1, + { kind: 'component', 'spec.type': 'service' }, + { 'metadata.tags': { $in: ['java'] } }, + { + $all: [ + { 'metadata.tags': { $contains: 'java' } }, + { 'metadata.tags': { $contains: 'spring' } }, + ], }, - }, - { - metadata: { $contains: { name: 'a' } }, - }, - { kind: 'component', 'spec.type': { $in: ['service', 'website'] } }, - { - $any: [ - { - $all: [ - { - kind: 'component', - 'spec.type': { $in: ['service', 'website'] }, - }, - ], + { 'metadata.tags': ['java', 'spring'] }, + { 'metadata.tags': { $in: ['go'] } }, + { 'metadata.tags.0': 'java' }, + { $not: { 'metadata.tags': { $in: ['java'] } } }, + { + $any: [ + { kind: 'component', 'spec.type': 'service' }, + { kind: 'group' }, + ], + }, + { + relations: { + $contains: { type: 'ownedBy', targetRef: 'group:default/g' }, }, - { $all: [{ kind: 'api', 'spec.type': 'grpc' }] }, - ], - }, - { kind: 'component', 'spec.type': { $in: ['service'] } }, - { 'spec.owner': { $exists: true } }, - { 'spec.owner': { $exists: false } }, - { 'spec.type': 'service' }, - { $not: { 'spec.type': 'service' } }, - { - kind: 'component', - 'metadata.annotations.github.com/repo': { $exists: true }, - }, - { $all: [{ x: { $exists: true } }] }, - { $any: [{ x: { $exists: true } }] }, - { $not: { x: { $exists: true } } }, - { $not: { $all: [{ x: { $exists: true } }] } }, - ])('should accept valid predicate %j', predicate => { - expect(schema.parse(predicate)).toEqual(predicate); + }, + { + metadata: { $contains: { name: 'a' } }, + }, + { kind: 'component', 'spec.type': { $in: ['service', 'website'] } }, + { + $any: [ + { + $all: [ + { + kind: 'component', + 'spec.type': { $in: ['service', 'website'] }, + }, + ], + }, + { $all: [{ kind: 'api', 'spec.type': 'grpc' }] }, + ], + }, + { kind: 'component', 'spec.type': { $in: ['service'] } }, + { 'spec.owner': { $exists: true } }, + { 'spec.owner': { $exists: false } }, + { 'spec.type': 'service' }, + { $not: { 'spec.type': 'service' } }, + { + kind: 'component', + 'metadata.annotations.github.com/repo': { $exists: true }, + }, + { $all: [{ x: { $exists: true } }] }, + { $any: [{ x: { $exists: true } }] }, + { $not: { x: { $exists: true } } }, + { $not: { $all: [{ x: { $exists: true } }] } }, + ]; + + it.each(predicates)('should accept valid predicate %j', predicate => { + expect(schema.parse(predicate)).toEqual(predicate); + }); }); - it.each([ - { kind: { 1: 'foo' } }, - { kind: { foo: 'bar' } }, - { kind: { $unknown: 'foo' } }, - { kind: { $in: 'foo' } }, - { kind: { $in: [{ x: 'foo' }] } }, - { kind: { $in: [{ x: 'foo' }] } }, - { 'spec.type': null }, - { $all: [{ x: { $unknown: true } }] }, - { $any: [{ x: { $unknown: true } }] }, - { $not: { x: { $unknown: true } } }, - { $not: { $all: [{ x: { $unknown: true } }] } }, - { $unknown: 'foo' }, - ])('should reject invalid predicate %j', predicate => { - const result = schema.safeParse(predicate); - expect(result.success).toBe(false); + describe('invalid predicates', () => { + const predicates: Array< + Exclude + > = [ + { kind: { 1: 'foo' } }, + { kind: { foo: 'bar' } }, + { kind: { $unknown: 'foo' } }, + { kind: { $in: 'foo' } }, + { kind: { $in: [{ x: 'foo' }] } }, + { kind: { $in: [{ x: 'foo' }] } }, + { 'spec.type': null }, + { $all: [{ x: { $unknown: true } }] }, + { $any: [{ x: { $unknown: true } }] }, + { $not: { x: { $unknown: true } } }, + { $not: { $all: [{ x: { $unknown: true } }] } }, + { $unknown: 'foo' }, + ]; + + it.each(predicates)('should reject invalid predicate %j', predicate => { + const result = schema.safeParse(predicate); + expect(result.success).toBe(false); + }); }); }); diff --git a/plugins/catalog-react/src/alpha/predicates/types.ts b/plugins/catalog-react/src/alpha/predicates/types.ts index a4a2e9199f..1a7b2faf36 100644 --- a/plugins/catalog-react/src/alpha/predicates/types.ts +++ b/plugins/catalog-react/src/alpha/predicates/types.ts @@ -18,6 +18,7 @@ export type EntityPredicate = | EntityPredicateExpression | EntityPredicatePrimitive + | EntityPredicatePrimitive[] | { $all: EntityPredicate[] } | { $any: EntityPredicate[] } | { $not: EntityPredicate }; @@ -32,9 +33,10 @@ export type EntityPredicateExpression = { /** @alpha */ export type EntityPredicateValue = | EntityPredicatePrimitive + | EntityPredicatePrimitive[] | { $exists: boolean } | { $in: EntityPredicatePrimitive[] } - | { $contains: EntityPredicateExpression }; + | { $contains: EntityPredicate }; /** @alpha */ export type EntityPredicatePrimitive = string | number | boolean; From d1b73d41d2803c3a30c7c1eac146ee21601a46d2 Mon Sep 17 00:00:00 2001 From: Sandor Karpf Date: Wed, 10 Dec 2025 18:08:34 +0100 Subject: [PATCH 2/5] fix: $contains may have only string value additionally in an entity filter. Signed-off-by: Sandor Karpf --- plugins/catalog-react/report-alpha.api.md | 4 +-- .../createEntityPredicateSchema.test.ts | 1 + .../predicates/createEntityPredicateSchema.ts | 35 +++++++++++++------ .../src/alpha/predicates/types.ts | 4 +-- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md index 44e8c29d30..40a83851b6 100644 --- a/plugins/catalog-react/report-alpha.api.md +++ b/plugins/catalog-react/report-alpha.api.md @@ -478,7 +478,7 @@ export const EntityIconLinkBlueprint: ExtensionBlueprint<{ export type EntityPredicate = | EntityPredicateExpression | EntityPredicatePrimitive - | EntityPredicatePrimitive[] + | never[] | { $all: EntityPredicate[]; } @@ -515,7 +515,7 @@ export type EntityPredicateValue = $in: EntityPredicatePrimitive[]; } | { - $contains: EntityPredicate; + $contains: EntityPredicateExpression | string; }; // @alpha diff --git a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts index 24d75c76fa..52e4d097af 100644 --- a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts +++ b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts @@ -91,6 +91,7 @@ describe('createEntityPredicateSchema', () => { const predicates: Array< Exclude > = [ + ['service', 'website'], { kind: { 1: 'foo' } }, { kind: { foo: 'bar' } }, { kind: { $unknown: 'foo' } }, diff --git a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts index cfd2e451df..2dcdadd826 100644 --- a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts +++ b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts @@ -14,36 +14,51 @@ * limitations under the License. */ -import { EntityPredicate, EntityPredicateValue } from './types'; +import { + EntityPredicate, + EntityPredicateExpression, + EntityPredicatePrimitive, + EntityPredicateValue, +} from './types'; import type { z as zImpl, ZodType } from 'zod'; /** @internal */ export function createEntityPredicateSchema(z: typeof zImpl) { - const primitiveSchema = z.union([z.string(), z.number(), z.boolean()]); + const primitiveSchema = z.union([ + z.string(), + z.number(), + z.boolean(), + ]) as ZodType; - const comparableValueSchema = z.union([ - primitiveSchema, - z.array(primitiveSchema), - ]); + const onlyEmptyArraySchema = z.array(z.never()) as ZodType; // eslint-disable-next-line prefer-const let valuePredicateSchema: ZodType; + const expressionSchema = z.lazy(() => + z.union([ + z.record(z.string().regex(/^(?!\$).*$/), valuePredicateSchema), + z.record(z.string().regex(/(?!\$)+/), z.never()), + ]), + ) as ZodType; + const predicateSchema = z.lazy(() => z.union([ - comparableValueSchema, + expressionSchema, + primitiveSchema, + onlyEmptyArraySchema, z.object({ $all: z.array(predicateSchema) }), z.object({ $any: z.array(predicateSchema) }), z.object({ $not: predicateSchema }), - z.record(z.string().regex(/^(?!\$).*$/), valuePredicateSchema), ]), ) as ZodType; valuePredicateSchema = z.union([ - comparableValueSchema, + primitiveSchema, + z.array(primitiveSchema), z.object({ $exists: z.boolean() }), z.object({ $in: z.array(primitiveSchema) }), - z.object({ $contains: predicateSchema }), + z.object({ $contains: z.union([expressionSchema, z.string()]) }), ]) as ZodType; return predicateSchema; diff --git a/plugins/catalog-react/src/alpha/predicates/types.ts b/plugins/catalog-react/src/alpha/predicates/types.ts index 1a7b2faf36..8745610eae 100644 --- a/plugins/catalog-react/src/alpha/predicates/types.ts +++ b/plugins/catalog-react/src/alpha/predicates/types.ts @@ -18,7 +18,7 @@ export type EntityPredicate = | EntityPredicateExpression | EntityPredicatePrimitive - | EntityPredicatePrimitive[] + | never[] | { $all: EntityPredicate[] } | { $any: EntityPredicate[] } | { $not: EntityPredicate }; @@ -36,7 +36,7 @@ export type EntityPredicateValue = | EntityPredicatePrimitive[] | { $exists: boolean } | { $in: EntityPredicatePrimitive[] } - | { $contains: EntityPredicate }; + | { $contains: EntityPredicateExpression | string }; /** @alpha */ export type EntityPredicatePrimitive = string | number | boolean; From 8cf6944d82d6f467fb78faaf806ab31ba49eb788 Mon Sep 17 00:00:00 2001 From: Sandor Karpf Date: Wed, 10 Dec 2025 18:09:07 +0100 Subject: [PATCH 3/5] test: update snapshot tests Signed-off-by: Sandor Karpf --- .../blueprints/EntityCardBlueprint.test.tsx | 142 ++++++++++-------- .../EntityContentBlueprint.test.tsx | 142 ++++++++++-------- .../EntityContextMenuItemBlueprint.test.tsx | 142 ++++++++++-------- 3 files changed, 246 insertions(+), 180 deletions(-) diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx index 3c9a020c93..8780a9f14f 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx @@ -60,20 +60,94 @@ describe('EntityCardBlueprint', () => { { "anyOf": [ { - "type": [ - "string", - "number", - "boolean", - ], + "additionalProperties": { + "anyOf": [ + { + "type": [ + "string", + "number", + "boolean", + ], + }, + { + "items": { + "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0/additionalProperties/anyOf/0", + }, + "type": "array", + }, + { + "additionalProperties": false, + "properties": { + "$exists": { + "type": "boolean", + }, + }, + "required": [ + "$exists", + ], + "type": "object", + }, + { + "additionalProperties": false, + "properties": { + "$in": { + "items": { + "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0/additionalProperties/anyOf/0", + }, + "type": "array", + }, + }, + "required": [ + "$in", + ], + "type": "object", + }, + { + "additionalProperties": false, + "properties": { + "$contains": { + "anyOf": [ + { + "$ref": "#/properties/filter/anyOf/1/anyOf/0", + }, + { + "type": "string", + }, + ], + }, + }, + "required": [ + "$contains", + ], + "type": "object", + }, + ], + }, + "propertyNames": { + "pattern": "^(?!\\$).*$", + }, + "type": "object", }, { - "items": { - "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0", + "additionalProperties": { + "not": {}, }, - "type": "array", + "propertyNames": { + "pattern": "(?!\\$)+", + }, + "type": "object", }, ], }, + { + "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0/additionalProperties/anyOf/0", + }, + { + "items": { + "not": {}, + }, + "type": "array", + }, { "additionalProperties": false, "properties": { @@ -116,58 +190,6 @@ describe('EntityCardBlueprint', () => { ], "type": "object", }, - { - "additionalProperties": { - "anyOf": [ - { - "$ref": "#/properties/filter/anyOf/1/anyOf/0", - }, - { - "additionalProperties": false, - "properties": { - "$exists": { - "type": "boolean", - }, - }, - "required": [ - "$exists", - ], - "type": "object", - }, - { - "additionalProperties": false, - "properties": { - "$in": { - "items": { - "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0", - }, - "type": "array", - }, - }, - "required": [ - "$in", - ], - "type": "object", - }, - { - "additionalProperties": false, - "properties": { - "$contains": { - "$ref": "#/properties/filter/anyOf/1", - }, - }, - "required": [ - "$contains", - ], - "type": "object", - }, - ], - }, - "propertyNames": { - "pattern": "^(?!\\$).*$", - }, - "type": "object", - }, ], }, ], diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx index 729f15079f..7275651315 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx @@ -62,20 +62,94 @@ describe('EntityContentBlueprint', () => { { "anyOf": [ { - "type": [ - "string", - "number", - "boolean", - ], + "additionalProperties": { + "anyOf": [ + { + "type": [ + "string", + "number", + "boolean", + ], + }, + { + "items": { + "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0/additionalProperties/anyOf/0", + }, + "type": "array", + }, + { + "additionalProperties": false, + "properties": { + "$exists": { + "type": "boolean", + }, + }, + "required": [ + "$exists", + ], + "type": "object", + }, + { + "additionalProperties": false, + "properties": { + "$in": { + "items": { + "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0/additionalProperties/anyOf/0", + }, + "type": "array", + }, + }, + "required": [ + "$in", + ], + "type": "object", + }, + { + "additionalProperties": false, + "properties": { + "$contains": { + "anyOf": [ + { + "$ref": "#/properties/filter/anyOf/1/anyOf/0", + }, + { + "type": "string", + }, + ], + }, + }, + "required": [ + "$contains", + ], + "type": "object", + }, + ], + }, + "propertyNames": { + "pattern": "^(?!\\$).*$", + }, + "type": "object", }, { - "items": { - "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0", + "additionalProperties": { + "not": {}, }, - "type": "array", + "propertyNames": { + "pattern": "(?!\\$)+", + }, + "type": "object", }, ], }, + { + "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0/additionalProperties/anyOf/0", + }, + { + "items": { + "not": {}, + }, + "type": "array", + }, { "additionalProperties": false, "properties": { @@ -118,58 +192,6 @@ describe('EntityContentBlueprint', () => { ], "type": "object", }, - { - "additionalProperties": { - "anyOf": [ - { - "$ref": "#/properties/filter/anyOf/1/anyOf/0", - }, - { - "additionalProperties": false, - "properties": { - "$exists": { - "type": "boolean", - }, - }, - "required": [ - "$exists", - ], - "type": "object", - }, - { - "additionalProperties": false, - "properties": { - "$in": { - "items": { - "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0", - }, - "type": "array", - }, - }, - "required": [ - "$in", - ], - "type": "object", - }, - { - "additionalProperties": false, - "properties": { - "$contains": { - "$ref": "#/properties/filter/anyOf/1", - }, - }, - "required": [ - "$contains", - ], - "type": "object", - }, - ], - }, - "propertyNames": { - "pattern": "^(?!\\$).*$", - }, - "type": "object", - }, ], }, ], diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx index 55598f5de1..25339130a7 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx @@ -73,20 +73,94 @@ describe('EntityContextMenuItemBlueprint', () => { { "anyOf": [ { - "type": [ - "string", - "number", - "boolean", - ], + "additionalProperties": { + "anyOf": [ + { + "type": [ + "string", + "number", + "boolean", + ], + }, + { + "items": { + "$ref": "#/properties/filter/anyOf/0/anyOf/0/additionalProperties/anyOf/0", + }, + "type": "array", + }, + { + "additionalProperties": false, + "properties": { + "$exists": { + "type": "boolean", + }, + }, + "required": [ + "$exists", + ], + "type": "object", + }, + { + "additionalProperties": false, + "properties": { + "$in": { + "items": { + "$ref": "#/properties/filter/anyOf/0/anyOf/0/additionalProperties/anyOf/0", + }, + "type": "array", + }, + }, + "required": [ + "$in", + ], + "type": "object", + }, + { + "additionalProperties": false, + "properties": { + "$contains": { + "anyOf": [ + { + "$ref": "#/properties/filter/anyOf/0", + }, + { + "type": "string", + }, + ], + }, + }, + "required": [ + "$contains", + ], + "type": "object", + }, + ], + }, + "propertyNames": { + "pattern": "^(?!\\$).*$", + }, + "type": "object", }, { - "items": { - "$ref": "#/properties/filter/anyOf/0/anyOf/0", + "additionalProperties": { + "not": {}, }, - "type": "array", + "propertyNames": { + "pattern": "(?!\\$)+", + }, + "type": "object", }, ], }, + { + "$ref": "#/properties/filter/anyOf/0/anyOf/0/additionalProperties/anyOf/0", + }, + { + "items": { + "not": {}, + }, + "type": "array", + }, { "additionalProperties": false, "properties": { @@ -129,58 +203,6 @@ describe('EntityContextMenuItemBlueprint', () => { ], "type": "object", }, - { - "additionalProperties": { - "anyOf": [ - { - "$ref": "#/properties/filter/anyOf/0", - }, - { - "additionalProperties": false, - "properties": { - "$exists": { - "type": "boolean", - }, - }, - "required": [ - "$exists", - ], - "type": "object", - }, - { - "additionalProperties": false, - "properties": { - "$in": { - "items": { - "$ref": "#/properties/filter/anyOf/0/anyOf/0", - }, - "type": "array", - }, - }, - "required": [ - "$in", - ], - "type": "object", - }, - { - "additionalProperties": false, - "properties": { - "$contains": { - "$ref": "#/properties/filter", - }, - }, - "required": [ - "$contains", - ], - "type": "object", - }, - ], - }, - "propertyNames": { - "pattern": "^(?!\\$).*$", - }, - "type": "object", - }, ], }, }, From e38200099700d6d4a4a0148452d85eada00ca8e3 Mon Sep 17 00:00:00 2001 From: Sandor Karpf Date: Mon, 15 Dec 2025 18:32:56 +0100 Subject: [PATCH 4/5] fix: disable array type in entity predicate types and zod schema validator Signed-off-by: Sandor Karpf --- plugins/catalog-react/report-alpha.api.md | 4 +--- .../blueprints/EntityCardBlueprint.test.tsx | 21 +------------------ .../EntityContentBlueprint.test.tsx | 21 +------------------ .../EntityContextMenuItemBlueprint.test.tsx | 21 +------------------ .../createEntityPredicateSchema.test.ts | 7 ++++--- .../predicates/createEntityPredicateSchema.ts | 6 +----- .../src/alpha/predicates/types.ts | 4 +--- 7 files changed, 10 insertions(+), 74 deletions(-) diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md index 40a83851b6..49c27318a7 100644 --- a/plugins/catalog-react/report-alpha.api.md +++ b/plugins/catalog-react/report-alpha.api.md @@ -478,7 +478,6 @@ export const EntityIconLinkBlueprint: ExtensionBlueprint<{ export type EntityPredicate = | EntityPredicateExpression | EntityPredicatePrimitive - | never[] | { $all: EntityPredicate[]; } @@ -507,7 +506,6 @@ export function entityPredicateToFilterFunction( // @alpha (undocumented) export type EntityPredicateValue = | EntityPredicatePrimitive - | EntityPredicatePrimitive[] | { $exists: boolean; } @@ -515,7 +513,7 @@ export type EntityPredicateValue = $in: EntityPredicatePrimitive[]; } | { - $contains: EntityPredicateExpression | string; + $contains: EntityPredicate; }; // @alpha diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx index 8780a9f14f..090f559915 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx @@ -69,12 +69,6 @@ describe('EntityCardBlueprint', () => { "boolean", ], }, - { - "items": { - "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0/additionalProperties/anyOf/0", - }, - "type": "array", - }, { "additionalProperties": false, "properties": { @@ -106,14 +100,7 @@ describe('EntityCardBlueprint', () => { "additionalProperties": false, "properties": { "$contains": { - "anyOf": [ - { - "$ref": "#/properties/filter/anyOf/1/anyOf/0", - }, - { - "type": "string", - }, - ], + "$ref": "#/properties/filter/anyOf/1", }, }, "required": [ @@ -142,12 +129,6 @@ describe('EntityCardBlueprint', () => { { "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0/additionalProperties/anyOf/0", }, - { - "items": { - "not": {}, - }, - "type": "array", - }, { "additionalProperties": false, "properties": { diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx index 7275651315..6c0f99b5b4 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx @@ -71,12 +71,6 @@ describe('EntityContentBlueprint', () => { "boolean", ], }, - { - "items": { - "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0/additionalProperties/anyOf/0", - }, - "type": "array", - }, { "additionalProperties": false, "properties": { @@ -108,14 +102,7 @@ describe('EntityContentBlueprint', () => { "additionalProperties": false, "properties": { "$contains": { - "anyOf": [ - { - "$ref": "#/properties/filter/anyOf/1/anyOf/0", - }, - { - "type": "string", - }, - ], + "$ref": "#/properties/filter/anyOf/1", }, }, "required": [ @@ -144,12 +131,6 @@ describe('EntityContentBlueprint', () => { { "$ref": "#/properties/filter/anyOf/1/anyOf/0/anyOf/0/additionalProperties/anyOf/0", }, - { - "items": { - "not": {}, - }, - "type": "array", - }, { "additionalProperties": false, "properties": { diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx index 25339130a7..ddfe63e7e6 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx @@ -82,12 +82,6 @@ describe('EntityContextMenuItemBlueprint', () => { "boolean", ], }, - { - "items": { - "$ref": "#/properties/filter/anyOf/0/anyOf/0/additionalProperties/anyOf/0", - }, - "type": "array", - }, { "additionalProperties": false, "properties": { @@ -119,14 +113,7 @@ describe('EntityContextMenuItemBlueprint', () => { "additionalProperties": false, "properties": { "$contains": { - "anyOf": [ - { - "$ref": "#/properties/filter/anyOf/0", - }, - { - "type": "string", - }, - ], + "$ref": "#/properties/filter", }, }, "required": [ @@ -155,12 +142,6 @@ describe('EntityContextMenuItemBlueprint', () => { { "$ref": "#/properties/filter/anyOf/0/anyOf/0/additionalProperties/anyOf/0", }, - { - "items": { - "not": {}, - }, - "type": "array", - }, { "additionalProperties": false, "properties": { diff --git a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts index 52e4d097af..3553fa932b 100644 --- a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts +++ b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.test.ts @@ -25,17 +25,16 @@ describe('createEntityPredicateSchema', () => { const predicates: EntityPredicate[] = [ 'string', '', - [], 1, { kind: 'component', 'spec.type': 'service' }, { 'metadata.tags': { $in: ['java'] } }, + { 'metadata.tags': { $contains: 'java' } }, { $all: [ { 'metadata.tags': { $contains: 'java' } }, { 'metadata.tags': { $contains: 'spring' } }, ], }, - { 'metadata.tags': ['java', 'spring'] }, { 'metadata.tags': { $in: ['go'] } }, { 'metadata.tags.0': 'java' }, { $not: { 'metadata.tags': { $in: ['java'] } } }, @@ -91,7 +90,8 @@ describe('createEntityPredicateSchema', () => { const predicates: Array< Exclude > = [ - ['service', 'website'], + [], + ['foo', 'bar'], { kind: { 1: 'foo' } }, { kind: { foo: 'bar' } }, { kind: { $unknown: 'foo' } }, @@ -104,6 +104,7 @@ describe('createEntityPredicateSchema', () => { { $not: { x: { $unknown: true } } }, { $not: { $all: [{ x: { $unknown: true } }] } }, { $unknown: 'foo' }, + { 'metadata.tags': ['foo', 'bar'] }, ]; it.each(predicates)('should reject invalid predicate %j', predicate => { diff --git a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts index 2dcdadd826..c04a74db57 100644 --- a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts +++ b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts @@ -30,8 +30,6 @@ export function createEntityPredicateSchema(z: typeof zImpl) { z.boolean(), ]) as ZodType; - const onlyEmptyArraySchema = z.array(z.never()) as ZodType; - // eslint-disable-next-line prefer-const let valuePredicateSchema: ZodType; @@ -46,7 +44,6 @@ export function createEntityPredicateSchema(z: typeof zImpl) { z.union([ expressionSchema, primitiveSchema, - onlyEmptyArraySchema, z.object({ $all: z.array(predicateSchema) }), z.object({ $any: z.array(predicateSchema) }), z.object({ $not: predicateSchema }), @@ -55,10 +52,9 @@ export function createEntityPredicateSchema(z: typeof zImpl) { valuePredicateSchema = z.union([ primitiveSchema, - z.array(primitiveSchema), z.object({ $exists: z.boolean() }), z.object({ $in: z.array(primitiveSchema) }), - z.object({ $contains: z.union([expressionSchema, z.string()]) }), + z.object({ $contains: predicateSchema }), ]) as ZodType; return predicateSchema; diff --git a/plugins/catalog-react/src/alpha/predicates/types.ts b/plugins/catalog-react/src/alpha/predicates/types.ts index 8745610eae..f19026c01b 100644 --- a/plugins/catalog-react/src/alpha/predicates/types.ts +++ b/plugins/catalog-react/src/alpha/predicates/types.ts @@ -18,7 +18,6 @@ export type EntityPredicate = | EntityPredicateExpression | EntityPredicatePrimitive - | never[] | { $all: EntityPredicate[] } | { $any: EntityPredicate[] } | { $not: EntityPredicate }; @@ -33,10 +32,9 @@ export type EntityPredicateExpression = { /** @alpha */ export type EntityPredicateValue = | EntityPredicatePrimitive - | EntityPredicatePrimitive[] | { $exists: boolean } | { $in: EntityPredicatePrimitive[] } - | { $contains: EntityPredicateExpression | string }; + | { $contains: EntityPredicate }; /** @alpha */ export type EntityPredicatePrimitive = string | number | boolean; From 7f014c4e38eb6ee18ae937084a241cfa5fc384d5 Mon Sep 17 00:00:00 2001 From: Sandor Karpf Date: Tue, 16 Dec 2025 09:08:15 +0100 Subject: [PATCH 5/5] fix: repair Zod validator for disabling variable in object name Signed-off-by: Sandor Karpf --- .../src/alpha/blueprints/EntityCardBlueprint.test.tsx | 2 +- .../src/alpha/blueprints/EntityContentBlueprint.test.tsx | 2 +- .../alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx | 2 +- .../src/alpha/predicates/createEntityPredicateSchema.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx index 090f559915..af04673ad1 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx @@ -120,7 +120,7 @@ describe('EntityCardBlueprint', () => { "not": {}, }, "propertyNames": { - "pattern": "(?!\\$)+", + "pattern": "^\\$", }, "type": "object", }, diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx index 6c0f99b5b4..e80834be92 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx @@ -122,7 +122,7 @@ describe('EntityContentBlueprint', () => { "not": {}, }, "propertyNames": { - "pattern": "(?!\\$)+", + "pattern": "^\\$", }, "type": "object", }, diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx index ddfe63e7e6..d92fcc1863 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx @@ -133,7 +133,7 @@ describe('EntityContextMenuItemBlueprint', () => { "not": {}, }, "propertyNames": { - "pattern": "(?!\\$)+", + "pattern": "^\\$", }, "type": "object", }, diff --git a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts index c04a74db57..3e31ec4d35 100644 --- a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts +++ b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts @@ -36,7 +36,7 @@ export function createEntityPredicateSchema(z: typeof zImpl) { const expressionSchema = z.lazy(() => z.union([ z.record(z.string().regex(/^(?!\$).*$/), valuePredicateSchema), - z.record(z.string().regex(/(?!\$)+/), z.never()), + z.record(z.string().regex(/^\$/), z.never()), ]), ) as ZodType;