From e5ee4d8da7f8315df8254274d12eed2eeedd75ba Mon Sep 17 00:00:00 2001 From: Nilay1999 Date: Mon, 12 Jan 2026 11:30:26 +0530 Subject: [PATCH 01/55] feat(catalog-backend): :sparkles: add predicate-based entity filtering - Add EntityPredicate and EntityPredicateRequest types to catalog types - Implement queryEntitiesByPredicate method in DefaultEntitiesCatalog for predicate-based filtering - Create applyPredicateEntityFilterToQuery utility to convert predicates to database queries Signed-off-by: Nilay1999 --- plugins/catalog-backend/src/catalog/types.ts | 18 +- .../src/service/DefaultEntitiesCatalog.ts | 89 +++++++ .../applyPredicateEntityFilterToQuery.ts | 239 ++++++++++++++++++ plugins/catalog-node/src/api/common.ts | 66 +++++ plugins/catalog-node/src/api/index.ts | 4 + 5 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 1fbf337986..1b87352318 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -16,7 +16,7 @@ import { BackstageCredentials } from '@backstage/backend-plugin-api'; import { Entity } from '@backstage/catalog-model'; -import { EntityFilter } from '@backstage/plugin-catalog-node'; +import { EntityFilter, EntityPredicate } from '@backstage/plugin-catalog-node'; /** * A pagination rule for entities. @@ -52,6 +52,13 @@ export type EntitiesRequest = { credentials: BackstageCredentials; }; +export type EntityPredicateRequest = { + filter?: EntityPredicate; + order?: EntityOrder[]; + pagination?: EntityPagination; + credentials: BackstageCredentials; +}; + /** * Encapsulates either a deserialized or serialized entities to be sent in a response. * @internal @@ -165,6 +172,15 @@ export interface EntitiesCatalog { */ queryEntities(request: QueryEntitiesRequest): Promise; + /** + * Fetch entities using predicate-based filters. + * + * @param request - Request options with predicate filter + */ + queryEntitiesByPredicate( + request?: EntityPredicateRequest, + ): Promise; + /** * Removes a single entity. * diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 92c0bb0ecf..c7b14282ef 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -31,6 +31,7 @@ import { EntityFacetsResponse, EntityOrder, EntityPagination, + EntityPredicateRequest, QueryEntitiesRequest, QueryEntitiesResponse, } from '../catalog/types'; @@ -52,6 +53,7 @@ import { import { EntityFilter } from '@backstage/plugin-catalog-node'; import { LoggerService } from '@backstage/backend-plugin-api'; import { applyEntityFilterToQuery } from './request/applyEntityFilterToQuery'; +import { applyPredicateEntityFilterToQuery } from './request/applyPredicateEntityFilterToQuery'; import { processRawEntitiesResult } from './response'; const DEFAULT_LIMIT = 200; @@ -213,6 +215,93 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { }; } + async queryEntitiesByPredicate( + request?: EntityPredicateRequest, + ): Promise { + const db = this.database; + const { limit, offset } = parsePagination(request?.pagination); + + let entitiesQuery = + db('final_entities').select('final_entities.*'); + + request?.order?.forEach(({ field }, index) => { + const alias = `order_${index}`; + entitiesQuery = entitiesQuery.leftOuterJoin( + { [alias]: 'search' }, + function search(inner) { + inner + .on(`${alias}.entity_id`, 'final_entities.entity_id') + .andOn(`${alias}.key`, db.raw('?', [field])); + }, + ); + }); + + entitiesQuery = entitiesQuery.whereNotNull('final_entities.final_entity'); + + if (request?.filter) { + entitiesQuery = applyPredicateEntityFilterToQuery({ + filter: request.filter, + targetQuery: entitiesQuery, + onEntityIdField: 'final_entities.entity_id', + knex: db, + }); + } + + request?.order?.forEach(({ order }, index) => { + if (db.client.config.client === 'pg') { + entitiesQuery = entitiesQuery.orderBy([ + { column: `order_${index}.value`, order, nulls: 'last' }, + ]); + } else { + entitiesQuery = entitiesQuery.orderBy([ + { column: `order_${index}.value`, order: undefined, nulls: 'last' }, + { column: `order_${index}.value`, order }, + ]); + } + }); + + if (!request?.order) { + entitiesQuery = entitiesQuery.orderBy('final_entities.entity_ref', 'asc'); + } else { + entitiesQuery.orderBy('final_entities.entity_id', 'asc'); + } + + if (limit !== undefined) { + entitiesQuery = entitiesQuery.limit(limit + 1); + } + if (offset !== undefined) { + entitiesQuery = entitiesQuery.offset(offset); + } + + let rows = await entitiesQuery; + let pageInfo: DbPageInfo; + if (limit === undefined || rows.length <= limit) { + pageInfo = { hasNextPage: false }; + } else { + rows = rows.slice(0, -1); + pageInfo = { + hasNextPage: true, + endCursor: stringifyPagination({ + limit, + offset: (offset ?? 0) + limit, + }), + }; + } + + return { + entities: processRawEntitiesResult( + rows.map(r => r.final_entity!), + this.enableRelationsCompatibility + ? e => { + expandLegacyCompoundRelationsInEntity(e); + return e; + } + : undefined, + ), + pageInfo, + }; + } + async entitiesBatch( request: EntitiesBatchRequest, ): Promise { diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts new file mode 100644 index 0000000000..59e08e7d9e --- /dev/null +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -0,0 +1,239 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + EntityPredicate, + EntityPredicatePrimitive, + EntityPredicateValue, +} from '@backstage/plugin-catalog-node'; +import { Knex } from 'knex'; +import { DbSearchRow } from '../../database/tables'; + +function isAllPredicate( + filter: EntityPredicate, +): filter is { $all: EntityPredicate[] } { + return typeof filter === 'object' && filter !== null && '$all' in filter; +} + +function isAnyPredicate( + filter: EntityPredicate, +): filter is { $any: EntityPredicate[] } { + return typeof filter === 'object' && filter !== null && '$any' in filter; +} + +function isNotPredicate( + filter: EntityPredicate, +): filter is { $not: EntityPredicate } { + return typeof filter === 'object' && filter !== null && '$not' in filter; +} + +function isPrimitive(value: unknown): value is EntityPredicatePrimitive { + return ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ); +} + +function isExistsValue( + value: EntityPredicateValue, +): value is { $exists: boolean } { + return typeof value === 'object' && value !== null && '$exists' in value; +} + +function isInValue( + value: EntityPredicateValue, +): value is { $in: EntityPredicatePrimitive[] } { + return typeof value === 'object' && value !== null && '$in' in value; +} + +function isFieldExpression(filter: EntityPredicate): boolean { + if (typeof filter !== 'object' || filter === null) { + return false; + } + // Not a logical operator + return !('$all' in filter || '$any' in filter || '$not' in filter); +} + +/** + * example generated query + * Selects all non-null final entities that: + * + * - Are of kind "component" + * - Have spec.type = "service" + * - Are owned by either: + * - backend-team + * - platform-team + * - Are NOT in experimental lifecycle + * + * Results are ordered by entity_ref in ascending order. + * + * SQL Reference: + * ``` + * SELECT final_entities.* + * FROM final_entities + * WHERE final_entities.final_entity IS NOT NULL + * AND final_entities.entity_id IN ( + * SELECT entity_id FROM search WHERE key = 'kind' AND value = 'component' + * ) + * AND final_entities.entity_id IN ( + * SELECT entity_id FROM search WHERE key = 'spec.type' AND value = 'service' + * ) + * AND ( + * final_entities.entity_id IN ( + * SELECT entity_id FROM search WHERE key = 'spec.owner' AND value = 'backend-team' + * ) + * OR + * final_entities.entity_id IN ( + * SELECT entity_id FROM search WHERE key = 'spec.owner' AND value = 'platform-team' + * ) + * ) + * AND final_entities.entity_id NOT IN ( + * SELECT entity_id FROM search WHERE key = 'spec.lifecycle' AND value = 'experimental' + * ) + * ORDER BY final_entities.entity_ref ASC; + * ``` + */ + +function applyPredicateInStrategy( + filter: EntityPredicate, + targetQuery: Knex.QueryBuilder, + onEntityIdField: string, + knex: Knex, + negate: boolean, +): Knex.QueryBuilder { + // Handle $not + if (isNotPredicate(filter)) { + return applyPredicateInStrategy( + filter.$not, + targetQuery, + onEntityIdField, + knex, + !negate, + ); + } + + // Handle $all (AND) + if (isAllPredicate(filter)) { + return targetQuery[negate ? 'andWhereNot' : 'andWhere']( + function allFilter() { + for (const subFilter of filter.$all) { + this.andWhere(subQuery => + applyPredicateInStrategy( + subFilter, + subQuery, + onEntityIdField, + knex, + false, + ), + ); + } + }, + ); + } + + // Handle $any (OR) + if (isAnyPredicate(filter)) { + return targetQuery[negate ? 'andWhereNot' : 'andWhere']( + function anyFilter() { + for (const subFilter of filter.$any) { + this.orWhere(subQuery => + applyPredicateInStrategy( + subFilter, + subQuery, + onEntityIdField, + knex, + false, + ), + ); + } + }, + ); + } + + // Handle primitive value at top level (e.g., "component" shorthand) + if (isPrimitive(filter)) { + const matchQuery = knex('search') + .select('search.entity_id') + .where({ value: String(filter).toLowerCase() }); + return targetQuery.andWhere( + onEntityIdField, + negate ? 'not in' : 'in', + matchQuery, + ); + } + + // Handle field expressions like { "kind": "component" } or { "spec.type": { "$in": ["service", "website"] } } + if (isFieldExpression(filter)) { + return targetQuery[negate ? 'andWhereNot' : 'andWhere']( + function fieldFilter() { + for (const [key, value] of Object.entries(filter)) { + const normalizedKey = key.toLowerCase(); + + if (isExistsValue(value)) { + // Handle $exists + const existsQuery = knex('search') + .select('search.entity_id') + .where({ key: normalizedKey }); + + if (value.$exists) { + this.andWhere(onEntityIdField, 'in', existsQuery); + } else { + this.andWhere(onEntityIdField, 'not in', existsQuery); + } + } else if (isInValue(value)) { + // Handle $in + const values = value.$in.map(v => String(v).toLowerCase()); + const matchQuery = knex('search') + .select('search.entity_id') + .where({ key: normalizedKey }) + .whereIn('value', values); + this.andWhere(onEntityIdField, 'in', matchQuery); + } else if (isPrimitive(value)) { + // Handle direct value match + const matchQuery = knex('search') + .select('search.entity_id') + .where({ + key: normalizedKey, + value: String(value).toLowerCase(), + }); + this.andWhere(onEntityIdField, 'in', matchQuery); + } + } + }, + ); + } + + return targetQuery; +} + +export function applyPredicateEntityFilterToQuery(options: { + filter: EntityPredicate; + targetQuery: Knex.QueryBuilder; + onEntityIdField: string; + knex: Knex; + strategy?: 'in' | 'join'; +}): Knex.QueryBuilder { + const { filter, targetQuery, onEntityIdField, knex } = options; + + return applyPredicateInStrategy( + filter, + targetQuery, + onEntityIdField, + knex, + false, + ); +} diff --git a/plugins/catalog-node/src/api/common.ts b/plugins/catalog-node/src/api/common.ts index 7f9bd3eabf..f88857a312 100644 --- a/plugins/catalog-node/src/api/common.ts +++ b/plugins/catalog-node/src/api/common.ts @@ -85,3 +85,69 @@ export type EntitiesSearchFilter = { */ values?: string[]; }; + +/** + * Defines a predicate used to filter entities using logical and comparison operators. + * + * This type supports: + * - Primitive equality matching + * - Field existence checks + * - Inclusion checks + * - Logical composition using $all, $any, and $not + * + * @public + */ +export type EntityPredicate = + | EntityPredicateExpression + | EntityPredicatePrimitive + | { $all: EntityPredicate[] } + | { $any: EntityPredicate[] } + | { $not: EntityPredicate }; + +/** + * Represents a field-based predicate expression. + * + * Each key is treated as a JSON path into the entity object. + * Keys starting with `$` are reserved for operators and are not allowed. + * + * Example: + * ```ts + * { + * "spec.type": "service", + * "metadata.name": { $in: ["payments", "orders"] } + * } + * ``` + * + * @public + */ +export type EntityPredicateExpression = { + /** JSON path of the entity field */ + [KPath in string]: EntityPredicateValue; +} & { + /** Operator keys are not allowed as field paths */ + [KPath in `$${string}`]: never; +}; + +/** + * Represents a value condition for a predicate field. + * + * Supported forms: + * - Primitive equality match + * - Existence check using `$exists` + * - Inclusion check using `$in` + * + * @public + */ +export type EntityPredicateValue = + | EntityPredicatePrimitive + | { $exists: boolean } + | { $in: EntityPredicatePrimitive[] }; + +/** + * Represents a primitive predicate value. + * + * Used for direct equality comparison. + * + * @public + */ +export type EntityPredicatePrimitive = string | number | boolean; diff --git a/plugins/catalog-node/src/api/index.ts b/plugins/catalog-node/src/api/index.ts index 292a6cd872..5b7abbb6b9 100644 --- a/plugins/catalog-node/src/api/index.ts +++ b/plugins/catalog-node/src/api/index.ts @@ -20,6 +20,10 @@ export type { LocationSpec, EntitiesSearchFilter, EntityFilter, + EntityPredicate, + EntityPredicateExpression, + EntityPredicateValue, + EntityPredicatePrimitive, } from './common'; export type { CatalogProcessor, From b883042faccf42b4a7ac3b6298783667b6a9c5cc Mon Sep 17 00:00:00 2001 From: Nilay1999 Date: Mon, 12 Jan 2026 11:32:28 +0530 Subject: [PATCH 02/55] =?UTF-8?q?feat(catalog-backend):=20=E2=9C=A8=20add?= =?UTF-8?q?=20post=20api=20for=20predicate-based=20entity=20filtering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add new /entities/by-predicates POST endpoint for expressive entity queries - Introduce EntityPredicate schema supporting logical operators ($all, $any, $not) - Add EntityPredicateValue schema with field operators ($exists, $in) - Generate OpenAPI models for predicate-based filtering - Update failing test case files Signed-off-by: Nilay1999 --- .../catalog-backend/src/schema/openapi.yaml | 165 ++++++++++++ .../openapi/generated/apis/Api.server.ts | 17 ++ .../generated/models/EntityPredicate.model.ts | 36 +++ .../models/EntityPredicateOneOf.model.ts | 27 ++ .../models/EntityPredicateOneOf1.model.ts | 27 ++ .../models/EntityPredicateOneOf2.model.ts | 27 ++ .../models/EntityPredicateValue.model.ts | 32 +++ .../models/EntityPredicateValueOneOf.model.ts | 26 ++ .../EntityPredicateValueOneOf1.model.ts | 27 ++ ...EntityPredicateValueOneOf1InInner.model.ts | 24 ++ ...etEntitiesByPredicates200Response.model.ts | 32 +++ ...esByPredicates200ResponsePageInfo.model.ts | 29 ++ .../GetEntitiesByPredicatesRequest.model.ts | 27 ++ .../schema/openapi/generated/models/index.ts | 11 + .../src/schema/openapi/generated/router.ts | 247 ++++++++++++++++++ .../service/AuthorizedEntitiesCatalog.test.ts | 1 + .../src/service/AuthorizedEntitiesCatalog.ts | 31 +++ .../src/service/createRouter.test.ts | 3 + .../src/service/createRouter.ts | 52 +++- 19 files changed, 840 insertions(+), 1 deletion(-) create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf1.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf2.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1InInner.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200Response.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200ResponsePageInfo.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicatesRequest.model.ts diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index 9f78cc6210..d4c7560c30 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -338,6 +338,71 @@ components: properties: {} description: A type representing all allowed JSON object values. additionalProperties: {} + EntityPredicate: + description: | + A predicate-based filter supporting logical operators. + - $all: All conditions must match (AND) + - $any: At least one condition must match (OR) + - $not: Negates the condition + - $exists: Check if field exists + - $in: Match any value in array + oneOf: + - type: string + - type: number + - type: boolean + - type: object + additionalProperties: false + properties: + $all: + type: array + items: + $ref: '#/components/schemas/EntityPredicate' + required: + - $all + - type: object + additionalProperties: false + properties: + $any: + type: array + items: + $ref: '#/components/schemas/EntityPredicate' + required: + - $any + - type: object + additionalProperties: false + properties: + $not: + $ref: '#/components/schemas/EntityPredicate' + required: + - $not + - type: object + additionalProperties: + $ref: '#/components/schemas/EntityPredicateValue' + EntityPredicateValue: + description: Value for a field predicate + oneOf: + - type: string + - type: number + - type: boolean + - type: object + additionalProperties: false + properties: + $exists: + type: boolean + required: + - $exists + - type: object + additionalProperties: false + properties: + $in: + type: array + items: + oneOf: + - type: string + - type: number + - type: boolean + required: + - $in MapStringString: type: object properties: {} @@ -1095,6 +1160,106 @@ paths: type: string explode: false style: form + /entities/by-predicates: + post: + operationId: GetEntitiesByPredicates + tags: + - Entity + description: | + Query entities using predicate-based filters. This endpoint supports a more + expressive filter syntax with logical operators ($all, $any, $not) and + value operators ($exists, $in). + + Example filter: + ```json + { + "filter": { + "$all": [ + {"kind": "component"}, + {"$any": [ + {"spec.type": "service"}, + {"spec.type": "website"} + ]}, + {"$not": {"spec.lifecycle": "experimental"}} + ] + } + } + ``` + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + items: + type: array + items: + $ref: '#/components/schemas/Entity' + description: The list of entities matching the predicate filter. + pageInfo: + type: object + properties: + nextCursor: + type: string + description: The cursor for the next batch of entities. + required: + - items + - pageInfo + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' + security: + - {} + - JWT: [] + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/offset' + - $ref: '#/components/parameters/after' + - name: order + in: query + allowReserved: true + required: false + schema: + type: array + items: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: false + properties: + filter: + $ref: '#/components/schemas/EntityPredicate' + examples: + Get all service components: + value: + filter: + $all: + - kind: component + - spec.type: service + Get components owned by specific teams: + value: + filter: + $all: + - kind: component + - spec.owner: + $in: + - backend-team + - platform-team + Get non-production services: + value: + filter: + $all: + - kind: component + - spec.type: service + - $not: + spec.lifecycle: production /entity-facets: get: operationId: GetEntityFacets diff --git a/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts b/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts index 8254df6bab..a185f9a359 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts @@ -25,6 +25,8 @@ import { EntitiesQueryResponse } from '../models/EntitiesQueryResponse.model'; import { Entity } from '../models/Entity.model'; import { EntityAncestryResponse } from '../models/EntityAncestryResponse.model'; import { EntityFacetsResponse } from '../models/EntityFacetsResponse.model'; +import { GetEntitiesByPredicates200Response } from '../models/GetEntitiesByPredicates200Response.model'; +import { GetEntitiesByPredicatesRequest } from '../models/GetEntitiesByPredicatesRequest.model'; import { GetEntitiesByRefsRequest } from '../models/GetEntitiesByRefsRequest.model'; import { RefreshEntityRequest } from '../models/RefreshEntityRequest.model'; import { ValidateEntity400Response } from '../models/ValidateEntity400Response.model'; @@ -53,6 +55,19 @@ export type GetEntities = { }; response: Array | Error | Error; }; +/** + * @public + */ +export type GetEntitiesByPredicates = { + body: GetEntitiesByPredicatesRequest; + query: { + limit?: number; + offset?: number; + after?: string; + order?: Array; + }; + response: GetEntitiesByPredicates200Response | Error | Error; +}; /** * @public */ @@ -208,6 +223,8 @@ export type EndpointMap = { '#get|/entities': GetEntities; + '#post|/entities/by-predicates': GetEntitiesByPredicates; + '#get|/entities/by-query': GetEntitiesByQuery; '#post|/entities/by-refs': GetEntitiesByRefs; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts new file mode 100644 index 0000000000..18b9038228 --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicateOneOf } from '../models/EntityPredicateOneOf.model'; +import { EntityPredicateOneOf1 } from '../models/EntityPredicateOneOf1.model'; +import { EntityPredicateOneOf2 } from '../models/EntityPredicateOneOf2.model'; +import { EntityPredicateValue } from '../models/EntityPredicateValue.model'; + +/** + * A predicate-based filter supporting logical operators. - $all: All conditions must match (AND) - $any: At least one condition must match (OR) - $not: Negates the condition - $exists: Check if field exists - $in: Match any value in array + * @public + */ +export type EntityPredicate = + | EntityPredicateOneOf + | EntityPredicateOneOf1 + | EntityPredicateOneOf2 + | boolean + | number + | string + | { [key: string]: EntityPredicateValue }; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf.model.ts new file mode 100644 index 0000000000..da6f4678b9 --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf.model.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicate } from '../models/EntityPredicate.model'; + +/** + * @public + */ +export interface EntityPredicateOneOf { + $all: Array; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf1.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf1.model.ts new file mode 100644 index 0000000000..8e2b1217f5 --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf1.model.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicate } from '../models/EntityPredicate.model'; + +/** + * @public + */ +export interface EntityPredicateOneOf1 { + $any: Array; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf2.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf2.model.ts new file mode 100644 index 0000000000..af1ca66a3a --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf2.model.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicate } from '../models/EntityPredicate.model'; + +/** + * @public + */ +export interface EntityPredicateOneOf2 { + $not: EntityPredicate; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts new file mode 100644 index 0000000000..0ea00bef2f --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicateValueOneOf } from '../models/EntityPredicateValueOneOf.model'; +import { EntityPredicateValueOneOf1 } from '../models/EntityPredicateValueOneOf1.model'; + +/** + * Value for a field predicate + * @public + */ +export type EntityPredicateValue = + | EntityPredicateValueOneOf + | EntityPredicateValueOneOf1 + | boolean + | number + | string; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf.model.ts new file mode 100644 index 0000000000..e260d2456a --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf.model.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** + +/** + * @public + */ +export interface EntityPredicateValueOneOf { + $exists: boolean; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1.model.ts new file mode 100644 index 0000000000..1bea769175 --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1.model.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicateValueOneOf1InInner } from '../models/EntityPredicateValueOneOf1InInner.model'; + +/** + * @public + */ +export interface EntityPredicateValueOneOf1 { + $in: Array; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1InInner.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1InInner.model.ts new file mode 100644 index 0000000000..45c7bfac4c --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1InInner.model.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** + +/** + * @public + */ +export type EntityPredicateValueOneOf1InInner = boolean | number | string; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200Response.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200Response.model.ts new file mode 100644 index 0000000000..0acbfe559e --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200Response.model.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { Entity } from '../models/Entity.model'; +import { GetEntitiesByPredicates200ResponsePageInfo } from '../models/GetEntitiesByPredicates200ResponsePageInfo.model'; + +/** + * @public + */ +export interface GetEntitiesByPredicates200Response { + /** + * The list of entities matching the predicate filter. + */ + items: Array; + pageInfo: GetEntitiesByPredicates200ResponsePageInfo; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200ResponsePageInfo.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200ResponsePageInfo.model.ts new file mode 100644 index 0000000000..64ea958943 --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200ResponsePageInfo.model.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** + +/** + * @public + */ +export interface GetEntitiesByPredicates200ResponsePageInfo { + /** + * The cursor for the next batch of entities. + */ + nextCursor?: string; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicatesRequest.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicatesRequest.model.ts new file mode 100644 index 0000000000..2937e27df5 --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicatesRequest.model.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicate } from '../models/EntityPredicate.model'; + +/** + * @public + */ +export interface GetEntitiesByPredicatesRequest { + filter?: EntityPredicate; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts index abdb58378c..abd35ed409 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts @@ -31,10 +31,21 @@ export * from '../models/EntityFacet.model'; export * from '../models/EntityFacetsResponse.model'; export * from '../models/EntityLink.model'; export * from '../models/EntityMeta.model'; +export * from '../models/EntityPredicate.model'; +export * from '../models/EntityPredicateOneOf.model'; +export * from '../models/EntityPredicateOneOf1.model'; +export * from '../models/EntityPredicateOneOf2.model'; +export * from '../models/EntityPredicateValue.model'; +export * from '../models/EntityPredicateValueOneOf.model'; +export * from '../models/EntityPredicateValueOneOf1.model'; +export * from '../models/EntityPredicateValueOneOf1InInner.model'; export * from '../models/EntityRelation.model'; export * from '../models/ErrorError.model'; export * from '../models/ErrorRequest.model'; export * from '../models/ErrorResponse.model'; +export * from '../models/GetEntitiesByPredicates200Response.model'; +export * from '../models/GetEntitiesByPredicates200ResponsePageInfo.model'; +export * from '../models/GetEntitiesByPredicatesRequest.model'; export * from '../models/GetEntitiesByRefsRequest.model'; export * from '../models/GetLocations200ResponseInner.model'; export * from '../models/GetLocationsByQueryRequest.model'; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/router.ts b/plugins/catalog-backend/src/schema/openapi/generated/router.ts index b4078df721..5fe2c642b7 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/router.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/router.ts @@ -262,6 +262,110 @@ export const spec = { description: 'A type representing all allowed JSON object values.', additionalProperties: {}, }, + EntityPredicate: { + description: + 'A predicate-based filter supporting logical operators.\n- $all: All conditions must match (AND)\n- $any: At least one condition must match (OR)\n- $not: Negates the condition\n- $exists: Check if field exists\n- $in: Match any value in array\n', + oneOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + { + type: 'object', + additionalProperties: false, + properties: { + $all: { + type: 'array', + items: { + $ref: '#/components/schemas/EntityPredicate', + }, + }, + }, + required: ['$all'], + }, + { + type: 'object', + additionalProperties: false, + properties: { + $any: { + type: 'array', + items: { + $ref: '#/components/schemas/EntityPredicate', + }, + }, + }, + required: ['$any'], + }, + { + type: 'object', + additionalProperties: false, + properties: { + $not: { + $ref: '#/components/schemas/EntityPredicate', + }, + }, + required: ['$not'], + }, + { + type: 'object', + additionalProperties: { + $ref: '#/components/schemas/EntityPredicateValue', + }, + }, + ], + }, + EntityPredicateValue: { + description: 'Value for a field predicate', + oneOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + { + type: 'object', + additionalProperties: false, + properties: { + $exists: { + type: 'boolean', + }, + }, + required: ['$exists'], + }, + { + type: 'object', + additionalProperties: false, + properties: { + $in: { + type: 'array', + items: { + oneOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + ], + }, + }, + }, + required: ['$in'], + }, + ], + }, MapStringString: { type: 'object', properties: {}, @@ -1229,6 +1333,149 @@ export const spec = { ], }, }, + '/entities/by-predicates': { + post: { + operationId: 'GetEntitiesByPredicates', + tags: ['Entity'], + description: + 'Query entities using predicate-based filters. This endpoint supports a more\nexpressive filter syntax with logical operators ($all, $any, $not) and\nvalue operators ($exists, $in).\n\nExample filter:\n```json\n{\n "filter": {\n "$all": [\n {"kind": "component"},\n {"$any": [\n {"spec.type": "service"},\n {"spec.type": "website"}\n ]},\n {"$not": {"spec.lifecycle": "experimental"}}\n ]\n }\n}\n```\n', + responses: { + '200': { + description: 'Ok', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + items: { + type: 'array', + items: { + $ref: '#/components/schemas/Entity', + }, + description: + 'The list of entities matching the predicate filter.', + }, + pageInfo: { + type: 'object', + properties: { + nextCursor: { + type: 'string', + description: + 'The cursor for the next batch of entities.', + }, + }, + }, + }, + required: ['items', 'pageInfo'], + }, + }, + }, + }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, + }, + security: [ + {}, + { + JWT: [], + }, + ], + parameters: [ + { + $ref: '#/components/parameters/limit', + }, + { + $ref: '#/components/parameters/offset', + }, + { + $ref: '#/components/parameters/after', + }, + { + name: 'order', + in: 'query', + allowReserved: true, + required: false, + schema: { + type: 'array', + items: { + type: 'string', + }, + }, + }, + ], + requestBody: { + required: true, + content: { + 'application/json': { + schema: { + type: 'object', + additionalProperties: false, + properties: { + filter: { + $ref: '#/components/schemas/EntityPredicate', + }, + }, + }, + examples: { + 'Get all service components': { + value: { + filter: { + $all: [ + { + kind: 'component', + }, + { + 'spec.type': 'service', + }, + ], + }, + }, + }, + 'Get components owned by specific teams': { + value: { + filter: { + $all: [ + { + kind: 'component', + }, + { + 'spec.owner': { + $in: ['backend-team', 'platform-team'], + }, + }, + ], + }, + }, + }, + 'Get non-production services': { + value: { + filter: { + $all: [ + { + kind: 'component', + }, + { + 'spec.type': 'service', + }, + { + $not: { + 'spec.lifecycle': 'production', + }, + }, + ], + }, + }, + }, + }, + }, + }, + }, + }, + }, '/entity-facets': { get: { operationId: 'GetEntityFacets', diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts index 894949a691..cf262c7778 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts @@ -29,6 +29,7 @@ describe('AuthorizedEntitiesCatalog', () => { const fakeCatalog = { entities: jest.fn(), entitiesBatch: jest.fn(), + queryEntitiesByPredicate: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), facets: jest.fn(), diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts index d3d0dba5b3..831a3bb76b 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts @@ -32,6 +32,7 @@ import { EntityAncestryResponse, EntityFacetsRequest, EntityFacetsResponse, + EntityPredicateRequest, QueryEntitiesRequest, QueryEntitiesResponse, } from '../catalog/types'; @@ -196,6 +197,36 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog { return this.entitiesCatalog.queryEntities(request); } + async queryEntitiesByPredicate( + request?: EntityPredicateRequest, + ): Promise { + if (!request) { + return { + entities: { type: 'object', entities: [] }, + pageInfo: { hasNextPage: false }, + }; + } + + const authorizeDecision = ( + await this.permissionApi.authorizeConditional( + [{ permission: catalogEntityReadPermission }], + { credentials: request.credentials }, + ) + )[0]; + + if (authorizeDecision.result === AuthorizeResult.DENY) { + return { + entities: { type: 'object', entities: [] }, + pageInfo: { hasNextPage: false }, + }; + } + + // Note: For CONDITIONAL results, we pass through to the underlying catalog + // since EntityPredicate filters are separate from EntityFilter permission conditions. + // The permission filter would need to be applied separately if needed. + return this.entitiesCatalog.queryEntitiesByPredicate(request); + } + async removeEntityByUid( uid: string, options: { credentials: BackstageCredentials }, diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index aa3ed3a551..2a792f1de1 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -72,6 +72,7 @@ describe('createRouter readonly disabled', () => { beforeEach(async () => { entitiesCatalog = { entities: jest.fn(), + queryEntitiesByPredicate: jest.fn(), entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), @@ -1120,6 +1121,7 @@ describe('createRouter readonly and raw json enabled', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), + queryEntitiesByPredicate: jest.fn(), entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), @@ -1336,6 +1338,7 @@ describe('NextRouter permissioning', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), + queryEntitiesByPredicate: jest.fn(), entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 11e459ae82..e26a0c1233 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -30,7 +30,10 @@ import { } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { InputError, serializeError } from '@backstage/errors'; -import { LocationAnalyzer } from '@backstage/plugin-catalog-node'; +import { + EntityPredicate, + LocationAnalyzer, +} from '@backstage/plugin-catalog-node'; import express from 'express'; import yn from 'yn'; import { z } from 'zod'; @@ -258,6 +261,53 @@ export async function createRouter( throw err; } }) + .post('/entities/by-predicates', async (req, res) => { + const auditorEvent = await auditor.createEvent({ + eventId: 'entity-fetch', + request: req, + meta: { + queryType: 'by-predicates', + }, + }); + + try { + const filter = req.body.filter as EntityPredicate | undefined; + const order = parseEntityOrderParams(req.query); + const pagination = parseEntityPaginationParams(req.query); + const credentials = await httpAuth.credentials(req); + + const { entities, pageInfo } = + await entitiesCatalog.queryEntitiesByPredicate({ + filter, + order, + pagination, + credentials, + }); + + const meta = { + pageInfo: { + ...(pageInfo.hasNextPage && { + nextCursor: pageInfo.endCursor, + }), + }, + }; + + await auditorEvent?.success({ meta }); + + await writeEntitiesResponse({ + res, + items: entities, + alwaysUseObjectMode: enableRelationsCompatibility, + responseWrapper: items => ({ + items, + ...meta, + }), + }); + } catch (err) { + await auditorEvent?.fail({ error: err }); + throw err; + } + }) .get('/entities/by-query', async (req, res) => { const auditorEvent = await auditor.createEvent({ eventId: 'entity-fetch', From ebeebd9012886f139409fb37de50997b0c45826d Mon Sep 17 00:00:00 2001 From: Nilay1999 Date: Thu, 22 Jan 2026 22:39:07 +0530 Subject: [PATCH 03/55] refactor(client): :recycle: update queryEntities to support query payload - add query support to do advanced search using queryEntities - update core query of queryEntitiesByPredicate to handle cursor based pagination Signed-off-by: Nilay1999 --- .../catalog-client/src/CatalogClient.test.ts | 305 ++++++++++++++++++ packages/catalog-client/src/CatalogClient.ts | 96 +++++- packages/catalog-client/src/utils.ts | 13 + plugins/catalog-backend/src/catalog/types.ts | 12 +- .../src/service/DefaultEntitiesCatalog.ts | 138 ++++++-- plugins/catalog-backend/src/service/util.ts | 2 +- 6 files changed, 530 insertions(+), 36 deletions(-) diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index d62323852e..31c5d62046 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -540,6 +540,311 @@ describe('CatalogClient', () => { }); }); + describe('queryEntities with predicate-based queries (POST endpoint)', () => { + const defaultResponse = { + items: [ + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'service-1', + namespace: 'default', + }, + spec: { + type: 'service', + owner: 'team-a', + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'service-2', + namespace: 'default', + }, + spec: { + type: 'service', + owner: 'team-b', + }, + }, + ], + pageInfo: {}, + }; + + it('should use POST endpoint when query is provided', async () => { + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + expect(req.method).toBe('POST'); + expect(req.body).toMatchObject({ + query: { + kind: 'component', + }, + }); + return res(ctx.json(defaultResponse)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + const response = await client.queryEntities({ + query: { kind: 'component' }, + limit: 20, + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + expect(response.items).toEqual(defaultResponse.items); + expect(response.totalItems).toBe(defaultResponse.items.length); + }); + + it('should throw error when both filter and query are provided', async () => { + await expect( + client.queryEntities({ + filter: { kind: 'component' }, + query: { kind: 'component' }, + } as any), + ).rejects.toThrow( + 'Cannot specify both "filter" and "query" in the same request', + ); + }); + + it('should support $all operator', async () => { + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + expect(req.body).toMatchObject({ + query: { + $all: [{ kind: 'component' }, { 'spec.type': 'service' }], + }, + }); + return res(ctx.json(defaultResponse)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + await client.queryEntities({ + query: { + $all: [{ kind: 'component' }, { 'spec.type': 'service' }], + }, + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + }); + + it('should support $any operator', async () => { + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + expect(req.body).toMatchObject({ + query: { + $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], + }, + }); + return res(ctx.json(defaultResponse)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + await client.queryEntities({ + query: { + $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], + }, + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + }); + + it('should support $not operator', async () => { + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + expect(req.body).toMatchObject({ + query: { + $not: { 'spec.lifecycle': 'experimental' }, + }, + }); + return res(ctx.json(defaultResponse)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + await client.queryEntities({ + query: { + $not: { 'spec.lifecycle': 'experimental' }, + }, + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + }); + + it('should support $exists operator', async () => { + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + expect(req.body).toMatchObject({ + query: { + 'spec.owner': { $exists: true }, + }, + }); + return res(ctx.json(defaultResponse)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + await client.queryEntities({ + query: { + 'spec.owner': { $exists: true }, + }, + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + }); + + it('should support $in operator', async () => { + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + expect(req.body).toMatchObject({ + query: { + 'spec.owner': { $in: ['team-a', 'team-b', 'team-c'] }, + }, + }); + return res(ctx.json(defaultResponse)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + await client.queryEntities({ + query: { + 'spec.owner': { $in: ['team-a', 'team-b', 'team-c'] }, + }, + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + }); + + it('should support complex nested predicates', async () => { + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + expect(req.body).toMatchObject({ + query: { + $all: [ + { kind: 'component' }, + { + $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], + }, + { + $not: { + 'spec.lifecycle': 'experimental', + }, + }, + ], + }, + }); + return res(ctx.json(defaultResponse)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + await client.queryEntities({ + query: { + $all: [ + { kind: 'component' }, + { + $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], + }, + { + $not: { + 'spec.lifecycle': 'experimental', + }, + }, + ], + }, + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + }); + + it('should send orderFields with correct format (field,order)', async () => { + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + const url = new URL(req.url); + expect(url.searchParams.getAll('orderField')).toEqual([ + 'metadata.name,asc', + ]); + return res(ctx.json(defaultResponse)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + await client.queryEntities({ + query: { kind: 'component' }, + orderFields: { field: 'metadata.name', order: 'asc' }, + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + }); + + it('should send multiple orderFields with correct format', async () => { + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + const url = new URL(req.url); + expect(url.searchParams.getAll('orderField')).toEqual([ + 'metadata.name,asc', + 'spec.type,desc', + ]); + return res(ctx.json(defaultResponse)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + await client.queryEntities({ + query: { kind: 'component' }, + orderFields: [ + { field: 'metadata.name', order: 'asc' }, + { field: 'spec.type', order: 'desc' }, + ], + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + }); + + it('should send limit and offset parameters', async () => { + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + const url = new URL(req.url); + expect(url.searchParams.get('limit')).toBe('50'); + expect(url.searchParams.get('offset')).toBe('10'); + return res(ctx.json(defaultResponse)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + await client.queryEntities({ + query: { kind: 'component' }, + limit: 50, + offset: 10, + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + }); + + it('should not allow cursor with query (cursor takes precedence)', async () => { + // When cursor is provided, it's not an initial request, so the query + // parameter is ignored and it goes to GET endpoint + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + // Should use GET endpoint, not POST + expect(req.method).toBe('GET'); + return res(ctx.json({ items: [], totalItems: 0, pageInfo: {} })); + }); + + server.use(rest.get(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + // This will use GET endpoint with cursor, ignoring the query parameter + await client.queryEntities({ + cursor: 'some-cursor', + query: { kind: 'component' }, + } as any); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + }); + + it('should handle errors from POST endpoint', async () => { + const mockedEndpoint = jest + .fn() + .mockImplementation((_req, res, ctx) => res(ctx.status(400))); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + await expect(() => + client.queryEntities({ query: { kind: 'component' } }), + ).rejects.toThrow(/Request failed with 400/); + }); + }); + describe('streamEntities', () => { const defaultResponse: QueryEntitiesResponse = { items: [ diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index a537e8b3a5..2d7fa7d957 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -46,7 +46,11 @@ import { StreamEntitiesRequest, ValidateEntityResponse, } from './types/api'; -import { isQueryEntitiesInitialRequest, splitRefsIntoChunks } from './utils'; +import { + isQueryEntitiesInitialRequest, + splitRefsIntoChunks, + cursorContainsQuery, +} from './utils'; import { DefaultApiClient, GetLocationsByQueryRequest, @@ -266,6 +270,30 @@ export class CatalogClient implements CatalogApi { request: QueryEntitiesRequest = {}, options?: CatalogRequestOptions, ): Promise { + // Validate that filter and query are mutually exclusive + if ( + isQueryEntitiesInitialRequest(request) && + request.filter && + request.query + ) { + throw new Error( + 'Cannot specify both "filter" and "query" in the same request. Use "filter" for traditional key-value filtering or "query" for predicate-based filtering.', + ); + } + + // Route to POST endpoint if query predicate is provided (initial request) + if (isQueryEntitiesInitialRequest(request) && request.query) { + return this.queryEntitiesByPredicate(request, options); + } + + // Route to POST endpoint if cursor contains a query predicate (pagination) + if ( + !isQueryEntitiesInitialRequest(request) && + cursorContainsQuery(request.cursor) + ) { + return this.queryEntitiesByPredicate(request, options); + } + const params: Partial< Parameters[0]['query'] > = {}; @@ -320,6 +348,72 @@ export class CatalogClient implements CatalogApi { ); } + /** + * Query entities using predicate-based filters (POST endpoint). + * @internal + */ + private async queryEntitiesByPredicate( + request: QueryEntitiesRequest, + options?: CatalogRequestOptions, + ): Promise { + const params: { + limit?: number; + offset?: number; + after?: string; + orderField?: string[]; + cursor?: string; + fields?: string[]; + } = {}; + + let query; + + if (isQueryEntitiesInitialRequest(request)) { + // Initial request with query predicate + const { query: requestQuery, limit, offset, orderFields } = request; + query = requestQuery; + + if (limit !== undefined) { + params.limit = limit; + } + if (offset !== undefined) { + params.offset = offset; + } + if (orderFields !== undefined) { + params.orderField = ( + Array.isArray(orderFields) ? orderFields : [orderFields] + ).map(({ field, order }) => `${field},${order}`); + } + } else { + // Cursor-based pagination request + const { cursor, limit } = request; + + params.after = cursor; + if (limit !== undefined) { + params.limit = limit; + } + // Query will be extracted from cursor on the backend + query = undefined; + } + + const response = await this.apiClient.queryEntitiesByPredicate( + { + body: query ? { query } : {}, + query: params, + }, + options, + ); + + const result = await this.requestRequired(response); + + return { + items: result.items, + totalItems: result.items.length, + pageInfo: { + nextCursor: result.pageInfo?.nextCursor, + }, + }; + } + /** * {@inheritdoc CatalogApi.getEntityByRef} */ diff --git a/packages/catalog-client/src/utils.ts b/packages/catalog-client/src/utils.ts index a16cf53db1..e7713623b9 100644 --- a/packages/catalog-client/src/utils.ts +++ b/packages/catalog-client/src/utils.ts @@ -26,6 +26,19 @@ export function isQueryEntitiesInitialRequest( return !(request as QueryEntitiesCursorRequest).cursor; } +/** + * Check if a cursor contains a predicate query by attempting to decode it. + * @internal + */ +export function cursorContainsQuery(cursor: string): boolean { + try { + const decoded = JSON.parse(Buffer.from(cursor, 'base64').toString('utf8')); + return !!decoded.query; + } catch { + return false; + } +} + /** * Takes a set of entity refs, and splits them into chunks (groups) such that * the total string length in each chunk does not exceed the default Express.js diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 1b87352318..aad176e390 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -53,7 +53,7 @@ export type EntitiesRequest = { }; export type EntityPredicateRequest = { - filter?: EntityPredicate; + query?: EntityPredicate; order?: EntityOrder[]; pagination?: EntityPagination; credentials: BackstageCredentials; @@ -228,6 +228,11 @@ export interface QueryEntitiesInitialRequest { limit?: number; offset?: number; filter?: EntityFilter; + /** + * Predicate-based query for filtering entities. + * Mutually exclusive with filter. + */ + query?: EntityPredicate; orderFields?: EntityOrder[]; fullTextFilter?: { term: string; @@ -289,6 +294,11 @@ export type Cursor = { * A filter to be applied to the full list of entities. */ filter?: EntityFilter; + /** + * A predicate-based query to be applied to the full list of entities. + * Mutually exclusive with filter. + */ + query?: EntityPredicate; /** * true if the cursor is a previous cursor. */ diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index c7b14282ef..151e345f5b 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -46,6 +46,8 @@ import { import { Stitcher } from '../stitching/types'; import { + decodeCursor, + encodeCursor, expandLegacyCompoundRelationsInEntity, isQueryEntitiesCursorRequest, isQueryEntitiesInitialRequest, @@ -221,70 +223,133 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { const db = this.database; const { limit, offset } = parsePagination(request?.pagination); - let entitiesQuery = - db('final_entities').select('final_entities.*'); + const cursor = request?.pagination?.after + ? decodeCursor(request.pagination.after) + : { + query: request?.query, + orderFields: request?.order || [], + isPrevious: false, + orderFieldValues: undefined, + firstSortFieldValues: undefined, + }; - request?.order?.forEach(({ field }, index) => { - const alias = `order_${index}`; - entitiesQuery = entitiesQuery.leftOuterJoin( - { [alias]: 'search' }, - function search(inner) { + // Use query from cursor if not provided in request (pagination case) + const effectiveQuery = request?.query ?? cursor.query; + const sortField = cursor.orderFields?.[0]; + + let entitiesQuery = db('final_entities'); + + // Join with search table if we have a sort field + if (sortField) { + entitiesQuery = entitiesQuery + .distinct() + .leftOuterJoin({ order_0: 'search' }, function search(inner) { inner - .on(`${alias}.entity_id`, 'final_entities.entity_id') - .andOn(`${alias}.key`, db.raw('?', [field])); - }, - ); - }); + .on('order_0.entity_id', 'final_entities.entity_id') + .andOn('order_0.key', db.raw('?', [sortField.field])); + }) + .select({ + entity_id: 'final_entities.entity_id', + final_entity: 'final_entities.final_entity', + value: 'order_0.value', + }); + } else { + entitiesQuery = entitiesQuery.select({ + entity_id: 'final_entities.entity_id', + final_entity: 'final_entities.final_entity', + }); + } entitiesQuery = entitiesQuery.whereNotNull('final_entities.final_entity'); - if (request?.filter) { + // Apply predicate filter from cursor + if (effectiveQuery) { entitiesQuery = applyPredicateEntityFilterToQuery({ - filter: request.filter, + filter: effectiveQuery, targetQuery: entitiesQuery, onEntityIdField: 'final_entities.entity_id', knex: db, }); } - request?.order?.forEach(({ order }, index) => { + // Apply cursor-based pagination (keyset pagination) + if (cursor.orderFieldValues) { + if (cursor.orderFieldValues.length === 2) { + const [sortValue, entityId] = cursor.orderFieldValues; + const isOrderingDescending = sortField?.order === 'desc'; + entitiesQuery = entitiesQuery.andWhere(function nested() { + this.where( + 'order_0.value', + isOrderingDescending ? '<' : '>', + sortValue, + ) + .orWhere('order_0.value', '=', sortValue) + .andWhere('final_entities.entity_id', '>', entityId); + }); + } else if (cursor.orderFieldValues.length === 1) { + const [entityId] = cursor.orderFieldValues; + entitiesQuery = entitiesQuery.andWhere( + 'final_entities.entity_id', + '>', + entityId, + ); + } + } + + if (sortField) { if (db.client.config.client === 'pg') { entitiesQuery = entitiesQuery.orderBy([ - { column: `order_${index}.value`, order, nulls: 'last' }, + { column: 'order_0.value', order: sortField.order, nulls: 'last' }, + { column: 'final_entities.entity_id', order: 'asc' }, ]); } else { entitiesQuery = entitiesQuery.orderBy([ - { column: `order_${index}.value`, order: undefined, nulls: 'last' }, - { column: `order_${index}.value`, order }, + { column: 'order_0.value', order: undefined, nulls: 'last' }, + { column: 'order_0.value', order: sortField.order }, + { column: 'final_entities.entity_id', order: 'asc' }, ]); } - }); - - if (!request?.order) { - entitiesQuery = entitiesQuery.orderBy('final_entities.entity_ref', 'asc'); } else { - entitiesQuery.orderBy('final_entities.entity_id', 'asc'); + entitiesQuery = entitiesQuery.orderBy('final_entities.entity_id', 'asc'); } - if (limit !== undefined) { - entitiesQuery = entitiesQuery.limit(limit + 1); - } - if (offset !== undefined) { + // Apply a manually set initial offset (only when not using cursor pagination) + if (!request?.pagination?.after && offset !== undefined) { entitiesQuery = entitiesQuery.offset(offset); } + const effectiveLimit = limit ?? DEFAULT_LIMIT; + entitiesQuery = entitiesQuery.limit(effectiveLimit + 1); let rows = await entitiesQuery; let pageInfo: DbPageInfo; - if (limit === undefined || rows.length <= limit) { + + if (rows.length <= effectiveLimit) { pageInfo = { hasNextPage: false }; } else { + // Remove the extra row rows = rows.slice(0, -1); + + const lastRow = rows[rows.length - 1]; + const firstRow = rows[0]; + + // Create proper cursor with query field + const nextCursor: Cursor = { + query: effectiveQuery, + orderFields: cursor.orderFields || [], + orderFieldValues: sortField + ? [(lastRow as any).value, lastRow.entity_id] + : [lastRow.entity_id], + isPrevious: false, + firstSortFieldValues: + cursor.firstSortFieldValues || + (sortField + ? [(firstRow as any).value, firstRow.entity_id] + : [firstRow.entity_id]), + }; + pageInfo = { hasNextPage: true, - endCursor: stringifyPagination({ - limit, - offset: (offset ?? 0) + limit, - }), + endCursor: encodeCursor(nextCursor), }; } @@ -831,11 +896,18 @@ function parseCursorFromRequest( if (isQueryEntitiesInitialRequest(request)) { const { filter, + query, orderFields: sortFields = [], fullTextFilter, skipTotalItems = false, } = request; - return { filter, orderFields: sortFields, fullTextFilter, skipTotalItems }; + return { + filter, + query, + orderFields: sortFields, + fullTextFilter, + skipTotalItems, + }; } if (isQueryEntitiesCursorRequest(request)) { return { diff --git a/plugins/catalog-backend/src/service/util.ts b/plugins/catalog-backend/src/service/util.ts index 7e0e8b42f9..dbdbd374cf 100644 --- a/plugins/catalog-backend/src/service/util.ts +++ b/plugins/catalog-backend/src/service/util.ts @@ -122,7 +122,7 @@ export const cursorParser: z.ZodSchema = z.object({ orderFieldValues: z.array(z.string().or(z.null())), filter: entityFilterParser.optional(), isPrevious: z.boolean(), - query: z.string().optional(), + query: z.any().optional(), firstSortFieldValues: z.array(z.string().or(z.null())).optional(), totalItems: z.number().optional(), }); From 8ddce231154066516f618837e40d1938da1338c7 Mon Sep 17 00:00:00 2001 From: Nilay1999 Date: Thu, 22 Jan 2026 22:39:42 +0530 Subject: [PATCH 04/55] =?UTF-8?q?refactor(api):=20=E2=9C=A8=20regenerate?= =?UTF-8?q?=20OpenAPI=20models=20for=20updated=20predicate-based=20endpoin?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update `/by-predicate` endpoint name to POST `/by-query` - regenerate openapi models to fix generic type names Signed-off-by: Nilay1999 --- .../openapi/generated/apis/Api.client.ts | 45 +++++ .../generated/models/EntityPredicate.model.ts | 36 ++++ .../models/EntityPredicateAll.model.ts | 3 +- .../models/EntityPredicateAny.model.ts | 28 +++ .../models/EntityPredicateExists.model.ts | 3 +- .../models/EntityPredicateIn.model.ts | 28 +++ .../models/EntityPredicateInInInner.model.ts | 2 +- .../models/EntityPredicateNot.model.ts | 3 +- .../models/EntityPredicateValue.model.ts | 32 ++++ ...eryEntitiesByPredicate200Response.model.ts | 6 +- ...iesByPredicate200ResponsePageInfo.model.ts | 2 +- .../QueryEntitiesByPredicateRequest.model.ts | 4 +- .../schema/openapi/generated/models/index.ts | 11 ++ packages/catalog-client/src/types/api.ts | 28 ++- packages/catalog-client/src/types/index.ts | 9 + .../catalog-client/src/types/predicate.ts | 122 +++++++++++++ .../catalog-backend/src/schema/openapi.yaml | 133 +++++++------- .../openapi/generated/apis/Api.server.ts | 34 ++-- .../generated/models/EntityPredicate.model.ts | 12 +- .../models/EntityPredicateAll.model.ts | 28 +++ .../models/EntityPredicateAny.model.ts | 28 +++ .../models/EntityPredicateExists.model.ts | 27 +++ .../models/EntityPredicateIn.model.ts | 28 +++ .../models/EntityPredicateInInInner.model.ts | 24 +++ .../models/EntityPredicateNot.model.ts | 28 +++ .../models/EntityPredicateValue.model.ts | 8 +- .../EntityPredicateValueOneOf1.model.ts | 27 --- ...eryEntitiesByPredicate200Response.model.ts | 32 ++++ ...iesByPredicate200ResponsePageInfo.model.ts | 29 +++ ... QueryEntitiesByPredicateRequest.model.ts} | 6 +- .../schema/openapi/generated/models/index.ts | 18 +- .../src/schema/openapi/generated/router.ts | 165 +++++++++--------- .../src/service/createRouter.ts | 11 +- 33 files changed, 777 insertions(+), 223 deletions(-) create mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicate.model.ts rename plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf.model.ts => packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAll.model.ts (92%) create mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAny.model.ts rename plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf.model.ts => packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateExists.model.ts (92%) create mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateIn.model.ts rename plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1InInner.model.ts => packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts (91%) rename plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf2.model.ts => packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateNot.model.ts (93%) create mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateValue.model.ts rename plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200Response.model.ts => packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts (80%) rename plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200ResponsePageInfo.model.ts => packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts (93%) rename plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf1.model.ts => packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts (92%) create mode 100644 packages/catalog-client/src/types/predicate.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAll.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAny.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateExists.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateIn.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateNot.model.ts delete mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts rename plugins/catalog-backend/src/schema/openapi/generated/models/{GetEntitiesByPredicatesRequest.model.ts => QueryEntitiesByPredicateRequest.model.ts} (87%) diff --git a/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts b/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts index 4530cd795c..bc34d1c769 100644 --- a/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts +++ b/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts @@ -29,6 +29,8 @@ import { Entity } from '../models/Entity.model'; import { EntityAncestryResponse } from '../models/EntityAncestryResponse.model'; import { EntityFacetsResponse } from '../models/EntityFacetsResponse.model'; import { GetEntitiesByRefsRequest } from '../models/GetEntitiesByRefsRequest.model'; +import { QueryEntitiesByPredicate200Response } from '../models/QueryEntitiesByPredicate200Response.model'; +import { QueryEntitiesByPredicateRequest } from '../models/QueryEntitiesByPredicateRequest.model'; import { RefreshEntityRequest } from '../models/RefreshEntityRequest.model'; import { ValidateEntityRequest } from '../models/ValidateEntityRequest.model'; import { AnalyzeLocationRequest } from '../models/AnalyzeLocationRequest.model'; @@ -139,6 +141,18 @@ export type GetEntityFacets = { filter?: Array; }; }; +/** + * @public + */ +export type QueryEntitiesByPredicate = { + body: QueryEntitiesByPredicateRequest; + query: { + limit?: number; + offset?: number; + orderField?: Array; + after?: string; + }; +}; /** * @public */ @@ -449,6 +463,37 @@ export class DefaultApiClient { }); } + /** + * Query entities using predicate-based filters. This endpoint provides an alternative filtering method with a more expressive filter syntax supporting logical operators ($all, $any, $not) and value operators ($exists, $in). Example query: ```json { \"query\": { \"$all\": [ {\"kind\": \"component\"}, {\"$any\": [ {\"spec.type\": \"service\"}, {\"spec.type\": \"website\"} ]}, {\"$not\": {\"spec.lifecycle\": \"experimental\"}} ] } } ``` + * @param queryEntitiesByPredicateRequest - + * @param limit - Number of records to return in the response. + * @param offset - Number of records to skip in the query page. + * @param orderField - By default the entities are returned ordered by their internal uid. You can customize the `orderField` query parameters to affect that ordering. For example, to return entities by their name: `/entities/by-query?orderField=metadata.name,asc` Each parameter can be followed by `asc` for ascending lexicographical order or `desc` for descending (reverse) lexicographical order. + * @param after - Pointer to the previous page of results. + */ + public async queryEntitiesByPredicate( + // @ts-ignore + request: QueryEntitiesByPredicate, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/entities/by-query{?limit,offset,orderField*,after}`; + + const uri = parser.parse(uriTemplate).expand({ + ...request.query, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'POST', + body: JSON.stringify(request.body), + }); + } + /** * Refresh the entity related to entityRef. * @param refreshEntityRequest - diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicate.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicate.model.ts new file mode 100644 index 0000000000..bdc862ca50 --- /dev/null +++ b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicate.model.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicateAll } from '../models/EntityPredicateAll.model'; +import { EntityPredicateAny } from '../models/EntityPredicateAny.model'; +import { EntityPredicateNot } from '../models/EntityPredicateNot.model'; +import { EntityPredicateValue } from '../models/EntityPredicateValue.model'; + +/** + * A predicate-based filter supporting logical operators. - $all: All conditions must match (AND) - $any: At least one condition must match (OR) - $not: Negates the condition - $exists: Check if field exists - $in: Match any value in array + * @public + */ +export type EntityPredicate = + | EntityPredicateAll + | EntityPredicateAny + | EntityPredicateNot + | boolean + | number + | string + | { [key: string]: EntityPredicateValue }; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAll.model.ts similarity index 92% rename from plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf.model.ts rename to packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAll.model.ts index da6f4678b9..080b8307b4 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAll.model.ts @@ -20,8 +20,9 @@ import { EntityPredicate } from '../models/EntityPredicate.model'; /** + * All conditions must match (AND logic) * @public */ -export interface EntityPredicateOneOf { +export interface EntityPredicateAll { $all: Array; } diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAny.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAny.model.ts new file mode 100644 index 0000000000..170ea3ab0c --- /dev/null +++ b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAny.model.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicate } from '../models/EntityPredicate.model'; + +/** + * At least one condition must match (OR logic) + * @public + */ +export interface EntityPredicateAny { + $any: Array; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateExists.model.ts similarity index 92% rename from plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf.model.ts rename to packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateExists.model.ts index e260d2456a..7800fcd20c 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateExists.model.ts @@ -19,8 +19,9 @@ // ****************************************************************** /** + * Check if field exists * @public */ -export interface EntityPredicateValueOneOf { +export interface EntityPredicateExists { $exists: boolean; } diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateIn.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateIn.model.ts new file mode 100644 index 0000000000..574ab4f45b --- /dev/null +++ b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateIn.model.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicateInInInner } from '../models/EntityPredicateInInInner.model'; + +/** + * Match any value in array + * @public + */ +export interface EntityPredicateIn { + $in: Array; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1InInner.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts similarity index 91% rename from plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1InInner.model.ts rename to packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts index 45c7bfac4c..2b4e3d7c2f 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1InInner.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts @@ -21,4 +21,4 @@ /** * @public */ -export type EntityPredicateValueOneOf1InInner = boolean | number | string; +export type EntityPredicateInInInner = boolean | number | string; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf2.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateNot.model.ts similarity index 93% rename from plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf2.model.ts rename to packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateNot.model.ts index af1ca66a3a..e56279b0fc 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf2.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateNot.model.ts @@ -20,8 +20,9 @@ import { EntityPredicate } from '../models/EntityPredicate.model'; /** + * Negates the condition * @public */ -export interface EntityPredicateOneOf2 { +export interface EntityPredicateNot { $not: EntityPredicate; } diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateValue.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateValue.model.ts new file mode 100644 index 0000000000..c230c2a984 --- /dev/null +++ b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateValue.model.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicateExists } from '../models/EntityPredicateExists.model'; +import { EntityPredicateIn } from '../models/EntityPredicateIn.model'; + +/** + * Value for a field predicate + * @public + */ +export type EntityPredicateValue = + | EntityPredicateExists + | EntityPredicateIn + | boolean + | number + | string; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200Response.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts similarity index 80% rename from plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200Response.model.ts rename to packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts index 0acbfe559e..3c4b74cd10 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200Response.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts @@ -18,15 +18,15 @@ // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** import { Entity } from '../models/Entity.model'; -import { GetEntitiesByPredicates200ResponsePageInfo } from '../models/GetEntitiesByPredicates200ResponsePageInfo.model'; +import { QueryEntitiesByPredicate200ResponsePageInfo } from '../models/QueryEntitiesByPredicate200ResponsePageInfo.model'; /** * @public */ -export interface GetEntitiesByPredicates200Response { +export interface QueryEntitiesByPredicate200Response { /** * The list of entities matching the predicate filter. */ items: Array; - pageInfo: GetEntitiesByPredicates200ResponsePageInfo; + pageInfo: QueryEntitiesByPredicate200ResponsePageInfo; } diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200ResponsePageInfo.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts similarity index 93% rename from plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200ResponsePageInfo.model.ts rename to packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts index 64ea958943..3ef9f73758 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicates200ResponsePageInfo.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts @@ -21,7 +21,7 @@ /** * @public */ -export interface GetEntitiesByPredicates200ResponsePageInfo { +export interface QueryEntitiesByPredicate200ResponsePageInfo { /** * The cursor for the next batch of entities. */ diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf1.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts similarity index 92% rename from plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf1.model.ts rename to packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts index 8e2b1217f5..8b8276c9da 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateOneOf1.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts @@ -22,6 +22,6 @@ import { EntityPredicate } from '../models/EntityPredicate.model'; /** * @public */ -export interface EntityPredicateOneOf1 { - $any: Array; +export interface QueryEntitiesByPredicateRequest { + query?: EntityPredicate; } diff --git a/packages/catalog-client/src/schema/openapi/generated/models/index.ts b/packages/catalog-client/src/schema/openapi/generated/models/index.ts index abdb58378c..9d7ee33602 100644 --- a/packages/catalog-client/src/schema/openapi/generated/models/index.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/index.ts @@ -31,6 +31,14 @@ export * from '../models/EntityFacet.model'; export * from '../models/EntityFacetsResponse.model'; export * from '../models/EntityLink.model'; export * from '../models/EntityMeta.model'; +export * from '../models/EntityPredicate.model'; +export * from '../models/EntityPredicateAll.model'; +export * from '../models/EntityPredicateAny.model'; +export * from '../models/EntityPredicateExists.model'; +export * from '../models/EntityPredicateIn.model'; +export * from '../models/EntityPredicateInInInner.model'; +export * from '../models/EntityPredicateNot.model'; +export * from '../models/EntityPredicateValue.model'; export * from '../models/EntityRelation.model'; export * from '../models/ErrorError.model'; export * from '../models/ErrorRequest.model'; @@ -45,6 +53,9 @@ export * from '../models/LocationsQueryResponse.model'; export * from '../models/LocationsQueryResponsePageInfo.model'; export * from '../models/ModelError.model'; export * from '../models/NullableEntity.model'; +export * from '../models/QueryEntitiesByPredicate200Response.model'; +export * from '../models/QueryEntitiesByPredicate200ResponsePageInfo.model'; +export * from '../models/QueryEntitiesByPredicateRequest.model'; export * from '../models/RecursivePartialEntity.model'; export * from '../models/RecursivePartialEntityMeta.model'; export * from '../models/RecursivePartialEntityMetaAllOf.model'; diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index 12c5e00c03..dda003ec90 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -418,16 +418,42 @@ export type QueryEntitiesRequest = * The method takes this type in an initial pagination request, * when requesting the first batch of entities. * - * The properties filter, sortField, query and sortFieldOrder, are going + * The properties filter, query, sortField and sortFieldOrder, are going * to be immutable for the entire lifecycle of the following requests. * + * @remarks + * + * Either `filter` or `query` can be provided, but not both: + * - `filter`: Uses the traditional key-value filter syntax (GET endpoint) + * - `query`: Uses the predicate-based filter syntax with logical operators (POST endpoint) + * * @public */ export type QueryEntitiesInitialRequest = { fields?: string[]; limit?: number; offset?: number; + /** + * Traditional key-value based filter. Mutually exclusive with `query`. + */ filter?: EntityFilterQuery; + /** + * Predicate-based filter with logical operators ($all, $any, $not, $exists, $in). + * Mutually exclusive with `filter`. + * + * @example + * ```typescript + * { + * query: { + * $all: [ + * { kind: 'component' }, + * { 'spec.type': 'service' } + * ] + * } + * } + * ``` + */ + query?: FilterPredicate; orderFields?: EntityOrderQuery; fullTextFilter?: { term: string; diff --git a/packages/catalog-client/src/types/index.ts b/packages/catalog-client/src/types/index.ts index b280f1874f..20a5f3303d 100644 --- a/packages/catalog-client/src/types/index.ts +++ b/packages/catalog-client/src/types/index.ts @@ -44,4 +44,13 @@ export type { QueryLocationsInitialRequest, QueryLocationsResponse, } from './api'; +export type { + EntityPredicate, + EntityPredicateAll, + EntityPredicateAny, + EntityPredicateNot, + EntityPredicateValue, + EntityPredicateExists, + EntityPredicateIn, +} from './predicate'; export { ENTITY_STATUS_CATALOG_PROCESSING_TYPE } from './status'; diff --git a/packages/catalog-client/src/types/predicate.ts b/packages/catalog-client/src/types/predicate.ts new file mode 100644 index 0000000000..11b081d650 --- /dev/null +++ b/packages/catalog-client/src/types/predicate.ts @@ -0,0 +1,122 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A predicate-based filter supporting logical operators. + * + * @remarks + * + * This provides a more expressive filter syntax compared to the traditional + * EntityFilterQuery. It supports: + * - Logical operators: $all (AND), $any (OR), $not (negation) + * - Value operators: $exists, $in + * - Direct field matching with primitive values + * + * @example + * ```typescript + * // Match all service components + * { + * $all: [ + * { kind: 'component' }, + * { 'spec.type': 'service' } + * ] + * } + * + * // Match components owned by specific teams + * { + * $all: [ + * { kind: 'component' }, + * { 'spec.owner': { $in: ['backend-team', 'platform-team'] } } + * ] + * } + * + * // Match non-production services + * { + * $all: [ + * { kind: 'component' }, + * { 'spec.type': 'service' }, + * { $not: { 'spec.lifecycle': 'production' } } + * ] + * } + * ``` + * + * @public + */ +export type EntityPredicate = + | EntityPredicateAll + | EntityPredicateAny + | EntityPredicateNot + | boolean + | number + | string + | { [key: string]: EntityPredicateValue }; + +/** + * All conditions must match (AND logic). + * + * @public + */ +export interface EntityPredicateAll { + $all: Array; +} + +/** + * At least one condition must match (OR logic). + * + * @public + */ +export interface EntityPredicateAny { + $any: Array; +} + +/** + * Negates the condition. + * + * @public + */ +export interface EntityPredicateNot { + $not: EntityPredicate; +} + +/** + * Value for a field predicate. + * + * @public + */ +export type EntityPredicateValue = + | EntityPredicateExists + | EntityPredicateIn + | boolean + | number + | string; + +/** + * Check if field exists. + * + * @public + */ +export interface EntityPredicateExists { + $exists: boolean; +} + +/** + * Match any value in array. + * + * @public + */ +export interface EntityPredicateIn { + $in: Array; +} diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index d4c7560c30..fba22d153a 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -350,59 +350,74 @@ components: - type: string - type: number - type: boolean - - type: object - additionalProperties: false - properties: - $all: - type: array - items: - $ref: '#/components/schemas/EntityPredicate' - required: - - $all - - type: object - additionalProperties: false - properties: - $any: - type: array - items: - $ref: '#/components/schemas/EntityPredicate' - required: - - $any - - type: object - additionalProperties: false - properties: - $not: - $ref: '#/components/schemas/EntityPredicate' - required: - - $not + - $ref: '#/components/schemas/EntityPredicateAll' + - $ref: '#/components/schemas/EntityPredicateAny' + - $ref: '#/components/schemas/EntityPredicateNot' - type: object additionalProperties: $ref: '#/components/schemas/EntityPredicateValue' + EntityPredicateAll: + type: object + description: All conditions must match (AND logic) + additionalProperties: false + properties: + $all: + type: array + items: + $ref: '#/components/schemas/EntityPredicate' + required: + - $all + EntityPredicateAny: + type: object + description: At least one condition must match (OR logic) + additionalProperties: false + properties: + $any: + type: array + items: + $ref: '#/components/schemas/EntityPredicate' + required: + - $any + EntityPredicateNot: + type: object + description: Negates the condition + additionalProperties: false + properties: + $not: + $ref: '#/components/schemas/EntityPredicate' + required: + - $not EntityPredicateValue: description: Value for a field predicate oneOf: - type: string - type: number - type: boolean - - type: object - additionalProperties: false - properties: - $exists: - type: boolean - required: - - $exists - - type: object - additionalProperties: false - properties: - $in: - type: array - items: - oneOf: - - type: string - - type: number - - type: boolean - required: - - $in + - $ref: '#/components/schemas/EntityPredicateExists' + - $ref: '#/components/schemas/EntityPredicateIn' + EntityPredicateExists: + type: object + description: Check if field exists + additionalProperties: false + properties: + $exists: + type: boolean + required: + - $exists + EntityPredicateIn: + type: object + description: Match any value in array + additionalProperties: false + properties: + $in: + type: array + items: + oneOf: + - type: string + - type: number + - type: boolean + required: + - $in MapStringString: type: object properties: {} @@ -1160,20 +1175,19 @@ paths: type: string explode: false style: form - /entities/by-predicates: post: - operationId: GetEntitiesByPredicates + operationId: QueryEntitiesByPredicate tags: - Entity description: | - Query entities using predicate-based filters. This endpoint supports a more - expressive filter syntax with logical operators ($all, $any, $not) and - value operators ($exists, $in). + Query entities using predicate-based filters. This endpoint provides an + alternative filtering method with a more expressive filter syntax supporting + logical operators ($all, $any, $not) and value operators ($exists, $in). - Example filter: + Example query: ```json { - "filter": { + "query": { "$all": [ {"kind": "component"}, {"$any": [ @@ -1217,15 +1231,8 @@ paths: parameters: - $ref: '#/components/parameters/limit' - $ref: '#/components/parameters/offset' + - $ref: '#/components/parameters/orderField' - $ref: '#/components/parameters/after' - - name: order - in: query - allowReserved: true - required: false - schema: - type: array - items: - type: string requestBody: required: true content: @@ -1234,18 +1241,18 @@ paths: type: object additionalProperties: false properties: - filter: + query: $ref: '#/components/schemas/EntityPredicate' examples: Get all service components: value: - filter: + query: $all: - kind: component - spec.type: service Get components owned by specific teams: value: - filter: + query: $all: - kind: component - spec.owner: @@ -1254,7 +1261,7 @@ paths: - platform-team Get non-production services: value: - filter: + query: $all: - kind: component - spec.type: service diff --git a/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts b/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts index a185f9a359..258c777067 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts @@ -25,9 +25,9 @@ import { EntitiesQueryResponse } from '../models/EntitiesQueryResponse.model'; import { Entity } from '../models/Entity.model'; import { EntityAncestryResponse } from '../models/EntityAncestryResponse.model'; import { EntityFacetsResponse } from '../models/EntityFacetsResponse.model'; -import { GetEntitiesByPredicates200Response } from '../models/GetEntitiesByPredicates200Response.model'; -import { GetEntitiesByPredicatesRequest } from '../models/GetEntitiesByPredicatesRequest.model'; import { GetEntitiesByRefsRequest } from '../models/GetEntitiesByRefsRequest.model'; +import { QueryEntitiesByPredicate200Response } from '../models/QueryEntitiesByPredicate200Response.model'; +import { QueryEntitiesByPredicateRequest } from '../models/QueryEntitiesByPredicateRequest.model'; import { RefreshEntityRequest } from '../models/RefreshEntityRequest.model'; import { ValidateEntity400Response } from '../models/ValidateEntity400Response.model'; import { ValidateEntityRequest } from '../models/ValidateEntityRequest.model'; @@ -55,19 +55,6 @@ export type GetEntities = { }; response: Array | Error | Error; }; -/** - * @public - */ -export type GetEntitiesByPredicates = { - body: GetEntitiesByPredicatesRequest; - query: { - limit?: number; - offset?: number; - after?: string; - order?: Array; - }; - response: GetEntitiesByPredicates200Response | Error | Error; -}; /** * @public */ @@ -135,6 +122,19 @@ export type GetEntityFacets = { }; response: EntityFacetsResponse | Error | Error; }; +/** + * @public + */ +export type QueryEntitiesByPredicate = { + body: QueryEntitiesByPredicateRequest; + query: { + limit?: number; + offset?: number; + orderField?: Array; + after?: string; + }; + response: QueryEntitiesByPredicate200Response | Error | Error; +}; /** * @public */ @@ -223,8 +223,6 @@ export type EndpointMap = { '#get|/entities': GetEntities; - '#post|/entities/by-predicates': GetEntitiesByPredicates; - '#get|/entities/by-query': GetEntitiesByQuery; '#post|/entities/by-refs': GetEntitiesByRefs; @@ -237,6 +235,8 @@ export type EndpointMap = { '#get|/entity-facets': GetEntityFacets; + '#post|/entities/by-query': QueryEntitiesByPredicate; + '#post|/refresh': RefreshEntity; '#post|/validate-entity': ValidateEntity; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts index 18b9038228..bdc862ca50 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts @@ -17,9 +17,9 @@ // ****************************************************************** // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** -import { EntityPredicateOneOf } from '../models/EntityPredicateOneOf.model'; -import { EntityPredicateOneOf1 } from '../models/EntityPredicateOneOf1.model'; -import { EntityPredicateOneOf2 } from '../models/EntityPredicateOneOf2.model'; +import { EntityPredicateAll } from '../models/EntityPredicateAll.model'; +import { EntityPredicateAny } from '../models/EntityPredicateAny.model'; +import { EntityPredicateNot } from '../models/EntityPredicateNot.model'; import { EntityPredicateValue } from '../models/EntityPredicateValue.model'; /** @@ -27,9 +27,9 @@ import { EntityPredicateValue } from '../models/EntityPredicateValue.model'; * @public */ export type EntityPredicate = - | EntityPredicateOneOf - | EntityPredicateOneOf1 - | EntityPredicateOneOf2 + | EntityPredicateAll + | EntityPredicateAny + | EntityPredicateNot | boolean | number | string diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAll.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAll.model.ts new file mode 100644 index 0000000000..080b8307b4 --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAll.model.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicate } from '../models/EntityPredicate.model'; + +/** + * All conditions must match (AND logic) + * @public + */ +export interface EntityPredicateAll { + $all: Array; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAny.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAny.model.ts new file mode 100644 index 0000000000..170ea3ab0c --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAny.model.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicate } from '../models/EntityPredicate.model'; + +/** + * At least one condition must match (OR logic) + * @public + */ +export interface EntityPredicateAny { + $any: Array; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateExists.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateExists.model.ts new file mode 100644 index 0000000000..7800fcd20c --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateExists.model.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** + +/** + * Check if field exists + * @public + */ +export interface EntityPredicateExists { + $exists: boolean; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateIn.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateIn.model.ts new file mode 100644 index 0000000000..574ab4f45b --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateIn.model.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicateInInInner } from '../models/EntityPredicateInInInner.model'; + +/** + * Match any value in array + * @public + */ +export interface EntityPredicateIn { + $in: Array; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts new file mode 100644 index 0000000000..2b4e3d7c2f --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** + +/** + * @public + */ +export type EntityPredicateInInInner = boolean | number | string; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateNot.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateNot.model.ts new file mode 100644 index 0000000000..e56279b0fc --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateNot.model.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { EntityPredicate } from '../models/EntityPredicate.model'; + +/** + * Negates the condition + * @public + */ +export interface EntityPredicateNot { + $not: EntityPredicate; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts index 0ea00bef2f..c230c2a984 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts @@ -17,16 +17,16 @@ // ****************************************************************** // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** -import { EntityPredicateValueOneOf } from '../models/EntityPredicateValueOneOf.model'; -import { EntityPredicateValueOneOf1 } from '../models/EntityPredicateValueOneOf1.model'; +import { EntityPredicateExists } from '../models/EntityPredicateExists.model'; +import { EntityPredicateIn } from '../models/EntityPredicateIn.model'; /** * Value for a field predicate * @public */ export type EntityPredicateValue = - | EntityPredicateValueOneOf - | EntityPredicateValueOneOf1 + | EntityPredicateExists + | EntityPredicateIn | boolean | number | string; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1.model.ts deleted file mode 100644 index 1bea769175..0000000000 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValueOneOf1.model.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicateValueOneOf1InInner } from '../models/EntityPredicateValueOneOf1InInner.model'; - -/** - * @public - */ -export interface EntityPredicateValueOneOf1 { - $in: Array; -} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts new file mode 100644 index 0000000000..3c4b74cd10 --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** +import { Entity } from '../models/Entity.model'; +import { QueryEntitiesByPredicate200ResponsePageInfo } from '../models/QueryEntitiesByPredicate200ResponsePageInfo.model'; + +/** + * @public + */ +export interface QueryEntitiesByPredicate200Response { + /** + * The list of entities matching the predicate filter. + */ + items: Array; + pageInfo: QueryEntitiesByPredicate200ResponsePageInfo; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts new file mode 100644 index 0000000000..3ef9f73758 --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** + +/** + * @public + */ +export interface QueryEntitiesByPredicate200ResponsePageInfo { + /** + * The cursor for the next batch of entities. + */ + nextCursor?: string; +} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicatesRequest.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts similarity index 87% rename from plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicatesRequest.model.ts rename to plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts index 2937e27df5..8b8276c9da 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/GetEntitiesByPredicatesRequest.model.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,6 @@ import { EntityPredicate } from '../models/EntityPredicate.model'; /** * @public */ -export interface GetEntitiesByPredicatesRequest { - filter?: EntityPredicate; +export interface QueryEntitiesByPredicateRequest { + query?: EntityPredicate; } diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts index abd35ed409..9d7ee33602 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts @@ -32,20 +32,17 @@ export * from '../models/EntityFacetsResponse.model'; export * from '../models/EntityLink.model'; export * from '../models/EntityMeta.model'; export * from '../models/EntityPredicate.model'; -export * from '../models/EntityPredicateOneOf.model'; -export * from '../models/EntityPredicateOneOf1.model'; -export * from '../models/EntityPredicateOneOf2.model'; +export * from '../models/EntityPredicateAll.model'; +export * from '../models/EntityPredicateAny.model'; +export * from '../models/EntityPredicateExists.model'; +export * from '../models/EntityPredicateIn.model'; +export * from '../models/EntityPredicateInInInner.model'; +export * from '../models/EntityPredicateNot.model'; export * from '../models/EntityPredicateValue.model'; -export * from '../models/EntityPredicateValueOneOf.model'; -export * from '../models/EntityPredicateValueOneOf1.model'; -export * from '../models/EntityPredicateValueOneOf1InInner.model'; export * from '../models/EntityRelation.model'; export * from '../models/ErrorError.model'; export * from '../models/ErrorRequest.model'; export * from '../models/ErrorResponse.model'; -export * from '../models/GetEntitiesByPredicates200Response.model'; -export * from '../models/GetEntitiesByPredicates200ResponsePageInfo.model'; -export * from '../models/GetEntitiesByPredicatesRequest.model'; export * from '../models/GetEntitiesByRefsRequest.model'; export * from '../models/GetLocations200ResponseInner.model'; export * from '../models/GetLocationsByQueryRequest.model'; @@ -56,6 +53,9 @@ export * from '../models/LocationsQueryResponse.model'; export * from '../models/LocationsQueryResponsePageInfo.model'; export * from '../models/ModelError.model'; export * from '../models/NullableEntity.model'; +export * from '../models/QueryEntitiesByPredicate200Response.model'; +export * from '../models/QueryEntitiesByPredicate200ResponsePageInfo.model'; +export * from '../models/QueryEntitiesByPredicateRequest.model'; export * from '../models/RecursivePartialEntity.model'; export * from '../models/RecursivePartialEntityMeta.model'; export * from '../models/RecursivePartialEntityMetaAllOf.model'; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/router.ts b/plugins/catalog-backend/src/schema/openapi/generated/router.ts index 5fe2c642b7..2625bd0125 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/router.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/router.ts @@ -276,40 +276,13 @@ export const spec = { type: 'boolean', }, { - type: 'object', - additionalProperties: false, - properties: { - $all: { - type: 'array', - items: { - $ref: '#/components/schemas/EntityPredicate', - }, - }, - }, - required: ['$all'], + $ref: '#/components/schemas/EntityPredicateAll', }, { - type: 'object', - additionalProperties: false, - properties: { - $any: { - type: 'array', - items: { - $ref: '#/components/schemas/EntityPredicate', - }, - }, - }, - required: ['$any'], + $ref: '#/components/schemas/EntityPredicateAny', }, { - type: 'object', - additionalProperties: false, - properties: { - $not: { - $ref: '#/components/schemas/EntityPredicate', - }, - }, - required: ['$not'], + $ref: '#/components/schemas/EntityPredicateNot', }, { type: 'object', @@ -319,6 +292,45 @@ export const spec = { }, ], }, + EntityPredicateAll: { + type: 'object', + description: 'All conditions must match (AND logic)', + additionalProperties: false, + properties: { + $all: { + type: 'array', + items: { + $ref: '#/components/schemas/EntityPredicate', + }, + }, + }, + required: ['$all'], + }, + EntityPredicateAny: { + type: 'object', + description: 'At least one condition must match (OR logic)', + additionalProperties: false, + properties: { + $any: { + type: 'array', + items: { + $ref: '#/components/schemas/EntityPredicate', + }, + }, + }, + required: ['$any'], + }, + EntityPredicateNot: { + type: 'object', + description: 'Negates the condition', + additionalProperties: false, + properties: { + $not: { + $ref: '#/components/schemas/EntityPredicate', + }, + }, + required: ['$not'], + }, EntityPredicateValue: { description: 'Value for a field predicate', oneOf: [ @@ -332,40 +344,48 @@ export const spec = { type: 'boolean', }, { - type: 'object', - additionalProperties: false, - properties: { - $exists: { - type: 'boolean', - }, - }, - required: ['$exists'], + $ref: '#/components/schemas/EntityPredicateExists', }, { - type: 'object', - additionalProperties: false, - properties: { - $in: { - type: 'array', - items: { - oneOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - type: 'boolean', - }, - ], - }, - }, - }, - required: ['$in'], + $ref: '#/components/schemas/EntityPredicateIn', }, ], }, + EntityPredicateExists: { + type: 'object', + description: 'Check if field exists', + additionalProperties: false, + properties: { + $exists: { + type: 'boolean', + }, + }, + required: ['$exists'], + }, + EntityPredicateIn: { + type: 'object', + description: 'Match any value in array', + additionalProperties: false, + properties: { + $in: { + type: 'array', + items: { + oneOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + ], + }, + }, + }, + required: ['$in'], + }, MapStringString: { type: 'object', properties: {}, @@ -1332,13 +1352,11 @@ export const spec = { }, ], }, - }, - '/entities/by-predicates': { post: { - operationId: 'GetEntitiesByPredicates', + operationId: 'QueryEntitiesByPredicate', tags: ['Entity'], description: - 'Query entities using predicate-based filters. This endpoint supports a more\nexpressive filter syntax with logical operators ($all, $any, $not) and\nvalue operators ($exists, $in).\n\nExample filter:\n```json\n{\n "filter": {\n "$all": [\n {"kind": "component"},\n {"$any": [\n {"spec.type": "service"},\n {"spec.type": "website"}\n ]},\n {"$not": {"spec.lifecycle": "experimental"}}\n ]\n }\n}\n```\n', + 'Query entities using predicate-based filters. This endpoint provides an\nalternative filtering method with a more expressive filter syntax supporting\nlogical operators ($all, $any, $not) and value operators ($exists, $in).\n\nExample query:\n```json\n{\n "query": {\n "$all": [\n {"kind": "component"},\n {"$any": [\n {"spec.type": "service"},\n {"spec.type": "website"}\n ]},\n {"$not": {"spec.lifecycle": "experimental"}}\n ]\n }\n}\n```\n', responses: { '200': { description: 'Ok', @@ -1392,19 +1410,10 @@ export const spec = { $ref: '#/components/parameters/offset', }, { - $ref: '#/components/parameters/after', + $ref: '#/components/parameters/orderField', }, { - name: 'order', - in: 'query', - allowReserved: true, - required: false, - schema: { - type: 'array', - items: { - type: 'string', - }, - }, + $ref: '#/components/parameters/after', }, ], requestBody: { @@ -1415,7 +1424,7 @@ export const spec = { type: 'object', additionalProperties: false, properties: { - filter: { + query: { $ref: '#/components/schemas/EntityPredicate', }, }, @@ -1423,7 +1432,7 @@ export const spec = { examples: { 'Get all service components': { value: { - filter: { + query: { $all: [ { kind: 'component', @@ -1437,7 +1446,7 @@ export const spec = { }, 'Get components owned by specific teams': { value: { - filter: { + query: { $all: [ { kind: 'component', @@ -1453,7 +1462,7 @@ export const spec = { }, 'Get non-production services': { value: { - filter: { + query: { $all: [ { kind: 'component', diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index e26a0c1233..6af2fc96b0 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -68,6 +68,7 @@ import { encodeLocationQueryCursor, parseLocationQuery, } from './request/parseLocationQuery'; +import { parseEntityOrderFieldParams } from './request/parseEntityOrderFieldParams'; /** * Options used by {@link createRouter}. @@ -261,24 +262,24 @@ export async function createRouter( throw err; } }) - .post('/entities/by-predicates', async (req, res) => { + .post('/entities/by-query', async (req, res) => { const auditorEvent = await auditor.createEvent({ eventId: 'entity-fetch', request: req, meta: { - queryType: 'by-predicates', + queryType: 'by-query-predicate', }, }); try { - const filter = req.body.filter as EntityPredicate | undefined; - const order = parseEntityOrderParams(req.query); + const query = req.body.query as EntityPredicate | undefined; + const order = parseEntityOrderFieldParams(req.query); const pagination = parseEntityPaginationParams(req.query); const credentials = await httpAuth.credentials(req); const { entities, pageInfo } = await entitiesCatalog.queryEntitiesByPredicate({ - filter, + query, order, pagination, credentials, From 4bc4eb06a0030050b5703d74d7f1aac4314b7c93 Mon Sep 17 00:00:00 2001 From: Nilay1999 Date: Thu, 12 Feb 2026 00:24:32 +0530 Subject: [PATCH 05/55] refactor(catalog): use @backstage/filter-predicates package types - Replace EntityPredicate with FilterPredicate - update package.json and yarn.lock - Add InputError handling for invalid predicate values in filter application - Update catalog-client utils.ts to use browser-compatible atob() for base64 decoding Signed-off-by: Nilay1999 --- packages/catalog-client/src/utils.ts | 3 +- plugins/catalog-backend/src/catalog/types.ts | 9 +-- .../src/service/createRouter.ts | 11 ++-- .../applyPredicateEntityFilterToQuery.ts | 42 +++++++----- plugins/catalog-backend/src/service/util.ts | 5 +- plugins/catalog-node/src/api/common.ts | 66 ------------------- plugins/catalog-node/src/api/index.ts | 4 -- 7 files changed, 42 insertions(+), 98 deletions(-) diff --git a/packages/catalog-client/src/utils.ts b/packages/catalog-client/src/utils.ts index e7713623b9..489093f65d 100644 --- a/packages/catalog-client/src/utils.ts +++ b/packages/catalog-client/src/utils.ts @@ -32,7 +32,8 @@ export function isQueryEntitiesInitialRequest( */ export function cursorContainsQuery(cursor: string): boolean { try { - const decoded = JSON.parse(Buffer.from(cursor, 'base64').toString('utf8')); + // Use browser-compatible base64 decoding + const decoded = JSON.parse(atob(cursor)); return !!decoded.query; } catch { return false; diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index aad176e390..04bfb93d4f 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -16,7 +16,8 @@ import { BackstageCredentials } from '@backstage/backend-plugin-api'; import { Entity } from '@backstage/catalog-model'; -import { EntityFilter, EntityPredicate } from '@backstage/plugin-catalog-node'; +import { EntityFilter } from '@backstage/plugin-catalog-node'; +import { FilterPredicate } from '@backstage/filter-predicates'; /** * A pagination rule for entities. @@ -53,7 +54,7 @@ export type EntitiesRequest = { }; export type EntityPredicateRequest = { - query?: EntityPredicate; + query?: FilterPredicate; order?: EntityOrder[]; pagination?: EntityPagination; credentials: BackstageCredentials; @@ -232,7 +233,7 @@ export interface QueryEntitiesInitialRequest { * Predicate-based query for filtering entities. * Mutually exclusive with filter. */ - query?: EntityPredicate; + query?: FilterPredicate; orderFields?: EntityOrder[]; fullTextFilter?: { term: string; @@ -298,7 +299,7 @@ export type Cursor = { * A predicate-based query to be applied to the full list of entities. * Mutually exclusive with filter. */ - query?: EntityPredicate; + query?: FilterPredicate; /** * true if the cursor is a previous cursor. */ diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 6af2fc96b0..c3608c19a6 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -30,10 +30,8 @@ import { } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { InputError, serializeError } from '@backstage/errors'; -import { - EntityPredicate, - LocationAnalyzer, -} from '@backstage/plugin-catalog-node'; +import { parseFilterPredicate } from '@backstage/filter-predicates'; +import { LocationAnalyzer } from '@backstage/plugin-catalog-node'; import express from 'express'; import yn from 'yn'; import { z } from 'zod'; @@ -272,7 +270,10 @@ export async function createRouter( }); try { - const query = req.body.query as EntityPredicate | undefined; + // Validate the query using the Zod schema from @backstage/filter-predicates + const query = req.body.query + ? parseFilterPredicate(req.body.query) + : undefined; const order = parseEntityOrderFieldParams(req.query); const pagination = parseEntityPaginationParams(req.query); const credentials = await httpAuth.credentials(req); diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index 59e08e7d9e..eff7c12f7e 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -15,32 +15,33 @@ */ import { - EntityPredicate, - EntityPredicatePrimitive, - EntityPredicateValue, -} from '@backstage/plugin-catalog-node'; + FilterPredicate, + FilterPredicatePrimitive, + FilterPredicateValue, +} from '@backstage/filter-predicates'; +import { InputError } from '@backstage/errors'; import { Knex } from 'knex'; import { DbSearchRow } from '../../database/tables'; function isAllPredicate( - filter: EntityPredicate, -): filter is { $all: EntityPredicate[] } { + filter: FilterPredicate, +): filter is { $all: FilterPredicate[] } { return typeof filter === 'object' && filter !== null && '$all' in filter; } function isAnyPredicate( - filter: EntityPredicate, -): filter is { $any: EntityPredicate[] } { + filter: FilterPredicate, +): filter is { $any: FilterPredicate[] } { return typeof filter === 'object' && filter !== null && '$any' in filter; } function isNotPredicate( - filter: EntityPredicate, -): filter is { $not: EntityPredicate } { + filter: FilterPredicate, +): filter is { $not: FilterPredicate } { return typeof filter === 'object' && filter !== null && '$not' in filter; } -function isPrimitive(value: unknown): value is EntityPredicatePrimitive { +function isPrimitive(value: unknown): value is FilterPredicatePrimitive { return ( typeof value === 'string' || typeof value === 'number' || @@ -49,18 +50,18 @@ function isPrimitive(value: unknown): value is EntityPredicatePrimitive { } function isExistsValue( - value: EntityPredicateValue, + value: FilterPredicateValue, ): value is { $exists: boolean } { return typeof value === 'object' && value !== null && '$exists' in value; } function isInValue( - value: EntityPredicateValue, -): value is { $in: EntityPredicatePrimitive[] } { + value: FilterPredicateValue, +): value is { $in: FilterPredicatePrimitive[] } { return typeof value === 'object' && value !== null && '$in' in value; } -function isFieldExpression(filter: EntityPredicate): boolean { +function isFieldExpression(filter: FilterPredicate): boolean { if (typeof filter !== 'object' || filter === null) { return false; } @@ -109,7 +110,7 @@ function isFieldExpression(filter: EntityPredicate): boolean { */ function applyPredicateInStrategy( - filter: EntityPredicate, + filter: FilterPredicate, targetQuery: Knex.QueryBuilder, onEntityIdField: string, knex: Knex, @@ -211,6 +212,13 @@ function applyPredicateInStrategy( value: String(value).toLowerCase(), }); this.andWhere(onEntityIdField, 'in', matchQuery); + } else { + // Reject unsupported/invalid predicate values + throw new InputError( + `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, or $in operator, but got ${JSON.stringify( + value, + )}`, + ); } } }, @@ -221,7 +229,7 @@ function applyPredicateInStrategy( } export function applyPredicateEntityFilterToQuery(options: { - filter: EntityPredicate; + filter: FilterPredicate; targetQuery: Knex.QueryBuilder; onEntityIdField: string; knex: Knex; diff --git a/plugins/catalog-backend/src/service/util.ts b/plugins/catalog-backend/src/service/util.ts index dbdbd374cf..708eec2de3 100644 --- a/plugins/catalog-backend/src/service/util.ts +++ b/plugins/catalog-backend/src/service/util.ts @@ -15,6 +15,7 @@ */ import { InputError, NotAllowedError } from '@backstage/errors'; +import { createZodV3FilterPredicateSchema } from '@backstage/filter-predicates'; import { Request } from 'express'; import lodash from 'lodash'; import { z } from 'zod'; @@ -109,6 +110,8 @@ const entityFilterParser: z.ZodSchema = z.lazy(() => .or(z.object({ allOf: z.array(entityFilterParser) })), ); +const filterPredicateSchema = createZodV3FilterPredicateSchema(z); + export const cursorParser: z.ZodSchema = z.object({ orderFields: z.array( z.object({ field: z.string(), order: z.enum(['asc', 'desc']) }), @@ -122,7 +125,7 @@ export const cursorParser: z.ZodSchema = z.object({ orderFieldValues: z.array(z.string().or(z.null())), filter: entityFilterParser.optional(), isPrevious: z.boolean(), - query: z.any().optional(), + query: filterPredicateSchema.optional(), firstSortFieldValues: z.array(z.string().or(z.null())).optional(), totalItems: z.number().optional(), }); diff --git a/plugins/catalog-node/src/api/common.ts b/plugins/catalog-node/src/api/common.ts index f88857a312..7f9bd3eabf 100644 --- a/plugins/catalog-node/src/api/common.ts +++ b/plugins/catalog-node/src/api/common.ts @@ -85,69 +85,3 @@ export type EntitiesSearchFilter = { */ values?: string[]; }; - -/** - * Defines a predicate used to filter entities using logical and comparison operators. - * - * This type supports: - * - Primitive equality matching - * - Field existence checks - * - Inclusion checks - * - Logical composition using $all, $any, and $not - * - * @public - */ -export type EntityPredicate = - | EntityPredicateExpression - | EntityPredicatePrimitive - | { $all: EntityPredicate[] } - | { $any: EntityPredicate[] } - | { $not: EntityPredicate }; - -/** - * Represents a field-based predicate expression. - * - * Each key is treated as a JSON path into the entity object. - * Keys starting with `$` are reserved for operators and are not allowed. - * - * Example: - * ```ts - * { - * "spec.type": "service", - * "metadata.name": { $in: ["payments", "orders"] } - * } - * ``` - * - * @public - */ -export type EntityPredicateExpression = { - /** JSON path of the entity field */ - [KPath in string]: EntityPredicateValue; -} & { - /** Operator keys are not allowed as field paths */ - [KPath in `$${string}`]: never; -}; - -/** - * Represents a value condition for a predicate field. - * - * Supported forms: - * - Primitive equality match - * - Existence check using `$exists` - * - Inclusion check using `$in` - * - * @public - */ -export type EntityPredicateValue = - | EntityPredicatePrimitive - | { $exists: boolean } - | { $in: EntityPredicatePrimitive[] }; - -/** - * Represents a primitive predicate value. - * - * Used for direct equality comparison. - * - * @public - */ -export type EntityPredicatePrimitive = string | number | boolean; diff --git a/plugins/catalog-node/src/api/index.ts b/plugins/catalog-node/src/api/index.ts index 5b7abbb6b9..292a6cd872 100644 --- a/plugins/catalog-node/src/api/index.ts +++ b/plugins/catalog-node/src/api/index.ts @@ -20,10 +20,6 @@ export type { LocationSpec, EntitiesSearchFilter, EntityFilter, - EntityPredicate, - EntityPredicateExpression, - EntityPredicateValue, - EntityPredicatePrimitive, } from './common'; export type { CatalogProcessor, From 51e23eb73e5480c2b4042d9d7a9eb2ff7c4a3979 Mon Sep 17 00:00:00 2001 From: Nilay1999 Date: Thu, 12 Feb 2026 00:32:09 +0530 Subject: [PATCH 06/55] =?UTF-8?q?chore:=20=F0=9F=93=9D=20add=20changeset?= =?UTF-8?q?=20for=20predicate-based=20entity=20querying?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nilay1999 --- .changeset/rich-ducks-ring.md | 15 ++++++++ packages/catalog-client/report.api.md | 51 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 .changeset/rich-ducks-ring.md diff --git a/.changeset/rich-ducks-ring.md b/.changeset/rich-ducks-ring.md new file mode 100644 index 0000000000..0ba4b86961 --- /dev/null +++ b/.changeset/rich-ducks-ring.md @@ -0,0 +1,15 @@ +--- +'@backstage/catalog-client': minor +'@backstage/plugin-catalog-backend': minor +--- + +New POST /entities/by-query endpoint + +- Supports predicate-based entity filtering using advanced query operators ($all, $any, $in, $not, $exists) +- Enables complex nested queries for more powerful entity searches +- Provides cursor-based pagination for efficient result traversal + +Updated Catalog Client + +- Enhanced queryEntities() method to automatically route requests to POST endpoint when query predicate is provided +- Validates mutual exclusivity between filter (legacy) and query (predicate-based) parameters diff --git a/packages/catalog-client/report.api.md b/packages/catalog-client/report.api.md index 58fc4bc8a1..10e5b5d684 100644 --- a/packages/catalog-client/report.api.md +++ b/packages/catalog-client/report.api.md @@ -231,6 +231,56 @@ export type EntityOrderQuery = order: 'asc' | 'desc'; }>; +// @public +export type EntityPredicate = + | EntityPredicateAll + | EntityPredicateAny + | EntityPredicateNot + | boolean + | number + | string + | { + [key: string]: EntityPredicateValue; + }; + +// @public +export interface EntityPredicateAll { + // (undocumented) + $all: Array; +} + +// @public +export interface EntityPredicateAny { + // (undocumented) + $any: Array; +} + +// @public +export interface EntityPredicateExists { + // (undocumented) + $exists: boolean; +} + +// @public +export interface EntityPredicateIn { + // (undocumented) + $in: Array; +} + +// @public +export interface EntityPredicateNot { + // (undocumented) + $not: EntityPredicate; +} + +// @public +export type EntityPredicateValue = + | EntityPredicateExists + | EntityPredicateIn + | boolean + | number + | string; + // @public export interface GetEntitiesByRefsRequest { entityRefs: string[]; @@ -320,6 +370,7 @@ export type QueryEntitiesInitialRequest = { limit?: number; offset?: number; filter?: EntityFilterQuery; + query?: EntityPredicate; orderFields?: EntityOrderQuery; fullTextFilter?: { term: string; From 02b33b0650f4b0707dd6c9d3c54ffa231b585511 Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 17 Feb 2026 10:10:53 +0100 Subject: [PATCH 07/55] refactor: integrate predicate filtering into queryEntities flow Remove separate queryEntitiesByPredicate method and integrate predicate-based filtering into the existing queryEntities path. POST /entities/by-query now uses the same queryEntities call with proper permission enforcement via applyEntityFilterToQuery. Aligns with the locations query pattern from PR #32846. Signed-off-by: benjdlambert --- .changeset/rich-ducks-ring.md | 11 +- packages/catalog-client/report.api.md | 54 +--- .../catalog-client/src/CatalogClient.test.ts | 43 +-- packages/catalog-client/src/CatalogClient.ts | 70 ++--- .../openapi/generated/apis/Api.client.ts | 19 +- .../generated/models/EntityPredicate.model.ts | 36 --- .../models/EntityPredicateAll.model.ts | 28 -- .../models/EntityPredicateAny.model.ts | 28 -- .../models/EntityPredicateIn.model.ts | 28 -- .../models/EntityPredicateInInInner.model.ts | 24 -- .../models/EntityPredicateNot.model.ts | 28 -- .../models/EntityPredicateValue.model.ts | 32 --- ...eryEntitiesByPredicate200Response.model.ts | 32 --- ...iesByPredicate200ResponsePageInfo.model.ts | 29 -- .../QueryEntitiesByPredicateRequest.model.ts | 13 +- ...sByPredicateRequestFullTextFilter.model.ts | 6 +- .../schema/openapi/generated/models/index.ts | 11 +- packages/catalog-client/src/types/api.ts | 2 +- packages/catalog-client/src/types/index.ts | 9 - .../catalog-client/src/types/predicate.ts | 122 --------- packages/catalog-client/src/utils.ts | 3 +- plugins/catalog-backend/src/catalog/types.ts | 16 -- .../catalog-backend/src/schema/openapi.yaml | 152 ++--------- .../openapi/generated/apis/Api.server.ts | 9 +- .../generated/models/EntityPredicate.model.ts | 36 --- .../models/EntityPredicateAll.model.ts | 28 -- .../models/EntityPredicateAny.model.ts | 28 -- .../models/EntityPredicateIn.model.ts | 28 -- .../models/EntityPredicateInInInner.model.ts | 24 -- .../models/EntityPredicateNot.model.ts | 28 -- .../models/EntityPredicateValue.model.ts | 32 --- ...eryEntitiesByPredicate200Response.model.ts | 32 --- ...iesByPredicate200ResponsePageInfo.model.ts | 29 -- .../QueryEntitiesByPredicateRequest.model.ts | 13 +- ...sByPredicateRequestFullTextFilter.model.ts | 6 +- .../schema/openapi/generated/models/index.ts | 11 +- .../src/schema/openapi/generated/router.ts | 249 +++--------------- .../service/AuthorizedEntitiesCatalog.test.ts | 1 - .../src/service/AuthorizedEntitiesCatalog.ts | 31 --- .../src/service/DefaultEntitiesCatalog.ts | 159 +---------- .../src/service/createRouter.test.ts | 62 ++++- .../src/service/createRouter.ts | 40 +-- .../request/applyEntityFilterToQuery.test.ts | 3 +- .../request/applyEntityFilterToQuery.ts | 25 +- .../applyPredicateEntityFilterToQuery.ts | 16 +- .../src/service/request/parseEntityQuery.ts | 115 ++++++++ 46 files changed, 364 insertions(+), 1437 deletions(-) delete mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicate.model.ts delete mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAll.model.ts delete mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAny.model.ts delete mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateIn.model.ts delete mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts delete mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateNot.model.ts delete mode 100644 packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateValue.model.ts delete mode 100644 packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts delete mode 100644 packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts rename plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateExists.model.ts => packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestFullTextFilter.model.ts (88%) delete mode 100644 packages/catalog-client/src/types/predicate.ts delete mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts delete mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAll.model.ts delete mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAny.model.ts delete mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateIn.model.ts delete mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts delete mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateNot.model.ts delete mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts delete mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts delete mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts rename packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateExists.model.ts => plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestFullTextFilter.model.ts (88%) create mode 100644 plugins/catalog-backend/src/service/request/parseEntityQuery.ts diff --git a/.changeset/rich-ducks-ring.md b/.changeset/rich-ducks-ring.md index 0ba4b86961..04848da4a7 100644 --- a/.changeset/rich-ducks-ring.md +++ b/.changeset/rich-ducks-ring.md @@ -3,13 +3,8 @@ '@backstage/plugin-catalog-backend': minor --- -New POST /entities/by-query endpoint +Added predicate-based entity filtering via POST /entities/by-query endpoint. -- Supports predicate-based entity filtering using advanced query operators ($all, $any, $in, $not, $exists) -- Enables complex nested queries for more powerful entity searches -- Provides cursor-based pagination for efficient result traversal +Supports `$all`, `$any`, `$not`, `$exists`, and `$in` operators for expressive entity queries. Integrated into the existing `queryEntities` flow with full cursor-based pagination, permission enforcement, and `totalItems` support. -Updated Catalog Client - -- Enhanced queryEntities() method to automatically route requests to POST endpoint when query predicate is provided -- Validates mutual exclusivity between filter (legacy) and query (predicate-based) parameters +The catalog client's `queryEntities()` method automatically routes to the POST endpoint when a `query` predicate is provided. diff --git a/packages/catalog-client/report.api.md b/packages/catalog-client/report.api.md index 10e5b5d684..cc83cce443 100644 --- a/packages/catalog-client/report.api.md +++ b/packages/catalog-client/report.api.md @@ -7,7 +7,7 @@ import type { AnalyzeLocationRequest } from '@backstage/plugin-catalog-common'; import type { AnalyzeLocationResponse } from '@backstage/plugin-catalog-common'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Entity } from '@backstage/catalog-model'; -import { FilterPredicate } from '@backstage/filter-predicates'; +import type { FilterPredicate } from '@backstage/filter-predicates'; import { SerializedError } from '@backstage/errors'; // @public @@ -231,56 +231,6 @@ export type EntityOrderQuery = order: 'asc' | 'desc'; }>; -// @public -export type EntityPredicate = - | EntityPredicateAll - | EntityPredicateAny - | EntityPredicateNot - | boolean - | number - | string - | { - [key: string]: EntityPredicateValue; - }; - -// @public -export interface EntityPredicateAll { - // (undocumented) - $all: Array; -} - -// @public -export interface EntityPredicateAny { - // (undocumented) - $any: Array; -} - -// @public -export interface EntityPredicateExists { - // (undocumented) - $exists: boolean; -} - -// @public -export interface EntityPredicateIn { - // (undocumented) - $in: Array; -} - -// @public -export interface EntityPredicateNot { - // (undocumented) - $not: EntityPredicate; -} - -// @public -export type EntityPredicateValue = - | EntityPredicateExists - | EntityPredicateIn - | boolean - | number - | string; - // @public export interface GetEntitiesByRefsRequest { entityRefs: string[]; @@ -370,7 +320,7 @@ export type QueryEntitiesInitialRequest = { limit?: number; offset?: number; filter?: EntityFilterQuery; - query?: EntityPredicate; + query?: FilterPredicate; orderFields?: EntityOrderQuery; fullTextFilter?: { term: string; diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index 31c5d62046..4ee93c0cba 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -568,6 +568,7 @@ describe('CatalogClient', () => { }, }, ], + totalItems: 2, pageInfo: {}, }; @@ -575,9 +576,8 @@ describe('CatalogClient', () => { const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { expect(req.method).toBe('POST'); expect(req.body).toMatchObject({ - query: { - kind: 'component', - }, + query: { kind: 'component' }, + limit: 20, }); return res(ctx.json(defaultResponse)); }); @@ -591,7 +591,7 @@ describe('CatalogClient', () => { expect(mockedEndpoint).toHaveBeenCalledTimes(1); expect(response.items).toEqual(defaultResponse.items); - expect(response.totalItems).toBe(defaultResponse.items.length); + expect(response.totalItems).toBe(2); }); it('should throw error when both filter and query are provided', async () => { @@ -753,10 +753,7 @@ describe('CatalogClient', () => { it('should send orderFields with correct format (field,order)', async () => { const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { - const url = new URL(req.url); - expect(url.searchParams.getAll('orderField')).toEqual([ - 'metadata.name,asc', - ]); + expect(req.body.orderField).toEqual(['metadata.name,asc']); return res(ctx.json(defaultResponse)); }); @@ -772,8 +769,7 @@ describe('CatalogClient', () => { it('should send multiple orderFields with correct format', async () => { const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { - const url = new URL(req.url); - expect(url.searchParams.getAll('orderField')).toEqual([ + expect(req.body.orderField).toEqual([ 'metadata.name,asc', 'spec.type,desc', ]); @@ -793,11 +789,9 @@ describe('CatalogClient', () => { expect(mockedEndpoint).toHaveBeenCalledTimes(1); }); - it('should send limit and offset parameters', async () => { + it('should send limit and offset parameters in the body', async () => { const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { - const url = new URL(req.url); - expect(url.searchParams.get('limit')).toBe('50'); - expect(url.searchParams.get('offset')).toBe('10'); + expect(req.body.limit).toBe(50); return res(ctx.json(defaultResponse)); }); @@ -806,32 +800,11 @@ describe('CatalogClient', () => { await client.queryEntities({ query: { kind: 'component' }, limit: 50, - offset: 10, }); expect(mockedEndpoint).toHaveBeenCalledTimes(1); }); - it('should not allow cursor with query (cursor takes precedence)', async () => { - // When cursor is provided, it's not an initial request, so the query - // parameter is ignored and it goes to GET endpoint - const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { - // Should use GET endpoint, not POST - expect(req.method).toBe('GET'); - return res(ctx.json({ items: [], totalItems: 0, pageInfo: {} })); - }); - - server.use(rest.get(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); - - // This will use GET endpoint with cursor, ignoring the query parameter - await client.queryEntities({ - cursor: 'some-cursor', - query: { kind: 'component' }, - } as any); - - expect(mockedEndpoint).toHaveBeenCalledTimes(1); - }); - it('should handle errors from POST endpoint', async () => { const mockedEndpoint = jest .fn() diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index 2d7fa7d957..51d57f715e 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -54,6 +54,7 @@ import { import { DefaultApiClient, GetLocationsByQueryRequest, + QueryEntitiesByPredicateRequest, TypedResponse, } from './schema/openapi'; import type { @@ -356,61 +357,48 @@ export class CatalogClient implements CatalogApi { request: QueryEntitiesRequest, options?: CatalogRequestOptions, ): Promise { - const params: { - limit?: number; - offset?: number; - after?: string; - orderField?: string[]; - cursor?: string; - fields?: string[]; - } = {}; - - let query; + const body: Record = {}; if (isQueryEntitiesInitialRequest(request)) { - // Initial request with query predicate - const { query: requestQuery, limit, offset, orderFields } = request; - query = requestQuery; - - if (limit !== undefined) { - params.limit = limit; + const { query, limit, orderFields, fullTextFilter, fields } = request; + if (query && typeof query === 'object') { + body.query = query; } - if (offset !== undefined) { - params.offset = offset; + if (limit !== undefined) { + body.limit = limit; } if (orderFields !== undefined) { - params.orderField = ( + body.orderField = ( Array.isArray(orderFields) ? orderFields : [orderFields] ).map(({ field, order }) => `${field},${order}`); } - } else { - // Cursor-based pagination request - const { cursor, limit } = request; - - params.after = cursor; - if (limit !== undefined) { - params.limit = limit; + if (fullTextFilter) { + body.fullTextFilter = fullTextFilter; + } + if (fields?.length) { + body.fields = fields; + } + } else { + body.cursor = request.cursor; + if (request.limit !== undefined) { + body.limit = request.limit; + } + if (request.fields?.length) { + body.fields = request.fields; } - // Query will be extracted from cursor on the backend - query = undefined; } - const response = await this.apiClient.queryEntitiesByPredicate( - { - body: query ? { query } : {}, - query: params, - }, - options, + const res = await this.requestRequired( + await this.apiClient.queryEntitiesByPredicate( + { body: body as unknown as QueryEntitiesByPredicateRequest }, + options, + ), ); - const result = await this.requestRequired(response); - return { - items: result.items, - totalItems: result.items.length, - pageInfo: { - nextCursor: result.pageInfo?.nextCursor, - }, + items: res.items, + totalItems: res.totalItems, + pageInfo: res.pageInfo, }; } diff --git a/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts b/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts index bc34d1c769..5a5eb2ec5d 100644 --- a/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts +++ b/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts @@ -29,7 +29,6 @@ import { Entity } from '../models/Entity.model'; import { EntityAncestryResponse } from '../models/EntityAncestryResponse.model'; import { EntityFacetsResponse } from '../models/EntityFacetsResponse.model'; import { GetEntitiesByRefsRequest } from '../models/GetEntitiesByRefsRequest.model'; -import { QueryEntitiesByPredicate200Response } from '../models/QueryEntitiesByPredicate200Response.model'; import { QueryEntitiesByPredicateRequest } from '../models/QueryEntitiesByPredicateRequest.model'; import { RefreshEntityRequest } from '../models/RefreshEntityRequest.model'; import { ValidateEntityRequest } from '../models/ValidateEntityRequest.model'; @@ -146,12 +145,6 @@ export type GetEntityFacets = { */ export type QueryEntitiesByPredicate = { body: QueryEntitiesByPredicateRequest; - query: { - limit?: number; - offset?: number; - orderField?: Array; - after?: string; - }; }; /** * @public @@ -466,23 +459,17 @@ export class DefaultApiClient { /** * Query entities using predicate-based filters. This endpoint provides an alternative filtering method with a more expressive filter syntax supporting logical operators ($all, $any, $not) and value operators ($exists, $in). Example query: ```json { \"query\": { \"$all\": [ {\"kind\": \"component\"}, {\"$any\": [ {\"spec.type\": \"service\"}, {\"spec.type\": \"website\"} ]}, {\"$not\": {\"spec.lifecycle\": \"experimental\"}} ] } } ``` * @param queryEntitiesByPredicateRequest - - * @param limit - Number of records to return in the response. - * @param offset - Number of records to skip in the query page. - * @param orderField - By default the entities are returned ordered by their internal uid. You can customize the `orderField` query parameters to affect that ordering. For example, to return entities by their name: `/entities/by-query?orderField=metadata.name,asc` Each parameter can be followed by `asc` for ascending lexicographical order or `desc` for descending (reverse) lexicographical order. - * @param after - Pointer to the previous page of results. */ public async queryEntitiesByPredicate( // @ts-ignore request: QueryEntitiesByPredicate, options?: RequestOptions, - ): Promise> { + ): Promise> { const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); - const uriTemplate = `/entities/by-query{?limit,offset,orderField*,after}`; + const uriTemplate = `/entities/by-query`; - const uri = parser.parse(uriTemplate).expand({ - ...request.query, - }); + const uri = parser.parse(uriTemplate).expand({}); return await this.fetchApi.fetch(`${baseUrl}${uri}`, { headers: { diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicate.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicate.model.ts deleted file mode 100644 index bdc862ca50..0000000000 --- a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicate.model.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicateAll } from '../models/EntityPredicateAll.model'; -import { EntityPredicateAny } from '../models/EntityPredicateAny.model'; -import { EntityPredicateNot } from '../models/EntityPredicateNot.model'; -import { EntityPredicateValue } from '../models/EntityPredicateValue.model'; - -/** - * A predicate-based filter supporting logical operators. - $all: All conditions must match (AND) - $any: At least one condition must match (OR) - $not: Negates the condition - $exists: Check if field exists - $in: Match any value in array - * @public - */ -export type EntityPredicate = - | EntityPredicateAll - | EntityPredicateAny - | EntityPredicateNot - | boolean - | number - | string - | { [key: string]: EntityPredicateValue }; diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAll.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAll.model.ts deleted file mode 100644 index 080b8307b4..0000000000 --- a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAll.model.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicate } from '../models/EntityPredicate.model'; - -/** - * All conditions must match (AND logic) - * @public - */ -export interface EntityPredicateAll { - $all: Array; -} diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAny.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAny.model.ts deleted file mode 100644 index 170ea3ab0c..0000000000 --- a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateAny.model.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicate } from '../models/EntityPredicate.model'; - -/** - * At least one condition must match (OR logic) - * @public - */ -export interface EntityPredicateAny { - $any: Array; -} diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateIn.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateIn.model.ts deleted file mode 100644 index 574ab4f45b..0000000000 --- a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateIn.model.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicateInInInner } from '../models/EntityPredicateInInInner.model'; - -/** - * Match any value in array - * @public - */ -export interface EntityPredicateIn { - $in: Array; -} diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts deleted file mode 100644 index 2b4e3d7c2f..0000000000 --- a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** - -/** - * @public - */ -export type EntityPredicateInInInner = boolean | number | string; diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateNot.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateNot.model.ts deleted file mode 100644 index e56279b0fc..0000000000 --- a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateNot.model.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicate } from '../models/EntityPredicate.model'; - -/** - * Negates the condition - * @public - */ -export interface EntityPredicateNot { - $not: EntityPredicate; -} diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateValue.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateValue.model.ts deleted file mode 100644 index c230c2a984..0000000000 --- a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateValue.model.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicateExists } from '../models/EntityPredicateExists.model'; -import { EntityPredicateIn } from '../models/EntityPredicateIn.model'; - -/** - * Value for a field predicate - * @public - */ -export type EntityPredicateValue = - | EntityPredicateExists - | EntityPredicateIn - | boolean - | number - | string; diff --git a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts deleted file mode 100644 index 3c4b74cd10..0000000000 --- a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { Entity } from '../models/Entity.model'; -import { QueryEntitiesByPredicate200ResponsePageInfo } from '../models/QueryEntitiesByPredicate200ResponsePageInfo.model'; - -/** - * @public - */ -export interface QueryEntitiesByPredicate200Response { - /** - * The list of entities matching the predicate filter. - */ - items: Array; - pageInfo: QueryEntitiesByPredicate200ResponsePageInfo; -} diff --git a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts deleted file mode 100644 index 3ef9f73758..0000000000 --- a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** - -/** - * @public - */ -export interface QueryEntitiesByPredicate200ResponsePageInfo { - /** - * The cursor for the next batch of entities. - */ - nextCursor?: string; -} diff --git a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts index 8b8276c9da..bdcd854a5c 100644 --- a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts @@ -17,11 +17,20 @@ // ****************************************************************** // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** -import { EntityPredicate } from '../models/EntityPredicate.model'; + +import { QueryEntitiesByPredicateRequestFullTextFilter } from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model'; /** * @public */ export interface QueryEntitiesByPredicateRequest { - query?: EntityPredicate; + cursor?: string; + limit?: number; + orderField?: Array; + fullTextFilter?: QueryEntitiesByPredicateRequestFullTextFilter; + fields?: Array; + /** + * A type representing all allowed JSON object values. + */ + query?: { [key: string]: any }; } diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateExists.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestFullTextFilter.model.ts similarity index 88% rename from plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateExists.model.ts rename to packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestFullTextFilter.model.ts index 7800fcd20c..e7e971e1ea 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateExists.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestFullTextFilter.model.ts @@ -19,9 +19,9 @@ // ****************************************************************** /** - * Check if field exists * @public */ -export interface EntityPredicateExists { - $exists: boolean; +export interface QueryEntitiesByPredicateRequestFullTextFilter { + term?: string; + fields?: Array; } diff --git a/packages/catalog-client/src/schema/openapi/generated/models/index.ts b/packages/catalog-client/src/schema/openapi/generated/models/index.ts index 9d7ee33602..889b5968fa 100644 --- a/packages/catalog-client/src/schema/openapi/generated/models/index.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/index.ts @@ -31,14 +31,6 @@ export * from '../models/EntityFacet.model'; export * from '../models/EntityFacetsResponse.model'; export * from '../models/EntityLink.model'; export * from '../models/EntityMeta.model'; -export * from '../models/EntityPredicate.model'; -export * from '../models/EntityPredicateAll.model'; -export * from '../models/EntityPredicateAny.model'; -export * from '../models/EntityPredicateExists.model'; -export * from '../models/EntityPredicateIn.model'; -export * from '../models/EntityPredicateInInInner.model'; -export * from '../models/EntityPredicateNot.model'; -export * from '../models/EntityPredicateValue.model'; export * from '../models/EntityRelation.model'; export * from '../models/ErrorError.model'; export * from '../models/ErrorRequest.model'; @@ -53,9 +45,8 @@ export * from '../models/LocationsQueryResponse.model'; export * from '../models/LocationsQueryResponsePageInfo.model'; export * from '../models/ModelError.model'; export * from '../models/NullableEntity.model'; -export * from '../models/QueryEntitiesByPredicate200Response.model'; -export * from '../models/QueryEntitiesByPredicate200ResponsePageInfo.model'; export * from '../models/QueryEntitiesByPredicateRequest.model'; +export * from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model'; export * from '../models/RecursivePartialEntity.model'; export * from '../models/RecursivePartialEntityMeta.model'; export * from '../models/RecursivePartialEntityMetaAllOf.model'; diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index dda003ec90..8bf13271b2 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -20,7 +20,7 @@ import type { AnalyzeLocationRequest, AnalyzeLocationResponse, } from '@backstage/plugin-catalog-common'; -import { FilterPredicate } from '@backstage/filter-predicates'; +import type { FilterPredicate } from '@backstage/filter-predicates'; /** * This symbol can be used in place of a value when passed to filters in e.g. diff --git a/packages/catalog-client/src/types/index.ts b/packages/catalog-client/src/types/index.ts index 20a5f3303d..b280f1874f 100644 --- a/packages/catalog-client/src/types/index.ts +++ b/packages/catalog-client/src/types/index.ts @@ -44,13 +44,4 @@ export type { QueryLocationsInitialRequest, QueryLocationsResponse, } from './api'; -export type { - EntityPredicate, - EntityPredicateAll, - EntityPredicateAny, - EntityPredicateNot, - EntityPredicateValue, - EntityPredicateExists, - EntityPredicateIn, -} from './predicate'; export { ENTITY_STATUS_CATALOG_PROCESSING_TYPE } from './status'; diff --git a/packages/catalog-client/src/types/predicate.ts b/packages/catalog-client/src/types/predicate.ts deleted file mode 100644 index 11b081d650..0000000000 --- a/packages/catalog-client/src/types/predicate.ts +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * A predicate-based filter supporting logical operators. - * - * @remarks - * - * This provides a more expressive filter syntax compared to the traditional - * EntityFilterQuery. It supports: - * - Logical operators: $all (AND), $any (OR), $not (negation) - * - Value operators: $exists, $in - * - Direct field matching with primitive values - * - * @example - * ```typescript - * // Match all service components - * { - * $all: [ - * { kind: 'component' }, - * { 'spec.type': 'service' } - * ] - * } - * - * // Match components owned by specific teams - * { - * $all: [ - * { kind: 'component' }, - * { 'spec.owner': { $in: ['backend-team', 'platform-team'] } } - * ] - * } - * - * // Match non-production services - * { - * $all: [ - * { kind: 'component' }, - * { 'spec.type': 'service' }, - * { $not: { 'spec.lifecycle': 'production' } } - * ] - * } - * ``` - * - * @public - */ -export type EntityPredicate = - | EntityPredicateAll - | EntityPredicateAny - | EntityPredicateNot - | boolean - | number - | string - | { [key: string]: EntityPredicateValue }; - -/** - * All conditions must match (AND logic). - * - * @public - */ -export interface EntityPredicateAll { - $all: Array; -} - -/** - * At least one condition must match (OR logic). - * - * @public - */ -export interface EntityPredicateAny { - $any: Array; -} - -/** - * Negates the condition. - * - * @public - */ -export interface EntityPredicateNot { - $not: EntityPredicate; -} - -/** - * Value for a field predicate. - * - * @public - */ -export type EntityPredicateValue = - | EntityPredicateExists - | EntityPredicateIn - | boolean - | number - | string; - -/** - * Check if field exists. - * - * @public - */ -export interface EntityPredicateExists { - $exists: boolean; -} - -/** - * Match any value in array. - * - * @public - */ -export interface EntityPredicateIn { - $in: Array; -} diff --git a/packages/catalog-client/src/utils.ts b/packages/catalog-client/src/utils.ts index 489093f65d..fc867d8670 100644 --- a/packages/catalog-client/src/utils.ts +++ b/packages/catalog-client/src/utils.ts @@ -32,9 +32,8 @@ export function isQueryEntitiesInitialRequest( */ export function cursorContainsQuery(cursor: string): boolean { try { - // Use browser-compatible base64 decoding const decoded = JSON.parse(atob(cursor)); - return !!decoded.query; + return 'query' in decoded; } catch { return false; } diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 04bfb93d4f..dda7e9a811 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -53,13 +53,6 @@ export type EntitiesRequest = { credentials: BackstageCredentials; }; -export type EntityPredicateRequest = { - query?: FilterPredicate; - order?: EntityOrder[]; - pagination?: EntityPagination; - credentials: BackstageCredentials; -}; - /** * Encapsulates either a deserialized or serialized entities to be sent in a response. * @internal @@ -173,15 +166,6 @@ export interface EntitiesCatalog { */ queryEntities(request: QueryEntitiesRequest): Promise; - /** - * Fetch entities using predicate-based filters. - * - * @param request - Request options with predicate filter - */ - queryEntitiesByPredicate( - request?: EntityPredicateRequest, - ): Promise; - /** * Removes a single entity. * diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index fba22d153a..f6b80ca062 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -338,86 +338,6 @@ components: properties: {} description: A type representing all allowed JSON object values. additionalProperties: {} - EntityPredicate: - description: | - A predicate-based filter supporting logical operators. - - $all: All conditions must match (AND) - - $any: At least one condition must match (OR) - - $not: Negates the condition - - $exists: Check if field exists - - $in: Match any value in array - oneOf: - - type: string - - type: number - - type: boolean - - $ref: '#/components/schemas/EntityPredicateAll' - - $ref: '#/components/schemas/EntityPredicateAny' - - $ref: '#/components/schemas/EntityPredicateNot' - - type: object - additionalProperties: - $ref: '#/components/schemas/EntityPredicateValue' - EntityPredicateAll: - type: object - description: All conditions must match (AND logic) - additionalProperties: false - properties: - $all: - type: array - items: - $ref: '#/components/schemas/EntityPredicate' - required: - - $all - EntityPredicateAny: - type: object - description: At least one condition must match (OR logic) - additionalProperties: false - properties: - $any: - type: array - items: - $ref: '#/components/schemas/EntityPredicate' - required: - - $any - EntityPredicateNot: - type: object - description: Negates the condition - additionalProperties: false - properties: - $not: - $ref: '#/components/schemas/EntityPredicate' - required: - - $not - EntityPredicateValue: - description: Value for a field predicate - oneOf: - - type: string - - type: number - - type: boolean - - $ref: '#/components/schemas/EntityPredicateExists' - - $ref: '#/components/schemas/EntityPredicateIn' - EntityPredicateExists: - type: object - description: Check if field exists - additionalProperties: false - properties: - $exists: - type: boolean - required: - - $exists - EntityPredicateIn: - type: object - description: Match any value in array - additionalProperties: false - properties: - $in: - type: array - items: - oneOf: - - type: string - - type: number - - type: boolean - required: - - $in MapStringString: type: object properties: {} @@ -1205,22 +1125,7 @@ paths: content: application/json: schema: - type: object - properties: - items: - type: array - items: - $ref: '#/components/schemas/Entity' - description: The list of entities matching the predicate filter. - pageInfo: - type: object - properties: - nextCursor: - type: string - description: The cursor for the next batch of entities. - required: - - items - - pageInfo + $ref: '#/components/schemas/EntitiesQueryResponse' '400': $ref: '#/components/responses/ErrorResponse' default: @@ -1228,45 +1133,36 @@ paths: security: - {} - JWT: [] - parameters: - - $ref: '#/components/parameters/limit' - - $ref: '#/components/parameters/offset' - - $ref: '#/components/parameters/orderField' - - $ref: '#/components/parameters/after' requestBody: - required: true + required: false content: application/json: schema: type: object - additionalProperties: false properties: + cursor: + type: string + limit: + type: number + orderField: + type: array + items: + type: string + fullTextFilter: + type: object + properties: + term: + type: string + fields: + type: array + items: + type: string + fields: + type: array + items: + type: string query: - $ref: '#/components/schemas/EntityPredicate' - examples: - Get all service components: - value: - query: - $all: - - kind: component - - spec.type: service - Get components owned by specific teams: - value: - query: - $all: - - kind: component - - spec.owner: - $in: - - backend-team - - platform-team - Get non-production services: - value: - query: - $all: - - kind: component - - spec.type: service - - $not: - spec.lifecycle: production + $ref: '#/components/schemas/JsonObject' /entity-facets: get: operationId: GetEntityFacets diff --git a/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts b/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts index 258c777067..dfb111359c 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/apis/Api.server.ts @@ -26,7 +26,6 @@ import { Entity } from '../models/Entity.model'; import { EntityAncestryResponse } from '../models/EntityAncestryResponse.model'; import { EntityFacetsResponse } from '../models/EntityFacetsResponse.model'; import { GetEntitiesByRefsRequest } from '../models/GetEntitiesByRefsRequest.model'; -import { QueryEntitiesByPredicate200Response } from '../models/QueryEntitiesByPredicate200Response.model'; import { QueryEntitiesByPredicateRequest } from '../models/QueryEntitiesByPredicateRequest.model'; import { RefreshEntityRequest } from '../models/RefreshEntityRequest.model'; import { ValidateEntity400Response } from '../models/ValidateEntity400Response.model'; @@ -127,13 +126,7 @@ export type GetEntityFacets = { */ export type QueryEntitiesByPredicate = { body: QueryEntitiesByPredicateRequest; - query: { - limit?: number; - offset?: number; - orderField?: Array; - after?: string; - }; - response: QueryEntitiesByPredicate200Response | Error | Error; + response: EntitiesQueryResponse | Error | Error; }; /** * @public diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts deleted file mode 100644 index bdc862ca50..0000000000 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicate.model.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicateAll } from '../models/EntityPredicateAll.model'; -import { EntityPredicateAny } from '../models/EntityPredicateAny.model'; -import { EntityPredicateNot } from '../models/EntityPredicateNot.model'; -import { EntityPredicateValue } from '../models/EntityPredicateValue.model'; - -/** - * A predicate-based filter supporting logical operators. - $all: All conditions must match (AND) - $any: At least one condition must match (OR) - $not: Negates the condition - $exists: Check if field exists - $in: Match any value in array - * @public - */ -export type EntityPredicate = - | EntityPredicateAll - | EntityPredicateAny - | EntityPredicateNot - | boolean - | number - | string - | { [key: string]: EntityPredicateValue }; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAll.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAll.model.ts deleted file mode 100644 index 080b8307b4..0000000000 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAll.model.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicate } from '../models/EntityPredicate.model'; - -/** - * All conditions must match (AND logic) - * @public - */ -export interface EntityPredicateAll { - $all: Array; -} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAny.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAny.model.ts deleted file mode 100644 index 170ea3ab0c..0000000000 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateAny.model.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicate } from '../models/EntityPredicate.model'; - -/** - * At least one condition must match (OR logic) - * @public - */ -export interface EntityPredicateAny { - $any: Array; -} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateIn.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateIn.model.ts deleted file mode 100644 index 574ab4f45b..0000000000 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateIn.model.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicateInInInner } from '../models/EntityPredicateInInInner.model'; - -/** - * Match any value in array - * @public - */ -export interface EntityPredicateIn { - $in: Array; -} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts deleted file mode 100644 index 2b4e3d7c2f..0000000000 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateInInInner.model.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** - -/** - * @public - */ -export type EntityPredicateInInInner = boolean | number | string; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateNot.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateNot.model.ts deleted file mode 100644 index e56279b0fc..0000000000 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateNot.model.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicate } from '../models/EntityPredicate.model'; - -/** - * Negates the condition - * @public - */ -export interface EntityPredicateNot { - $not: EntityPredicate; -} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts deleted file mode 100644 index c230c2a984..0000000000 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/EntityPredicateValue.model.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { EntityPredicateExists } from '../models/EntityPredicateExists.model'; -import { EntityPredicateIn } from '../models/EntityPredicateIn.model'; - -/** - * Value for a field predicate - * @public - */ -export type EntityPredicateValue = - | EntityPredicateExists - | EntityPredicateIn - | boolean - | number - | string; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts deleted file mode 100644 index 3c4b74cd10..0000000000 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200Response.model.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import { Entity } from '../models/Entity.model'; -import { QueryEntitiesByPredicate200ResponsePageInfo } from '../models/QueryEntitiesByPredicate200ResponsePageInfo.model'; - -/** - * @public - */ -export interface QueryEntitiesByPredicate200Response { - /** - * The list of entities matching the predicate filter. - */ - items: Array; - pageInfo: QueryEntitiesByPredicate200ResponsePageInfo; -} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts deleted file mode 100644 index 3ef9f73758..0000000000 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicate200ResponsePageInfo.model.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2026 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** - -/** - * @public - */ -export interface QueryEntitiesByPredicate200ResponsePageInfo { - /** - * The cursor for the next batch of entities. - */ - nextCursor?: string; -} diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts index 8b8276c9da..bdcd854a5c 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts @@ -17,11 +17,20 @@ // ****************************************************************** // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** -import { EntityPredicate } from '../models/EntityPredicate.model'; + +import { QueryEntitiesByPredicateRequestFullTextFilter } from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model'; /** * @public */ export interface QueryEntitiesByPredicateRequest { - query?: EntityPredicate; + cursor?: string; + limit?: number; + orderField?: Array; + fullTextFilter?: QueryEntitiesByPredicateRequestFullTextFilter; + fields?: Array; + /** + * A type representing all allowed JSON object values. + */ + query?: { [key: string]: any }; } diff --git a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateExists.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestFullTextFilter.model.ts similarity index 88% rename from packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateExists.model.ts rename to plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestFullTextFilter.model.ts index 7800fcd20c..e7e971e1ea 100644 --- a/packages/catalog-client/src/schema/openapi/generated/models/EntityPredicateExists.model.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestFullTextFilter.model.ts @@ -19,9 +19,9 @@ // ****************************************************************** /** - * Check if field exists * @public */ -export interface EntityPredicateExists { - $exists: boolean; +export interface QueryEntitiesByPredicateRequestFullTextFilter { + term?: string; + fields?: Array; } diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts index 9d7ee33602..889b5968fa 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts @@ -31,14 +31,6 @@ export * from '../models/EntityFacet.model'; export * from '../models/EntityFacetsResponse.model'; export * from '../models/EntityLink.model'; export * from '../models/EntityMeta.model'; -export * from '../models/EntityPredicate.model'; -export * from '../models/EntityPredicateAll.model'; -export * from '../models/EntityPredicateAny.model'; -export * from '../models/EntityPredicateExists.model'; -export * from '../models/EntityPredicateIn.model'; -export * from '../models/EntityPredicateInInInner.model'; -export * from '../models/EntityPredicateNot.model'; -export * from '../models/EntityPredicateValue.model'; export * from '../models/EntityRelation.model'; export * from '../models/ErrorError.model'; export * from '../models/ErrorRequest.model'; @@ -53,9 +45,8 @@ export * from '../models/LocationsQueryResponse.model'; export * from '../models/LocationsQueryResponsePageInfo.model'; export * from '../models/ModelError.model'; export * from '../models/NullableEntity.model'; -export * from '../models/QueryEntitiesByPredicate200Response.model'; -export * from '../models/QueryEntitiesByPredicate200ResponsePageInfo.model'; export * from '../models/QueryEntitiesByPredicateRequest.model'; +export * from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model'; export * from '../models/RecursivePartialEntity.model'; export * from '../models/RecursivePartialEntityMeta.model'; export * from '../models/RecursivePartialEntityMetaAllOf.model'; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/router.ts b/plugins/catalog-backend/src/schema/openapi/generated/router.ts index 2625bd0125..afac4ce5f8 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/router.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/router.ts @@ -262,130 +262,6 @@ export const spec = { description: 'A type representing all allowed JSON object values.', additionalProperties: {}, }, - EntityPredicate: { - description: - 'A predicate-based filter supporting logical operators.\n- $all: All conditions must match (AND)\n- $any: At least one condition must match (OR)\n- $not: Negates the condition\n- $exists: Check if field exists\n- $in: Match any value in array\n', - oneOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - type: 'boolean', - }, - { - $ref: '#/components/schemas/EntityPredicateAll', - }, - { - $ref: '#/components/schemas/EntityPredicateAny', - }, - { - $ref: '#/components/schemas/EntityPredicateNot', - }, - { - type: 'object', - additionalProperties: { - $ref: '#/components/schemas/EntityPredicateValue', - }, - }, - ], - }, - EntityPredicateAll: { - type: 'object', - description: 'All conditions must match (AND logic)', - additionalProperties: false, - properties: { - $all: { - type: 'array', - items: { - $ref: '#/components/schemas/EntityPredicate', - }, - }, - }, - required: ['$all'], - }, - EntityPredicateAny: { - type: 'object', - description: 'At least one condition must match (OR logic)', - additionalProperties: false, - properties: { - $any: { - type: 'array', - items: { - $ref: '#/components/schemas/EntityPredicate', - }, - }, - }, - required: ['$any'], - }, - EntityPredicateNot: { - type: 'object', - description: 'Negates the condition', - additionalProperties: false, - properties: { - $not: { - $ref: '#/components/schemas/EntityPredicate', - }, - }, - required: ['$not'], - }, - EntityPredicateValue: { - description: 'Value for a field predicate', - oneOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - type: 'boolean', - }, - { - $ref: '#/components/schemas/EntityPredicateExists', - }, - { - $ref: '#/components/schemas/EntityPredicateIn', - }, - ], - }, - EntityPredicateExists: { - type: 'object', - description: 'Check if field exists', - additionalProperties: false, - properties: { - $exists: { - type: 'boolean', - }, - }, - required: ['$exists'], - }, - EntityPredicateIn: { - type: 'object', - description: 'Match any value in array', - additionalProperties: false, - properties: { - $in: { - type: 'array', - items: { - oneOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - type: 'boolean', - }, - ], - }, - }, - }, - required: ['$in'], - }, MapStringString: { type: 'object', properties: {}, @@ -1363,28 +1239,7 @@ export const spec = { content: { 'application/json': { schema: { - type: 'object', - properties: { - items: { - type: 'array', - items: { - $ref: '#/components/schemas/Entity', - }, - description: - 'The list of entities matching the predicate filter.', - }, - pageInfo: { - type: 'object', - properties: { - nextCursor: { - type: 'string', - description: - 'The cursor for the next batch of entities.', - }, - }, - }, - }, - required: ['items', 'pageInfo'], + $ref: '#/components/schemas/EntitiesQueryResponse', }, }, }, @@ -1402,81 +1257,47 @@ export const spec = { JWT: [], }, ], - parameters: [ - { - $ref: '#/components/parameters/limit', - }, - { - $ref: '#/components/parameters/offset', - }, - { - $ref: '#/components/parameters/orderField', - }, - { - $ref: '#/components/parameters/after', - }, - ], requestBody: { - required: true, + required: false, content: { 'application/json': { schema: { type: 'object', - additionalProperties: false, properties: { + cursor: { + type: 'string', + }, + limit: { + type: 'number', + }, + orderField: { + type: 'array', + items: { + type: 'string', + }, + }, + fullTextFilter: { + type: 'object', + properties: { + term: { + type: 'string', + }, + fields: { + type: 'array', + items: { + type: 'string', + }, + }, + }, + }, + fields: { + type: 'array', + items: { + type: 'string', + }, + }, query: { - $ref: '#/components/schemas/EntityPredicate', - }, - }, - }, - examples: { - 'Get all service components': { - value: { - query: { - $all: [ - { - kind: 'component', - }, - { - 'spec.type': 'service', - }, - ], - }, - }, - }, - 'Get components owned by specific teams': { - value: { - query: { - $all: [ - { - kind: 'component', - }, - { - 'spec.owner': { - $in: ['backend-team', 'platform-team'], - }, - }, - ], - }, - }, - }, - 'Get non-production services': { - value: { - query: { - $all: [ - { - kind: 'component', - }, - { - 'spec.type': 'service', - }, - { - $not: { - 'spec.lifecycle': 'production', - }, - }, - ], - }, + $ref: '#/components/schemas/JsonObject', }, }, }, diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts index cf262c7778..894949a691 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts @@ -29,7 +29,6 @@ describe('AuthorizedEntitiesCatalog', () => { const fakeCatalog = { entities: jest.fn(), entitiesBatch: jest.fn(), - queryEntitiesByPredicate: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), facets: jest.fn(), diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts index 831a3bb76b..d3d0dba5b3 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts @@ -32,7 +32,6 @@ import { EntityAncestryResponse, EntityFacetsRequest, EntityFacetsResponse, - EntityPredicateRequest, QueryEntitiesRequest, QueryEntitiesResponse, } from '../catalog/types'; @@ -197,36 +196,6 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog { return this.entitiesCatalog.queryEntities(request); } - async queryEntitiesByPredicate( - request?: EntityPredicateRequest, - ): Promise { - if (!request) { - return { - entities: { type: 'object', entities: [] }, - pageInfo: { hasNextPage: false }, - }; - } - - const authorizeDecision = ( - await this.permissionApi.authorizeConditional( - [{ permission: catalogEntityReadPermission }], - { credentials: request.credentials }, - ) - )[0]; - - if (authorizeDecision.result === AuthorizeResult.DENY) { - return { - entities: { type: 'object', entities: [] }, - pageInfo: { hasNextPage: false }, - }; - } - - // Note: For CONDITIONAL results, we pass through to the underlying catalog - // since EntityPredicate filters are separate from EntityFilter permission conditions. - // The permission filter would need to be applied separately if needed. - return this.entitiesCatalog.queryEntitiesByPredicate(request); - } - async removeEntityByUid( uid: string, options: { credentials: BackstageCredentials }, diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 151e345f5b..11bce7bd86 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -31,7 +31,6 @@ import { EntityFacetsResponse, EntityOrder, EntityPagination, - EntityPredicateRequest, QueryEntitiesRequest, QueryEntitiesResponse, } from '../catalog/types'; @@ -46,8 +45,6 @@ import { import { Stitcher } from '../stitching/types'; import { - decodeCursor, - encodeCursor, expandLegacyCompoundRelationsInEntity, isQueryEntitiesCursorRequest, isQueryEntitiesInitialRequest, @@ -55,7 +52,6 @@ import { import { EntityFilter } from '@backstage/plugin-catalog-node'; import { LoggerService } from '@backstage/backend-plugin-api'; import { applyEntityFilterToQuery } from './request/applyEntityFilterToQuery'; -import { applyPredicateEntityFilterToQuery } from './request/applyPredicateEntityFilterToQuery'; import { processRawEntitiesResult } from './response'; const DEFAULT_LIMIT = 200; @@ -217,156 +213,6 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { }; } - async queryEntitiesByPredicate( - request?: EntityPredicateRequest, - ): Promise { - const db = this.database; - const { limit, offset } = parsePagination(request?.pagination); - - const cursor = request?.pagination?.after - ? decodeCursor(request.pagination.after) - : { - query: request?.query, - orderFields: request?.order || [], - isPrevious: false, - orderFieldValues: undefined, - firstSortFieldValues: undefined, - }; - - // Use query from cursor if not provided in request (pagination case) - const effectiveQuery = request?.query ?? cursor.query; - const sortField = cursor.orderFields?.[0]; - - let entitiesQuery = db('final_entities'); - - // Join with search table if we have a sort field - if (sortField) { - entitiesQuery = entitiesQuery - .distinct() - .leftOuterJoin({ order_0: 'search' }, function search(inner) { - inner - .on('order_0.entity_id', 'final_entities.entity_id') - .andOn('order_0.key', db.raw('?', [sortField.field])); - }) - .select({ - entity_id: 'final_entities.entity_id', - final_entity: 'final_entities.final_entity', - value: 'order_0.value', - }); - } else { - entitiesQuery = entitiesQuery.select({ - entity_id: 'final_entities.entity_id', - final_entity: 'final_entities.final_entity', - }); - } - - entitiesQuery = entitiesQuery.whereNotNull('final_entities.final_entity'); - - // Apply predicate filter from cursor - if (effectiveQuery) { - entitiesQuery = applyPredicateEntityFilterToQuery({ - filter: effectiveQuery, - targetQuery: entitiesQuery, - onEntityIdField: 'final_entities.entity_id', - knex: db, - }); - } - - // Apply cursor-based pagination (keyset pagination) - if (cursor.orderFieldValues) { - if (cursor.orderFieldValues.length === 2) { - const [sortValue, entityId] = cursor.orderFieldValues; - const isOrderingDescending = sortField?.order === 'desc'; - entitiesQuery = entitiesQuery.andWhere(function nested() { - this.where( - 'order_0.value', - isOrderingDescending ? '<' : '>', - sortValue, - ) - .orWhere('order_0.value', '=', sortValue) - .andWhere('final_entities.entity_id', '>', entityId); - }); - } else if (cursor.orderFieldValues.length === 1) { - const [entityId] = cursor.orderFieldValues; - entitiesQuery = entitiesQuery.andWhere( - 'final_entities.entity_id', - '>', - entityId, - ); - } - } - - if (sortField) { - if (db.client.config.client === 'pg') { - entitiesQuery = entitiesQuery.orderBy([ - { column: 'order_0.value', order: sortField.order, nulls: 'last' }, - { column: 'final_entities.entity_id', order: 'asc' }, - ]); - } else { - entitiesQuery = entitiesQuery.orderBy([ - { column: 'order_0.value', order: undefined, nulls: 'last' }, - { column: 'order_0.value', order: sortField.order }, - { column: 'final_entities.entity_id', order: 'asc' }, - ]); - } - } else { - entitiesQuery = entitiesQuery.orderBy('final_entities.entity_id', 'asc'); - } - - // Apply a manually set initial offset (only when not using cursor pagination) - if (!request?.pagination?.after && offset !== undefined) { - entitiesQuery = entitiesQuery.offset(offset); - } - const effectiveLimit = limit ?? DEFAULT_LIMIT; - entitiesQuery = entitiesQuery.limit(effectiveLimit + 1); - - let rows = await entitiesQuery; - let pageInfo: DbPageInfo; - - if (rows.length <= effectiveLimit) { - pageInfo = { hasNextPage: false }; - } else { - // Remove the extra row - rows = rows.slice(0, -1); - - const lastRow = rows[rows.length - 1]; - const firstRow = rows[0]; - - // Create proper cursor with query field - const nextCursor: Cursor = { - query: effectiveQuery, - orderFields: cursor.orderFields || [], - orderFieldValues: sortField - ? [(lastRow as any).value, lastRow.entity_id] - : [lastRow.entity_id], - isPrevious: false, - firstSortFieldValues: - cursor.firstSortFieldValues || - (sortField - ? [(firstRow as any).value, firstRow.entity_id] - : [firstRow.entity_id]), - }; - - pageInfo = { - hasNextPage: true, - endCursor: encodeCursor(nextCursor), - }; - } - - return { - entities: processRawEntitiesResult( - rows.map(r => r.final_entity!), - this.enableRelationsCompatibility - ? e => { - expandLegacyCompoundRelationsInEntity(e); - return e; - } - : undefined, - ), - pageInfo, - }; - } - async entitiesBatch( request: EntitiesBatchRequest, ): Promise { @@ -457,10 +303,11 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { }); } - // Add regular filters, if given - if (cursor.filter) { + // Add regular filters and/or predicate query, if given + if (cursor.filter || cursor.query) { applyEntityFilterToQuery({ filter: cursor.filter, + query: cursor.query, targetQuery: inner, onEntityIdField: 'final_entities.entity_id', knex: this.database, diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index 2a792f1de1..4571d3bc68 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -72,7 +72,7 @@ describe('createRouter readonly disabled', () => { beforeEach(async () => { entitiesCatalog = { entities: jest.fn(), - queryEntitiesByPredicate: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), @@ -480,6 +480,62 @@ describe('createRouter readonly disabled', () => { }); }); + describe('POST /entities/by-query', () => { + it('queries entities with a predicate filter', async () => { + const items: Entity[] = [ + { apiVersion: 'a', kind: 'b', metadata: { name: 'n' } }, + ]; + entitiesCatalog.queryEntities.mockResolvedValue({ + items: { type: 'object', entities: items }, + pageInfo: {}, + totalItems: 1, + }); + + const response = await request(app) + .post('/entities/by-query') + .send({ query: { kind: 'b' }, limit: 10 }); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + items, + totalItems: 1, + pageInfo: {}, + }); + expect(entitiesCatalog.queryEntities).toHaveBeenCalledWith( + expect.objectContaining({ + query: { kind: 'b' }, + limit: 10, + credentials: mockCredentials.user(), + }), + ); + }); + + it('paginates with a cursor in the body', async () => { + const items: Entity[] = [ + { apiVersion: 'a', kind: 'b', metadata: { name: 'n' } }, + ]; + const cursor = mockCursor({ totalItems: 100, isPrevious: false }); + + entitiesCatalog.queryEntities.mockResolvedValue({ + items: { type: 'object', entities: items }, + pageInfo: { nextCursor: mockCursor() }, + totalItems: 100, + }); + + const response = await request(app) + .post('/entities/by-query') + .send({ cursor: encodeCursor(cursor) }); + + expect(response.status).toEqual(200); + expect(entitiesCatalog.queryEntities).toHaveBeenCalledWith( + expect.objectContaining({ + cursor, + credentials: mockCredentials.user(), + }), + ); + }); + }); + describe('GET /entities/by-uid/:uid', () => { it('can fetch entity by uid', async () => { const entity: Entity = { @@ -1121,7 +1177,7 @@ describe('createRouter readonly and raw json enabled', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), - queryEntitiesByPredicate: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), @@ -1338,7 +1394,7 @@ describe('NextRouter permissioning', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), - queryEntitiesByPredicate: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index c3608c19a6..48bf7961b8 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -30,7 +30,6 @@ import { } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { InputError, serializeError } from '@backstage/errors'; -import { parseFilterPredicate } from '@backstage/filter-predicates'; import { LocationAnalyzer } from '@backstage/plugin-catalog-node'; import express from 'express'; import yn from 'yn'; @@ -66,7 +65,7 @@ import { encodeLocationQueryCursor, parseLocationQuery, } from './request/parseLocationQuery'; -import { parseEntityOrderFieldParams } from './request/parseEntityOrderFieldParams'; +import { parseEntityQuery } from './request/parseEntityQuery'; /** * Options used by {@link createRouter}. @@ -265,31 +264,34 @@ export async function createRouter( eventId: 'entity-fetch', request: req, meta: { - queryType: 'by-query-predicate', + queryType: 'by-query', }, }); try { - // Validate the query using the Zod schema from @backstage/filter-predicates - const query = req.body.query - ? parseFilterPredicate(req.body.query) - : undefined; - const order = parseEntityOrderFieldParams(req.query); - const pagination = parseEntityPaginationParams(req.query); const credentials = await httpAuth.credentials(req); + const { fields: rawFields, ...parsed } = parseEntityQuery( + req.body ?? {}, + ); + const fields = rawFields?.length + ? parseEntityTransformParams({ fields: rawFields }) + : undefined; - const { entities, pageInfo } = - await entitiesCatalog.queryEntitiesByPredicate({ - query, - order, - pagination, + const { items, pageInfo, totalItems } = + await entitiesCatalog.queryEntities({ credentials, + fields, + ...parsed, }); const meta = { + totalItems, pageInfo: { - ...(pageInfo.hasNextPage && { - nextCursor: pageInfo.endCursor, + ...(pageInfo.nextCursor && { + nextCursor: encodeCursor(pageInfo.nextCursor), + }), + ...(pageInfo.prevCursor && { + prevCursor: encodeCursor(pageInfo.prevCursor), }), }, }; @@ -298,10 +300,10 @@ export async function createRouter( await writeEntitiesResponse({ res, - items: entities, + items, alwaysUseObjectMode: enableRelationsCompatibility, - responseWrapper: items => ({ - items, + responseWrapper: entities => ({ + items: entities, ...meta, }), }); diff --git a/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.test.ts b/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.test.ts index ea64f5b400..8d3db17934 100644 --- a/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.test.ts @@ -108,7 +108,7 @@ describe.each(databases.eachSupportedId())( } // #endregion - describe.each(strategies)('with strategy %p', strategy => { + describe.each(strategies)('with strategy %p', _strategy => { async function query(filter: EntityFilter): Promise { const q = knex('final_entities').whereNotNull( @@ -119,7 +119,6 @@ describe.each(databases.eachSupportedId())( targetQuery: q, onEntityIdField: 'final_entities.entity_id', knex, - strategy, }); return await q.then(rows => rows diff --git a/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.ts index b2a7fb6708..7cbe4f5f58 100644 --- a/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.ts @@ -18,8 +18,10 @@ import { EntitiesSearchFilter, EntityFilter, } from '@backstage/plugin-catalog-node'; +import { FilterPredicate } from '@backstage/filter-predicates'; import { Knex } from 'knex'; import { DbSearchRow } from '../../database/tables'; +import { applyPredicateEntityFilterToQuery } from './applyPredicateEntityFilterToQuery'; function isEntitiesSearchFilter( filter: EntitiesSearchFilter | EntityFilter, @@ -118,13 +120,28 @@ function applyInStrategy( // The actual exported function export function applyEntityFilterToQuery(options: { - filter: EntityFilter; + filter?: EntityFilter; + query?: FilterPredicate; targetQuery: Knex.QueryBuilder; onEntityIdField: string; knex: Knex; - strategy?: 'in' | 'join'; }): Knex.QueryBuilder { - const { filter, targetQuery, onEntityIdField, knex } = options; + const { filter, query, targetQuery, onEntityIdField, knex } = options; - return applyInStrategy(filter, targetQuery, onEntityIdField, knex, false); + let result = targetQuery; + + if (filter) { + result = applyInStrategy(filter, result, onEntityIdField, knex, false); + } + + if (query) { + result = applyPredicateEntityFilterToQuery({ + filter: query, + targetQuery: result, + onEntityIdField, + knex, + }); + } + + return result; } diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index eff7c12f7e..db39a2195a 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -165,15 +165,14 @@ function applyPredicateInStrategy( ); } - // Handle primitive value at top level (e.g., "component" shorthand) + // Reject primitives at the top level. Matching by value without specifying + // a field key is ambiguous and should not be allowed. if (isPrimitive(filter)) { - const matchQuery = knex('search') - .select('search.entity_id') - .where({ value: String(filter).toLowerCase() }); - return targetQuery.andWhere( - onEntityIdField, - negate ? 'not in' : 'in', - matchQuery, + throw new InputError( + `Invalid filter predicate: top-level primitive values are not supported. ` + + `Wrap the value in a field expression, e.g. { "kind": ${JSON.stringify( + filter, + )} }`, ); } @@ -233,7 +232,6 @@ export function applyPredicateEntityFilterToQuery(options: { targetQuery: Knex.QueryBuilder; onEntityIdField: string; knex: Knex; - strategy?: 'in' | 'join'; }): Knex.QueryBuilder { const { filter, targetQuery, onEntityIdField, knex } = options; diff --git a/plugins/catalog-backend/src/service/request/parseEntityQuery.ts b/plugins/catalog-backend/src/service/request/parseEntityQuery.ts new file mode 100644 index 0000000000..c22d849391 --- /dev/null +++ b/plugins/catalog-backend/src/service/request/parseEntityQuery.ts @@ -0,0 +1,115 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InputError } from '@backstage/errors'; +import { + createZodV3FilterPredicateSchema, + FilterPredicate, +} from '@backstage/filter-predicates'; +import { z } from 'zod/v3'; +import { fromZodError } from 'zod-validation-error'; +import { QueryEntitiesByPredicateRequest } from '../../schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model'; +import { EntityOrder } from '../../catalog/types'; +import { Cursor } from '../../catalog/types'; +import { decodeCursor } from '../util'; + +const filterPredicateSchema = createZodV3FilterPredicateSchema(z); + +function isSupportedFilterPredicateRoot( + value: FilterPredicate | undefined, +): boolean { + if (value === undefined) { + return true; + } + if (typeof value !== 'object' || value === null || Array.isArray(value)) { + return false; + } + return true; +} + +function parseOrderFields( + orderField: string[] | undefined, +): EntityOrder[] | undefined { + if (!orderField?.length) { + return undefined; + } + return orderField.map(entry => { + const [field, order] = entry.split(','); + if (order !== undefined && order !== 'asc' && order !== 'desc') { + throw new InputError('Invalid order field order, must be asc or desc'); + } + return { field, order: order as 'asc' | 'desc' }; + }); +} + +export type ParsedEntityQuery = + | { + cursor: Cursor; + fields?: string[]; + limit?: number; + } + | { + query?: FilterPredicate; + orderFields?: EntityOrder[]; + fullTextFilter?: { term: string; fields?: string[] }; + fields?: string[]; + limit?: number; + offset?: number; + }; + +export function parseEntityQuery( + request: Readonly, +): ParsedEntityQuery { + if (request.cursor !== undefined) { + if (!request.cursor) { + throw new InputError('Cursor cannot be empty'); + } + + const cursor = decodeCursor(request.cursor); + return { + cursor, + fields: request.fields, + limit: request.limit, + }; + } + + let query: FilterPredicate | undefined; + if (request.query !== undefined) { + const result = filterPredicateSchema.safeParse(request.query); + if (!result.success) { + throw new InputError(`Invalid query: ${fromZodError(result.error)}`); + } + if (!isSupportedFilterPredicateRoot(result.data)) { + throw new InputError('Query must be an object'); + } + query = result.data; + } + + const orderFields = parseOrderFields(request.orderField); + + return { + query, + orderFields, + fullTextFilter: request.fullTextFilter + ? { + term: request.fullTextFilter.term ?? '', + fields: request.fullTextFilter.fields, + } + : undefined, + fields: request.fields, + limit: request.limit, + }; +} From b2b4f06f617748230661db65f0f5d4cb21800359 Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 17 Feb 2026 10:35:23 +0100 Subject: [PATCH 08/55] chore: small fix Signed-off-by: benjdlambert --- plugins/catalog-backend/src/service/request/parseEntityQuery.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend/src/service/request/parseEntityQuery.ts b/plugins/catalog-backend/src/service/request/parseEntityQuery.ts index c22d849391..5ad505b8a1 100644 --- a/plugins/catalog-backend/src/service/request/parseEntityQuery.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityQuery.ts @@ -20,7 +20,7 @@ import { FilterPredicate, } from '@backstage/filter-predicates'; import { z } from 'zod/v3'; -import { fromZodError } from 'zod-validation-error'; +import { fromZodError } from 'zod-validation-error/v3'; import { QueryEntitiesByPredicateRequest } from '../../schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model'; import { EntityOrder } from '../../catalog/types'; import { Cursor } from '../../catalog/types'; From 58edede71653ad9d19c284653444d80ac9cac6d8 Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 17 Feb 2026 10:45:55 +0100 Subject: [PATCH 09/55] chore: refactor a little bit Signed-off-by: benjdlambert --- .../openapi/generated/apis/Api.client.ts | 2 +- .../catalog-backend/src/schema/openapi.yaml | 21 +------------------ .../src/schema/openapi/generated/router.ts | 3 +-- .../request/applyEntityFilterToQuery.test.ts | 3 ++- .../request/applyEntityFilterToQuery.ts | 1 + 5 files changed, 6 insertions(+), 24 deletions(-) diff --git a/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts b/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts index 5a5eb2ec5d..086f3695d4 100644 --- a/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts +++ b/packages/catalog-client/src/schema/openapi/generated/apis/Api.client.ts @@ -457,7 +457,7 @@ export class DefaultApiClient { } /** - * Query entities using predicate-based filters. This endpoint provides an alternative filtering method with a more expressive filter syntax supporting logical operators ($all, $any, $not) and value operators ($exists, $in). Example query: ```json { \"query\": { \"$all\": [ {\"kind\": \"component\"}, {\"$any\": [ {\"spec.type\": \"service\"}, {\"spec.type\": \"website\"} ]}, {\"$not\": {\"spec.lifecycle\": \"experimental\"}} ] } } ``` + * Query entities using predicate-based filters. * @param queryEntitiesByPredicateRequest - */ public async queryEntitiesByPredicate( diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index f6b80ca062..f44ac026e4 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -1099,26 +1099,7 @@ paths: operationId: QueryEntitiesByPredicate tags: - Entity - description: | - Query entities using predicate-based filters. This endpoint provides an - alternative filtering method with a more expressive filter syntax supporting - logical operators ($all, $any, $not) and value operators ($exists, $in). - - Example query: - ```json - { - "query": { - "$all": [ - {"kind": "component"}, - {"$any": [ - {"spec.type": "service"}, - {"spec.type": "website"} - ]}, - {"$not": {"spec.lifecycle": "experimental"}} - ] - } - } - ``` + description: Query entities using predicate-based filters. responses: '200': description: Ok diff --git a/plugins/catalog-backend/src/schema/openapi/generated/router.ts b/plugins/catalog-backend/src/schema/openapi/generated/router.ts index afac4ce5f8..86ddeddf61 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/router.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/router.ts @@ -1231,8 +1231,7 @@ export const spec = { post: { operationId: 'QueryEntitiesByPredicate', tags: ['Entity'], - description: - 'Query entities using predicate-based filters. This endpoint provides an\nalternative filtering method with a more expressive filter syntax supporting\nlogical operators ($all, $any, $not) and value operators ($exists, $in).\n\nExample query:\n```json\n{\n "query": {\n "$all": [\n {"kind": "component"},\n {"$any": [\n {"spec.type": "service"},\n {"spec.type": "website"}\n ]},\n {"$not": {"spec.lifecycle": "experimental"}}\n ]\n }\n}\n```\n', + description: 'Query entities using predicate-based filters.', responses: { '200': { description: 'Ok', diff --git a/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.test.ts b/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.test.ts index 8d3db17934..ea64f5b400 100644 --- a/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.test.ts @@ -108,7 +108,7 @@ describe.each(databases.eachSupportedId())( } // #endregion - describe.each(strategies)('with strategy %p', _strategy => { + describe.each(strategies)('with strategy %p', strategy => { async function query(filter: EntityFilter): Promise { const q = knex('final_entities').whereNotNull( @@ -119,6 +119,7 @@ describe.each(databases.eachSupportedId())( targetQuery: q, onEntityIdField: 'final_entities.entity_id', knex, + strategy, }); return await q.then(rows => rows diff --git a/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.ts index 7cbe4f5f58..49356cd0ff 100644 --- a/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyEntityFilterToQuery.ts @@ -125,6 +125,7 @@ export function applyEntityFilterToQuery(options: { targetQuery: Knex.QueryBuilder; onEntityIdField: string; knex: Knex; + strategy?: 'in' | 'join'; }): Knex.QueryBuilder { const { filter, query, targetQuery, onEntityIdField, knex } = options; From 723b94aa16b28dcaecfa8a5b9fb74bf4d341ab05 Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 17 Feb 2026 10:57:37 +0100 Subject: [PATCH 10/55] chore: added some tests Signed-off-by: benjdlambert --- .../applyPredicateEntityFilterToQuery.test.ts | 220 ++++++++++++++++++ .../service/request/parseEntityQuery.test.ts | 199 ++++++++++++++++ 2 files changed, 419 insertions(+) create mode 100644 plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts create mode 100644 plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts new file mode 100644 index 0000000000..63b0bb9b24 --- /dev/null +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts @@ -0,0 +1,220 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestDatabases } from '@backstage/backend-test-utils'; +import { applyEntityFilterToQuery } from './applyEntityFilterToQuery'; +import { + DbFinalEntitiesRow, + DbRefreshStateRow, + DbSearchRow, +} from '../../database/tables'; +import { Knex } from 'knex'; +import { applyDatabaseMigrations } from '../../database/migrations'; +import { FilterPredicate } from '@backstage/filter-predicates'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { v4 as uuid } from 'uuid'; +import { buildEntitySearch } from '../../database/operations/stitcher/buildEntitySearch'; + +jest.setTimeout(60_000); + +const databases = TestDatabases.create(); + +describe.each(databases.eachSupportedId())( + 'applyEntityFilterToQuery with predicate queries, %p', + databaseId => { + let knex: Knex; + + beforeAll(async () => { + knex = await databases.init(databaseId); + await applyDatabaseMigrations(knex); + await addEntity({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { name: 'service-a', namespace: 'default' }, + spec: { type: 'service', lifecycle: 'production', owner: 'team-a' }, + }); + await addEntity({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { name: 'service-b', namespace: 'default' }, + spec: { type: 'service', lifecycle: 'experimental', owner: 'team-b' }, + }); + await addEntity({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { name: 'website-c', namespace: 'default' }, + spec: { type: 'website', lifecycle: 'production', owner: 'team-a' }, + }); + await addEntity({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { name: 'api-d', namespace: 'default' }, + spec: { type: 'openapi', lifecycle: 'production' }, + }); + await addEntity({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { name: 'bare-e', namespace: 'default' }, + spec: {}, + }); + }); + + afterAll(async () => { + knex.destroy(); + }); + + async function addEntity(entity: Entity) { + const id = uuid(); + const entityRef = stringifyEntityRef(entity); + const entityJson = JSON.stringify(entity); + + await knex('refresh_state').insert({ + entity_id: id, + entity_ref: entityRef, + unprocessed_entity: entityJson, + errors: '[]', + next_update_at: '2031-01-01 23:00:00', + last_discovery_at: '2021-04-01 13:37:00', + }); + + await knex('final_entities').insert({ + entity_id: id, + entity_ref: entityRef, + final_entity: entityJson, + hash: 'h', + stitch_ticket: '', + }); + + const search = await buildEntitySearch(id, entity); + await knex('search').insert(search); + } + + async function query(predicate: FilterPredicate): Promise { + const q = + knex('final_entities').whereNotNull('final_entity'); + applyEntityFilterToQuery({ + query: predicate, + targetQuery: q, + onEntityIdField: 'final_entities.entity_id', + knex, + }); + return await q.then(rows => + rows.map(row => JSON.parse(row.final_entity!).metadata.name).toSorted(), + ); + } + + it('filters by direct field value', async () => { + await expect(query({ kind: 'component' })).resolves.toEqual([ + 'bare-e', + 'service-a', + 'service-b', + 'website-c', + ]); + }); + + it('filters by exact spec field value', async () => { + await expect(query({ 'spec.type': 'service' })).resolves.toEqual([ + 'service-a', + 'service-b', + ]); + }); + + it('filters with $all', async () => { + await expect( + query({ + $all: [{ kind: 'component' }, { 'spec.type': 'service' }], + }), + ).resolves.toEqual(['service-a', 'service-b']); + }); + + it('filters with $any', async () => { + await expect( + query({ + $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], + }), + ).resolves.toEqual(['service-a', 'service-b', 'website-c']); + }); + + it('filters with $not', async () => { + await expect( + query({ + $all: [ + { kind: 'component' }, + { $not: { 'spec.lifecycle': 'experimental' } }, + ], + }), + ).resolves.toEqual(['bare-e', 'service-a', 'website-c']); + }); + + it('filters with $in', async () => { + await expect( + query({ 'spec.type': { $in: ['service', 'openapi'] } }), + ).resolves.toEqual(['api-d', 'service-a', 'service-b']); + }); + + it('filters with $exists true', async () => { + await expect( + query({ + $all: [{ kind: 'component' }, { 'spec.owner': { $exists: true } }], + }), + ).resolves.toEqual(['service-a', 'service-b', 'website-c']); + }); + + it('filters with $exists false', async () => { + await expect( + query({ + $all: [{ kind: 'component' }, { 'spec.owner': { $exists: false } }], + }), + ).resolves.toEqual(['bare-e']); + }); + + it('handles nested logical operators', async () => { + await expect( + query({ + $all: [ + { kind: 'component' }, + { + $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], + }, + { $not: { 'spec.lifecycle': 'experimental' } }, + ], + }), + ).resolves.toEqual(['service-a', 'website-c']); + }); + + it('throws on top-level primitive', async () => { + await expect(query('bad-value' as any)).rejects.toThrow( + /top-level primitive values are not supported/, + ); + }); + + it('combines filter and query independently', async () => { + const q = + knex('final_entities').whereNotNull('final_entity'); + applyEntityFilterToQuery({ + filter: { key: 'kind', values: ['component'] }, + query: { 'spec.type': 'service' }, + targetQuery: q, + onEntityIdField: 'final_entities.entity_id', + knex, + }); + const result = await q.then(rows => + rows.map(row => JSON.parse(row.final_entity!).metadata.name).toSorted(), + ); + expect(result).toEqual(['service-a', 'service-b']); + }); + }, +); diff --git a/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts b/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts new file mode 100644 index 0000000000..8b6d1c21b7 --- /dev/null +++ b/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts @@ -0,0 +1,199 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { parseEntityQuery } from './parseEntityQuery'; +import { encodeCursor } from '../util'; +import { Cursor } from '../../catalog/types'; + +describe('parseEntityQuery', () => { + describe('initial request', () => { + it('returns empty result for empty request', () => { + const result = parseEntityQuery({}); + expect(result).toEqual({ + query: undefined, + orderFields: undefined, + fullTextFilter: undefined, + fields: undefined, + limit: undefined, + }); + }); + + it('parses a simple query predicate', () => { + const query = { kind: 'component' }; + const result = parseEntityQuery({ query }); + expect(result).toEqual( + expect.objectContaining({ query: { kind: 'component' } }), + ); + }); + + it('parses a complex query with $all, $any, $not', () => { + const query = { + $all: [ + { kind: 'component' }, + { $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }] }, + { $not: { 'spec.lifecycle': 'experimental' } }, + ], + }; + const result = parseEntityQuery({ query }); + expect(result).toEqual(expect.objectContaining({ query })); + }); + + it('parses query with $exists operator', () => { + const query = { 'metadata.labels.team': { $exists: true } }; + const result = parseEntityQuery({ query }); + expect(result).toEqual(expect.objectContaining({ query })); + }); + + it('parses query with $in operator', () => { + const query = { kind: { $in: ['component', 'api'] } }; + const result = parseEntityQuery({ query }); + expect(result).toEqual(expect.objectContaining({ query })); + }); + + it('passes through limit', () => { + const result = parseEntityQuery({ limit: 50 }); + expect(result).toEqual(expect.objectContaining({ limit: 50 })); + }); + + it('passes through fields', () => { + const result = parseEntityQuery({ + fields: ['metadata.name', 'kind'], + }); + expect(result).toEqual( + expect.objectContaining({ fields: ['metadata.name', 'kind'] }), + ); + }); + + it('parses orderField into orderFields', () => { + const result = parseEntityQuery({ + orderField: ['metadata.name,asc', 'metadata.namespace,desc'], + }); + expect(result).toEqual( + expect.objectContaining({ + orderFields: [ + { field: 'metadata.name', order: 'asc' }, + { field: 'metadata.namespace', order: 'desc' }, + ], + }), + ); + }); + + it('parses fullTextFilter', () => { + const result = parseEntityQuery({ + fullTextFilter: { term: 'search term', fields: ['metadata.name'] }, + }); + expect(result).toEqual( + expect.objectContaining({ + fullTextFilter: { + term: 'search term', + fields: ['metadata.name'], + }, + }), + ); + }); + + it('defaults fullTextFilter term to empty string when missing', () => { + const result = parseEntityQuery({ + fullTextFilter: {} as any, + }); + expect(result).toEqual( + expect.objectContaining({ + fullTextFilter: { term: '', fields: undefined }, + }), + ); + }); + + it('throws on invalid query predicate', () => { + expect(() => + parseEntityQuery({ query: { $invalid: true } as any }), + ).toThrow(/Invalid query/); + }); + + it('throws when query root is not an object', () => { + expect(() => parseEntityQuery({ query: 'bad' as any })).toThrow( + /Query must be an object/, + ); + }); + + it('throws on invalid orderField order value', () => { + expect(() => + parseEntityQuery({ orderField: ['metadata.name,sideways'] }), + ).toThrow(/Invalid order field order/); + }); + }); + + describe('cursor request', () => { + function makeCursor(partial: Partial): string { + const full: Cursor = { + orderFields: [], + orderFieldValues: [], + isPrevious: false, + ...partial, + }; + return encodeCursor(full); + } + + it('decodes a valid cursor', () => { + const cursor = makeCursor({ + orderFields: [{ field: 'metadata.name', order: 'asc' }], + orderFieldValues: ['test'], + isPrevious: false, + }); + const result = parseEntityQuery({ cursor }); + expect(result).toEqual( + expect.objectContaining({ + cursor: expect.objectContaining({ + orderFields: [{ field: 'metadata.name', order: 'asc' }], + orderFieldValues: ['test'], + isPrevious: false, + }), + }), + ); + }); + + it('passes through limit and fields with cursor', () => { + const cursor = makeCursor({}); + const result = parseEntityQuery({ + cursor, + limit: 25, + fields: ['metadata.name'], + }); + expect(result).toEqual( + expect.objectContaining({ + limit: 25, + fields: ['metadata.name'], + }), + ); + }); + + it('throws on empty cursor string', () => { + expect(() => parseEntityQuery({ cursor: '' })).toThrow( + /Cursor cannot be empty/, + ); + }); + + it('throws on invalid base64 cursor', () => { + expect(() => parseEntityQuery({ cursor: '!!not-valid!!' })).toThrow( + /Malformed cursor/, + ); + }); + + it('throws on invalid JSON in cursor', () => { + const cursor = Buffer.from('not json', 'utf8').toString('base64'); + expect(() => parseEntityQuery({ cursor })).toThrow(/Malformed cursor/); + }); + }); +}); From e172faf7be85179aace9922216f279c8e45cc79c Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 17 Feb 2026 11:02:14 +0100 Subject: [PATCH 11/55] feat: add support for the querying in the catalog client Signed-off-by: benjdlambert --- .../testUtils/InMemoryCatalogClient.test.ts | 77 +++++++++++++++++++ .../src/testUtils/InMemoryCatalogClient.ts | 13 ++++ 2 files changed, 90 insertions(+) diff --git a/packages/catalog-client/src/testUtils/InMemoryCatalogClient.test.ts b/packages/catalog-client/src/testUtils/InMemoryCatalogClient.test.ts index d867ab1d28..aa3b624050 100644 --- a/packages/catalog-client/src/testUtils/InMemoryCatalogClient.test.ts +++ b/packages/catalog-client/src/testUtils/InMemoryCatalogClient.test.ts @@ -683,6 +683,83 @@ describe('InMemoryCatalogClient', () => { ]); }); + it('filters by predicate query', async () => { + const client = new InMemoryCatalogClient({ entities }); + const result = await client.queryEntities({ + query: { kind: 'CustomKind' }, + }); + expect(result.items).toEqual([entity1, entity3]); + expect(result.totalItems).toBe(2); + }); + + it('filters by predicate query with $all', async () => { + const client = new InMemoryCatalogClient({ entities }); + const result = await client.queryEntities({ + query: { + $all: [{ kind: 'CustomKind' }, { 'spec.type': 'service' }], + }, + }); + expect(result.items).toEqual([entity1, entity3]); + }); + + it('filters by predicate query with $any', async () => { + const client = new InMemoryCatalogClient({ entities }); + const result = await client.queryEntities({ + query: { + $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], + }, + }); + expect(result.items).toEqual([entity1, entity3, entity4]); + }); + + it('filters by predicate query with $not', async () => { + const client = new InMemoryCatalogClient({ entities }); + const result = await client.queryEntities({ + query: { + $all: [ + { kind: 'CustomKind' }, + { $not: { 'spec.lifecycle': 'production' } }, + ], + }, + }); + expect(result.items).toEqual([]); + }); + + it('filters by predicate query with $in', async () => { + const client = new InMemoryCatalogClient({ entities }); + const result = await client.queryEntities({ + query: { 'spec.type': { $in: ['service', 'library'] } }, + }); + expect(result.items).toEqual([entity1, entity2, entity3]); + }); + + it('filters by predicate query with $exists', async () => { + const client = new InMemoryCatalogClient({ entities }); + const result = await client.queryEntities({ + query: { 'spec.lifecycle': { $exists: false } }, + }); + expect(result.items).toEqual([entity4]); + }); + + it('preserves query predicate through cursor pagination', async () => { + const client = new InMemoryCatalogClient({ entities }); + const page1 = await client.queryEntities({ + query: { kind: 'CustomKind' }, + orderFields: { field: 'metadata.name', order: 'asc' }, + limit: 1, + }); + expect(page1.items.map(e => e.metadata.name)).toEqual(['e1']); + expect(page1.totalItems).toBe(2); + expect(page1.pageInfo.nextCursor).toBeDefined(); + + const page2 = await client.queryEntities({ + cursor: page1.pageInfo.nextCursor!, + limit: 1, + }); + expect(page2.items.map(e => e.metadata.name)).toEqual(['e3']); + expect(page2.pageInfo.nextCursor).toBeUndefined(); + }); + it('throws InputError for invalid cursor', async () => { const client = new InMemoryCatalogClient({ entities }); await expect( diff --git a/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts b/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts index e64706ff8d..5ffaafa16f 100644 --- a/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts +++ b/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts @@ -51,6 +51,7 @@ import { NotFoundError, NotImplementedError, } from '@backstage/errors'; +import { filterPredicateToFilterFunction } from '@backstage/filter-predicates'; import lodash from 'lodash'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { traverse } from '../../../../plugins/catalog-backend/src/database/operations/stitcher/buildEntitySearch'; @@ -373,6 +374,7 @@ export class InMemoryCatalogClient implements CatalogApi { ): Promise { // Decode query parameters from cursor or from the request directly let filter: EntityFilterQuery | undefined; + let query: Record | undefined; let orderFields: EntityOrderQuery | undefined; let fullTextFilter: { term: string; fields?: string[] } | undefined; let offset: number; @@ -386,12 +388,17 @@ export class InMemoryCatalogClient implements CatalogApi { throw new InputError('Invalid cursor'); } filter = deserializeFilter(c.filter as any[]); + query = c.query as Record | undefined; orderFields = c.orderFields as EntityOrderQuery | undefined; fullTextFilter = c.fullTextFilter as typeof fullTextFilter; offset = c.offset as number; limit = request.limit; } else { filter = request?.filter; + query = + request?.query && typeof request.query === 'object' + ? (request.query as Record) + : undefined; orderFields = request?.orderFields; fullTextFilter = request?.fullTextFilter; offset = request?.offset ?? 0; @@ -401,6 +408,11 @@ export class InMemoryCatalogClient implements CatalogApi { // Apply filter let items = this.#entities.filter(createFilter(filter)); + // Apply predicate-based query filter + if (query) { + items = items.filter(filterPredicateToFilterFunction(query)); + } + // Apply full-text filter, defaulting to the sort field or metadata.uid if (fullTextFilter) { const orderFieldsList = orderFields ? [orderFields].flat() : []; @@ -432,6 +444,7 @@ export class InMemoryCatalogClient implements CatalogApi { const cursorBase = { filter: serializeFilter(filter), + query, orderFields, fullTextFilter, totalItems, From c01f24006972acf5cdc0908692b6f870d460518e Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 17 Feb 2026 12:00:58 +0100 Subject: [PATCH 12/55] chore: fix typescript Signed-off-by: benjdlambert --- .../src/testUtils/InMemoryCatalogClient.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts b/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts index 5ffaafa16f..0a4dc57386 100644 --- a/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts +++ b/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts @@ -51,7 +51,10 @@ import { NotFoundError, NotImplementedError, } from '@backstage/errors'; -import { filterPredicateToFilterFunction } from '@backstage/filter-predicates'; +import { + FilterPredicate, + filterPredicateToFilterFunction, +} from '@backstage/filter-predicates'; import lodash from 'lodash'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { traverse } from '../../../../plugins/catalog-backend/src/database/operations/stitcher/buildEntitySearch'; @@ -374,7 +377,7 @@ export class InMemoryCatalogClient implements CatalogApi { ): Promise { // Decode query parameters from cursor or from the request directly let filter: EntityFilterQuery | undefined; - let query: Record | undefined; + let query: FilterPredicate | undefined; let orderFields: EntityOrderQuery | undefined; let fullTextFilter: { term: string; fields?: string[] } | undefined; let offset: number; @@ -388,17 +391,14 @@ export class InMemoryCatalogClient implements CatalogApi { throw new InputError('Invalid cursor'); } filter = deserializeFilter(c.filter as any[]); - query = c.query as Record | undefined; + query = c.query as FilterPredicate | undefined; orderFields = c.orderFields as EntityOrderQuery | undefined; fullTextFilter = c.fullTextFilter as typeof fullTextFilter; offset = c.offset as number; limit = request.limit; } else { filter = request?.filter; - query = - request?.query && typeof request.query === 'object' - ? (request.query as Record) - : undefined; + query = request?.query; orderFields = request?.orderFields; fullTextFilter = request?.fullTextFilter; offset = request?.offset ?? 0; From f0876da49f6a98baa378b28a9e67218f15528d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 10:35:15 +0100 Subject: [PATCH 13/55] use a structured orderBy for ordering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../catalog-client/src/CatalogClient.test.ts | 12 ++++--- packages/catalog-client/src/CatalogClient.ts | 4 +-- .../QueryEntitiesByPredicateRequest.model.ts | 3 +- ...iesByPredicateRequestOrderByInner.model.ts | 34 +++++++++++++++++++ .../schema/openapi/generated/models/index.ts | 1 + .../catalog-backend/src/schema/openapi.yaml | 15 ++++++-- .../QueryEntitiesByPredicateRequest.model.ts | 3 +- ...iesByPredicateRequestOrderByInner.model.ts | 34 +++++++++++++++++++ .../schema/openapi/generated/models/index.ts | 1 + .../src/schema/openapi/generated/router.ts | 14 ++++++-- .../service/request/parseEntityQuery.test.ts | 14 +++++--- .../src/service/request/parseEntityQuery.ts | 11 +++--- 12 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestOrderByInner.model.ts create mode 100644 plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestOrderByInner.model.ts diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index 4ee93c0cba..aee10cd3f2 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -751,9 +751,11 @@ describe('CatalogClient', () => { expect(mockedEndpoint).toHaveBeenCalledTimes(1); }); - it('should send orderFields with correct format (field,order)', async () => { + it('should send orderFields with correct format', async () => { const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { - expect(req.body.orderField).toEqual(['metadata.name,asc']); + expect(req.body.orderBy).toEqual([ + { field: 'metadata.name', order: 'asc' }, + ]); return res(ctx.json(defaultResponse)); }); @@ -769,9 +771,9 @@ describe('CatalogClient', () => { it('should send multiple orderFields with correct format', async () => { const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { - expect(req.body.orderField).toEqual([ - 'metadata.name,asc', - 'spec.type,desc', + expect(req.body.orderBy).toEqual([ + { field: 'metadata.name', order: 'asc' }, + { field: 'spec.type', order: 'desc' }, ]); return res(ctx.json(defaultResponse)); }); diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index 51d57f715e..a6f47933f2 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -368,9 +368,7 @@ export class CatalogClient implements CatalogApi { body.limit = limit; } if (orderFields !== undefined) { - body.orderField = ( - Array.isArray(orderFields) ? orderFields : [orderFields] - ).map(({ field, order }) => `${field},${order}`); + body.orderBy = [orderFields].flat(); } if (fullTextFilter) { body.fullTextFilter = fullTextFilter; diff --git a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts index bdcd854a5c..818a780b4a 100644 --- a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts @@ -19,6 +19,7 @@ // ****************************************************************** import { QueryEntitiesByPredicateRequestFullTextFilter } from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model'; +import { QueryEntitiesByPredicateRequestOrderByInner } from '../models/QueryEntitiesByPredicateRequestOrderByInner.model'; /** * @public @@ -26,7 +27,7 @@ import { QueryEntitiesByPredicateRequestFullTextFilter } from '../models/QueryEn export interface QueryEntitiesByPredicateRequest { cursor?: string; limit?: number; - orderField?: Array; + orderBy?: Array; fullTextFilter?: QueryEntitiesByPredicateRequestFullTextFilter; fields?: Array; /** diff --git a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestOrderByInner.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestOrderByInner.model.ts new file mode 100644 index 0000000000..373ca9715f --- /dev/null +++ b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestOrderByInner.model.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** + +/** + * @public + */ +export interface QueryEntitiesByPredicateRequestOrderByInner { + field: string; + order: QueryEntitiesByPredicateRequestOrderByInnerOrderEnum; +} + +/** + * @public + */ +export type QueryEntitiesByPredicateRequestOrderByInnerOrderEnum = + | 'asc' + | 'desc'; diff --git a/packages/catalog-client/src/schema/openapi/generated/models/index.ts b/packages/catalog-client/src/schema/openapi/generated/models/index.ts index 889b5968fa..9461ac8d28 100644 --- a/packages/catalog-client/src/schema/openapi/generated/models/index.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/index.ts @@ -47,6 +47,7 @@ export * from '../models/ModelError.model'; export * from '../models/NullableEntity.model'; export * from '../models/QueryEntitiesByPredicateRequest.model'; export * from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model'; +export * from '../models/QueryEntitiesByPredicateRequestOrderByInner.model'; export * from '../models/RecursivePartialEntity.model'; export * from '../models/RecursivePartialEntityMeta.model'; export * from '../models/RecursivePartialEntityMetaAllOf.model'; diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index f44ac026e4..41738c1148 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -1125,10 +1125,21 @@ paths: type: string limit: type: number - orderField: + orderBy: type: array items: - type: string + type: object + required: + - field + - order + properties: + field: + type: string + order: + type: string + enum: + - asc + - desc fullTextFilter: type: object properties: diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts index bdcd854a5c..818a780b4a 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts @@ -19,6 +19,7 @@ // ****************************************************************** import { QueryEntitiesByPredicateRequestFullTextFilter } from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model'; +import { QueryEntitiesByPredicateRequestOrderByInner } from '../models/QueryEntitiesByPredicateRequestOrderByInner.model'; /** * @public @@ -26,7 +27,7 @@ import { QueryEntitiesByPredicateRequestFullTextFilter } from '../models/QueryEn export interface QueryEntitiesByPredicateRequest { cursor?: string; limit?: number; - orderField?: Array; + orderBy?: Array; fullTextFilter?: QueryEntitiesByPredicateRequestFullTextFilter; fields?: Array; /** diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestOrderByInner.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestOrderByInner.model.ts new file mode 100644 index 0000000000..373ca9715f --- /dev/null +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequestOrderByInner.model.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** + +/** + * @public + */ +export interface QueryEntitiesByPredicateRequestOrderByInner { + field: string; + order: QueryEntitiesByPredicateRequestOrderByInnerOrderEnum; +} + +/** + * @public + */ +export type QueryEntitiesByPredicateRequestOrderByInnerOrderEnum = + | 'asc' + | 'desc'; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts index 889b5968fa..9461ac8d28 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/index.ts @@ -47,6 +47,7 @@ export * from '../models/ModelError.model'; export * from '../models/NullableEntity.model'; export * from '../models/QueryEntitiesByPredicateRequest.model'; export * from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model'; +export * from '../models/QueryEntitiesByPredicateRequestOrderByInner.model'; export * from '../models/RecursivePartialEntity.model'; export * from '../models/RecursivePartialEntityMeta.model'; export * from '../models/RecursivePartialEntityMetaAllOf.model'; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/router.ts b/plugins/catalog-backend/src/schema/openapi/generated/router.ts index 86ddeddf61..ed9536d6b9 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/router.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/router.ts @@ -1269,10 +1269,20 @@ export const spec = { limit: { type: 'number', }, - orderField: { + orderBy: { type: 'array', items: { - type: 'string', + type: 'object', + required: ['field', 'order'], + properties: { + field: { + type: 'string', + }, + order: { + type: 'string', + enum: ['asc', 'desc'], + }, + }, }, }, fullTextFilter: { diff --git a/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts b/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts index 8b6d1c21b7..b95c4c6c0e 100644 --- a/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts @@ -77,9 +77,12 @@ describe('parseEntityQuery', () => { ); }); - it('parses orderField into orderFields', () => { + it('parses orderBy into orderFields', () => { const result = parseEntityQuery({ - orderField: ['metadata.name,asc', 'metadata.namespace,desc'], + orderBy: [ + { field: 'metadata.name', order: 'asc' }, + { field: 'metadata.namespace', order: 'desc' }, + ], }); expect(result).toEqual( expect.objectContaining({ @@ -128,9 +131,12 @@ describe('parseEntityQuery', () => { ); }); - it('throws on invalid orderField order value', () => { + it('throws on invalid orderBy order value', () => { expect(() => - parseEntityQuery({ orderField: ['metadata.name,sideways'] }), + parseEntityQuery({ + // @ts-expect-error - invalid order value + orderBy: [{ field: 'metadata.name', order: 'sideways' }], + }), ).toThrow(/Invalid order field order/); }); }); diff --git a/plugins/catalog-backend/src/service/request/parseEntityQuery.ts b/plugins/catalog-backend/src/service/request/parseEntityQuery.ts index 5ad505b8a1..c33492e740 100644 --- a/plugins/catalog-backend/src/service/request/parseEntityQuery.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityQuery.ts @@ -41,17 +41,16 @@ function isSupportedFilterPredicateRoot( } function parseOrderFields( - orderField: string[] | undefined, + orderField: Array<{ field: string; order: string }> | undefined, ): EntityOrder[] | undefined { if (!orderField?.length) { return undefined; } - return orderField.map(entry => { - const [field, order] = entry.split(','); - if (order !== undefined && order !== 'asc' && order !== 'desc') { + return orderField.map(({ field, order }) => { + if (order !== 'asc' && order !== 'desc') { throw new InputError('Invalid order field order, must be asc or desc'); } - return { field, order: order as 'asc' | 'desc' }; + return { field, order }; }); } @@ -98,7 +97,7 @@ export function parseEntityQuery( query = result.data; } - const orderFields = parseOrderFields(request.orderField); + const orderFields = parseOrderFields(request.orderBy); return { query, From f076495eea5bdf960a250251ccc7a4acd90db327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 10:48:44 +0100 Subject: [PATCH 14/55] add todo about cursor introspection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- packages/catalog-client/src/CatalogClient.ts | 34 +++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index a6f47933f2..fa8a88c1ca 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -53,6 +53,7 @@ import { } from './utils'; import { DefaultApiClient, + GetEntitiesByQuery, GetLocationsByQueryRequest, QueryEntitiesByPredicateRequest, TypedResponse, @@ -271,35 +272,33 @@ export class CatalogClient implements CatalogApi { request: QueryEntitiesRequest = {}, options?: CatalogRequestOptions, ): Promise { + const isInitialRequest = isQueryEntitiesInitialRequest(request); + // Validate that filter and query are mutually exclusive - if ( - isQueryEntitiesInitialRequest(request) && - request.filter && - request.query - ) { + if (isInitialRequest && request.filter && request.query) { throw new Error( 'Cannot specify both "filter" and "query" in the same request. Use "filter" for traditional key-value filtering or "query" for predicate-based filtering.', ); } // Route to POST endpoint if query predicate is provided (initial request) - if (isQueryEntitiesInitialRequest(request) && request.query) { + if (isInitialRequest && request.query) { return this.queryEntitiesByPredicate(request, options); } // Route to POST endpoint if cursor contains a query predicate (pagination) - if ( - !isQueryEntitiesInitialRequest(request) && - cursorContainsQuery(request.cursor) - ) { + // TODO(freben): It's costly and non-opaque to have to introspect the cursor + // like this. It should be refactored in the future to not need this. + // Suggestion: make the GET and POST endpoints understand the same cursor + // format, and pick which one to call ONLY based on whether the cursor size + // risks hitting url length limits + if (!isInitialRequest && cursorContainsQuery(request.cursor)) { return this.queryEntitiesByPredicate(request, options); } - const params: Partial< - Parameters[0]['query'] - > = {}; + const params: Partial = {}; - if (isQueryEntitiesInitialRequest(request)) { + if (isInitialRequest) { const { fields = [], filter, @@ -357,7 +356,7 @@ export class CatalogClient implements CatalogApi { request: QueryEntitiesRequest, options?: CatalogRequestOptions, ): Promise { - const body: Record = {}; + const body: QueryEntitiesByPredicateRequest = {}; if (isQueryEntitiesInitialRequest(request)) { const { query, limit, orderFields, fullTextFilter, fields } = request; @@ -387,10 +386,7 @@ export class CatalogClient implements CatalogApi { } const res = await this.requestRequired( - await this.apiClient.queryEntitiesByPredicate( - { body: body as unknown as QueryEntitiesByPredicateRequest }, - options, - ), + await this.apiClient.queryEntitiesByPredicate({ body }, options), ); return { From f3b3b1dafb8eddc77f0506b2bf949a9c9568bab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 10:55:24 +0100 Subject: [PATCH 15/55] implement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../applyPredicateEntityFilterToQuery.test.ts | 12 +++++++++++ .../applyPredicateEntityFilterToQuery.ts | 21 ++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts index 63b0bb9b24..2f216fd219 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts @@ -181,6 +181,18 @@ describe.each(databases.eachSupportedId())( ).resolves.toEqual(['bare-e']); }); + it('filters with $hasPrefix', async () => { + await expect( + query({ 'metadata.name': { $hasPrefix: 'service' } }), + ).resolves.toEqual(['service-a', 'service-b']); + }); + + it('filters with $hasPrefix case-insensitively', async () => { + await expect( + query({ 'metadata.name': { $hasPrefix: 'Service' } }), + ).resolves.toEqual(['service-a', 'service-b']); + }); + it('handles nested logical operators', async () => { await expect( query({ diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index db39a2195a..d077bd1460 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -61,6 +61,12 @@ function isInValue( return typeof value === 'object' && value !== null && '$in' in value; } +function isHasPrefixValue( + value: FilterPredicateValue, +): value is { $hasPrefix: string } { + return typeof value === 'object' && value !== null && '$hasPrefix' in value; +} + function isFieldExpression(filter: FilterPredicate): boolean { if (typeof filter !== 'object' || filter === null) { return false; @@ -202,6 +208,19 @@ function applyPredicateInStrategy( .where({ key: normalizedKey }) .whereIn('value', values); this.andWhere(onEntityIdField, 'in', matchQuery); + } else if (isHasPrefixValue(value)) { + // Handle $hasPrefix + const prefix = value.$hasPrefix.toLowerCase(); + const escaped = prefix.replace(/[%_\\]/g, c => `\\${c}`); + const matchQuery = knex('search') + .select('search.entity_id') + .where({ key: normalizedKey }) + .andWhereRaw('?? like ? escape ?', [ + 'value', + `${escaped}%`, + '\\', + ]); + this.andWhere(onEntityIdField, 'in', matchQuery); } else if (isPrimitive(value)) { // Handle direct value match const matchQuery = knex('search') @@ -214,7 +233,7 @@ function applyPredicateInStrategy( } else { // Reject unsupported/invalid predicate values throw new InputError( - `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, or $in operator, but got ${JSON.stringify( + `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, $in, or $hasPrefix operator, but got ${JSON.stringify( value, )}`, ); From 02443480b3f22752d2e4d11f7dbe642514f1bdfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 10:56:31 +0100 Subject: [PATCH 16/55] locale lowercase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../request/applyPredicateEntityFilterToQuery.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index d077bd1460..91b29b6ce1 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -187,7 +187,7 @@ function applyPredicateInStrategy( return targetQuery[negate ? 'andWhereNot' : 'andWhere']( function fieldFilter() { for (const [key, value] of Object.entries(filter)) { - const normalizedKey = key.toLowerCase(); + const normalizedKey = key.toLocaleLowerCase('en-US'); if (isExistsValue(value)) { // Handle $exists @@ -202,7 +202,9 @@ function applyPredicateInStrategy( } } else if (isInValue(value)) { // Handle $in - const values = value.$in.map(v => String(v).toLowerCase()); + const values = value.$in.map(v => + String(v).toLocaleLowerCase('en-US'), + ); const matchQuery = knex('search') .select('search.entity_id') .where({ key: normalizedKey }) @@ -210,7 +212,7 @@ function applyPredicateInStrategy( this.andWhere(onEntityIdField, 'in', matchQuery); } else if (isHasPrefixValue(value)) { // Handle $hasPrefix - const prefix = value.$hasPrefix.toLowerCase(); + const prefix = value.$hasPrefix.toLocaleLowerCase('en-US'); const escaped = prefix.replace(/[%_\\]/g, c => `\\${c}`); const matchQuery = knex('search') .select('search.entity_id') @@ -227,7 +229,7 @@ function applyPredicateInStrategy( .select('search.entity_id') .where({ key: normalizedKey, - value: String(value).toLowerCase(), + value: String(value).toLocaleLowerCase('en-US'), }); this.andWhere(onEntityIdField, 'in', matchQuery); } else { From fa4e24d75265bc3267815f82cc87e887f08dd741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 11:04:28 +0100 Subject: [PATCH 17/55] remove the unnecessary inversion boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../applyPredicateEntityFilterToQuery.ts | 200 ++++++++---------- 1 file changed, 86 insertions(+), 114 deletions(-) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index 91b29b6ce1..eb6e65604a 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -115,60 +115,55 @@ function isFieldExpression(filter: FilterPredicate): boolean { * ``` */ -function applyPredicateInStrategy( - filter: FilterPredicate, - targetQuery: Knex.QueryBuilder, - onEntityIdField: string, - knex: Knex, - negate: boolean, -): Knex.QueryBuilder { +export function applyPredicateEntityFilterToQuery(options: { + filter: FilterPredicate; + targetQuery: Knex.QueryBuilder; + onEntityIdField: string; + knex: Knex; +}): Knex.QueryBuilder { + const { filter, targetQuery, onEntityIdField, knex } = options; // Handle $not if (isNotPredicate(filter)) { - return applyPredicateInStrategy( - filter.$not, - targetQuery, - onEntityIdField, - knex, - !negate, + return targetQuery.andWhereNot(subQuery => + applyPredicateEntityFilterToQuery({ + filter: filter.$not, + targetQuery: subQuery, + onEntityIdField, + knex, + }), ); } // Handle $all (AND) if (isAllPredicate(filter)) { - return targetQuery[negate ? 'andWhereNot' : 'andWhere']( - function allFilter() { - for (const subFilter of filter.$all) { - this.andWhere(subQuery => - applyPredicateInStrategy( - subFilter, - subQuery, - onEntityIdField, - knex, - false, - ), - ); - } - }, - ); + return targetQuery.andWhere(function allFilter() { + for (const subFilter of filter.$all) { + this.andWhere(subQuery => + applyPredicateEntityFilterToQuery({ + filter: subFilter, + targetQuery: subQuery, + onEntityIdField, + knex, + }), + ); + } + }); } // Handle $any (OR) if (isAnyPredicate(filter)) { - return targetQuery[negate ? 'andWhereNot' : 'andWhere']( - function anyFilter() { - for (const subFilter of filter.$any) { - this.orWhere(subQuery => - applyPredicateInStrategy( - subFilter, - subQuery, - onEntityIdField, - knex, - false, - ), - ); - } - }, - ); + return targetQuery.andWhere(function anyFilter() { + for (const subFilter of filter.$any) { + this.orWhere(subQuery => + applyPredicateEntityFilterToQuery({ + filter: subFilter, + targetQuery: subQuery, + onEntityIdField, + knex, + }), + ); + } + }); } // Reject primitives at the top level. Matching by value without specifying @@ -184,83 +179,60 @@ function applyPredicateInStrategy( // Handle field expressions like { "kind": "component" } or { "spec.type": { "$in": ["service", "website"] } } if (isFieldExpression(filter)) { - return targetQuery[negate ? 'andWhereNot' : 'andWhere']( - function fieldFilter() { - for (const [key, value] of Object.entries(filter)) { - const normalizedKey = key.toLocaleLowerCase('en-US'); + return targetQuery.andWhere(function fieldFilter() { + for (const [key, value] of Object.entries(filter)) { + const normalizedKey = key.toLocaleLowerCase('en-US'); - if (isExistsValue(value)) { - // Handle $exists - const existsQuery = knex('search') - .select('search.entity_id') - .where({ key: normalizedKey }); + if (isExistsValue(value)) { + // Handle $exists + const existsQuery = knex('search') + .select('search.entity_id') + .where({ key: normalizedKey }); - if (value.$exists) { - this.andWhere(onEntityIdField, 'in', existsQuery); - } else { - this.andWhere(onEntityIdField, 'not in', existsQuery); - } - } else if (isInValue(value)) { - // Handle $in - const values = value.$in.map(v => - String(v).toLocaleLowerCase('en-US'), - ); - const matchQuery = knex('search') - .select('search.entity_id') - .where({ key: normalizedKey }) - .whereIn('value', values); - this.andWhere(onEntityIdField, 'in', matchQuery); - } else if (isHasPrefixValue(value)) { - // Handle $hasPrefix - const prefix = value.$hasPrefix.toLocaleLowerCase('en-US'); - const escaped = prefix.replace(/[%_\\]/g, c => `\\${c}`); - const matchQuery = knex('search') - .select('search.entity_id') - .where({ key: normalizedKey }) - .andWhereRaw('?? like ? escape ?', [ - 'value', - `${escaped}%`, - '\\', - ]); - this.andWhere(onEntityIdField, 'in', matchQuery); - } else if (isPrimitive(value)) { - // Handle direct value match - const matchQuery = knex('search') - .select('search.entity_id') - .where({ - key: normalizedKey, - value: String(value).toLocaleLowerCase('en-US'), - }); - this.andWhere(onEntityIdField, 'in', matchQuery); + if (value.$exists) { + this.andWhere(onEntityIdField, 'in', existsQuery); } else { - // Reject unsupported/invalid predicate values - throw new InputError( - `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, $in, or $hasPrefix operator, but got ${JSON.stringify( - value, - )}`, - ); + this.andWhere(onEntityIdField, 'not in', existsQuery); } + } else if (isInValue(value)) { + // Handle $in + const values = value.$in.map(v => + String(v).toLocaleLowerCase('en-US'), + ); + const matchQuery = knex('search') + .select('search.entity_id') + .where({ key: normalizedKey }) + .whereIn('value', values); + this.andWhere(onEntityIdField, 'in', matchQuery); + } else if (isHasPrefixValue(value)) { + // Handle $hasPrefix + const prefix = value.$hasPrefix.toLocaleLowerCase('en-US'); + const escaped = prefix.replace(/[%_\\]/g, c => `\\${c}`); + const matchQuery = knex('search') + .select('search.entity_id') + .where({ key: normalizedKey }) + .andWhereRaw('?? like ? escape ?', ['value', `${escaped}%`, '\\']); + this.andWhere(onEntityIdField, 'in', matchQuery); + } else if (isPrimitive(value)) { + // Handle direct value match + const matchQuery = knex('search') + .select('search.entity_id') + .where({ + key: normalizedKey, + value: String(value).toLocaleLowerCase('en-US'), + }); + this.andWhere(onEntityIdField, 'in', matchQuery); + } else { + // Reject unsupported/invalid predicate values + throw new InputError( + `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, $in, or $hasPrefix operator, but got ${JSON.stringify( + value, + )}`, + ); } - }, - ); + } + }); } return targetQuery; } - -export function applyPredicateEntityFilterToQuery(options: { - filter: FilterPredicate; - targetQuery: Knex.QueryBuilder; - onEntityIdField: string; - knex: Knex; -}): Knex.QueryBuilder { - const { filter, targetQuery, onEntityIdField, knex } = options; - - return applyPredicateInStrategy( - filter, - targetQuery, - onEntityIdField, - knex, - false, - ); -} From b218fb07df3c5475e0dc50d4db2078a756c446c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 11:30:45 +0100 Subject: [PATCH 18/55] arrow functions and simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../applyPredicateEntityFilterToQuery.test.ts | 18 ++ .../applyPredicateEntityFilterToQuery.ts | 265 ++++++++---------- 2 files changed, 141 insertions(+), 142 deletions(-) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts index 2f216fd219..21e5d2c37a 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts @@ -116,6 +116,16 @@ describe.each(databases.eachSupportedId())( ); } + it('matches everything for empty field expression', async () => { + await expect(query({})).resolves.toEqual([ + 'api-d', + 'bare-e', + 'service-a', + 'service-b', + 'website-c', + ]); + }); + it('filters by direct field value', async () => { await expect(query({ kind: 'component' })).resolves.toEqual([ 'bare-e', @@ -140,6 +150,10 @@ describe.each(databases.eachSupportedId())( ).resolves.toEqual(['service-a', 'service-b']); }); + it('returns nothing for empty $all', async () => { + await expect(query({ $all: [] })).resolves.toEqual([]); + }); + it('filters with $any', async () => { await expect( query({ @@ -148,6 +162,10 @@ describe.each(databases.eachSupportedId())( ).resolves.toEqual(['service-a', 'service-b', 'website-c']); }); + it('returns nothing for empty $any', async () => { + await expect(query({ $any: [] })).resolves.toEqual([]); + }); + it('filters with $not', async () => { await expect( query({ diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index eb6e65604a..88b064866d 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -23,24 +23,6 @@ import { InputError } from '@backstage/errors'; import { Knex } from 'knex'; import { DbSearchRow } from '../../database/tables'; -function isAllPredicate( - filter: FilterPredicate, -): filter is { $all: FilterPredicate[] } { - return typeof filter === 'object' && filter !== null && '$all' in filter; -} - -function isAnyPredicate( - filter: FilterPredicate, -): filter is { $any: FilterPredicate[] } { - return typeof filter === 'object' && filter !== null && '$any' in filter; -} - -function isNotPredicate( - filter: FilterPredicate, -): filter is { $not: FilterPredicate } { - return typeof filter === 'object' && filter !== null && '$not' in filter; -} - function isPrimitive(value: unknown): value is FilterPredicatePrimitive { return ( typeof value === 'string' || @@ -49,30 +31,8 @@ function isPrimitive(value: unknown): value is FilterPredicatePrimitive { ); } -function isExistsValue( - value: FilterPredicateValue, -): value is { $exists: boolean } { - return typeof value === 'object' && value !== null && '$exists' in value; -} - -function isInValue( - value: FilterPredicateValue, -): value is { $in: FilterPredicatePrimitive[] } { - return typeof value === 'object' && value !== null && '$in' in value; -} - -function isHasPrefixValue( - value: FilterPredicateValue, -): value is { $hasPrefix: string } { - return typeof value === 'object' && value !== null && '$hasPrefix' in value; -} - -function isFieldExpression(filter: FilterPredicate): boolean { - if (typeof filter !== 'object' || filter === null) { - return false; - } - // Not a logical operator - return !('$all' in filter || '$any' in filter || '$not' in filter); +function isObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); } /** @@ -114,7 +74,6 @@ function isFieldExpression(filter: FilterPredicate): boolean { * ORDER BY final_entities.entity_ref ASC; * ``` */ - export function applyPredicateEntityFilterToQuery(options: { filter: FilterPredicate; targetQuery: Knex.QueryBuilder; @@ -122,53 +81,9 @@ export function applyPredicateEntityFilterToQuery(options: { knex: Knex; }): Knex.QueryBuilder { const { filter, targetQuery, onEntityIdField, knex } = options; - // Handle $not - if (isNotPredicate(filter)) { - return targetQuery.andWhereNot(subQuery => - applyPredicateEntityFilterToQuery({ - filter: filter.$not, - targetQuery: subQuery, - onEntityIdField, - knex, - }), - ); - } - // Handle $all (AND) - if (isAllPredicate(filter)) { - return targetQuery.andWhere(function allFilter() { - for (const subFilter of filter.$all) { - this.andWhere(subQuery => - applyPredicateEntityFilterToQuery({ - filter: subFilter, - targetQuery: subQuery, - onEntityIdField, - knex, - }), - ); - } - }); - } - - // Handle $any (OR) - if (isAnyPredicate(filter)) { - return targetQuery.andWhere(function anyFilter() { - for (const subFilter of filter.$any) { - this.orWhere(subQuery => - applyPredicateEntityFilterToQuery({ - filter: subFilter, - targetQuery: subQuery, - onEntityIdField, - knex, - }), - ); - } - }); - } - - // Reject primitives at the top level. Matching by value without specifying - // a field key is ambiguous and should not be allowed. - if (isPrimitive(filter)) { + // We do not support top-level primitives; all matching happens through objects + if (!filter || typeof filter !== 'object' || Array.isArray(filter)) { throw new InputError( `Invalid filter predicate: top-level primitive values are not supported. ` + `Wrap the value in a field expression, e.g. { "kind": ${JSON.stringify( @@ -177,62 +92,128 @@ export function applyPredicateEntityFilterToQuery(options: { ); } - // Handle field expressions like { "kind": "component" } or { "spec.type": { "$in": ["service", "website"] } } - if (isFieldExpression(filter)) { - return targetQuery.andWhere(function fieldFilter() { - for (const [key, value] of Object.entries(filter)) { - const normalizedKey = key.toLocaleLowerCase('en-US'); + if ('$not' in filter) { + return targetQuery.andWhereNot(inner => + applyPredicateEntityFilterToQuery({ + filter: filter.$not, + targetQuery: inner, + onEntityIdField, + knex, + }), + ); + } - if (isExistsValue(value)) { - // Handle $exists - const existsQuery = knex('search') - .select('search.entity_id') - .where({ key: normalizedKey }); - - if (value.$exists) { - this.andWhere(onEntityIdField, 'in', existsQuery); - } else { - this.andWhere(onEntityIdField, 'not in', existsQuery); - } - } else if (isInValue(value)) { - // Handle $in - const values = value.$in.map(v => - String(v).toLocaleLowerCase('en-US'), - ); - const matchQuery = knex('search') - .select('search.entity_id') - .where({ key: normalizedKey }) - .whereIn('value', values); - this.andWhere(onEntityIdField, 'in', matchQuery); - } else if (isHasPrefixValue(value)) { - // Handle $hasPrefix - const prefix = value.$hasPrefix.toLocaleLowerCase('en-US'); - const escaped = prefix.replace(/[%_\\]/g, c => `\\${c}`); - const matchQuery = knex('search') - .select('search.entity_id') - .where({ key: normalizedKey }) - .andWhereRaw('?? like ? escape ?', ['value', `${escaped}%`, '\\']); - this.andWhere(onEntityIdField, 'in', matchQuery); - } else if (isPrimitive(value)) { - // Handle direct value match - const matchQuery = knex('search') - .select('search.entity_id') - .where({ - key: normalizedKey, - value: String(value).toLocaleLowerCase('en-US'), - }); - this.andWhere(onEntityIdField, 'in', matchQuery); - } else { - // Reject unsupported/invalid predicate values - throw new InputError( - `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, $in, or $hasPrefix operator, but got ${JSON.stringify( - value, - )}`, - ); - } + if ('$all' in filter) { + if (filter.$all.length === 0) { + return targetQuery.andWhereRaw('1 = 0'); + } + return targetQuery.andWhere(outer => { + for (const subFilter of filter.$all) { + outer.andWhere(inner => + applyPredicateEntityFilterToQuery({ + filter: subFilter, + targetQuery: inner, + onEntityIdField, + knex, + }), + ); } }); } - return targetQuery; + if ('$any' in filter) { + if (filter.$any.length === 0) { + return targetQuery.andWhereRaw('1 = 0'); + } + return targetQuery.andWhere(outer => { + for (const subFilter of filter.$any) { + outer.orWhere(inner => + applyPredicateEntityFilterToQuery({ + filter: subFilter, + targetQuery: inner, + onEntityIdField, + knex, + }), + ); + } + }); + } + + // Treat the filter as a field expression like { "kind": "component" } or { "spec.type": { "$in": ["service", "website"] } } + if (Object.keys(filter).length === 0) { + return targetQuery; + } + return targetQuery.andWhere(inner => { + for (const [keyAnyCase, value] of Object.entries(filter)) { + applyFieldCondition({ + key: keyAnyCase.toLocaleLowerCase('en-US'), + value, + targetQuery: inner, + onEntityIdField, + knex, + }); + } + }); +} + +/** + * Applies a single { key: value } filter to the target query. + */ +function applyFieldCondition(options: { + key: string; + value: FilterPredicateValue; + targetQuery: Knex.QueryBuilder; + onEntityIdField: string; + knex: Knex; +}): Knex.QueryBuilder { + const { key, value, targetQuery, onEntityIdField, knex } = options; + + if (isPrimitive(value)) { + const matchQuery = knex('search') + .select('search.entity_id') + .where({ + key, + value: String(value).toLocaleLowerCase('en-US'), + }); + return targetQuery.andWhere(onEntityIdField, 'in', matchQuery); + } + + if (isObject(value)) { + if ('$exists' in value) { + const existsQuery = knex('search') + .select('search.entity_id') + .where({ key }); + if (value.$exists) { + return targetQuery.andWhere(onEntityIdField, 'in', existsQuery); + } + return targetQuery.andWhere(onEntityIdField, 'not in', existsQuery); + } + + if ('$in' in value) { + const values = (value.$in as FilterPredicatePrimitive[]).map(v => + String(v).toLocaleLowerCase('en-US'), + ); + const matchQuery = knex('search') + .select('search.entity_id') + .where({ key }) + .whereIn('value', values); + return targetQuery.andWhere(onEntityIdField, 'in', matchQuery); + } + + if ('$hasPrefix' in value) { + const prefix = (value.$hasPrefix as string).toLocaleLowerCase('en-US'); + const escaped = prefix.replace(/[%_\\]/g, c => `\\${c}`); + const matchQuery = knex('search') + .select('search.entity_id') + .where({ key }) + .andWhereRaw('?? like ? escape ?', ['value', `${escaped}%`, '\\']); + return targetQuery.andWhere(onEntityIdField, 'in', matchQuery); + } + } + + throw new InputError( + `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, $in, or $hasPrefix operator, but got ${JSON.stringify( + value, + )}`, + ); } From 59d458202033be0d15c742a6559be75155c6a441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 11:33:08 +0100 Subject: [PATCH 19/55] remove extra spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- plugins/catalog-backend/src/service/createRouter.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index 4571d3bc68..ebfadc66c7 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -72,7 +72,6 @@ describe('createRouter readonly disabled', () => { beforeEach(async () => { entitiesCatalog = { entities: jest.fn(), - entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), @@ -1177,7 +1176,6 @@ describe('createRouter readonly and raw json enabled', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), - entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), @@ -1394,7 +1392,6 @@ describe('NextRouter permissioning', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), - entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), From 42d89b5025b4372c2deeba39a0268f826be32e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 11:47:51 +0100 Subject: [PATCH 20/55] small fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/rich-ducks-ring.md | 2 +- .../src/service/request/applyPredicateEntityFilterToQuery.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/rich-ducks-ring.md b/.changeset/rich-ducks-ring.md index 04848da4a7..ea8a3e8016 100644 --- a/.changeset/rich-ducks-ring.md +++ b/.changeset/rich-ducks-ring.md @@ -5,6 +5,6 @@ Added predicate-based entity filtering via POST /entities/by-query endpoint. -Supports `$all`, `$any`, `$not`, `$exists`, and `$in` operators for expressive entity queries. Integrated into the existing `queryEntities` flow with full cursor-based pagination, permission enforcement, and `totalItems` support. +Supports `$all`, `$any`, `$not`, `$exists`, `$in`, and `$hasPrefix` operators for expressive entity queries. Integrated into the existing `queryEntities` flow with full cursor-based pagination, permission enforcement, and `totalItems` support. The catalog client's `queryEntities()` method automatically routes to the POST endpoint when a `query` predicate is provided. diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index 88b064866d..5deb97019c 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,7 +83,7 @@ export function applyPredicateEntityFilterToQuery(options: { const { filter, targetQuery, onEntityIdField, knex } = options; // We do not support top-level primitives; all matching happens through objects - if (!filter || typeof filter !== 'object' || Array.isArray(filter)) { + if (!isObject(filter)) { throw new InputError( `Invalid filter predicate: top-level primitive values are not supported. ` + `Wrap the value in a field expression, e.g. { "kind": ${JSON.stringify( From 06ac10a0c27a34a53c1e1ed17937e7dd350a21cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 11:53:33 +0100 Subject: [PATCH 21/55] make sure to pass through offset too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- packages/catalog-client/src/CatalogClient.ts | 6 ++++- .../QueryEntitiesByPredicateRequest.model.ts | 1 + .../catalog-backend/src/schema/openapi.yaml | 2 ++ .../QueryEntitiesByPredicateRequest.model.ts | 1 + .../src/schema/openapi/generated/router.ts | 3 +++ .../src/service/createRouter.test.ts | 25 +++++++++++++++++++ .../service/request/parseEntityQuery.test.ts | 13 ++++++++++ .../src/service/request/parseEntityQuery.ts | 1 + 8 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index fa8a88c1ca..62111abf9e 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -359,13 +359,17 @@ export class CatalogClient implements CatalogApi { const body: QueryEntitiesByPredicateRequest = {}; if (isQueryEntitiesInitialRequest(request)) { - const { query, limit, orderFields, fullTextFilter, fields } = request; + const { query, limit, offset, orderFields, fullTextFilter, fields } = + request; if (query && typeof query === 'object') { body.query = query; } if (limit !== undefined) { body.limit = limit; } + if (offset !== undefined) { + body.offset = offset; + } if (orderFields !== undefined) { body.orderBy = [orderFields].flat(); } diff --git a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts index 818a780b4a..17a9c9962f 100644 --- a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts @@ -27,6 +27,7 @@ import { QueryEntitiesByPredicateRequestOrderByInner } from '../models/QueryEnti export interface QueryEntitiesByPredicateRequest { cursor?: string; limit?: number; + offset?: number; orderBy?: Array; fullTextFilter?: QueryEntitiesByPredicateRequestFullTextFilter; fields?: Array; diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index 41738c1148..0531a9a1d3 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -1125,6 +1125,8 @@ paths: type: string limit: type: number + offset: + type: number orderBy: type: array items: diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts index 818a780b4a..17a9c9962f 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts @@ -27,6 +27,7 @@ import { QueryEntitiesByPredicateRequestOrderByInner } from '../models/QueryEnti export interface QueryEntitiesByPredicateRequest { cursor?: string; limit?: number; + offset?: number; orderBy?: Array; fullTextFilter?: QueryEntitiesByPredicateRequestFullTextFilter; fields?: Array; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/router.ts b/plugins/catalog-backend/src/schema/openapi/generated/router.ts index ed9536d6b9..6c739eb2ee 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/router.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/router.ts @@ -1269,6 +1269,9 @@ export const spec = { limit: { type: 'number', }, + offset: { + type: 'number', + }, orderBy: { type: 'array', items: { diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index ebfadc66c7..acfac3b8d1 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -509,6 +509,31 @@ describe('createRouter readonly disabled', () => { ); }); + it('queries entities with an offset', async () => { + const items: Entity[] = [ + { apiVersion: 'a', kind: 'b', metadata: { name: 'n' } }, + ]; + entitiesCatalog.queryEntities.mockResolvedValue({ + items: { type: 'object', entities: items }, + pageInfo: {}, + totalItems: 5, + }); + + const response = await request(app) + .post('/entities/by-query') + .send({ query: { kind: 'b' }, limit: 2, offset: 3 }); + + expect(response.status).toEqual(200); + expect(entitiesCatalog.queryEntities).toHaveBeenCalledWith( + expect.objectContaining({ + query: { kind: 'b' }, + limit: 2, + offset: 3, + credentials: mockCredentials.user(), + }), + ); + }); + it('paginates with a cursor in the body', async () => { const items: Entity[] = [ { apiVersion: 'a', kind: 'b', metadata: { name: 'n' } }, diff --git a/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts b/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts index b95c4c6c0e..7380259188 100644 --- a/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts @@ -28,6 +28,7 @@ describe('parseEntityQuery', () => { fullTextFilter: undefined, fields: undefined, limit: undefined, + offset: undefined, }); }); @@ -68,6 +69,18 @@ describe('parseEntityQuery', () => { expect(result).toEqual(expect.objectContaining({ limit: 50 })); }); + it('passes through offset', () => { + const result = parseEntityQuery({ offset: 100 }); + expect(result).toEqual(expect.objectContaining({ offset: 100 })); + }); + + it('passes through limit and offset together', () => { + const result = parseEntityQuery({ limit: 50, offset: 100 }); + expect(result).toEqual( + expect.objectContaining({ limit: 50, offset: 100 }), + ); + }); + it('passes through fields', () => { const result = parseEntityQuery({ fields: ['metadata.name', 'kind'], diff --git a/plugins/catalog-backend/src/service/request/parseEntityQuery.ts b/plugins/catalog-backend/src/service/request/parseEntityQuery.ts index c33492e740..27992c273a 100644 --- a/plugins/catalog-backend/src/service/request/parseEntityQuery.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityQuery.ts @@ -110,5 +110,6 @@ export function parseEntityQuery( : undefined, fields: request.fields, limit: request.limit, + offset: request.offset, }; } From b8355e72089ecfac9202913a7d5b2ffb079247cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 12:09:38 +0100 Subject: [PATCH 22/55] empty should match everything MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../request/applyPredicateEntityFilterToQuery.test.ts | 10 ++++++++-- .../request/applyPredicateEntityFilterToQuery.ts | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts index 21e5d2c37a..70d5461c3a 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts @@ -150,8 +150,14 @@ describe.each(databases.eachSupportedId())( ).resolves.toEqual(['service-a', 'service-b']); }); - it('returns nothing for empty $all', async () => { - await expect(query({ $all: [] })).resolves.toEqual([]); + it('matches everything for empty $all', async () => { + await expect(query({ $all: [] })).resolves.toEqual([ + 'api-d', + 'bare-e', + 'service-a', + 'service-b', + 'website-c', + ]); }); it('filters with $any', async () => { diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index 5deb97019c..2a404b88bf 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -105,7 +105,7 @@ export function applyPredicateEntityFilterToQuery(options: { if ('$all' in filter) { if (filter.$all.length === 0) { - return targetQuery.andWhereRaw('1 = 0'); + return targetQuery; } return targetQuery.andWhere(outer => { for (const subFilter of filter.$all) { From 430b8978402961929506df10fbff31a5469eff72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 12:14:08 +0100 Subject: [PATCH 23/55] add explicit clear rejection of MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../request/applyPredicateEntityFilterToQuery.test.ts | 6 ++++++ .../service/request/applyPredicateEntityFilterToQuery.ts | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts index 70d5461c3a..b90e0ca409 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts @@ -231,6 +231,12 @@ describe.each(databases.eachSupportedId())( ).resolves.toEqual(['service-a', 'website-c']); }); + it('throws on $contains operator', async () => { + await expect( + query({ 'metadata.name': { $contains: 'service' } as any }), + ).rejects.toThrow(/\$contains operator is not supported/); + }); + it('throws on top-level primitive', async () => { await expect(query('bad-value' as any)).rejects.toThrow( /top-level primitive values are not supported/, diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index 2a404b88bf..56d85a9da1 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -209,6 +209,11 @@ function applyFieldCondition(options: { .andWhereRaw('?? like ? escape ?', ['value', `${escaped}%`, '\\']); return targetQuery.andWhere(onEntityIdField, 'in', matchQuery); } + + if ('$contains' in value) { + // TODO(freben): Implement this, AT LEAST for some special cases such as tags and relations. + throw new InputError('The $contains operator is not supported'); + } } throw new InputError( From 9dd2f112f0a920531987e31f9be67773f9143737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 12:17:57 +0100 Subject: [PATCH 24/55] add more pagination tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../catalog-client/src/CatalogClient.test.ts | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index aee10cd3f2..7ce9e94396 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -807,6 +807,81 @@ describe('CatalogClient', () => { expect(mockedEndpoint).toHaveBeenCalledTimes(1); }); + it('should paginate using POST when cursor contains a query', async () => { + // Simulate a cursor that contains a query predicate (as the server would encode it) + const cursorPayload = Buffer.from( + JSON.stringify({ + orderFields: [], + orderFieldValues: [], + isPrevious: false, + query: { kind: 'component' }, + totalItems: 100, + }), + ).toString('base64'); + + const page2Response = { + items: [ + { + apiVersion: '1', + kind: 'Component', + metadata: { name: 'service-3', namespace: 'default' }, + }, + ], + totalItems: 100, + pageInfo: {}, + }; + + const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { + expect(req.method).toBe('POST'); + expect(req.body).toMatchObject({ cursor: cursorPayload }); + return res(ctx.json(page2Response)); + }); + + server.use(rest.post(`${mockBaseUrl}/entities/by-query`, mockedEndpoint)); + + const response = await client.queryEntities({ + cursor: cursorPayload, + }); + + expect(mockedEndpoint).toHaveBeenCalledTimes(1); + expect(response.items).toEqual(page2Response.items); + expect(response.totalItems).toBe(100); + }); + + it('should use GET endpoint for cursor without query', async () => { + // A cursor that does NOT contain a query field should go to GET + const cursorPayload = Buffer.from( + JSON.stringify({ + orderFields: [], + orderFieldValues: [], + isPrevious: false, + totalItems: 50, + }), + ).toString('base64'); + + const mockedGetEndpoint = jest.fn().mockImplementation((_req, res, ctx) => + res( + ctx.json({ + items: [], + totalItems: 50, + pageInfo: {}, + }), + ), + ); + + const mockedPostEndpoint = jest.fn(); + + server.use( + rest.get(`${mockBaseUrl}/entities/by-query`, mockedGetEndpoint), + rest.post(`${mockBaseUrl}/entities/by-query`, mockedPostEndpoint), + ); + + await client.queryEntities({ cursor: cursorPayload }); + + expect(mockedGetEndpoint).toHaveBeenCalledTimes(1); + expect(mockedPostEndpoint).not.toHaveBeenCalled(); + }); + it('should handle errors from POST endpoint', async () => { const mockedEndpoint = jest .fn() From 1ed51c2d8a7641b052529d7311299ec70cabae57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 13:34:06 +0100 Subject: [PATCH 25/55] make sure that permissions apply MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../service/AuthorizedEntitiesCatalog.test.ts | 172 ++++++++++++++++++ .../src/service/AuthorizedEntitiesCatalog.ts | 64 ++++++- .../applyPredicateEntityFilterToQuery.ts | 2 +- 3 files changed, 231 insertions(+), 7 deletions(-) diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts index 894949a691..f074f25397 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts @@ -23,6 +23,7 @@ import { AuthorizedEntitiesCatalog } from './AuthorizedEntitiesCatalog'; import { Cursor, QueryEntitiesResponse } from '../catalog/types'; import { Entity } from '@backstage/catalog-model'; import { EntityFilter } from '@backstage/plugin-catalog-node'; +import { FilterPredicate } from '@backstage/filter-predicates'; import { mockCredentials } from '@backstage/backend-test-utils'; describe('AuthorizedEntitiesCatalog', () => { @@ -306,6 +307,177 @@ describe('AuthorizedEntitiesCatalog', () => { }, }); }); + + it('combines permission filter into query field using $all on CONDITIONAL with initial request', async () => { + fakePermissionApi.authorizeConditional.mockResolvedValue([ + { + result: AuthorizeResult.CONDITIONAL, + conditions: { + rule: 'IS_ENTITY_KIND', + params: { kinds: ['b'] }, + }, + }, + ]); + + const userQuery: FilterPredicate = { 'metadata.name': 'my-entity' }; + + const entities = [ + { + kind: 'component', + namespace: 'default', + name: 'a', + } as unknown as Entity, + ]; + + fakeCatalog.queryEntities.mockResolvedValue({ + items: { type: 'object', entities }, + pageInfo: { + nextCursor: { + isPrevious: false, + orderFieldValues: ['xxx', null], + query: { $all: [{ kind: 'b' }, userQuery] }, + orderFields: [{ field: 'name', order: 'asc' }], + }, + }, + totalItems: 1, + } as QueryEntitiesResponse); + + const catalog = createCatalog(isEntityKind); + + const response = await catalog.queryEntities({ + credentials: mockCredentials.none(), + query: userQuery, + }); + + expect(fakeCatalog.queryEntities).toHaveBeenCalledWith({ + credentials: mockCredentials.none(), + query: { $all: [{ kind: 'b' }, userQuery] }, + filter: undefined, + }); + + expect(response.pageInfo.nextCursor).toEqual({ + isPrevious: false, + orderFieldValues: ['xxx', null], + query: userQuery, + filter: undefined, + orderFields: [{ field: 'name', order: 'asc' }], + }); + }); + + it('combines permission filter into cursor query field using $all on CONDITIONAL with cursor request', async () => { + fakePermissionApi.authorizeConditional.mockResolvedValue([ + { + result: AuthorizeResult.CONDITIONAL, + conditions: { + rule: 'IS_ENTITY_KIND', + params: { kinds: ['b'] }, + }, + }, + ]); + + const userQuery: FilterPredicate = { 'metadata.name': 'my-entity' }; + + const entities = [ + { + kind: 'component', + namespace: 'default', + name: 'a', + } as unknown as Entity, + ]; + + fakeCatalog.queryEntities.mockResolvedValue({ + items: { type: 'object', entities }, + pageInfo: { + nextCursor: { + isPrevious: false, + orderFieldValues: ['yyy', null], + query: { $all: [{ kind: 'b' }, userQuery] }, + orderFields: [{ field: 'name', order: 'asc' }], + }, + prevCursor: { + isPrevious: true, + orderFieldValues: ['aaa', null], + query: { $all: [{ kind: 'b' }, userQuery] }, + orderFields: [{ field: 'name', order: 'asc' }], + }, + }, + totalItems: 3, + } as QueryEntitiesResponse); + + const catalog = createCatalog(isEntityKind); + + const cursor: Cursor = { + query: userQuery, + orderFields: [{ field: 'name', order: 'asc' }], + isPrevious: false, + orderFieldValues: ['xxx', null], + }; + + const response = await catalog.queryEntities({ + credentials: mockCredentials.none(), + cursor, + }); + + expect(fakeCatalog.queryEntities).toHaveBeenCalledWith({ + credentials: mockCredentials.none(), + cursor: { + ...cursor, + query: { $all: [{ kind: 'b' }, userQuery] }, + filter: undefined, + }, + }); + + expect(response.pageInfo.nextCursor).toEqual({ + isPrevious: false, + orderFieldValues: ['yyy', null], + query: userQuery, + filter: undefined, + orderFields: [{ field: 'name', order: 'asc' }], + }); + + expect(response.pageInfo.prevCursor).toEqual({ + isPrevious: true, + orderFieldValues: ['aaa', null], + query: userQuery, + filter: undefined, + orderFields: [{ field: 'name', order: 'asc' }], + }); + }); + + it('converts multi-value permission filter with $in when converting to predicate', async () => { + fakePermissionApi.authorizeConditional.mockResolvedValue([ + { + result: AuthorizeResult.CONDITIONAL, + conditions: { + rule: 'IS_ENTITY_KIND', + params: { kinds: ['component', 'api'] }, + }, + }, + ]); + + const userQuery: FilterPredicate = { 'metadata.name': 'my-entity' }; + + fakeCatalog.queryEntities.mockResolvedValue({ + items: { type: 'object', entities: [] }, + pageInfo: {}, + totalItems: 0, + } as QueryEntitiesResponse); + + const catalog = createCatalog(isEntityKind); + + await catalog.queryEntities({ + credentials: mockCredentials.none(), + query: userQuery, + }); + + expect(fakeCatalog.queryEntities).toHaveBeenCalledWith({ + credentials: mockCredentials.none(), + query: { + $all: [{ kind: { $in: ['component', 'api'] } }, userQuery], + }, + filter: undefined, + }); + }); }); describe('removeEntityByUid', () => { diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts index d3d0dba5b3..bb81144f25 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts @@ -35,6 +35,7 @@ import { QueryEntitiesRequest, QueryEntitiesResponse, } from '../catalog/types'; +import { FilterPredicate } from '@backstage/filter-predicates'; import { basicEntityFilter } from './request'; import { isQueryEntitiesCursorRequest } from './util'; import { EntityFilter } from '@backstage/plugin-catalog-node'; @@ -43,6 +44,30 @@ import { PermissionsService, } from '@backstage/backend-plugin-api'; +function entityFilterToFilterPredicate(filter: EntityFilter): FilterPredicate { + if ('allOf' in filter) { + return { $all: filter.allOf.map(entityFilterToFilterPredicate) }; + } + + if ('anyOf' in filter) { + return { $any: filter.anyOf.map(entityFilterToFilterPredicate) }; + } + + if ('not' in filter) { + return { $not: entityFilterToFilterPredicate(filter.not) }; + } + + if (!filter.values) { + return { [filter.key]: { $exists: true } } as FilterPredicate; + } + + if (filter.values.length === 1) { + return { [filter.key]: filter.values[0] } as FilterPredicate; + } + + return { [filter.key]: { $in: filter.values } } as FilterPredicate; +} + export class AuthorizedEntitiesCatalog implements EntitiesCatalog { private readonly entitiesCatalog: EntitiesCatalog; private readonly permissionApi: PermissionsService; @@ -147,18 +172,42 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog { let permissionedRequest: QueryEntitiesRequest; let requestFilter: EntityFilter | undefined; + let requestQuery: FilterPredicate | undefined; if (isQueryEntitiesCursorRequest(request)) { requestFilter = request.cursor.filter; + requestQuery = request.cursor.query; + if (request.cursor.query) { + const permissionPredicate = + entityFilterToFilterPredicate(permissionFilter); + permissionedRequest = { + ...request, + cursor: { + ...request.cursor, + query: { $all: [permissionPredicate, request.cursor.query] }, + filter: undefined, + }, + }; + } else { + permissionedRequest = { + ...request, + cursor: { + ...request.cursor, + filter: request.cursor.filter + ? { allOf: [permissionFilter, request.cursor.filter] } + : permissionFilter, + }, + }; + } + } else if (request.query) { + const permissionPredicate = + entityFilterToFilterPredicate(permissionFilter); + requestQuery = request.query; permissionedRequest = { ...request, - cursor: { - ...request.cursor, - filter: request.cursor.filter - ? { allOf: [permissionFilter, request.cursor.filter] } - : permissionFilter, - }, + query: { $all: [permissionPredicate, request.query] }, + filter: undefined, }; } else { permissionedRequest = { @@ -177,11 +226,13 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog { const prevCursor: Cursor | undefined = response.pageInfo.prevCursor && { ...response.pageInfo.prevCursor, filter: requestFilter, + query: requestQuery, }; const nextCursor: Cursor | undefined = response.pageInfo.nextCursor && { ...response.pageInfo.nextCursor, filter: requestFilter, + query: requestQuery, }; return { @@ -193,6 +244,7 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog { }; } + // The ALLOW case return this.entitiesCatalog.queryEntities(request); } diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index 56d85a9da1..b42252369d 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -105,7 +105,7 @@ export function applyPredicateEntityFilterToQuery(options: { if ('$all' in filter) { if (filter.$all.length === 0) { - return targetQuery; + return targetQuery.andWhereRaw('1 = 1'); } return targetQuery.andWhere(outer => { for (const subFilter of filter.$all) { From 345a36aa1b3f549546122ad2e86f51312af05f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 13:57:11 +0100 Subject: [PATCH 26/55] remove the exclusivity constraint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../catalog-client/src/CatalogClient.test.ts | 11 ---- packages/catalog-client/src/CatalogClient.ts | 7 --- packages/catalog-client/src/types/api.ts | 11 +++- plugins/catalog-backend/src/catalog/types.ts | 2 - .../service/AuthorizedEntitiesCatalog.test.ts | 55 ++++------------ .../src/service/AuthorizedEntitiesCatalog.ts | 62 +++---------------- .../service/DefaultEntitiesCatalog.test.ts | 32 ++++++++++ 7 files changed, 60 insertions(+), 120 deletions(-) diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index 7ce9e94396..819fa70381 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -594,17 +594,6 @@ describe('CatalogClient', () => { expect(response.totalItems).toBe(2); }); - it('should throw error when both filter and query are provided', async () => { - await expect( - client.queryEntities({ - filter: { kind: 'component' }, - query: { kind: 'component' }, - } as any), - ).rejects.toThrow( - 'Cannot specify both "filter" and "query" in the same request', - ); - }); - it('should support $all operator', async () => { const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => { expect(req.body).toMatchObject({ diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index 62111abf9e..bd557cfdd2 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -274,13 +274,6 @@ export class CatalogClient implements CatalogApi { ): Promise { const isInitialRequest = isQueryEntitiesInitialRequest(request); - // Validate that filter and query are mutually exclusive - if (isInitialRequest && request.filter && request.query) { - throw new Error( - 'Cannot specify both "filter" and "query" in the same request. Use "filter" for traditional key-value filtering or "query" for predicate-based filtering.', - ); - } - // Route to POST endpoint if query predicate is provided (initial request) if (isInitialRequest && request.query) { return this.queryEntitiesByPredicate(request, options); diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index 8bf13271b2..2dffb35570 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -593,6 +593,7 @@ export interface CatalogApi { * const response = await catalogClient.queryEntities({ * filter: [{ kind: 'group' }], * limit: 20, + * fields: ['metadata', 'kind'], * fullTextFilter: { * term: 'A', * }, @@ -609,11 +610,15 @@ export interface CatalogApi { * * ``` * const secondBatchResponse = await catalogClient - * .queryEntities({ cursor: response.nextCursor }); + * .queryEntities({ + * cursor: response.nextCursor, + * limit: 20, + * fields: ['metadata', 'kind'], + * }); * ``` * - * secondBatchResponse will contain the next batch of (maximum) 20 entities, - * together with a prevCursor property, useful to fetch the previous batch. + * `secondBatchResponse` will contain the next batch of (maximum) 20 entities, + * together with a `prevCursor` property, useful to fetch the previous batch. * * @public * diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index dda7e9a811..5d733ad14c 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -215,7 +215,6 @@ export interface QueryEntitiesInitialRequest { filter?: EntityFilter; /** * Predicate-based query for filtering entities. - * Mutually exclusive with filter. */ query?: FilterPredicate; orderFields?: EntityOrder[]; @@ -281,7 +280,6 @@ export type Cursor = { filter?: EntityFilter; /** * A predicate-based query to be applied to the full list of entities. - * Mutually exclusive with filter. */ query?: FilterPredicate; /** diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts index f074f25397..85fbd16eeb 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts @@ -308,7 +308,7 @@ describe('AuthorizedEntitiesCatalog', () => { }); }); - it('combines permission filter into query field using $all on CONDITIONAL with initial request', async () => { + it('passes through query alongside permission filter on CONDITIONAL with initial request', async () => { fakePermissionApi.authorizeConditional.mockResolvedValue([ { result: AuthorizeResult.CONDITIONAL, @@ -335,7 +335,8 @@ describe('AuthorizedEntitiesCatalog', () => { nextCursor: { isPrevious: false, orderFieldValues: ['xxx', null], - query: { $all: [{ kind: 'b' }, userQuery] }, + query: userQuery, + filter: { key: 'kind', values: ['b'] }, orderFields: [{ field: 'name', order: 'asc' }], }, }, @@ -351,8 +352,8 @@ describe('AuthorizedEntitiesCatalog', () => { expect(fakeCatalog.queryEntities).toHaveBeenCalledWith({ credentials: mockCredentials.none(), - query: { $all: [{ kind: 'b' }, userQuery] }, - filter: undefined, + query: userQuery, + filter: { key: 'kind', values: ['b'] }, }); expect(response.pageInfo.nextCursor).toEqual({ @@ -364,7 +365,7 @@ describe('AuthorizedEntitiesCatalog', () => { }); }); - it('combines permission filter into cursor query field using $all on CONDITIONAL with cursor request', async () => { + it('passes through cursor query alongside permission filter on CONDITIONAL with cursor request', async () => { fakePermissionApi.authorizeConditional.mockResolvedValue([ { result: AuthorizeResult.CONDITIONAL, @@ -391,13 +392,15 @@ describe('AuthorizedEntitiesCatalog', () => { nextCursor: { isPrevious: false, orderFieldValues: ['yyy', null], - query: { $all: [{ kind: 'b' }, userQuery] }, + query: userQuery, + filter: { key: 'kind', values: ['b'] }, orderFields: [{ field: 'name', order: 'asc' }], }, prevCursor: { isPrevious: true, orderFieldValues: ['aaa', null], - query: { $all: [{ kind: 'b' }, userQuery] }, + query: userQuery, + filter: { key: 'kind', values: ['b'] }, orderFields: [{ field: 'name', order: 'asc' }], }, }, @@ -422,8 +425,7 @@ describe('AuthorizedEntitiesCatalog', () => { credentials: mockCredentials.none(), cursor: { ...cursor, - query: { $all: [{ kind: 'b' }, userQuery] }, - filter: undefined, + filter: { key: 'kind', values: ['b'] }, }, }); @@ -443,41 +445,6 @@ describe('AuthorizedEntitiesCatalog', () => { orderFields: [{ field: 'name', order: 'asc' }], }); }); - - it('converts multi-value permission filter with $in when converting to predicate', async () => { - fakePermissionApi.authorizeConditional.mockResolvedValue([ - { - result: AuthorizeResult.CONDITIONAL, - conditions: { - rule: 'IS_ENTITY_KIND', - params: { kinds: ['component', 'api'] }, - }, - }, - ]); - - const userQuery: FilterPredicate = { 'metadata.name': 'my-entity' }; - - fakeCatalog.queryEntities.mockResolvedValue({ - items: { type: 'object', entities: [] }, - pageInfo: {}, - totalItems: 0, - } as QueryEntitiesResponse); - - const catalog = createCatalog(isEntityKind); - - await catalog.queryEntities({ - credentials: mockCredentials.none(), - query: userQuery, - }); - - expect(fakeCatalog.queryEntities).toHaveBeenCalledWith({ - credentials: mockCredentials.none(), - query: { - $all: [{ kind: { $in: ['component', 'api'] } }, userQuery], - }, - filter: undefined, - }); - }); }); describe('removeEntityByUid', () => { diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts index bb81144f25..910188b036 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts @@ -44,30 +44,6 @@ import { PermissionsService, } from '@backstage/backend-plugin-api'; -function entityFilterToFilterPredicate(filter: EntityFilter): FilterPredicate { - if ('allOf' in filter) { - return { $all: filter.allOf.map(entityFilterToFilterPredicate) }; - } - - if ('anyOf' in filter) { - return { $any: filter.anyOf.map(entityFilterToFilterPredicate) }; - } - - if ('not' in filter) { - return { $not: entityFilterToFilterPredicate(filter.not) }; - } - - if (!filter.values) { - return { [filter.key]: { $exists: true } } as FilterPredicate; - } - - if (filter.values.length === 1) { - return { [filter.key]: filter.values[0] } as FilterPredicate; - } - - return { [filter.key]: { $in: filter.values } } as FilterPredicate; -} - export class AuthorizedEntitiesCatalog implements EntitiesCatalog { private readonly entitiesCatalog: EntitiesCatalog; private readonly permissionApi: PermissionsService; @@ -178,45 +154,25 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog { requestFilter = request.cursor.filter; requestQuery = request.cursor.query; - if (request.cursor.query) { - const permissionPredicate = - entityFilterToFilterPredicate(permissionFilter); - permissionedRequest = { - ...request, - cursor: { - ...request.cursor, - query: { $all: [permissionPredicate, request.cursor.query] }, - filter: undefined, - }, - }; - } else { - permissionedRequest = { - ...request, - cursor: { - ...request.cursor, - filter: request.cursor.filter - ? { allOf: [permissionFilter, request.cursor.filter] } - : permissionFilter, - }, - }; - } - } else if (request.query) { - const permissionPredicate = - entityFilterToFilterPredicate(permissionFilter); - requestQuery = request.query; permissionedRequest = { ...request, - query: { $all: [permissionPredicate, request.query] }, - filter: undefined, + cursor: { + ...request.cursor, + filter: request.cursor.filter + ? { allOf: [permissionFilter, request.cursor.filter] } + : permissionFilter, + }, }; } else { + requestFilter = request.filter; + requestQuery = request.query; + permissionedRequest = { ...request, filter: request.filter ? { allOf: [permissionFilter, request.filter] } : permissionFilter, }; - requestFilter = request.filter; } const response = await this.entitiesCatalog.queryEntities( diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 7012c2e505..f9e41b679c 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -2054,6 +2054,38 @@ describe('DefaultEntitiesCatalog', () => { ]); }, ); + + it.each(databases.eachSupportedId())( + 'should apply both filter and query when both are given, %p', + async databaseId => { + await createDatabase(databaseId); + + // Add entities with different kinds and names + await addEntityToSearch(entityFrom('A', { kind: 'component' })); + await addEntityToSearch(entityFrom('B', { kind: 'component' })); + await addEntityToSearch(entityFrom('C', { kind: 'api' })); + await addEntityToSearch(entityFrom('D', { kind: 'api' })); + + const catalog = new DefaultEntitiesCatalog({ + database: knex, + logger: mockServices.logger.mock(), + stitcher, + }); + + // Use filter to restrict to kind=component, and query to restrict to name=A + const response = await catalog.queryEntities({ + filter: { key: 'kind', values: ['component'] }, + query: { 'metadata.name': 'a' }, + orderFields: [{ field: 'metadata.name', order: 'asc' }], + credentials: mockCredentials.none(), + }); + + const resultEntities = entitiesResponseToObjects(response.items); + expect(resultEntities).toEqual([ + entityFrom('A', { kind: 'component' }), + ]); + }, + ); }); describe('removeEntityByUid', () => { From 7f0f4139300049969ad727bd629dee1cc1479ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 14:34:22 +0100 Subject: [PATCH 27/55] Update packages/catalog-client/src/types/api.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Fredrik Adelöw --- packages/catalog-client/src/types/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index 2dffb35570..a99f36573e 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -438,7 +438,7 @@ export type QueryEntitiesInitialRequest = { */ filter?: EntityFilterQuery; /** - * Predicate-based filter with logical operators ($all, $any, $not, $exists, $in). + * Predicate-based filter with logical operators ($all, $any, $not, $exists, $in, $hasPrefix). * Mutually exclusive with `filter`. * * @example From 7969c48889e4207e21e9a60219fe69b03e5fc663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 14:42:17 +0100 Subject: [PATCH 28/55] remove dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- packages/catalog-client/src/types/api.ts | 5 ++-- .../src/service/DefaultEntitiesCatalog.ts | 25 ------------------- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index a99f36573e..64d01c0972 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -423,7 +423,7 @@ export type QueryEntitiesRequest = * * @remarks * - * Either `filter` or `query` can be provided, but not both: + * Either `filter` or `query` can be provided, or even both: * - `filter`: Uses the traditional key-value filter syntax (GET endpoint) * - `query`: Uses the predicate-based filter syntax with logical operators (POST endpoint) * @@ -434,12 +434,11 @@ export type QueryEntitiesInitialRequest = { limit?: number; offset?: number; /** - * Traditional key-value based filter. Mutually exclusive with `query`. + * Traditional key-value based filter. */ filter?: EntityFilterQuery; /** * Predicate-based filter with logical operators ($all, $any, $not, $exists, $in, $hasPrefix). - * Mutually exclusive with `filter`. * * @example * ```typescript diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 11bce7bd86..dab605d176 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -18,7 +18,6 @@ import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { InputError, NotFoundError } from '@backstage/errors'; import { Knex } from 'knex'; import { chunk as lodashChunk, isEqual } from 'lodash'; -import { z } from 'zod'; import { Cursor, EntitiesBatchRequest, @@ -49,7 +48,6 @@ import { isQueryEntitiesCursorRequest, isQueryEntitiesInitialRequest, } from './util'; -import { EntityFilter } from '@backstage/plugin-catalog-node'; import { LoggerService } from '@backstage/backend-plugin-api'; import { applyEntityFilterToQuery } from './request/applyEntityFilterToQuery'; import { processRawEntitiesResult } from './response'; @@ -714,29 +712,6 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { } } -const entityFilterParser: z.ZodSchema = z.lazy(() => - z - .object({ - key: z.string(), - values: z.array(z.string()).optional(), - }) - .or(z.object({ not: entityFilterParser })) - .or(z.object({ anyOf: z.array(entityFilterParser) })) - .or(z.object({ allOf: z.array(entityFilterParser) })), -); - -export const cursorParser: z.ZodSchema = z.object({ - orderFields: z.array( - z.object({ field: z.string(), order: z.enum(['asc', 'desc']) }), - ), - orderFieldValues: z.array(z.string().or(z.null())), - filter: entityFilterParser.optional(), - isPrevious: z.boolean(), - query: z.string().optional(), - firstSortFieldValues: z.array(z.string().or(z.null())).optional(), - totalItems: z.number().optional(), -}); - function parseCursorFromRequest( request?: QueryEntitiesRequest, ): Partial & { skipTotalItems: boolean } { From a3e4add213840810696dca100a60dc61020b9082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 15:01:38 +0100 Subject: [PATCH 29/55] remove some unnecessary noise MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../applyPredicateEntityFilterToQuery.ts | 45 +------------------ 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index b42252369d..d376b132ff 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -35,45 +35,6 @@ function isObject(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value); } -/** - * example generated query - * Selects all non-null final entities that: - * - * - Are of kind "component" - * - Have spec.type = "service" - * - Are owned by either: - * - backend-team - * - platform-team - * - Are NOT in experimental lifecycle - * - * Results are ordered by entity_ref in ascending order. - * - * SQL Reference: - * ``` - * SELECT final_entities.* - * FROM final_entities - * WHERE final_entities.final_entity IS NOT NULL - * AND final_entities.entity_id IN ( - * SELECT entity_id FROM search WHERE key = 'kind' AND value = 'component' - * ) - * AND final_entities.entity_id IN ( - * SELECT entity_id FROM search WHERE key = 'spec.type' AND value = 'service' - * ) - * AND ( - * final_entities.entity_id IN ( - * SELECT entity_id FROM search WHERE key = 'spec.owner' AND value = 'backend-team' - * ) - * OR - * final_entities.entity_id IN ( - * SELECT entity_id FROM search WHERE key = 'spec.owner' AND value = 'platform-team' - * ) - * ) - * AND final_entities.entity_id NOT IN ( - * SELECT entity_id FROM search WHERE key = 'spec.lifecycle' AND value = 'experimental' - * ) - * ORDER BY final_entities.entity_ref ASC; - * ``` - */ export function applyPredicateEntityFilterToQuery(options: { filter: FilterPredicate; targetQuery: Knex.QueryBuilder; @@ -190,9 +151,7 @@ function applyFieldCondition(options: { } if ('$in' in value) { - const values = (value.$in as FilterPredicatePrimitive[]).map(v => - String(v).toLocaleLowerCase('en-US'), - ); + const values = value.$in.map(v => String(v).toLocaleLowerCase('en-US')); const matchQuery = knex('search') .select('search.entity_id') .where({ key }) @@ -201,7 +160,7 @@ function applyFieldCondition(options: { } if ('$hasPrefix' in value) { - const prefix = (value.$hasPrefix as string).toLocaleLowerCase('en-US'); + const prefix = value.$hasPrefix.toLocaleLowerCase('en-US'); const escaped = prefix.replace(/[%_\\]/g, c => `\\${c}`); const matchQuery = knex('search') .select('search.entity_id') From c7c0dd5ed2688c2e19567054f5f31ecd4f48f905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 15:11:59 +0100 Subject: [PATCH 30/55] add test for empty matcher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../request/applyPredicateEntityFilterToQuery.test.ts | 4 ++++ .../request/applyPredicateEntityFilterToQuery.ts | 11 ++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts index b90e0ca409..e48be8169a 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts @@ -126,6 +126,10 @@ describe.each(databases.eachSupportedId())( ]); }); + it('matches nothing for {$not: {}}', async () => { + await expect(query({ $not: {} })).resolves.toEqual([]); + }); + it('filters by direct field value', async () => { await expect(query({ kind: 'component' })).resolves.toEqual([ 'bare-e', diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index d376b132ff..e7cf152184 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -102,7 +102,7 @@ export function applyPredicateEntityFilterToQuery(options: { // Treat the filter as a field expression like { "kind": "component" } or { "spec.type": { "$in": ["service", "website"] } } if (Object.keys(filter).length === 0) { - return targetQuery; + return targetQuery.andWhereRaw('1 = 1'); } return targetQuery.andWhere(inner => { for (const [keyAnyCase, value] of Object.entries(filter)) { @@ -144,10 +144,11 @@ function applyFieldCondition(options: { const existsQuery = knex('search') .select('search.entity_id') .where({ key }); - if (value.$exists) { - return targetQuery.andWhere(onEntityIdField, 'in', existsQuery); - } - return targetQuery.andWhere(onEntityIdField, 'not in', existsQuery); + return targetQuery.andWhere( + onEntityIdField, + value.$exists ? 'in' : 'not in', + existsQuery, + ); } if ('$in' in value) { From c25c589f9cd8733c771efe5c56e9d0732a513833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 15:29:39 +0100 Subject: [PATCH 31/55] add primitive $contains support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../applyPredicateEntityFilterToQuery.test.ts | 328 ++++++++++-------- .../applyPredicateEntityFilterToQuery.ts | 41 ++- 2 files changed, 215 insertions(+), 154 deletions(-) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts index e48be8169a..7f07cad11e 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts @@ -67,7 +67,11 @@ describe.each(databases.eachSupportedId())( await addEntity({ apiVersion: 'backstage.io/v1alpha1', kind: 'Component', - metadata: { name: 'bare-e', namespace: 'default' }, + metadata: { + name: 'bare-e', + namespace: 'default', + tags: ['java', 'backend'], + }, spec: {}, }); }); @@ -116,151 +120,185 @@ describe.each(databases.eachSupportedId())( ); } - it('matches everything for empty field expression', async () => { - await expect(query({})).resolves.toEqual([ - 'api-d', - 'bare-e', - 'service-a', - 'service-b', - 'website-c', - ]); - }); - - it('matches nothing for {$not: {}}', async () => { - await expect(query({ $not: {} })).resolves.toEqual([]); - }); - - it('filters by direct field value', async () => { - await expect(query({ kind: 'component' })).resolves.toEqual([ - 'bare-e', - 'service-a', - 'service-b', - 'website-c', - ]); - }); - - it('filters by exact spec field value', async () => { - await expect(query({ 'spec.type': 'service' })).resolves.toEqual([ - 'service-a', - 'service-b', - ]); - }); - - it('filters with $all', async () => { - await expect( - query({ - $all: [{ kind: 'component' }, { 'spec.type': 'service' }], - }), - ).resolves.toEqual(['service-a', 'service-b']); - }); - - it('matches everything for empty $all', async () => { - await expect(query({ $all: [] })).resolves.toEqual([ - 'api-d', - 'bare-e', - 'service-a', - 'service-b', - 'website-c', - ]); - }); - - it('filters with $any', async () => { - await expect( - query({ - $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], - }), - ).resolves.toEqual(['service-a', 'service-b', 'website-c']); - }); - - it('returns nothing for empty $any', async () => { - await expect(query({ $any: [] })).resolves.toEqual([]); - }); - - it('filters with $not', async () => { - await expect( - query({ - $all: [ - { kind: 'component' }, - { $not: { 'spec.lifecycle': 'experimental' } }, - ], - }), - ).resolves.toEqual(['bare-e', 'service-a', 'website-c']); - }); - - it('filters with $in', async () => { - await expect( - query({ 'spec.type': { $in: ['service', 'openapi'] } }), - ).resolves.toEqual(['api-d', 'service-a', 'service-b']); - }); - - it('filters with $exists true', async () => { - await expect( - query({ - $all: [{ kind: 'component' }, { 'spec.owner': { $exists: true } }], - }), - ).resolves.toEqual(['service-a', 'service-b', 'website-c']); - }); - - it('filters with $exists false', async () => { - await expect( - query({ - $all: [{ kind: 'component' }, { 'spec.owner': { $exists: false } }], - }), - ).resolves.toEqual(['bare-e']); - }); - - it('filters with $hasPrefix', async () => { - await expect( - query({ 'metadata.name': { $hasPrefix: 'service' } }), - ).resolves.toEqual(['service-a', 'service-b']); - }); - - it('filters with $hasPrefix case-insensitively', async () => { - await expect( - query({ 'metadata.name': { $hasPrefix: 'Service' } }), - ).resolves.toEqual(['service-a', 'service-b']); - }); - - it('handles nested logical operators', async () => { - await expect( - query({ - $all: [ - { kind: 'component' }, - { - $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], - }, - { $not: { 'spec.lifecycle': 'experimental' } }, - ], - }), - ).resolves.toEqual(['service-a', 'website-c']); - }); - - it('throws on $contains operator', async () => { - await expect( - query({ 'metadata.name': { $contains: 'service' } as any }), - ).rejects.toThrow(/\$contains operator is not supported/); - }); - - it('throws on top-level primitive', async () => { - await expect(query('bad-value' as any)).rejects.toThrow( - /top-level primitive values are not supported/, - ); - }); - - it('combines filter and query independently', async () => { - const q = - knex('final_entities').whereNotNull('final_entity'); - applyEntityFilterToQuery({ - filter: { key: 'kind', values: ['component'] }, - query: { 'spec.type': 'service' }, - targetQuery: q, - onEntityIdField: 'final_entities.entity_id', - knex, + describe('field expressions', () => { + it('matches everything for empty field expression', async () => { + await expect(query({})).resolves.toEqual([ + 'api-d', + 'bare-e', + 'service-a', + 'service-b', + 'website-c', + ]); + }); + + it('filters by direct field value', async () => { + await expect(query({ kind: 'component' })).resolves.toEqual([ + 'bare-e', + 'service-a', + 'service-b', + 'website-c', + ]); + }); + + it('filters by exact spec field value', async () => { + await expect(query({ 'spec.type': 'service' })).resolves.toEqual([ + 'service-a', + 'service-b', + ]); + }); + + it('throws on top-level primitive', async () => { + await expect(query('bad-value' as any)).rejects.toThrow( + /top-level primitive values are not supported/, + ); + }); + }); + + describe('$all', () => { + it('filters with $all', async () => { + await expect( + query({ + $all: [{ kind: 'component' }, { 'spec.type': 'service' }], + }), + ).resolves.toEqual(['service-a', 'service-b']); + }); + + it('matches everything for empty $all', async () => { + await expect(query({ $all: [] })).resolves.toEqual([ + 'api-d', + 'bare-e', + 'service-a', + 'service-b', + 'website-c', + ]); + }); + }); + + describe('$any', () => { + it('filters with $any', async () => { + await expect( + query({ + $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], + }), + ).resolves.toEqual(['service-a', 'service-b', 'website-c']); + }); + + it('returns nothing for empty $any', async () => { + await expect(query({ $any: [] })).resolves.toEqual([]); + }); + }); + + describe('$not', () => { + it('filters with $not', async () => { + await expect( + query({ + $all: [ + { kind: 'component' }, + { $not: { 'spec.lifecycle': 'experimental' } }, + ], + }), + ).resolves.toEqual(['bare-e', 'service-a', 'website-c']); + }); + + it('matches nothing for {$not: {}}', async () => { + await expect(query({ $not: {} })).resolves.toEqual([]); + }); + }); + + describe('$in', () => { + it('filters with $in', async () => { + await expect( + query({ 'spec.type': { $in: ['service', 'openapi'] } }), + ).resolves.toEqual(['api-d', 'service-a', 'service-b']); + }); + }); + + describe('$exists', () => { + it('filters with $exists true', async () => { + await expect( + query({ + $all: [{ kind: 'component' }, { 'spec.owner': { $exists: true } }], + }), + ).resolves.toEqual(['service-a', 'service-b', 'website-c']); + }); + + it('filters with $exists false', async () => { + await expect( + query({ + $all: [{ kind: 'component' }, { 'spec.owner': { $exists: false } }], + }), + ).resolves.toEqual(['bare-e']); + }); + }); + + describe('$hasPrefix', () => { + it('filters with $hasPrefix', async () => { + await expect( + query({ 'metadata.name': { $hasPrefix: 'service' } }), + ).resolves.toEqual(['service-a', 'service-b']); + }); + + it('filters with $hasPrefix case-insensitively', async () => { + await expect( + query({ 'metadata.name': { $hasPrefix: 'Service' } }), + ).resolves.toEqual(['service-a', 'service-b']); + }); + }); + + describe('$contains', () => { + it('filters with primitive $contains on array fields', async () => { + await expect( + query({ 'metadata.tags': { $contains: 'java' } }), + ).resolves.toEqual(['bare-e']); + }); + + it('throws on non-primitive $contains', async () => { + await expect( + query({ + 'metadata.tags': { $contains: { nested: 'object' } } as any, + }), + ).rejects.toThrow( + /Non primitive forms of the \$contains operator is not supported/, + ); + }); + }); + + describe('nested operators', () => { + it('handles nested logical operators', async () => { + await expect( + query({ + $all: [ + { kind: 'component' }, + { + $any: [{ 'spec.type': 'service' }, { 'spec.type': 'website' }], + }, + { $not: { 'spec.lifecycle': 'experimental' } }, + ], + }), + ).resolves.toEqual(['service-a', 'website-c']); + }); + }); + + describe('combined filter and query', () => { + it('combines filter and query independently', async () => { + const q = + knex('final_entities').whereNotNull( + 'final_entity', + ); + applyEntityFilterToQuery({ + filter: { key: 'kind', values: ['component'] }, + query: { 'spec.type': 'service' }, + targetQuery: q, + onEntityIdField: 'final_entities.entity_id', + knex, + }); + const result = await q.then(rows => + rows + .map(row => JSON.parse(row.final_entity!).metadata.name) + .toSorted(), + ); + expect(result).toEqual(['service-a', 'service-b']); }); - const result = await q.then(rows => - rows.map(row => JSON.parse(row.final_entity!).metadata.name).toSorted(), - ); - expect(result).toEqual(['service-a', 'service-b']); }); }, ); diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index e7cf152184..27701a90ef 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -45,11 +45,9 @@ export function applyPredicateEntityFilterToQuery(options: { // We do not support top-level primitives; all matching happens through objects if (!isObject(filter)) { + const actual = JSON.stringify(filter); throw new InputError( - `Invalid filter predicate: top-level primitive values are not supported. ` + - `Wrap the value in a field expression, e.g. { "kind": ${JSON.stringify( - filter, - )} }`, + `Invalid filter predicate: top-level primitive values are not supported. Wrap the value in a field expression, e.g. { "kind": ${actual} }`, ); } @@ -171,14 +169,39 @@ function applyFieldCondition(options: { } if ('$contains' in value) { - // TODO(freben): Implement this, AT LEAST for some special cases such as tags and relations. - throw new InputError('The $contains operator is not supported'); + const target = value.$contains; + + // If the target is a primitive, match on the special array syntax. + // + // FROM: `{ "a": { "$contains": "b" } }` + // + // TO: `{ "a.b": "true" }` + // + // The search table does not actually show us that "a" was an array to + // begin with, so this can mistakenly also match on an object that had a + // "b" key with a true value. We'll consider that an acceptable tradeoff + // though. + if (isPrimitive(target)) { + const matchQuery = knex('search') + .select('search.entity_id') + .where({ + key: `${key}.${String(target).toLocaleLowerCase('en-US')}`, + value: 'true', + }); + return targetQuery.andWhere(onEntityIdField, 'in', matchQuery); + } + + // TODO(freben): Implement this for more use cases - for example simple + // objects (resulting in a $and and appending key strings) and maybe + // a subset of relations? + throw new InputError( + 'Non primitive forms of the $contains operator is not supported', + ); } } + const actual = JSON.stringify(value); throw new InputError( - `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, $in, or $hasPrefix operator, but got ${JSON.stringify( - value, - )}`, + `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, $in, or $hasPrefix operator, but got ${actual}`, ); } From bec9fe21d7bd2d070ba07b870600e94c7d1a024a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Feb 2026 17:04:59 +0100 Subject: [PATCH 32/55] implement relations contains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../applyPredicateEntityFilterToQuery.test.ts | 132 +++++++++++++++++- .../applyPredicateEntityFilterToQuery.ts | 132 +++++++++++++++++- 2 files changed, 256 insertions(+), 8 deletions(-) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts index 7f07cad11e..cea9d961f7 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.test.ts @@ -45,18 +45,24 @@ describe.each(databases.eachSupportedId())( kind: 'Component', metadata: { name: 'service-a', namespace: 'default' }, spec: { type: 'service', lifecycle: 'production', owner: 'team-a' }, + relations: [ + { type: 'ownedBy', targetRef: 'group:default/team-a' }, + { type: 'consumesApi', targetRef: 'api:default/api-d' }, + ], }); await addEntity({ apiVersion: 'backstage.io/v1alpha1', kind: 'Component', metadata: { name: 'service-b', namespace: 'default' }, spec: { type: 'service', lifecycle: 'experimental', owner: 'team-b' }, + relations: [{ type: 'ownedBy', targetRef: 'group:default/team-b' }], }); await addEntity({ apiVersion: 'backstage.io/v1alpha1', kind: 'Component', metadata: { name: 'website-c', namespace: 'default' }, spec: { type: 'website', lifecycle: 'production', owner: 'team-a' }, + relations: [{ type: 'ownedBy', targetRef: 'group:default/team-a' }], }); await addEntity({ apiVersion: 'backstage.io/v1alpha1', @@ -147,6 +153,12 @@ describe.each(databases.eachSupportedId())( ]); }); + it('throws on unsupported field operator', async () => { + await expect(query({ kind: { $bad: 'value' } as any })).rejects.toThrow( + /\$contains operator, but got/, + ); + }); + it('throws on top-level primitive', async () => { await expect(query('bad-value' as any)).rejects.toThrow( /top-level primitive values are not supported/, @@ -252,13 +264,127 @@ describe.each(databases.eachSupportedId())( ).resolves.toEqual(['bare-e']); }); - it('throws on non-primitive $contains', async () => { + it('matches relations by type only (existence)', async () => { + await expect( + query({ relations: { $contains: { type: 'consumesApi' } } }), + ).resolves.toEqual(['service-a']); + }); + + it('matches relations by type and exact targetRef', async () => { await expect( query({ - 'metadata.tags': { $contains: { nested: 'object' } } as any, + relations: { + $contains: { + type: 'ownedBy', + targetRef: 'group:default/team-a', + }, + }, + }), + ).resolves.toEqual(['service-a', 'website-c']); + }); + + it('matches relations by type and targetRef case-insensitively', async () => { + await expect( + query({ + relations: { + $contains: { + type: 'OwnedBy', + targetRef: 'Group:Default/Team-A', + }, + }, + }), + ).resolves.toEqual(['service-a', 'website-c']); + }); + + it('handles mixed-case keys in $contains object', async () => { + await expect( + query({ + relations: { + $contains: { + TyPe: 'ownedBy', + TargetRef: 'group:default/team-a', + }, + }, + } as any), + ).resolves.toEqual(['service-a', 'website-c']); + }); + + it('matches relations by type and targetRef with $in', async () => { + await expect( + query({ + relations: { + $contains: { + type: 'ownedBy', + targetRef: { + $in: ['group:default/team-a', 'group:default/team-b'], + }, + }, + }, + }), + ).resolves.toEqual(['service-a', 'service-b', 'website-c']); + }); + + it('throws on unsupported keys in $contains object', async () => { + await expect( + query({ + relations: { + $contains: { type: 'ownedBy', badKey: 'value' }, + } as any, + }), + ).rejects.toThrow(/Unsupported key "badKey" in \$contains/); + }); + + it('throws on duplicate keys in $contains object', async () => { + await expect( + query({ + relations: { + // JSON.parse allows duplicate keys, last one wins, but we + // simulate via an object that has both casings + $contains: JSON.parse('{"type": "ownedBy", "Type": "ownedBy"}'), + }, + }), + ).rejects.toThrow(/Duplicate key "Type" in \$contains/); + }); + + it('throws on $contains object without type', async () => { + await expect( + query({ + relations: { + $contains: { targetRef: 'group:default/team-a' }, + } as any, + }), + ).rejects.toThrow(/requires a "type" string property/); + }); + + it('throws on empty $in array in targetRef', async () => { + await expect( + query({ + relations: { + $contains: { type: 'ownedBy', targetRef: { $in: [] } }, + }, + }), + ).rejects.toThrow(/Empty "\$in" array for \$contains on "relations"/); + }); + + it('throws on unsupported targetRef value', async () => { + await expect( + query({ + relations: { + $contains: { type: 'ownedBy', targetRef: ['bad'] }, + } as any, + }), + ).rejects.toThrow(/Unsupported value in \$contains for "relations"/); + }); + + it('throws on object $contains for non-relations fields', async () => { + await expect( + query({ + 'metadata.tags': { + $contains: { type: 'something' }, + } as any, }), ).rejects.toThrow( - /Non primitive forms of the \$contains operator is not supported/, + /Object form of \$contains is not supported for field/, ); }); }); diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index 27701a90ef..a44f878abd 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -191,17 +191,139 @@ function applyFieldCondition(options: { return targetQuery.andWhere(onEntityIdField, 'in', matchQuery); } - // TODO(freben): Implement this for more use cases - for example simple - // objects (resulting in a $and and appending key strings) and maybe - // a subset of relations? + // Object form of $contains - currently only supports relation-style + // objects with "type" and optional "targetRef" keys. + // + // FROM: `{ "relations": { "$contains": { "type": "ownedBy", "targetRef": "group:default/team-a" } } }` + // + // TO: search for key = "relations.ownedby" AND value = "group:default/team-a" + if (isObject(target)) { + if (key === 'relations') { + return applyContainsRelation({ + target, + targetQuery, + onEntityIdField, + knex, + }); + } + + throw new InputError( + `Object form of $contains is not supported for field "${key}"`, + ); + } + + const actual = JSON.stringify(target); throw new InputError( - 'Non primitive forms of the $contains operator is not supported', + `Unsupported $contains target for field "${key}": ${actual}`, ); } } const actual = JSON.stringify(value); throw new InputError( - `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, $in, or $hasPrefix operator, but got ${actual}`, + `Invalid filter predicate value for field "${key}": expected a primitive value, $exists, $in, $hasPrefix, or $contains operator, but got ${actual}`, ); } + +/** + * Handles expressions on the form + * + * ``` + * { + * "relations": { + * "$contains": { + * "type": "ownedBy", + * "targetRef": "group:default/team-a" + * } + * } + * } + * ``` + * + * which map onto the search table's special `relation.: ` + * syntax. + * + * Only the keys "type" and "targetRef" are supported. The "type" key is + * required. If "targetRef" is omitted, it becomes an existence check for any + * relation of that type. The "targetRef" value can be a string or an `$in` + * array. + */ +function applyContainsRelation(options: { + target: Record; + targetQuery: Knex.QueryBuilder; + onEntityIdField: string; + knex: Knex; +}): Knex.QueryBuilder { + const { target: rawTarget, targetQuery, onEntityIdField, knex } = options; + + function parseStringOrIn(value: unknown): string[] { + if (typeof value === 'string') { + return [value.toLocaleLowerCase('en-US')]; + } + if ( + isObject(value) && + Object.keys(value).length === 1 && + '$in' in value && + Array.isArray(value.$in) && + value.$in.every((v): v is string => typeof v === 'string') + ) { + if (value.$in.length === 0) { + throw new InputError( + `Empty "$in" array for $contains on "relations" is not allowed`, + ); + } + return value.$in.map(v => v.toLocaleLowerCase('en-US')); + } + const actual = JSON.stringify(value); + throw new InputError( + `Unsupported value in $contains for "relations": expected a string or { "$in": [strings] }, but got ${actual}`, + ); + } + + let type: string | undefined; + let targetRef: string[] | undefined; + + for (const [rawKey, value] of Object.entries(rawTarget)) { + const key = rawKey.toLocaleLowerCase('en-US'); + + if (key === 'type') { + if (type !== undefined) { + throw new InputError( + `Duplicate key "${rawKey}" in $contains for "relations"`, + ); + } + if (typeof value !== 'string') { + throw new InputError( + `The $contains operator for "relations" requires a "type" string property`, + ); + } + type = value; + } else if (key === 'targetref') { + if (targetRef !== undefined) { + throw new InputError( + `Duplicate key "${rawKey}" in $contains for "relations"`, + ); + } + targetRef = parseStringOrIn(value); + } else { + throw new InputError( + `Unsupported key "${rawKey}" in $contains for "relations". Only "type" and "targetRef" are supported`, + ); + } + } + + if (!type) { + throw new InputError( + `The $contains operator for "relations" requires a "type" string property`, + ); + } + + const matchQuery = knex('search') + .select('search.entity_id') + .where({ key: `relations.${type.toLocaleLowerCase('en-US')}` }); + + if (targetRef) { + matchQuery.whereIn('value', targetRef); + } + + return targetQuery.andWhere(onEntityIdField, 'in', matchQuery); +} From c4aa99e8d5a7d31ee312e426ff9c9b74379a94b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sat, 21 Feb 2026 14:34:56 +0100 Subject: [PATCH 33/55] clarify changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/rich-ducks-ring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/rich-ducks-ring.md b/.changeset/rich-ducks-ring.md index ea8a3e8016..5f046e31b2 100644 --- a/.changeset/rich-ducks-ring.md +++ b/.changeset/rich-ducks-ring.md @@ -5,6 +5,6 @@ Added predicate-based entity filtering via POST /entities/by-query endpoint. -Supports `$all`, `$any`, `$not`, `$exists`, `$in`, and `$hasPrefix` operators for expressive entity queries. Integrated into the existing `queryEntities` flow with full cursor-based pagination, permission enforcement, and `totalItems` support. +Supports `$all`, `$any`, `$not`, `$exists`, `$in`, `$hasPrefix`, and (partially) `$contains` operators for expressive entity queries. Integrated into the existing `queryEntities` flow with full cursor-based pagination, permission enforcement, and `totalItems` support. The catalog client's `queryEntities()` method automatically routes to the POST endpoint when a `query` predicate is provided. From 4a47c603e1ec2eedbe26821be292a30f41e66c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sat, 21 Feb 2026 16:10:40 +0100 Subject: [PATCH 34/55] add docs too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/features/software-catalog/api.md | 187 +++++++++++++++++++++++ packages/catalog-client/report.api.md | 2 +- packages/catalog-client/src/types/api.ts | 10 +- 3 files changed, 194 insertions(+), 5 deletions(-) diff --git a/docs/features/software-catalog/api.md b/docs/features/software-catalog/api.md index ca8b8f638d..3cd70538c2 100644 --- a/docs/features/software-catalog/api.md +++ b/docs/features/software-catalog/api.md @@ -210,6 +210,193 @@ if `prevCursor` exists, it can be used to retrieve the previous batch of entitie it isn't possible to change any of [`filter`, `orderField`, `fullTextFilter`] when passing `cursor` as query parameters, as changing any of these properties will affect pagination. If any of `filter`, `orderField`, `fullTextFilter` is specified together with `cursor`, only the latter is taken into consideration. +### `POST /entities/by-query` + +This supports the same features as the `GET` variant, but in a `POST` body to +not have to abide by URL length limits. Additionally, it supports advanced, more +expressive querying format - see below. The response format is identical. + +#### Querying by filter predicate + +You can pass in a filter predicate to select a subset of entities in the +catalog. They are comprised of an optional logical expression tree (using +`$all`, `$any`, `$not`), ending in filter sets that can have custom matchers +(e.g. `$exists`, `$in`, `$hasPrefix`, `$contains`). + +This is an example of what such a filter predicate expression might look like: + +```js +{ + "query": { + "$all": [ + { + "kind": "Component", + "spec.type": { "$in": ["service", "website"] } + }, + { + "$not": { + "metadata.annotations.backstage.io/orphan": "true" + } + } + ] + } +} +``` + +A filter set is an object whose keys are dot separated paths into an object, and +the values are either primitives (string, number, or boolean) or custom matchers +as per below. An example of a simple such filter set is: + +```js +// All of the following must be true for a given entity (there's an +// implicit AND between them) +{ + // The kind field is matched against a literal, case insensitively + "kind": "Component", + // The type field inside the spec is matched using a custom matcher, see below + "spec.type": { "$in": ["service", "website"] } +} +``` + +The root of the query is always an object, whether there is a logic expression +tree or not. Nodes with a single key that starts with a `$` sign have special +meaning. + +- `$not`: Logical negation. + + Its value must be a single expression. Example: + + ```js + // Matches entities that do NOT have kind Component + { + "$not": { + "kind": "Component", + } + } + ``` + + Note that `$not` cannot be used in a right hand side value matcher. + + ```js + // ❌ WRONG + { "kind": { "$not": "Component" } } + // ✅ CORRECT + { "$not": { "kind": "Component" } } + ``` + +- `$all`: Require that all given expressions match each entity. + + Its value must be an array of expressions. Example: + + ```js + // Matches entities that BOTH have kind Component and type website + { + "$all": [ + { "kind": "Component" }, + { "spec.type": "website" } + ] + } + ``` + + An empty array always matches every entity. + +- `$any`: Require that at least one of a set of expressions match a given entity. + + Its value must be an array of expressions. Example: + + ```js + // Matches entities that EITHER have kind Component or type website + { + "$any": [ + { "kind": "Component" }, + { "spec.type": "website" } + ] + } + ``` + + An empty array never matches anything. + +- `$exists`: Assert on the existence of fields. + + Its value is either `true`, meaning that the field must exist on the entity + (no matter what its value), or `false`, meaning that it must not exist. + Example: + + ```js + // Matches entities that DO NOT have that annotation, ignoring what the + // value might be + { + "metadata.annotations.backstage.io/orphan": { + "$exists": false + }, + } + ``` + +- `$in`: Assert that a field has any of a set of primitive values. + + Its value must be an array of string, number, and/or boolean values. Example: + + ```js + // Matches entities whose type is EITHER service or website + { + "spec.type": { + "$in": ["service", "website"] + } + } + ``` + + The matching is case insensitive. An empty array never matches anything. + +- `$hasPrefix`: Assert that a field is a string that starts with a certain prefix text. + + Its value is a string. Example: + + ```js + // Matches entities whose project slug annotation starts with "backstage/" + { + "metadata.annotations.github.com/project-slug": { + "$hasPrefix": "backstage/" + } + } + ``` + + The matching is case insensitive, and captures both exact matches and strings + that start with the given prefix. + +- `$contains`: Assert that an array contains an element that matches the given expression. + + There is only limited support for this matcher. One use case is for relations: + + ```js + { + // Specifically type and (optionally) targetRef supported, and only + // with equality or "$in" for the targetRef + "relations": { + "$contains": { + "type": "ownedBy", + "targetRef": { + "$in": ["user:default/foo", "group:default/bar"] + } + } + } + } + ``` + + The other use case is for arrays where you match with a primitive value, such + as labels. Example: + + ```js + { + // Works for any field, as long as the value is a primitive + // (either string, number, or boolean) + "metadata.labels": { + "$contains": "java" + } + } + ``` + + In every case, the matching is case insensitive. + ### `GET /entities` Lists entities. diff --git a/packages/catalog-client/report.api.md b/packages/catalog-client/report.api.md index cc83cce443..0d71e6bc59 100644 --- a/packages/catalog-client/report.api.md +++ b/packages/catalog-client/report.api.md @@ -8,7 +8,7 @@ import type { AnalyzeLocationResponse } from '@backstage/plugin-catalog-common'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Entity } from '@backstage/catalog-model'; import type { FilterPredicate } from '@backstage/filter-predicates'; -import { SerializedError } from '@backstage/errors'; +import type { SerializedError } from '@backstage/errors'; // @public export type AddLocationRequest = { diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index 64d01c0972..c39c0c872e 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { CompoundEntityRef, Entity } from '@backstage/catalog-model'; -import { SerializedError } from '@backstage/errors'; +import type { CompoundEntityRef, Entity } from '@backstage/catalog-model'; +import type { SerializedError } from '@backstage/errors'; import type { AnalyzeLocationRequest, AnalyzeLocationResponse, @@ -438,7 +438,9 @@ export type QueryEntitiesInitialRequest = { */ filter?: EntityFilterQuery; /** - * Predicate-based filter with logical operators ($all, $any, $not, $exists, $in, $hasPrefix). + * Predicate-based filter with operators for logical expressions (`$all`, + * `$any`, and `$not`) and matching (`$exists`, `$in`, `$hasPrefix`, and + * (partially) `$contains`). * * @example * ```typescript @@ -446,7 +448,7 @@ export type QueryEntitiesInitialRequest = { * query: { * $all: [ * { kind: 'component' }, - * { 'spec.type': 'service' } + * { 'spec.type': { $in: ['service', 'website'] } } * ] * } * } From 0a4b3a16aed0c96ca57a4340a7e8735d3d274b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 23 Feb 2026 09:54:43 +0100 Subject: [PATCH 35/55] Update docs/features/software-catalog/api.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Fredrik Adelöw --- docs/features/software-catalog/api.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/features/software-catalog/api.md b/docs/features/software-catalog/api.md index 3cd70538c2..97f579c5a3 100644 --- a/docs/features/software-catalog/api.md +++ b/docs/features/software-catalog/api.md @@ -383,20 +383,18 @@ meaning. ``` The other use case is for arrays where you match with a primitive value, such - as labels. Example: + as tags. Example: ```js { - // Works for any field, as long as the value is a primitive - // (either string, number, or boolean) - "metadata.labels": { + // Works for array fields whose items are primitive values + // (typically strings, but numbers and booleans are also supported) + "metadata.tags": { "$contains": "java" } } ``` - In every case, the matching is case insensitive. - ### `GET /entities` Lists entities. From b06bcdcb353ddb93c2291f11f1e30fd37c55a2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 23 Feb 2026 13:54:29 +0100 Subject: [PATCH 36/55] transfer filters across to the query too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- packages/catalog-client/src/CatalogClient.ts | 38 +++++++-- packages/catalog-client/src/utils.test.ts | 82 +++++++++++++++++++- packages/catalog-client/src/utils.ts | 45 +++++++++++ 3 files changed, 159 insertions(+), 6 deletions(-) diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index bd557cfdd2..2bdf63cc49 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -20,7 +20,8 @@ import { parseEntityRef, stringifyLocationRef, } from '@backstage/catalog-model'; -import { ResponseError } from '@backstage/errors'; +import { InputError, ResponseError } from '@backstage/errors'; +import { FilterPredicate } from '@backstage/filter-predicates'; import { AddLocationRequest, AddLocationResponse, @@ -47,6 +48,7 @@ import { ValidateEntityResponse, } from './types/api'; import { + convertFilterToPredicate, isQueryEntitiesInitialRequest, splitRefsIntoChunks, cursorContainsQuery, @@ -352,11 +354,37 @@ export class CatalogClient implements CatalogApi { const body: QueryEntitiesByPredicateRequest = {}; if (isQueryEntitiesInitialRequest(request)) { - const { query, limit, offset, orderFields, fullTextFilter, fields } = - request; - if (query && typeof query === 'object') { - body.query = query; + const { + filter, + query, + limit, + offset, + orderFields, + fullTextFilter, + fields, + } = request; + + let filterPredicate: FilterPredicate | undefined; + if (query !== undefined) { + if ( + typeof query !== 'object' || + query === null || + Array.isArray(query) + ) { + throw new InputError('Query must be an object'); + } + filterPredicate = query; } + if (filter !== undefined) { + const converted = convertFilterToPredicate(filter); + filterPredicate = filterPredicate + ? { $all: [filterPredicate, converted] } + : converted; + } + if (filterPredicate !== undefined) { + body.query = filterPredicate as unknown as { [key: string]: any }; + } + if (limit !== undefined) { body.limit = limit; } diff --git a/packages/catalog-client/src/utils.test.ts b/packages/catalog-client/src/utils.test.ts index 2f00859985..ce60cc3083 100644 --- a/packages/catalog-client/src/utils.test.ts +++ b/packages/catalog-client/src/utils.test.ts @@ -14,7 +14,87 @@ * limitations under the License. */ -import { splitRefsIntoChunks } from './utils'; +import { CATALOG_FILTER_EXISTS } from './types/api'; +import { convertFilterToPredicate, splitRefsIntoChunks } from './utils'; + +describe('convertFilterToPredicate', () => { + it('converts a single string value', () => { + expect(convertFilterToPredicate({ kind: 'component' })).toEqual({ + kind: 'component', + }); + }); + + it('converts multiple keys into $all', () => { + expect( + convertFilterToPredicate({ + kind: 'component', + 'spec.type': 'service', + }), + ).toEqual({ + $all: [{ kind: 'component' }, { 'spec.type': 'service' }], + }); + }); + + it('converts an array of string values into $in', () => { + expect( + convertFilterToPredicate({ 'spec.type': ['service', 'website'] }), + ).toEqual({ + 'spec.type': { $in: ['service', 'website'] }, + }); + }); + + it('converts CATALOG_FILTER_EXISTS into $exists', () => { + expect( + convertFilterToPredicate({ 'spec.owner': CATALOG_FILTER_EXISTS }), + ).toEqual({ + 'spec.owner': { $exists: true }, + }); + }); + + it('converts an array of records into $any (OR)', () => { + expect( + convertFilterToPredicate([{ kind: 'component' }, { kind: 'api' }]), + ).toEqual({ + $any: [{ kind: 'component' }, { kind: 'api' }], + }); + }); + + it('converts array of records with multiple keys each', () => { + expect( + convertFilterToPredicate([ + { kind: 'component', 'spec.type': 'service' }, + { kind: 'api' }, + ]), + ).toEqual({ + $any: [ + { $all: [{ kind: 'component' }, { 'spec.type': 'service' }] }, + { kind: 'api' }, + ], + }); + }); + + it('treats CATALOG_FILTER_EXISTS mixed with string values as just existence', () => { + expect( + convertFilterToPredicate({ + 'spec.owner': [CATALOG_FILTER_EXISTS, 'team-a'], + }), + ).toEqual({ + 'spec.owner': { $exists: true }, + }); + }); + + it('converts a single-element array filter without wrapping in $any', () => { + expect(convertFilterToPredicate([{ kind: 'component' }])).toEqual({ + kind: 'component', + }); + }); + + it('ignores entries with no valid values', () => { + expect( + convertFilterToPredicate({ kind: 'component', other: [] as string[] }), + ).toEqual({ kind: 'component' }); + }); +}); describe('splitRefsIntoChunks', () => { it('splits by count limit', () => { diff --git a/packages/catalog-client/src/utils.ts b/packages/catalog-client/src/utils.ts index fc867d8670..231aa2409f 100644 --- a/packages/catalog-client/src/utils.ts +++ b/packages/catalog-client/src/utils.ts @@ -14,7 +14,13 @@ * limitations under the License. */ +import type { + FilterPredicate, + FilterPredicateExpression, +} from '@backstage/filter-predicates'; import { + CATALOG_FILTER_EXISTS, + EntityFilterQuery, QueryEntitiesCursorRequest, QueryEntitiesInitialRequest, QueryEntitiesRequest, @@ -39,6 +45,45 @@ export function cursorContainsQuery(cursor: string): boolean { } } +/** + * Converts an {@link EntityFilterQuery} into a predicate query object. + * @internal + */ +export function convertFilterToPredicate(filter: EntityFilterQuery): + | FilterPredicateExpression + | { + $all: FilterPredicate[]; + } + | { + $any: FilterPredicate[]; + } { + const records = [filter].flat(); + + const clauses = records.map(record => { + const parts: FilterPredicateExpression[] = []; + + for (const [key, value] of Object.entries(record)) { + const values = [value].flat(); + const strings = values.filter((v): v is string => typeof v === 'string'); + const hasExists = values.some(v => v === CATALOG_FILTER_EXISTS); + + if (hasExists) { + // Ignore whether there ALSO were some strings - that would boil down to + // just existence anyway since there's effectively an OR between them + parts.push({ [key]: { $exists: true } } as FilterPredicateExpression); + } else if (strings.length === 1) { + parts.push({ [key]: strings[0] } as FilterPredicateExpression); + } else if (strings.length > 1) { + parts.push({ [key]: { $in: strings } } as FilterPredicateExpression); + } + } + + return parts.length === 1 ? parts[0] : { $all: parts }; + }); + + return clauses.length === 1 ? clauses[0] : { $any: clauses }; +} + /** * Takes a set of entity refs, and splits them into chunks (groups) such that * the total string length in each chunk does not exceed the default Express.js From 63ad13904b0bb9e72aa077e621cb9de3ee5f679c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 23 Feb 2026 16:43:36 +0100 Subject: [PATCH 37/55] do not use the very particular 'true' array syntax, but instead the normal one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../request/applyPredicateEntityFilterToQuery.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts index a44f878abd..d915b8c4b5 100644 --- a/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts +++ b/plugins/catalog-backend/src/service/request/applyPredicateEntityFilterToQuery.ts @@ -175,18 +175,18 @@ function applyFieldCondition(options: { // // FROM: `{ "a": { "$contains": "b" } }` // - // TO: `{ "a.b": "true" }` + // TO: `{ "a": "b" }` // // The search table does not actually show us that "a" was an array to // begin with, so this can mistakenly also match on an object that had a - // "b" key with a true value. We'll consider that an acceptable tradeoff - // though. + // "b" key with a primitive value. We'll consider that an acceptable + // tradeoff though. if (isPrimitive(target)) { const matchQuery = knex('search') .select('search.entity_id') .where({ - key: `${key}.${String(target).toLocaleLowerCase('en-US')}`, - value: 'true', + key, + value: String(target).toLocaleLowerCase('en-US'), }); return targetQuery.andWhere(onEntityIdField, 'in', matchQuery); } From 4bd6a3a1afd007b1d87359de5ea74dce7288021f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 24 Feb 2026 19:24:06 +0000 Subject: [PATCH 38/55] Version Packages (next) --- .changeset/pre.json | 40 +- docs/releases/v1.49.0-next.0-changelog.md | 2362 +++++++++++++++++ package.json | 2 +- packages/app-defaults/CHANGELOG.md | 11 + packages/app-defaults/package.json | 2 +- packages/app-example-plugin/CHANGELOG.md | 8 + packages/app-example-plugin/package.json | 2 +- packages/app-legacy/CHANGELOG.md | 44 + packages/app-legacy/package.json | 2 +- packages/app/CHANGELOG.md | 50 + packages/app/package.json | 2 +- packages/backend-app-api/CHANGELOG.md | 9 + packages/backend-app-api/package.json | 2 +- packages/backend-defaults/CHANGELOG.md | 25 + packages/backend-defaults/package.json | 2 +- .../CHANGELOG.md | 26 + .../package.json | 2 +- packages/backend-openapi-utils/CHANGELOG.md | 9 + packages/backend-openapi-utils/package.json | 2 +- packages/backend-plugin-api/CHANGELOG.md | 14 + packages/backend-plugin-api/package.json | 2 +- packages/backend-test-utils/CHANGELOG.md | 16 + packages/backend-test-utils/package.json | 2 +- packages/backend/CHANGELOG.md | 42 + packages/backend/package.json | 2 +- packages/catalog-client/CHANGELOG.md | 10 + packages/catalog-client/package.json | 2 +- packages/cli-common/CHANGELOG.md | 34 + packages/cli-common/package.json | 2 +- packages/cli-node/CHANGELOG.md | 11 + packages/cli-node/package.json | 2 +- packages/cli/CHANGELOG.md | 29 + packages/cli/package.json | 2 +- packages/codemods/CHANGELOG.md | 9 + packages/codemods/package.json | 2 +- packages/config-loader/CHANGELOG.md | 11 + packages/config-loader/package.json | 2 +- packages/core-app-api/CHANGELOG.md | 10 + packages/core-app-api/package.json | 2 +- packages/core-compat-api/CHANGELOG.md | 12 + packages/core-compat-api/package.json | 2 +- packages/core-components/CHANGELOG.md | 11 + packages/core-components/package.json | 2 +- packages/core-plugin-api/CHANGELOG.md | 11 + packages/core-plugin-api/package.json | 2 +- packages/create-app/CHANGELOG.md | 9 + packages/create-app/package.json | 2 +- packages/dev-utils/CHANGELOG.md | 15 + packages/dev-utils/package.json | 2 +- packages/e2e-test/CHANGELOG.md | 9 + packages/e2e-test/package.json | 2 +- packages/eslint-plugin/CHANGELOG.md | 6 + packages/eslint-plugin/package.json | 2 +- packages/frontend-app-api/CHANGELOG.md | 14 + packages/frontend-app-api/package.json | 2 +- packages/frontend-defaults/CHANGELOG.md | 12 + packages/frontend-defaults/package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- packages/frontend-internal/CHANGELOG.md | 9 + packages/frontend-internal/package.json | 2 +- packages/frontend-plugin-api/CHANGELOG.md | 10 + packages/frontend-plugin-api/package.json | 2 +- packages/frontend-test-utils/CHANGELOG.md | 19 + packages/frontend-test-utils/package.json | 2 +- packages/integration-react/CHANGELOG.md | 9 + packages/integration-react/package.json | 2 +- packages/integration/CHANGELOG.md | 12 + packages/integration/package.json | 2 +- packages/repo-tools/CHANGELOG.md | 17 + packages/repo-tools/package.json | 2 +- packages/scaffolder-internal/CHANGELOG.md | 8 + packages/scaffolder-internal/package.json | 2 +- .../techdocs-cli-embedded-app/CHANGELOG.md | 21 + .../techdocs-cli-embedded-app/package.json | 2 +- packages/techdocs-cli/CHANGELOG.md | 13 + packages/techdocs-cli/package.json | 2 +- packages/test-utils/CHANGELOG.md | 13 + packages/test-utils/package.json | 2 +- packages/ui/CHANGELOG.md | 17 + packages/ui/package.json | 2 +- packages/yarn-plugin/CHANGELOG.md | 9 + packages/yarn-plugin/package.json | 2 +- plugins/api-docs/CHANGELOG.md | 15 + plugins/api-docs/package.json | 2 +- plugins/app-backend/CHANGELOG.md | 13 + plugins/app-backend/package.json | 2 +- plugins/app-node/CHANGELOG.md | 8 + plugins/app-node/package.json | 2 +- plugins/app-react/CHANGELOG.md | 8 + plugins/app-react/package.json | 2 +- plugins/app-visualizer/CHANGELOG.md | 10 + plugins/app-visualizer/package.json | 2 +- plugins/app/CHANGELOG.md | 17 + plugins/app/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- plugins/auth-backend/CHANGELOG.md | 15 + plugins/auth-backend/package.json | 2 +- plugins/auth-node/CHANGELOG.md | 12 + plugins/auth-node/package.json | 2 +- plugins/auth-react/CHANGELOG.md | 9 + plugins/auth-react/package.json | 2 +- plugins/auth/CHANGELOG.md | 10 + plugins/auth/package.json | 2 +- plugins/bitbucket-cloud-common/CHANGELOG.md | 7 + plugins/bitbucket-cloud-common/package.json | 2 +- .../catalog-backend-module-aws/CHANGELOG.md | 16 + .../catalog-backend-module-aws/package.json | 2 +- .../catalog-backend-module-azure/CHANGELOG.md | 11 + .../catalog-backend-module-azure/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../catalog-backend-module-gcp/CHANGELOG.md | 11 + .../catalog-backend-module-gcp/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../catalog-backend-module-gitea/CHANGELOG.md | 12 + .../catalog-backend-module-gitea/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 15 + .../package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- .../catalog-backend-module-ldap/CHANGELOG.md | 13 + .../catalog-backend-module-ldap/package.json | 2 +- .../catalog-backend-module-logs/CHANGELOG.md | 9 + .../catalog-backend-module-logs/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- plugins/catalog-backend/CHANGELOG.md | 30 + plugins/catalog-backend/package.json | 2 +- plugins/catalog-graph/CHANGELOG.md | 13 + plugins/catalog-graph/package.json | 2 +- plugins/catalog-import/CHANGELOG.md | 18 + plugins/catalog-import/package.json | 2 +- plugins/catalog-node/CHANGELOG.md | 21 + plugins/catalog-node/package.json | 2 +- plugins/catalog-react/CHANGELOG.md | 22 + plugins/catalog-react/package.json | 2 +- .../catalog-unprocessed-entities/CHANGELOG.md | 13 + .../catalog-unprocessed-entities/package.json | 2 +- plugins/catalog/CHANGELOG.md | 25 + plugins/catalog/package.json | 2 +- plugins/config-schema/CHANGELOG.md | 10 + plugins/config-schema/package.json | 2 +- plugins/devtools-backend/CHANGELOG.md | 15 + plugins/devtools-backend/package.json | 2 +- plugins/devtools-react/CHANGELOG.md | 8 + plugins/devtools-react/package.json | 2 +- plugins/devtools/CHANGELOG.md | 14 + plugins/devtools/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../events-backend-module-azure/CHANGELOG.md | 8 + .../events-backend-module-azure/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../events-backend-module-gerrit/CHANGELOG.md | 8 + .../events-backend-module-gerrit/package.json | 2 +- .../events-backend-module-github/CHANGELOG.md | 11 + .../events-backend-module-github/package.json | 2 +- .../events-backend-module-gitlab/CHANGELOG.md | 9 + .../events-backend-module-gitlab/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../events-backend-module-kafka/CHANGELOG.md | 10 + .../events-backend-module-kafka/package.json | 2 +- .../events-backend-test-utils/CHANGELOG.md | 7 + .../events-backend-test-utils/package.json | 2 +- plugins/events-backend/CHANGELOG.md | 12 + plugins/events-backend/package.json | 2 +- plugins/events-node/CHANGELOG.md | 9 + plugins/events-node/package.json | 2 +- .../example-todo-list-backend/CHANGELOG.md | 8 + .../example-todo-list-backend/package.json | 2 +- plugins/example-todo-list/CHANGELOG.md | 8 + plugins/example-todo-list/package.json | 2 +- plugins/gateway-backend/CHANGELOG.md | 7 + plugins/gateway-backend/package.json | 2 +- plugins/home-react/CHANGELOG.md | 10 + plugins/home-react/package.json | 2 +- plugins/home/CHANGELOG.md | 17 + plugins/home/package.json | 2 +- plugins/kubernetes-backend/CHANGELOG.md | 18 + plugins/kubernetes-backend/package.json | 2 +- plugins/kubernetes-cluster/CHANGELOG.md | 13 + plugins/kubernetes-cluster/package.json | 2 +- plugins/kubernetes-node/CHANGELOG.md | 10 + plugins/kubernetes-node/package.json | 2 +- plugins/kubernetes-react/CHANGELOG.md | 12 + plugins/kubernetes-react/package.json | 2 +- plugins/kubernetes/CHANGELOG.md | 14 + plugins/kubernetes/package.json | 2 +- plugins/mcp-actions-backend/CHANGELOG.md | 16 + plugins/mcp-actions-backend/package.json | 2 +- plugins/mui-to-bui/CHANGELOG.md | 10 + plugins/mui-to-bui/package.json | 2 +- .../CHANGELOG.md | 15 + .../package.json | 2 +- .../CHANGELOG.md | 18 + .../package.json | 2 +- plugins/notifications-backend/CHANGELOG.md | 15 + plugins/notifications-backend/package.json | 2 +- plugins/notifications-node/CHANGELOG.md | 11 + plugins/notifications-node/package.json | 2 +- plugins/notifications/CHANGELOG.md | 13 + plugins/notifications/package.json | 2 +- plugins/org-react/CHANGELOG.md | 11 + plugins/org-react/package.json | 2 +- plugins/org/CHANGELOG.md | 12 + plugins/org/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- plugins/permission-backend/CHANGELOG.md | 12 + plugins/permission-backend/package.json | 2 +- plugins/permission-node/CHANGELOG.md | 11 + plugins/permission-node/package.json | 2 +- plugins/permission-react/CHANGELOG.md | 9 + plugins/permission-react/package.json | 2 +- plugins/proxy-backend/CHANGELOG.md | 9 + plugins/proxy-backend/package.json | 2 +- plugins/proxy-node/CHANGELOG.md | 7 + plugins/proxy-node/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- plugins/scaffolder-backend/CHANGELOG.md | 20 + plugins/scaffolder-backend/package.json | 2 +- plugins/scaffolder-common/CHANGELOG.md | 11 + plugins/scaffolder-common/package.json | 2 +- .../scaffolder-node-test-utils/CHANGELOG.md | 10 + .../scaffolder-node-test-utils/package.json | 2 +- plugins/scaffolder-node/CHANGELOG.md | 13 + plugins/scaffolder-node/package.json | 2 +- plugins/scaffolder-react/CHANGELOG.md | 17 + plugins/scaffolder-react/package.json | 2 +- plugins/scaffolder/CHANGELOG.md | 24 + plugins/scaffolder/package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- plugins/search-backend-module-pg/CHANGELOG.md | 10 + plugins/search-backend-module-pg/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- plugins/search-backend-node/CHANGELOG.md | 11 + plugins/search-backend-node/package.json | 2 +- plugins/search-backend/CHANGELOG.md | 15 + plugins/search-backend/package.json | 2 +- plugins/search-react/CHANGELOG.md | 14 + plugins/search-react/package.json | 2 +- plugins/search/CHANGELOG.md | 16 + plugins/search/package.json | 2 +- plugins/signals-backend/CHANGELOG.md | 11 + plugins/signals-backend/package.json | 2 +- plugins/signals-node/CHANGELOG.md | 11 + plugins/signals-node/package.json | 2 +- plugins/signals-react/CHANGELOG.md | 8 + plugins/signals-react/package.json | 2 +- plugins/signals/CHANGELOG.md | 12 + plugins/signals/package.json | 2 +- .../techdocs-addons-test-utils/CHANGELOG.md | 15 + .../techdocs-addons-test-utils/package.json | 2 +- plugins/techdocs-backend/CHANGELOG.md | 15 + plugins/techdocs-backend/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- plugins/techdocs-node/CHANGELOG.md | 14 + plugins/techdocs-node/package.json | 2 +- plugins/techdocs-react/CHANGELOG.md | 13 + plugins/techdocs-react/package.json | 2 +- plugins/techdocs/CHANGELOG.md | 22 + plugins/techdocs/package.json | 2 +- plugins/user-settings-backend/CHANGELOG.md | 12 + plugins/user-settings-backend/package.json | 2 +- plugins/user-settings/CHANGELOG.md | 17 + plugins/user-settings/package.json | 2 +- 369 files changed, 4936 insertions(+), 185 deletions(-) create mode 100644 docs/releases/v1.49.0-next.0-changelog.md diff --git a/.changeset/pre.json b/.changeset/pre.json index 00dbb0f757..7113b1ee96 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -210,5 +210,43 @@ "@backstage/plugin-user-settings-backend": "0.4.0", "@backstage/plugin-user-settings-common": "0.1.0" }, - "changesets": [] + "changesets": [ + "add-masked-and-hidden-option", + "blue-moons-crash", + "bright-moons-open", + "brown-towns-find", + "bump-bfj-v9", + "cli-common-cached-paths", + "cli-internal-refactor", + "cli-node-parallel-helpers", + "cli-translations-export-import", + "dependabot-d2ec7e9", + "fancy-ends-turn", + "fix-frontend-feature-compat", + "fix-prettier-existence-check", + "fluffy-owls-act", + "long-hairs-throw", + "mean-fans-decide", + "metal-humans-move", + "migrate-to-target-paths", + "ninety-corners-flash", + "orange-mugs-post-1", + "orange-mugs-post-2", + "pink-terms-know", + "polite-singers-lead", + "pretty-days-taste", + "rare-adults-attack", + "renovate-8b1c21e", + "rude-groups-shout", + "scaffolder-export-form-fields-api", + "silver-pigs-remain", + "sixty-pianos-begin", + "stable-translation-plugin-app", + "stable-translation-test-utils", + "stupid-pans-hope", + "swift-flowers-grin", + "swift-ravens-jog", + "tired-bushes-write", + "twenty-worlds-create" + ] } diff --git a/docs/releases/v1.49.0-next.0-changelog.md b/docs/releases/v1.49.0-next.0-changelog.md new file mode 100644 index 0000000000..fcfc092d5e --- /dev/null +++ b/docs/releases/v1.49.0-next.0-changelog.md @@ -0,0 +1,2362 @@ +# Release v1.49.0-next.0 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.49.0-next.0](https://backstage.github.io/upgrade-helper/?to=1.49.0-next.0) + +## @backstage/cli-common@0.2.0-next.0 + +### Minor Changes + +- 56bd494: Added `targetPaths` and `findOwnPaths` as replacements for `findPaths`, with a cleaner separation between target project paths and package-relative paths. + + To migrate existing `findPaths` usage: + + ```ts + // Before + import { findPaths } from '@backstage/cli-common'; + const paths = findPaths(__dirname); + + // After — for target project paths (cwd-based): + import { targetPaths } from '@backstage/cli-common'; + // paths.targetDir → targetPaths.dir + // paths.targetRoot → targetPaths.rootDir + // paths.resolveTarget('src') → targetPaths.resolve('src') + // paths.resolveTargetRoot('yarn.lock') → targetPaths.resolveRoot('yarn.lock') + + // After — for package-relative paths: + import { findOwnPaths } from '@backstage/cli-common'; + const own = findOwnPaths(__dirname); + // paths.ownDir → own.dir + // paths.ownRoot → own.rootDir + // paths.resolveOwn('config/jest.js') → own.resolve('config/jest.js') + // paths.resolveOwnRoot('tsconfig.json') → own.resolveRoot('tsconfig.json') + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.7 + +## @backstage/integration@1.21.0-next.0 + +### Minor Changes + +- d933f62: Add configurable throttling and retry mechanism for GitLab integration. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + +## @backstage/plugin-catalog-backend@3.5.0-next.0 + +### Minor Changes + +- bf71677: Added opentelemetry metrics for SCM events: + + - `catalog.events.scm.messages` with attribute `eventType`: Counter for the number of SCM events actually received by the catalog backend. The `eventType` is currently either `location` or `repository`. + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- fbf382f: Minor internal optimisation +- 1ee5b28: Migrates existing catalog metrics to use the alpha MetricsService. This release is a 1:1 migration with no breaking changes. +- 3181973: Changed the `search` table foreign key to point to `final_entities` instead of `refresh_state` +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/filter-predicates@0.1.0 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + +## @backstage/plugin-catalog-node@2.1.0-next.0 + +### Minor Changes + +- bf71677: Added the ability for SCM events subscribers to mark the fact that they have taken actions based on events, which produces output metrics: + + - `catalog.events.scm.actions` with attribute `action`: Counter for the number of actions actually taken by catalog internals or other subscribers, based on SCM events. The `action` is currently either `create`, `delete`, `refresh`, or `move`. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/backend-test-utils@1.11.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + +## @backstage/plugin-notifications-backend-module-slack@0.4.0-next.0 + +### Minor Changes + +- 749ba60: Add an extension for custom Slack message layouts + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-notifications-node@0.2.24-next.0 + +## @backstage/app-defaults@1.7.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-permission-react@0.4.41-next.0 + +## @backstage/backend-app-api@1.5.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + +## @backstage/backend-defaults@0.15.3-next.0 + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- d933f62: Add configurable throttling and retry mechanism for GitLab integration. +- b99158a: Fixed `yarn backstage-cli config:check --strict --config app-config.yaml` config validation error by adding + an optional `default` type discriminator to PostgreSQL connection configuration, + allowing `config:check` to properly validate `default` connection configurations. +- 1ee5b28: Adds an alpha `MetricsService` to provide a unified interface for metrics instrumentation across Backstage plugins. +- Updated dependencies + - @backstage/cli-node@0.2.19-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-app-api@1.5.1-next.0 + - @backstage/backend-dev-utils@0.1.7 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.20 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-node@0.10.11-next.0 + +## @backstage/backend-dynamic-feature-service@0.7.10-next.0 + +### Patch Changes + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/cli-node@0.2.19-next.0 + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/plugin-catalog-backend@3.5.0-next.0 + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-app-node@0.1.43-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-events-backend@0.5.12-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + +## @backstage/backend-openapi-utils@0.6.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/backend-plugin-api@1.7.1-next.0 + +### Patch Changes + +- 1ee5b28: Adds an alpha `MetricsService` to provide a unified interface for metrics instrumentation across Backstage plugins. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + +## @backstage/backend-test-utils@1.11.1-next.0 + +### Patch Changes + +- 1ee5b28: Adds a new metrics service mock to be leveraged in tests +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-app-api@1.5.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-common@0.9.6 + +## @backstage/catalog-client@1.13.1-next.0 + +### Patch Changes + +- d2494d6: Minor update to catalog client docs +- Updated dependencies + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/filter-predicates@0.1.0 + +## @backstage/cli@0.35.5-next.0 + +### Patch Changes + +- 246877a: Updated dependency `bfj` to `^9.0.2`. + +- bba2e49: Internal refactor to use new concurrency utilities from `@backstage/cli-node`. + +- fd50cb3: Added `translations export` and `translations import` commands for managing translation files. + + The `translations export` command discovers all `TranslationRef` definitions across frontend plugin dependencies and exports their default messages as JSON files. The `translations import` command generates `TranslationResource` wiring code from translated JSON files, ready to be plugged into the app. + + Both commands support a `--pattern` option for controlling the message file layout, for example `--pattern '{lang}/{id}.json'` for language-based directory grouping. + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. + +- de62a9d: Upgraded `commander` dependency from `^12.0.0` to `^14.0.3` across all CLI packages. + +- 092b41f: Updated dependency `webpack` to `~5.105.0`. + +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/cli-node@0.2.19-next.0 + - @backstage/eslint-plugin@0.2.2-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/config-loader@1.10.9-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/module-federation-common@0.1.0 + - @backstage/release-manifests@0.0.13 + - @backstage/types@1.2.2 + +## @backstage/cli-node@0.2.19-next.0 + +### Patch Changes + +- 06c2015: Added `runConcurrentTasks` and `runWorkerQueueThreads` utilities, moved from the `@backstage/cli` internal code. +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/codemods@0.1.55-next.0 + +### Patch Changes + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- de62a9d: Upgraded `commander` dependency from `^12.0.0` to `^14.0.3` across all CLI packages. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + +## @backstage/config-loader@1.10.9-next.0 + +### Patch Changes + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/core-app-api@1.19.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + +## @backstage/core-compat-api@0.5.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-app-react@0.2.1-next.0 + +## @backstage/core-components@0.18.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.2 + - @backstage/version-bridge@1.0.12 + +## @backstage/core-plugin-api@1.12.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + +## @backstage/create-app@0.7.10-next.0 + +### Patch Changes + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- de62a9d: Upgraded `commander` dependency from `^12.0.0` to `^14.0.3` across all CLI packages. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + +## @backstage/dev-utils@1.1.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/app-defaults@1.7.6-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/theme@0.7.2 + +## @backstage/eslint-plugin@0.2.2-next.0 + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 + +## @backstage/frontend-app-api@0.15.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-defaults@0.4.1-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + +## @backstage/frontend-defaults@0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-app@0.4.1-next.0 + - @backstage/config@1.3.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-app-api@0.15.1-next.0 + +## @backstage/frontend-dynamic-feature-loader@0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/config@1.3.6 + - @backstage/module-federation-common@0.1.0 + +## @backstage/frontend-plugin-api@0.14.2-next.0 + +### Patch Changes + +- 9c81af9: Made the `pluginId` property optional in the `FrontendFeature` type, allowing plugins published against older versions of the framework to be used without type errors. +- Updated dependencies + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + +## @backstage/frontend-test-utils@0.5.1-next.0 + +### Patch Changes + +- 909c742: Switched `MockTranslationApi` and related test utility imports from `@backstage/core-plugin-api/alpha` to the stable `@backstage/frontend-plugin-api` export. The `TranslationApi` type in the API report is now sourced from a single package. This has no effect on runtime behavior. +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-app@0.4.1-next.0 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/frontend-app-api@0.15.1-next.0 + - @backstage/test-utils@1.7.16-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-app-react@0.2.1-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-react@0.4.41-next.0 + +## @backstage/integration-react@1.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/config@1.3.6 + - @backstage/core-plugin-api@1.12.4-next.0 + +## @backstage/repo-tools@0.16.6-next.0 + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- 2a51546: Fixed prettier existence checks in OpenAPI commands to use `fs.pathExists` instead of checking the resolved path string, which was always truthy. +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- de62a9d: Upgraded `commander` dependency from `^12.0.0` to `^14.0.3` across all CLI packages. +- 18a946c: Updated `@microsoft/api-extractor` to `7.57.3` and added tests for `getTsDocConfig` +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/cli-node@0.2.19-next.0 + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + +## @techdocs/cli@1.10.6-next.0 + +### Patch Changes + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- de62a9d: Upgraded `commander` dependency from `^12.0.0` to `^14.0.3` across all CLI packages. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-techdocs-node@1.14.3-next.0 + +## @backstage/test-utils@1.7.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-react@0.4.41-next.0 + +## @backstage/ui@0.12.1-next.0 + +### Patch Changes + +- a1f4bee: Made Accordion a `bg` provider so nested components like Button auto-increment their background level. Updated `useDefinition` to resolve `bg` `propDef` defaults for provider components. + +- 8909359: Fixed focus-visible outline styles for Menu and Select components. + + **Affected components:** Menu, Select + +- 0f462f8: Improved type safety in `useDefinition` by centralizing prop resolution and strengthening the `BgPropsConstraint` to require that `bg` provider components declare `children` as a required prop in their OwnProps type. + +- 8909359: Added proper cursor styles for RadioGroup items. + + **Affected components:** RadioGroup + +- Updated dependencies + - @backstage/version-bridge@1.0.12 + +## @backstage/plugin-api-docs@0.13.5-next.0 + +### Patch Changes + +- 9c9d425: Fixed invisible text in parameter input fields when using dark mode in OpenAPI definition pages +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog@1.33.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-react@0.4.41-next.0 + +## @backstage/plugin-app@0.4.1-next.0 + +### Patch Changes + +- 909c742: Switched translation API imports (`translationApiRef`, `appLanguageApiRef`) from the alpha `@backstage/core-plugin-api/alpha` path to the stable `@backstage/frontend-plugin-api` export. This has no effect on runtime behavior. +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-app-react@0.2.1-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + +## @backstage/plugin-app-backend@0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-app-node@0.1.43-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-app-node@0.1.43-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + +## @backstage/plugin-app-react@0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + +## @backstage/plugin-app-visualizer@0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + +## @backstage/plugin-auth@0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.2 + +## @backstage/plugin-auth-backend@0.27.1-next.0 + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- 619be54: Update migrations to be reversible +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-auth0-provider@0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.27.1-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-github-provider@0.5.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-google-provider@0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-guest-provider@0.2.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.4.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.27.1-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-okta-provider@0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-onelogin-provider@0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-openshift-provider@0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/plugin-auth-node@0.6.14-next.0 + +## @backstage/plugin-auth-node@0.6.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-auth-react@0.1.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-bitbucket-cloud-common@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + +## @backstage/plugin-catalog@1.33.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-react@0.4.41-next.0 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + +## @backstage/plugin-catalog-backend-module-aws@0.4.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.20 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-kubernetes-common@0.9.10 + +## @backstage/plugin-catalog-backend-module-azure@0.3.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-catalog-common@1.1.8 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.5.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-bitbucket-cloud-common@0.3.8-next.0 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.5.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-catalog-backend-module-gcp@0.3.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-kubernetes-common@0.9.10 + +## @backstage/plugin-catalog-backend-module-gerrit@0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.8 + +## @backstage/plugin-catalog-backend-module-gitea@0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.8 + +## @backstage/plugin-catalog-backend-module-github@0.12.3-next.0 + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.12.3-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab@0.8.1-next.0 + +### Patch Changes + +- d933f62: Add configurable throttling and retry mechanism for GitLab integration. +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.8.1-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.7.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/plugin-catalog-backend@3.5.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-common@0.9.6 + +## @backstage/plugin-catalog-backend-module-ldap@0.12.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + +## @backstage/plugin-catalog-backend-module-logs@0.1.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.5.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-catalog-backend-module-msgraph@0.9.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-catalog-common@1.1.8 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.6.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.13 + - @backstage/plugin-permission-common@0.9.6 + +## @backstage/plugin-catalog-graph@0.5.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-catalog-import@0.13.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-react@0.4.41-next.0 + +## @backstage/plugin-catalog-react@2.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/frontend-test-utils@0.5.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/filter-predicates@0.1.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-react@0.4.41-next.0 + +## @backstage/plugin-catalog-unprocessed-entities@0.2.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.13 + - @backstage/plugin-devtools-react@0.1.2-next.0 + +## @backstage/plugin-config-schema@0.1.78-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-devtools@0.1.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.22 + - @backstage/plugin-devtools-react@0.1.2-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + +## @backstage/plugin-devtools-backend@0.5.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-devtools-common@0.1.22 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + +## @backstage/plugin-devtools-react@0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + +## @backstage/plugin-events-backend@0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-backend-module-aws-sqs@0.4.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-backend-module-azure@0.2.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-backend-module-bitbucket-server@0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-backend-module-gerrit@0.2.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-backend-module-github@0.4.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-backend-module-gitlab@0.3.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-backend-module-google-pubsub@0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/filter-predicates@0.1.0 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-backend-module-kafka@0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-backend-test-utils@0.1.53-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-events-node@0.4.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-gateway-backend@1.1.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + +## @backstage/plugin-home@0.9.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-home-react@0.1.36-next.0 + +## @backstage/plugin-home-react@0.1.36-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + +## @backstage/plugin-kubernetes@0.12.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/plugin-kubernetes-common@0.9.10 + - @backstage/plugin-kubernetes-react@0.5.17-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + +## @backstage/plugin-kubernetes-backend@0.21.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.20 + - @backstage/types@1.2.2 + - @backstage/plugin-kubernetes-common@0.9.10 + - @backstage/plugin-kubernetes-node@0.4.2-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + +## @backstage/plugin-kubernetes-cluster@0.0.35-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/plugin-kubernetes-common@0.9.10 + - @backstage/plugin-kubernetes-react@0.5.17-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + +## @backstage/plugin-kubernetes-node@0.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/types@1.2.2 + - @backstage/plugin-kubernetes-common@0.9.10 + +## @backstage/plugin-kubernetes-react@0.5.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-kubernetes-common@0.9.10 + +## @backstage/plugin-mcp-actions-backend@0.1.10-next.0 + +### Patch Changes + +- dc81af1: Adds two new metrics to track MCP server operations and sessions. + + - `mcp.server.operation.duration`: The duration taken to process an individual MCP operation + - `mcp.server.session.duration`: The duration of the MCP session from the perspective of the server + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-mui-to-bui@0.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + +## @backstage/plugin-notifications@0.5.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.2 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-signals-react@0.0.20-next.0 + +## @backstage/plugin-notifications-backend@0.6.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-notifications-node@0.2.24-next.0 + - @backstage/plugin-signals-node@0.1.29-next.0 + +## @backstage/plugin-notifications-backend-module-email@0.3.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/integration-aws-node@0.1.20 + - @backstage/types@1.2.2 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-notifications-node@0.2.24-next.0 + +## @backstage/plugin-notifications-node@0.2.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-signals-node@0.1.29-next.0 + +## @backstage/plugin-org@0.6.50-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/plugin-catalog-common@1.1.8 + +## @backstage/plugin-org-react@0.1.48-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + +## @backstage/plugin-permission-backend@0.7.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.2.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + +## @backstage/plugin-permission-node@0.10.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-permission-common@0.9.6 + +## @backstage/plugin-permission-react@0.4.41-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/plugin-permission-common@0.9.6 + +## @backstage/plugin-proxy-backend@0.6.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-proxy-node@0.1.13-next.0 + +## @backstage/plugin-proxy-node@0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + +## @backstage/plugin-scaffolder@1.35.5-next.0 + +### Patch Changes + +- bd5b842: Added a new `ui:autoSelect` option to the EntityPicker field that controls whether an entity is automatically selected when the field loses focus. When set to `false`, the field will remain empty if the user closes it without explicitly selecting an entity, preventing unintentional selections. Defaults to `true` for backward compatibility. +- ee87720: Added back the `formFieldsApiRef` and `ScaffolderFormFieldsApi` alpha exports that were unintentionally removed. +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-react@0.4.41-next.0 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + - @backstage/plugin-scaffolder-react@1.19.8-next.0 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + +## @backstage/plugin-scaffolder-backend@3.1.4-next.0 + +### Patch Changes + +- 4e39e63: Removed unused dependencies +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.3.4-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.19-next.0 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-bitbucket-cloud-common@0.3.8-next.0 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-github@0.9.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.11.4-next.0 + +### Patch Changes + +- 5730c8e: Added `maskedAndHidden` option to `gitlab:projectVariable:create` and `publish:gitlab` action to support creating GitLab project variables that are both masked and hidden. Updated gitbeaker to version 43.8.0 for proper type support. +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-notifications-node@0.2.24-next.0 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + - @backstage/plugin-scaffolder-node-test-utils@0.3.9-next.0 + +## @backstage/plugin-scaffolder-common@1.7.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.6 + +## @backstage/plugin-scaffolder-node@0.12.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + +## @backstage/plugin-scaffolder-node-test-utils@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-test-utils@1.11.1-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + +## @backstage/plugin-scaffolder-react@1.19.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-permission-react@0.4.41-next.0 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + +## @backstage/plugin-search@1.6.2-next.0 + +### Patch Changes + +- d5eb954: Fixes the search component not registering the first search on navigate to the search page. +- Updated dependencies + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-search-common@1.2.22 + +## @backstage/plugin-search-backend@2.0.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + +## @backstage/plugin-search-backend-module-catalog@0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + +## @backstage/plugin-search-backend-module-elasticsearch@1.8.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/integration-aws-node@0.1.20 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + +## @backstage/plugin-search-backend-module-explore@0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + +## @backstage/plugin-search-backend-module-pg@0.5.53-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.3.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + +## @backstage/plugin-search-backend-module-techdocs@0.4.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-techdocs-node@1.14.3-next.0 + +## @backstage/plugin-search-backend-node@1.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-search-common@1.2.22 + +## @backstage/plugin-search-react@1.10.5-next.0 + +### Patch Changes + +- d5eb954: Fixes the search component not registering the first search on navigate to the search page. +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-search-common@1.2.22 + +## @backstage/plugin-signals@0.0.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-react@0.0.20-next.0 + +## @backstage/plugin-signals-backend@0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-signals-node@0.1.29-next.0 + +## @backstage/plugin-signals-node@0.1.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + +## @backstage/plugin-signals-react@0.0.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-techdocs@1.17.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-auth-react@0.1.25-next.0 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + +## @backstage/plugin-techdocs-addons-test-utils@2.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/plugin-catalog@1.33.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/plugin-techdocs@1.17.1-next.0 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/test-utils@1.7.16-next.0 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + +## @backstage/plugin-techdocs-backend@2.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-techdocs-node@1.14.3-next.0 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.34-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + +## @backstage/plugin-techdocs-node@1.14.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.20 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-techdocs-react@1.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-user-settings@0.9.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-react@0.0.20-next.0 + - @backstage/plugin-user-settings-common@0.1.0 + +## @backstage/plugin-user-settings-backend@0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-signals-node@0.1.29-next.0 + - @backstage/plugin-user-settings-common@0.1.0 + +## example-app@0.0.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/plugin-search@1.6.2-next.0 + - @backstage/plugin-api-docs@0.13.5-next.0 + - @backstage/cli@0.35.5-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-scaffolder@1.35.5-next.0 + - @backstage/plugin-app@0.4.1-next.0 + - @backstage/plugin-app-visualizer@0.2.1-next.0 + - @backstage/plugin-catalog@1.33.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/plugin-techdocs@1.17.1-next.0 + - @backstage/app-defaults@1.7.6-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/frontend-app-api@0.15.1-next.0 + - @backstage/frontend-defaults@0.4.1-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-app-react@0.2.1-next.0 + - @backstage/plugin-auth@0.1.6-next.0 + - @backstage/plugin-auth-react@0.1.25-next.0 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-catalog-graph@0.5.8-next.0 + - @backstage/plugin-catalog-import@0.13.11-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.27-next.0 + - @backstage/plugin-devtools@0.1.37-next.0 + - @backstage/plugin-home@0.9.3-next.0 + - @backstage/plugin-home-react@0.1.36-next.0 + - @backstage/plugin-kubernetes@0.12.17-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.35-next.0 + - @backstage/plugin-notifications@0.5.15-next.0 + - @backstage/plugin-org@0.6.50-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + - @backstage/plugin-scaffolder-react@1.19.8-next.0 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-signals@0.0.29-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.34-next.0 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + - @backstage/plugin-user-settings@0.9.1-next.0 + +## app-example-plugin@0.0.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + +## example-app-legacy@0.2.119-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/plugin-search@1.6.2-next.0 + - @backstage/plugin-api-docs@0.13.5-next.0 + - @backstage/cli@0.35.5-next.0 + - @backstage/plugin-scaffolder@1.35.5-next.0 + - @backstage/plugin-catalog@1.33.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/plugin-mui-to-bui@0.2.5-next.0 + - @backstage/plugin-techdocs@1.17.1-next.0 + - @backstage/app-defaults@1.7.6-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/frontend-app-api@0.15.1-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-auth-react@0.1.25-next.0 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-catalog-graph@0.5.8-next.0 + - @backstage/plugin-catalog-import@0.13.11-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.27-next.0 + - @backstage/plugin-devtools@0.1.37-next.0 + - @backstage/plugin-home@0.9.3-next.0 + - @backstage/plugin-home-react@0.1.36-next.0 + - @backstage/plugin-kubernetes@0.12.17-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.35-next.0 + - @backstage/plugin-notifications@0.5.15-next.0 + - @backstage/plugin-org@0.6.50-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + - @backstage/plugin-scaffolder-react@1.19.8-next.0 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-signals@0.0.29-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.34-next.0 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + - @backstage/plugin-user-settings@0.9.1-next.0 + +## example-backend@0.0.48-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/plugin-auth-backend@0.27.1-next.0 + - @backstage/plugin-catalog-backend@3.5.0-next.0 + - @backstage/plugin-scaffolder-backend@3.1.4-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-mcp-actions-backend@0.1.10-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/plugin-app-backend@0.5.12-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.5.1-next.0 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.17-next.0 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.5-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.12-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.20-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.18-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.9-next.0 + - @backstage/plugin-devtools-backend@0.5.15-next.0 + - @backstage/plugin-events-backend@0.5.12-next.0 + - @backstage/plugin-events-backend-module-google-pubsub@0.2.1-next.0 + - @backstage/plugin-kubernetes-backend@0.21.2-next.0 + - @backstage/plugin-notifications-backend@0.6.3-next.0 + - @backstage/plugin-permission-backend@0.7.10-next.0 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.17-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + - @backstage/plugin-proxy-backend@0.6.11-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.9.7-next.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.20-next.0 + - @backstage/plugin-search-backend@2.0.13-next.0 + - @backstage/plugin-search-backend-module-catalog@0.3.13-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@1.8.1-next.0 + - @backstage/plugin-search-backend-module-explore@0.3.12-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.12-next.0 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-signals-backend@0.3.13-next.0 + - @backstage/plugin-techdocs-backend@2.1.6-next.0 + +## e2e-test@0.2.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/create-app@0.7.10-next.0 + - @backstage/errors@1.2.7 + +## @internal/frontend@0.0.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + +## @internal/scaffolder@0.0.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-scaffolder-react@1.19.8-next.0 + +## techdocs-cli-embedded-app@0.2.118-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/cli@0.35.5-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog@1.33.1-next.0 + - @backstage/plugin-techdocs@1.17.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/frontend-defaults@0.4.1-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/test-utils@1.7.16-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-app-react@0.2.1-next.0 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + +## yarn-plugin-backstage@0.0.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/errors@1.2.7 + - @backstage/release-manifests@0.0.13 + +## @internal/plugin-todo-list@1.0.49-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + +## @internal/plugin-todo-list-backend@1.0.48-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 diff --git a/package.json b/package.json index a0a530adb8..a6a5ca8084 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "root", - "version": "1.48.0", + "version": "1.49.0-next.0", "backstage": { "cli": { "new": { diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index a5516a33a9..e7bfdf0c89 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/app-defaults +## 1.7.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-permission-react@0.4.41-next.0 + ## 1.7.5 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 71b5da64d7..b35830dbd1 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/app-defaults", - "version": "1.7.5", + "version": "1.7.6-next.0", "description": "Provides the default wiring of a Backstage App", "backstage": { "role": "web-library" diff --git a/packages/app-example-plugin/CHANGELOG.md b/packages/app-example-plugin/CHANGELOG.md index a23772bb3b..f0784d88d2 100644 --- a/packages/app-example-plugin/CHANGELOG.md +++ b/packages/app-example-plugin/CHANGELOG.md @@ -1,5 +1,13 @@ # app-example-plugin +## 0.0.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + ## 0.0.32 ### Patch Changes diff --git a/packages/app-example-plugin/package.json b/packages/app-example-plugin/package.json index 2bdfd931fd..93f2954355 100644 --- a/packages/app-example-plugin/package.json +++ b/packages/app-example-plugin/package.json @@ -1,6 +1,6 @@ { "name": "app-example-plugin", - "version": "0.0.32", + "version": "0.0.33-next.0", "description": "Backstage internal example plugin", "backstage": { "role": "frontend-plugin", diff --git a/packages/app-legacy/CHANGELOG.md b/packages/app-legacy/CHANGELOG.md index 2212011ecd..c0e84cb9d7 100644 --- a/packages/app-legacy/CHANGELOG.md +++ b/packages/app-legacy/CHANGELOG.md @@ -1,5 +1,49 @@ # example-app-legacy +## 0.2.119-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/plugin-search@1.6.2-next.0 + - @backstage/plugin-api-docs@0.13.5-next.0 + - @backstage/cli@0.35.5-next.0 + - @backstage/plugin-scaffolder@1.35.5-next.0 + - @backstage/plugin-catalog@1.33.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/plugin-mui-to-bui@0.2.5-next.0 + - @backstage/plugin-techdocs@1.17.1-next.0 + - @backstage/app-defaults@1.7.6-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/frontend-app-api@0.15.1-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-auth-react@0.1.25-next.0 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-catalog-graph@0.5.8-next.0 + - @backstage/plugin-catalog-import@0.13.11-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.27-next.0 + - @backstage/plugin-devtools@0.1.37-next.0 + - @backstage/plugin-home@0.9.3-next.0 + - @backstage/plugin-home-react@0.1.36-next.0 + - @backstage/plugin-kubernetes@0.12.17-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.35-next.0 + - @backstage/plugin-notifications@0.5.15-next.0 + - @backstage/plugin-org@0.6.50-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + - @backstage/plugin-scaffolder-react@1.19.8-next.0 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-signals@0.0.29-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.34-next.0 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + - @backstage/plugin-user-settings@0.9.1-next.0 + ## 0.2.118 ### Patch Changes diff --git a/packages/app-legacy/package.json b/packages/app-legacy/package.json index 6be63b7a14..4bdf649665 100644 --- a/packages/app-legacy/package.json +++ b/packages/app-legacy/package.json @@ -1,6 +1,6 @@ { "name": "example-app-legacy", - "version": "0.2.118", + "version": "0.2.119-next.0", "backstage": { "role": "frontend" }, diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index daa9908400..37e2dcfdb5 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,55 @@ # example-app +## 0.0.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/plugin-search@1.6.2-next.0 + - @backstage/plugin-api-docs@0.13.5-next.0 + - @backstage/cli@0.35.5-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-scaffolder@1.35.5-next.0 + - @backstage/plugin-app@0.4.1-next.0 + - @backstage/plugin-app-visualizer@0.2.1-next.0 + - @backstage/plugin-catalog@1.33.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/plugin-techdocs@1.17.1-next.0 + - @backstage/app-defaults@1.7.6-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/frontend-app-api@0.15.1-next.0 + - @backstage/frontend-defaults@0.4.1-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-app-react@0.2.1-next.0 + - @backstage/plugin-auth@0.1.6-next.0 + - @backstage/plugin-auth-react@0.1.25-next.0 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-catalog-graph@0.5.8-next.0 + - @backstage/plugin-catalog-import@0.13.11-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.27-next.0 + - @backstage/plugin-devtools@0.1.37-next.0 + - @backstage/plugin-home@0.9.3-next.0 + - @backstage/plugin-home-react@0.1.36-next.0 + - @backstage/plugin-kubernetes@0.12.17-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.35-next.0 + - @backstage/plugin-notifications@0.5.15-next.0 + - @backstage/plugin-org@0.6.50-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + - @backstage/plugin-scaffolder-react@1.19.8-next.0 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-signals@0.0.29-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.34-next.0 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + - @backstage/plugin-user-settings@0.9.1-next.0 + ## 0.0.32 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index b0d46befa8..7283f3064f 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.0.32", + "version": "0.0.33-next.0", "backstage": { "role": "frontend" }, diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index f1d26a7f4a..b07cb0869d 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/backend-app-api +## 1.5.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + ## 1.5.0 ### Minor Changes diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index 97e5964e39..08b12d7d6d 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-app-api", - "version": "1.5.0", + "version": "1.5.1-next.0", "description": "Core API used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index da970f1f45..9ca1fae948 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/backend-defaults +## 0.15.3-next.0 + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- d933f62: Add configurable throttling and retry mechanism for GitLab integration. +- b99158a: Fixed `yarn backstage-cli config:check --strict --config app-config.yaml` config validation error by adding + an optional `default` type discriminator to PostgreSQL connection configuration, + allowing `config:check` to properly validate `default` connection configurations. +- 1ee5b28: Adds an alpha `MetricsService` to provide a unified interface for metrics instrumentation across Backstage plugins. +- Updated dependencies + - @backstage/cli-node@0.2.19-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-app-api@1.5.1-next.0 + - @backstage/backend-dev-utils@0.1.7 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.20 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-node@0.10.11-next.0 + ## 0.15.2 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 6f705de7ba..1a7e6f7e41 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-defaults", - "version": "0.15.2", + "version": "0.15.3-next.0", "description": "Backend defaults used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index a9be5cd0ba..24c8b882db 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/backend-dynamic-feature-service +## 0.7.10-next.0 + +### Patch Changes + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/cli-node@0.2.19-next.0 + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/plugin-catalog-backend@3.5.0-next.0 + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-app-node@0.1.43-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-events-backend@0.5.12-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + ## 0.7.9 ### Patch Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index c197ad041c..2ba6bbc68f 100644 --- a/packages/backend-dynamic-feature-service/package.json +++ b/packages/backend-dynamic-feature-service/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-dynamic-feature-service", - "version": "0.7.9", + "version": "0.7.10-next.0", "description": "Backstage dynamic feature service", "backstage": { "role": "node-library" diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index 58436b3235..ce142b728e 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/backend-openapi-utils +## 0.6.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.6.6 ### Patch Changes diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index 5c6a9c5773..1f5bc417a8 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-openapi-utils", - "version": "0.6.6", + "version": "0.6.7-next.0", "description": "OpenAPI typescript support.", "backstage": { "role": "node-library" diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index 94768add54..b100b47e26 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/backend-plugin-api +## 1.7.1-next.0 + +### Patch Changes + +- 1ee5b28: Adds an alpha `MetricsService` to provide a unified interface for metrics instrumentation across Backstage plugins. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + ## 1.7.0 ### Minor Changes diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 131f24bafb..6acc8a384b 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-plugin-api", - "version": "1.7.0", + "version": "1.7.1-next.0", "description": "Core API used by Backstage backend plugins", "backstage": { "role": "node-library" diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 0b144df1da..4e9f955a83 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/backend-test-utils +## 1.11.1-next.0 + +### Patch Changes + +- 1ee5b28: Adds a new metrics service mock to be leveraged in tests +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-app-api@1.5.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-common@0.9.6 + ## 1.11.0 ### Minor Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index ee65ddbc78..a4fe84b9c0 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-test-utils", - "version": "1.11.0", + "version": "1.11.1-next.0", "description": "Test helpers library for Backstage backends", "backstage": { "role": "node-library" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 27ef95a0f3..b871e48357 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,47 @@ # example-backend +## 0.0.48-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/plugin-auth-backend@0.27.1-next.0 + - @backstage/plugin-catalog-backend@3.5.0-next.0 + - @backstage/plugin-scaffolder-backend@3.1.4-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-mcp-actions-backend@0.1.10-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/plugin-app-backend@0.5.12-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.5.1-next.0 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.17-next.0 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.5-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.12-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.20-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.18-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.9-next.0 + - @backstage/plugin-devtools-backend@0.5.15-next.0 + - @backstage/plugin-events-backend@0.5.12-next.0 + - @backstage/plugin-events-backend-module-google-pubsub@0.2.1-next.0 + - @backstage/plugin-kubernetes-backend@0.21.2-next.0 + - @backstage/plugin-notifications-backend@0.6.3-next.0 + - @backstage/plugin-permission-backend@0.7.10-next.0 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.17-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + - @backstage/plugin-proxy-backend@0.6.11-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.9.7-next.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.20-next.0 + - @backstage/plugin-search-backend@2.0.13-next.0 + - @backstage/plugin-search-backend-module-catalog@0.3.13-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@1.8.1-next.0 + - @backstage/plugin-search-backend-module-explore@0.3.12-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.12-next.0 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-signals-backend@0.3.13-next.0 + - @backstage/plugin-techdocs-backend@2.1.6-next.0 + ## 0.0.47 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index b6b97f214e..b53b1933bb 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.0.47", + "version": "0.0.48-next.0", "backstage": { "role": "backend" }, diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 563a0ef828..03f4e627b2 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/catalog-client +## 1.13.1-next.0 + +### Patch Changes + +- d2494d6: Minor update to catalog client docs +- Updated dependencies + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/filter-predicates@0.1.0 + ## 1.13.0 ### Minor Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 1a299182e8..9ca0335241 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "1.13.0", + "version": "1.13.1-next.0", "description": "An isomorphic client for the catalog backend", "backstage": { "role": "common-library" diff --git a/packages/cli-common/CHANGELOG.md b/packages/cli-common/CHANGELOG.md index c36322d60f..6f527693b4 100644 --- a/packages/cli-common/CHANGELOG.md +++ b/packages/cli-common/CHANGELOG.md @@ -1,5 +1,39 @@ # @backstage/cli-common +## 0.2.0-next.0 + +### Minor Changes + +- 56bd494: Added `targetPaths` and `findOwnPaths` as replacements for `findPaths`, with a cleaner separation between target project paths and package-relative paths. + + To migrate existing `findPaths` usage: + + ```ts + // Before + import { findPaths } from '@backstage/cli-common'; + const paths = findPaths(__dirname); + + // After — for target project paths (cwd-based): + import { targetPaths } from '@backstage/cli-common'; + // paths.targetDir → targetPaths.dir + // paths.targetRoot → targetPaths.rootDir + // paths.resolveTarget('src') → targetPaths.resolve('src') + // paths.resolveTargetRoot('yarn.lock') → targetPaths.resolveRoot('yarn.lock') + + // After — for package-relative paths: + import { findOwnPaths } from '@backstage/cli-common'; + const own = findOwnPaths(__dirname); + // paths.ownDir → own.dir + // paths.ownRoot → own.rootDir + // paths.resolveOwn('config/jest.js') → own.resolve('config/jest.js') + // paths.resolveOwnRoot('tsconfig.json') → own.resolveRoot('tsconfig.json') + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.7 + ## 0.1.18 ### Patch Changes diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index 4eae1d96a4..4e38607467 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli-common", - "version": "0.1.18", + "version": "0.2.0-next.0", "description": "Common functionality used by cli, backend, and create-app", "backstage": { "role": "node-library" diff --git a/packages/cli-node/CHANGELOG.md b/packages/cli-node/CHANGELOG.md index b960abfbac..074ba4097c 100644 --- a/packages/cli-node/CHANGELOG.md +++ b/packages/cli-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/cli-node +## 0.2.19-next.0 + +### Patch Changes + +- 06c2015: Added `runConcurrentTasks` and `runWorkerQueueThreads` utilities, moved from the `@backstage/cli` internal code. +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.2.18 ### Patch Changes diff --git a/packages/cli-node/package.json b/packages/cli-node/package.json index a4629c3de2..7d198f0a3f 100644 --- a/packages/cli-node/package.json +++ b/packages/cli-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli-node", - "version": "0.2.18", + "version": "0.2.19-next.0", "description": "Node.js library for Backstage CLIs", "backstage": { "role": "node-library" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 0fe123854c..6ccd21fb31 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/cli +## 0.35.5-next.0 + +### Patch Changes + +- 246877a: Updated dependency `bfj` to `^9.0.2`. +- bba2e49: Internal refactor to use new concurrency utilities from `@backstage/cli-node`. +- fd50cb3: Added `translations export` and `translations import` commands for managing translation files. + + The `translations export` command discovers all `TranslationRef` definitions across frontend plugin dependencies and exports their default messages as JSON files. The `translations import` command generates `TranslationResource` wiring code from translated JSON files, ready to be plugged into the app. + + Both commands support a `--pattern` option for controlling the message file layout, for example `--pattern '{lang}/{id}.json'` for language-based directory grouping. + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- de62a9d: Upgraded `commander` dependency from `^12.0.0` to `^14.0.3` across all CLI packages. +- 092b41f: Updated dependency `webpack` to `~5.105.0`. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/cli-node@0.2.19-next.0 + - @backstage/eslint-plugin@0.2.2-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/config-loader@1.10.9-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/module-federation-common@0.1.0 + - @backstage/release-manifests@0.0.13 + - @backstage/types@1.2.2 + ## 0.35.4 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 9e1d0b77f0..27a1b92471 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.35.4", + "version": "0.35.5-next.0", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index 77b4d0f9cb..38ccd0f874 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/codemods +## 0.1.55-next.0 + +### Patch Changes + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- de62a9d: Upgraded `commander` dependency from `^12.0.0` to `^14.0.3` across all CLI packages. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + ## 0.1.54 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 6a8a464a37..c531c4ee69 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/codemods", - "version": "0.1.54", + "version": "0.1.55-next.0", "description": "A collection of codemods for Backstage projects", "backstage": { "role": "cli" diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index 97831338a6..22a9f2c76f 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/config-loader +## 1.10.9-next.0 + +### Patch Changes + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 1.10.8 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 985bdef1a5..18b16787ab 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/config-loader", - "version": "1.10.8", + "version": "1.10.9-next.0", "description": "Config loading functionality used by Backstage backend, and CLI", "backstage": { "role": "node-library" diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index b9d741ea9c..42d0581f64 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-app-api +## 1.19.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + ## 1.19.5 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 242db797ad..231ee0f4c1 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-app-api", - "version": "1.19.5", + "version": "1.19.6-next.0", "description": "Core app API used by Backstage apps", "backstage": { "role": "web-library" diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index 7a45beca2d..9b6e2a4576 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/core-compat-api +## 0.5.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-app-react@0.2.1-next.0 + ## 0.5.8 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 1a03a36bd2..3a27730600 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-compat-api", - "version": "0.5.8", + "version": "0.5.9-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index a9b0d68ddf..4407f55bd7 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/core-components +## 0.18.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.2 + - @backstage/version-bridge@1.0.12 + ## 0.18.7 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 0ba7a89ceb..02f9b782c1 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-components", - "version": "0.18.7", + "version": "0.18.8-next.0", "description": "Core components used by Backstage plugins and apps", "backstage": { "role": "web-library" diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index eebb94a45f..61c5d409c2 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/core-plugin-api +## 1.12.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + ## 1.12.3 ### Patch Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 202f532b95..d199bfa8da 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-plugin-api", - "version": "1.12.3", + "version": "1.12.4-next.0", "description": "Core API used by Backstage plugins", "backstage": { "role": "web-library" diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 39c6951d85..834d31aef9 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/create-app +## 0.7.10-next.0 + +### Patch Changes + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- de62a9d: Upgraded `commander` dependency from `^12.0.0` to `^14.0.3` across all CLI packages. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + ## 0.7.9 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 4841169251..31a6e5347d 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/create-app", - "version": "0.7.9", + "version": "0.7.10-next.0", "description": "A CLI that helps you create your own Backstage app", "backstage": { "role": "cli" diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 51957793b4..0ce067fe4f 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/dev-utils +## 1.1.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/app-defaults@1.7.6-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/theme@0.7.2 + ## 1.1.20 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 0f59ec8d27..9f83d94cd1 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.1.20", + "version": "1.1.21-next.0", "description": "Utilities for developing Backstage plugins.", "backstage": { "role": "web-library" diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index 7fbdca51c4..525bc47606 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/create-app@0.7.10-next.0 + - @backstage/errors@1.2.7 + ## 0.2.37 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 1a2142d6e9..782fea0710 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,6 +1,6 @@ { "name": "e2e-test", - "version": "0.2.37", + "version": "0.2.38-next.0", "description": "E2E test for verifying Backstage packages", "backstage": { "role": "cli" diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index fa9e3d5c84..71b385bcfe 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/eslint-plugin +## 0.2.2-next.0 + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 + ## 0.2.1 ### Patch Changes diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index bfc3996ee3..89e08206ce 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/eslint-plugin", - "version": "0.2.1", + "version": "0.2.2-next.0", "description": "Backstage ESLint plugin", "publishConfig": { "access": "public" diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 0ca0df5ac2..d35e1a6dfc 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/frontend-app-api +## 0.15.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-defaults@0.4.1-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + ## 0.15.0 ### Minor Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index b3e978990c..997c3c6110 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-app-api", - "version": "0.15.0", + "version": "0.15.1-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-defaults/CHANGELOG.md b/packages/frontend-defaults/CHANGELOG.md index b157fd2907..9168af715c 100644 --- a/packages/frontend-defaults/CHANGELOG.md +++ b/packages/frontend-defaults/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/frontend-defaults +## 0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-app@0.4.1-next.0 + - @backstage/config@1.3.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-app-api@0.15.1-next.0 + ## 0.4.0 ### Minor Changes diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index 26411b6d98..92951da7c5 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-defaults", - "version": "0.4.0", + "version": "0.4.1-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-dynamic-feature-loader/CHANGELOG.md b/packages/frontend-dynamic-feature-loader/CHANGELOG.md index 1525b390d8..d29ecadc52 100644 --- a/packages/frontend-dynamic-feature-loader/CHANGELOG.md +++ b/packages/frontend-dynamic-feature-loader/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/frontend-dynamic-feature-loader +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/config@1.3.6 + - @backstage/module-federation-common@0.1.0 + ## 0.1.9 ### Patch Changes diff --git a/packages/frontend-dynamic-feature-loader/package.json b/packages/frontend-dynamic-feature-loader/package.json index 49007bdd39..36480783db 100644 --- a/packages/frontend-dynamic-feature-loader/package.json +++ b/packages/frontend-dynamic-feature-loader/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-dynamic-feature-loader", - "version": "0.1.9", + "version": "0.1.10-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-internal/CHANGELOG.md b/packages/frontend-internal/CHANGELOG.md index 958c0f3fb6..4005f83616 100644 --- a/packages/frontend-internal/CHANGELOG.md +++ b/packages/frontend-internal/CHANGELOG.md @@ -1,5 +1,14 @@ # @internal/frontend +## 0.0.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + ## 0.0.17 ### Patch Changes diff --git a/packages/frontend-internal/package.json b/packages/frontend-internal/package.json index 57b4eba729..cab3779acd 100644 --- a/packages/frontend-internal/package.json +++ b/packages/frontend-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/frontend", - "version": "0.0.17", + "version": "0.0.18-next.0", "backstage": { "role": "web-library", "inline": true diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index 7238a08512..ed6349d9e4 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/frontend-plugin-api +## 0.14.2-next.0 + +### Patch Changes + +- 9c81af9: Made the `pluginId` property optional in the `FrontendFeature` type, allowing plugins published against older versions of the framework to be used without type errors. +- Updated dependencies + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + ## 0.14.0 ### Minor Changes diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 7b4ae8f36b..5002ed98a0 100644 --- a/packages/frontend-plugin-api/package.json +++ b/packages/frontend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-plugin-api", - "version": "0.14.0", + "version": "0.14.2-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index 040cdac005..74e112c067 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/frontend-test-utils +## 0.5.1-next.0 + +### Patch Changes + +- 909c742: Switched `MockTranslationApi` and related test utility imports from `@backstage/core-plugin-api/alpha` to the stable `@backstage/frontend-plugin-api` export. The `TranslationApi` type in the API report is now sourced from a single package. This has no effect on runtime behavior. +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-app@0.4.1-next.0 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/frontend-app-api@0.15.1-next.0 + - @backstage/test-utils@1.7.16-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-app-react@0.2.1-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-react@0.4.41-next.0 + ## 0.5.0 ### Minor Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index 6e7f487654..aa35b3dff9 100644 --- a/packages/frontend-test-utils/package.json +++ b/packages/frontend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-test-utils", - "version": "0.5.0", + "version": "0.5.1-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index c5b3247473..a038cd3ac5 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/integration-react +## 1.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/config@1.3.6 + - @backstage/core-plugin-api@1.12.4-next.0 + ## 1.2.15 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index a2c4cd7e78..b8a7f6a678 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration-react", - "version": "1.2.15", + "version": "1.2.16-next.0", "description": "Frontend package for managing integrations towards external systems", "backstage": { "role": "web-library" diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 888613617f..947f559196 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/integration +## 1.21.0-next.0 + +### Minor Changes + +- d933f62: Add configurable throttling and retry mechanism for GitLab integration. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + ## 1.20.0 ### Minor Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index c3e0b07340..8f5bfd3e8b 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration", - "version": "1.20.0", + "version": "1.21.0-next.0", "description": "Helpers for managing integrations towards external systems", "backstage": { "role": "common-library" diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index 8f1d51361f..67ebd2fec6 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/repo-tools +## 0.16.6-next.0 + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- 2a51546: Fixed prettier existence checks in OpenAPI commands to use `fs.pathExists` instead of checking the resolved path string, which was always truthy. +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- de62a9d: Upgraded `commander` dependency from `^12.0.0` to `^14.0.3` across all CLI packages. +- 18a946c: Updated `@microsoft/api-extractor` to `7.57.3` and added tests for `getTsDocConfig` +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/cli-node@0.2.19-next.0 + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + ## 0.16.4 ### Patch Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index bc27ae4a42..cca2b5a86a 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/repo-tools", - "version": "0.16.4", + "version": "0.16.6-next.0", "description": "CLI for Backstage repo tooling ", "backstage": { "role": "cli" diff --git a/packages/scaffolder-internal/CHANGELOG.md b/packages/scaffolder-internal/CHANGELOG.md index 3ae462b888..13e8c5694c 100644 --- a/packages/scaffolder-internal/CHANGELOG.md +++ b/packages/scaffolder-internal/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/scaffolder +## 0.0.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-scaffolder-react@1.19.8-next.0 + ## 0.0.18 ### Patch Changes diff --git a/packages/scaffolder-internal/package.json b/packages/scaffolder-internal/package.json index d249b46969..32d77a2b31 100644 --- a/packages/scaffolder-internal/package.json +++ b/packages/scaffolder-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/scaffolder", - "version": "0.0.18", + "version": "0.0.19-next.0", "backstage": { "role": "web-library", "inline": true diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 5dab0b834b..2557e1f13b 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,26 @@ # techdocs-cli-embedded-app +## 0.2.118-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/cli@0.35.5-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog@1.33.1-next.0 + - @backstage/plugin-techdocs@1.17.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/frontend-defaults@0.4.1-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/test-utils@1.7.16-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-app-react@0.2.1-next.0 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + ## 0.2.117 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 027ab9f5a0..b903033a0d 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -1,6 +1,6 @@ { "name": "techdocs-cli-embedded-app", - "version": "0.2.117", + "version": "0.2.118-next.0", "backstage": { "role": "frontend" }, diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 6c96f392ba..ce38cf8516 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,18 @@ # @techdocs/cli +## 1.10.6-next.0 + +### Patch Changes + +- 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. +- de62a9d: Upgraded `commander` dependency from `^12.0.0` to `^14.0.3` across all CLI packages. +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-techdocs-node@1.14.3-next.0 + ## 1.10.5 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 8299e76a2f..bccab2c228 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,6 +1,6 @@ { "name": "@techdocs/cli", - "version": "1.10.5", + "version": "1.10.6-next.0", "description": "Utility CLI for managing TechDocs sites in Backstage.", "backstage": { "role": "cli" diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 6d01201f32..86a6b08bb6 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/test-utils +## 1.7.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-react@0.4.41-next.0 + ## 1.7.15 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 138b27126c..c5b4f771d2 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/test-utils", - "version": "1.7.15", + "version": "1.7.16-next.0", "description": "Utilities to test Backstage plugins and apps.", "backstage": { "role": "web-library" diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 5911274c45..d5c6a13f66 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/ui +## 0.12.1-next.0 + +### Patch Changes + +- a1f4bee: Made Accordion a `bg` provider so nested components like Button auto-increment their background level. Updated `useDefinition` to resolve `bg` `propDef` defaults for provider components. +- 8909359: Fixed focus-visible outline styles for Menu and Select components. + + **Affected components:** Menu, Select + +- 0f462f8: Improved type safety in `useDefinition` by centralizing prop resolution and strengthening the `BgPropsConstraint` to require that `bg` provider components declare `children` as a required prop in their OwnProps type. +- 8909359: Added proper cursor styles for RadioGroup items. + + **Affected components:** RadioGroup + +- Updated dependencies + - @backstage/version-bridge@1.0.12 + ## 0.12.0 ### Minor Changes diff --git a/packages/ui/package.json b/packages/ui/package.json index 39e99b4be7..e43f31c6c4 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/ui", - "version": "0.12.0", + "version": "0.12.1-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/yarn-plugin/CHANGELOG.md b/packages/yarn-plugin/CHANGELOG.md index f110bdf7d0..e5ee13b8f0 100644 --- a/packages/yarn-plugin/CHANGELOG.md +++ b/packages/yarn-plugin/CHANGELOG.md @@ -1,5 +1,14 @@ # yarn-plugin-backstage +## 0.0.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/errors@1.2.7 + - @backstage/release-manifests@0.0.13 + ## 0.0.9 ### Patch Changes diff --git a/packages/yarn-plugin/package.json b/packages/yarn-plugin/package.json index 1508d76d47..0326e3d4e9 100644 --- a/packages/yarn-plugin/package.json +++ b/packages/yarn-plugin/package.json @@ -1,6 +1,6 @@ { "name": "yarn-plugin-backstage", - "version": "0.0.9", + "version": "0.0.10-next.0", "description": "Yarn plugin for working with Backstage monorepos", "backstage": { "role": "node-library" diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index db43c0c9dd..9db7a5ed41 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-api-docs +## 0.13.5-next.0 + +### Patch Changes + +- 9c9d425: Fixed invisible text in parameter input fields when using dark mode in OpenAPI definition pages +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog@1.33.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-react@0.4.41-next.0 + ## 0.13.4 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 72562c51bd..c3f5a2c42b 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.13.4", + "version": "0.13.5-next.0", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 1750238b78..ea52804fdf 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-app-backend +## 0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-app-node@0.1.43-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.5.11 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index d38826ddc4..298208ac61 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.5.11", + "version": "0.5.12-next.0", "description": "A Backstage backend plugin that serves the Backstage frontend app", "backstage": { "role": "backend-plugin", diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index c21429e06a..9d5856ac62 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-app-node +## 0.1.43-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + ## 0.1.42 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index 3ef2f0a682..2454c3563a 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-node", - "version": "0.1.42", + "version": "0.1.43-next.0", "description": "Node.js library for the app plugin", "backstage": { "role": "node-library", diff --git a/plugins/app-react/CHANGELOG.md b/plugins/app-react/CHANGELOG.md index 75e91969e7..6374706f5c 100644 --- a/plugins/app-react/CHANGELOG.md +++ b/plugins/app-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-app-react +## 0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/app-react/package.json b/plugins/app-react/package.json index 3dc8213bc8..9c6887c1ba 100644 --- a/plugins/app-react/package.json +++ b/plugins/app-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-react", - "version": "0.2.0", + "version": "0.2.1-next.0", "description": "Web library for the app plugin", "backstage": { "role": "web-library", diff --git a/plugins/app-visualizer/CHANGELOG.md b/plugins/app-visualizer/CHANGELOG.md index 3cd61a7955..f61e2935b9 100644 --- a/plugins/app-visualizer/CHANGELOG.md +++ b/plugins/app-visualizer/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-app-visualizer +## 0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/app-visualizer/package.json b/plugins/app-visualizer/package.json index 173c1cb9f1..5353caa37e 100644 --- a/plugins/app-visualizer/package.json +++ b/plugins/app-visualizer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-visualizer", - "version": "0.2.0", + "version": "0.2.1-next.0", "description": "Visualizes the Backstage app structure", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app/CHANGELOG.md b/plugins/app/CHANGELOG.md index a13ad26fff..e4cd22f5b4 100644 --- a/plugins/app/CHANGELOG.md +++ b/plugins/app/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-app +## 0.4.1-next.0 + +### Patch Changes + +- 909c742: Switched translation API imports (`translationApiRef`, `appLanguageApiRef`) from the alpha `@backstage/core-plugin-api/alpha` path to the stable `@backstage/frontend-plugin-api` export. This has no effect on runtime behavior. +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-app-react@0.2.1-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + ## 0.4.0 ### Minor Changes diff --git a/plugins/app/package.json b/plugins/app/package.json index 780996e3b0..3572d7da28 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app", - "version": "0.4.0", + "version": "0.4.1-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "app", diff --git a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md index 88849b31dc..08e0dac595 100644 --- a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-atlassian-provider +## 0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.4.12 ### Patch Changes diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index cc5c8243b9..86a75917e8 100644 --- a/plugins/auth-backend-module-atlassian-provider/package.json +++ b/plugins/auth-backend-module-atlassian-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-atlassian-provider", - "version": "0.4.12", + "version": "0.4.13-next.0", "description": "The atlassian-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-auth0-provider/CHANGELOG.md b/plugins/auth-backend-module-auth0-provider/CHANGELOG.md index 9626a18c37..79ff975f48 100644 --- a/plugins/auth-backend-module-auth0-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-auth0-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-auth0-provider +## 0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.3.0 ### Minor Changes diff --git a/plugins/auth-backend-module-auth0-provider/package.json b/plugins/auth-backend-module-auth0-provider/package.json index 2d39019295..e1d8ef0cd8 100644 --- a/plugins/auth-backend-module-auth0-provider/package.json +++ b/plugins/auth-backend-module-auth0-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-auth0-provider", - "version": "0.3.0", + "version": "0.3.1-next.0", "description": "The auth0-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md index c510c9dcc0..ec48ad5eb2 100644 --- a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-aws-alb-provider +## 0.4.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.27.1-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.4.13 ### Patch Changes diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index d61cadd863..e86e425ccd 100644 --- a/plugins/auth-backend-module-aws-alb-provider/package.json +++ b/plugins/auth-backend-module-aws-alb-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-aws-alb-provider", - "version": "0.4.13", + "version": "0.4.14-next.0", "description": "The aws-alb provider module for the Backstage auth backend.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md index 8583ca3ff0..ae68e22115 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-azure-easyauth-provider +## 0.2.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.2.17 ### Patch Changes diff --git a/plugins/auth-backend-module-azure-easyauth-provider/package.json b/plugins/auth-backend-module-azure-easyauth-provider/package.json index 71c5c7366a..78f55326d8 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/package.json +++ b/plugins/auth-backend-module-azure-easyauth-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-azure-easyauth-provider", - "version": "0.2.17", + "version": "0.2.18-next.0", "description": "The azure-easyauth-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md index 365bbb6172..d25a7748a2 100644 --- a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-bitbucket-provider +## 0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.3.12 ### Patch Changes diff --git a/plugins/auth-backend-module-bitbucket-provider/package.json b/plugins/auth-backend-module-bitbucket-provider/package.json index 2584bdf8fa..e5368834e7 100644 --- a/plugins/auth-backend-module-bitbucket-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-bitbucket-provider", - "version": "0.3.12", + "version": "0.3.13-next.0", "description": "The bitbucket-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md index 4bee591f4e..31d965325e 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-bitbucket-server-provider +## 0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.2.12 ### Patch Changes diff --git a/plugins/auth-backend-module-bitbucket-server-provider/package.json b/plugins/auth-backend-module-bitbucket-server-provider/package.json index 7dd8b610af..dceb88d24d 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-server-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-bitbucket-server-provider", - "version": "0.2.12", + "version": "0.2.13-next.0", "description": "The bitbucket-server-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md index e5c120a1e8..22625985a7 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-cloudflare-access-provider +## 0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.4.12 ### Patch Changes diff --git a/plugins/auth-backend-module-cloudflare-access-provider/package.json b/plugins/auth-backend-module-cloudflare-access-provider/package.json index 3a318f6dd4..cb471664db 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/package.json +++ b/plugins/auth-backend-module-cloudflare-access-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-cloudflare-access-provider", - "version": "0.4.12", + "version": "0.4.13-next.0", "description": "The cloudflare-access-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index cc0568608c..1180e9a217 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-gcp-iap-provider +## 0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.4.12 ### Patch Changes diff --git a/plugins/auth-backend-module-gcp-iap-provider/package.json b/plugins/auth-backend-module-gcp-iap-provider/package.json index 029b66a509..29af42c8ae 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/package.json +++ b/plugins/auth-backend-module-gcp-iap-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-gcp-iap-provider", - "version": "0.4.12", + "version": "0.4.13-next.0", "description": "A GCP IAP auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-github-provider/CHANGELOG.md b/plugins/auth-backend-module-github-provider/CHANGELOG.md index 528a53ffc7..d864e8389f 100644 --- a/plugins/auth-backend-module-github-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-github-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-github-provider +## 0.5.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.5.0 ### Minor Changes diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index da0c22725b..e507be91f6 100644 --- a/plugins/auth-backend-module-github-provider/package.json +++ b/plugins/auth-backend-module-github-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-github-provider", - "version": "0.5.0", + "version": "0.5.1-next.0", "description": "The github-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index 11352547a4..42c087bf03 100644 --- a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-gitlab-provider +## 0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.4.0 ### Minor Changes diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index b933bb50a2..195560d8e9 100644 --- a/plugins/auth-backend-module-gitlab-provider/package.json +++ b/plugins/auth-backend-module-gitlab-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-gitlab-provider", - "version": "0.4.0", + "version": "0.4.1-next.0", "description": "The gitlab-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-google-provider/CHANGELOG.md b/plugins/auth-backend-module-google-provider/CHANGELOG.md index 4b3ee7ecb0..2d260538af 100644 --- a/plugins/auth-backend-module-google-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-google-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-google-provider +## 0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.3.12 ### Patch Changes diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index 1683c8447f..d9a355d204 100644 --- a/plugins/auth-backend-module-google-provider/package.json +++ b/plugins/auth-backend-module-google-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-google-provider", - "version": "0.3.12", + "version": "0.3.13-next.0", "description": "A Google auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-guest-provider/CHANGELOG.md b/plugins/auth-backend-module-guest-provider/CHANGELOG.md index c249a36e78..bf229cf304 100644 --- a/plugins/auth-backend-module-guest-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-guest-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-guest-provider +## 0.2.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.2.16 ### Patch Changes diff --git a/plugins/auth-backend-module-guest-provider/package.json b/plugins/auth-backend-module-guest-provider/package.json index 0cda2cd9cd..2b05b628be 100644 --- a/plugins/auth-backend-module-guest-provider/package.json +++ b/plugins/auth-backend-module-guest-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-guest-provider", - "version": "0.2.16", + "version": "0.2.17-next.0", "description": "The guest-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md index e5597b1bb2..045decf2fb 100644 --- a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-microsoft-provider +## 0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.3.12 ### Patch Changes diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index 3bdc28d423..2ad18ac6de 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-microsoft-provider", - "version": "0.3.12", + "version": "0.3.13-next.0", "description": "The microsoft-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md index 77dcbeb9e5..8b04f8824b 100644 --- a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-oauth2-provider +## 0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.4.12 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index 6dc9f68157..b2b929bb11 100644 --- a/plugins/auth-backend-module-oauth2-provider/package.json +++ b/plugins/auth-backend-module-oauth2-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-provider", - "version": "0.4.12", + "version": "0.4.13-next.0", "description": "The oauth2-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md index e22f2a783e..2053d99d83 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-oauth2-proxy-provider +## 0.2.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.2.17 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/package.json b/plugins/auth-backend-module-oauth2-proxy-provider/package.json index c1adcc93fe..299c00ea19 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/package.json +++ b/plugins/auth-backend-module-oauth2-proxy-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-proxy-provider", - "version": "0.2.17", + "version": "0.2.18-next.0", "description": "The oauth2-proxy-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md index 71936899b9..4acdfe423b 100644 --- a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-auth-backend-module-oidc-provider +## 0.4.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.27.1-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.4.13 ### Patch Changes diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 813846adcc..662c6dcd58 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oidc-provider", - "version": "0.4.13", + "version": "0.4.14-next.0", "description": "The oidc-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-okta-provider/CHANGELOG.md b/plugins/auth-backend-module-okta-provider/CHANGELOG.md index d8a1fbe809..2bca29cbde 100644 --- a/plugins/auth-backend-module-okta-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-okta-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-okta-provider +## 0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.2.12 ### Patch Changes diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index 8e7e96a930..021fc5a9cd 100644 --- a/plugins/auth-backend-module-okta-provider/package.json +++ b/plugins/auth-backend-module-okta-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-okta-provider", - "version": "0.2.12", + "version": "0.2.13-next.0", "description": "The okta-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md index 8b1b1a0474..cf8e8e89e5 100644 --- a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-onelogin-provider +## 0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.3.12 ### Patch Changes diff --git a/plugins/auth-backend-module-onelogin-provider/package.json b/plugins/auth-backend-module-onelogin-provider/package.json index bed4161c81..c36dea44be 100644 --- a/plugins/auth-backend-module-onelogin-provider/package.json +++ b/plugins/auth-backend-module-onelogin-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-onelogin-provider", - "version": "0.3.12", + "version": "0.3.13-next.0", "description": "The onelogin-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-openshift-provider/CHANGELOG.md b/plugins/auth-backend-module-openshift-provider/CHANGELOG.md index aa1dc8fb84..02ae1c6562 100644 --- a/plugins/auth-backend-module-openshift-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-openshift-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-openshift-provider +## 0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.1.4 ### Patch Changes diff --git a/plugins/auth-backend-module-openshift-provider/package.json b/plugins/auth-backend-module-openshift-provider/package.json index 35e405e9e5..40b8bdd241 100644 --- a/plugins/auth-backend-module-openshift-provider/package.json +++ b/plugins/auth-backend-module-openshift-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-openshift-provider", - "version": "0.1.4", + "version": "0.1.5-next.0", "description": "The OpenShift backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md index 9c85d5ae24..10f9e6d561 100644 --- a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-pinniped-provider +## 0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.3.11 ### Patch Changes diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index d3206effae..2d05830c78 100644 --- a/plugins/auth-backend-module-pinniped-provider/package.json +++ b/plugins/auth-backend-module-pinniped-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-pinniped-provider", - "version": "0.3.11", + "version": "0.3.12-next.0", "description": "The pinniped-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md index 35b2761818..2235daf7e0 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-vmware-cloud-provider +## 0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.5.11 ### Patch Changes diff --git a/plugins/auth-backend-module-vmware-cloud-provider/package.json b/plugins/auth-backend-module-vmware-cloud-provider/package.json index 203234d90c..d0b8034ae7 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/package.json +++ b/plugins/auth-backend-module-vmware-cloud-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-vmware-cloud-provider", - "version": "0.5.11", + "version": "0.5.12-next.0", "description": "The vmware-cloud-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 9f77de97e7..068a085520 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-auth-backend +## 0.27.1-next.0 + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- 619be54: Update migrations to be reversible +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + ## 0.27.0 ### Minor Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index da5f9c4ea6..45d17d6f6d 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.27.0", + "version": "0.27.1-next.0", "description": "A Backstage backend plugin that handles authentication", "backstage": { "role": "backend-plugin", diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index ece86309bd..651d2752d3 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-auth-node +## 0.6.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.6.13 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index c6eb92e607..440f6a4913 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.6.13", + "version": "0.6.14-next.0", "backstage": { "role": "node-library", "pluginId": "auth", diff --git a/plugins/auth-react/CHANGELOG.md b/plugins/auth-react/CHANGELOG.md index 6028f0218c..1b8da082e0 100644 --- a/plugins/auth-react/CHANGELOG.md +++ b/plugins/auth-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-react +## 0.1.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + ## 0.1.24 ### Patch Changes diff --git a/plugins/auth-react/package.json b/plugins/auth-react/package.json index f61542d1a3..9466e09e19 100644 --- a/plugins/auth-react/package.json +++ b/plugins/auth-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-react", - "version": "0.1.24", + "version": "0.1.25-next.0", "description": "Web library for the auth plugin", "backstage": { "role": "web-library", diff --git a/plugins/auth/CHANGELOG.md b/plugins/auth/CHANGELOG.md index e2e1da46d0..469964a57c 100644 --- a/plugins/auth/CHANGELOG.md +++ b/plugins/auth/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth +## 0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.2 + ## 0.1.5 ### Patch Changes diff --git a/plugins/auth/package.json b/plugins/auth/package.json index 23484cc5e5..02d5073f2b 100644 --- a/plugins/auth/package.json +++ b/plugins/auth/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth", - "version": "0.1.5", + "version": "0.1.6-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "auth", diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index 134a370bda..9ad8a14276 100644 --- a/plugins/bitbucket-cloud-common/CHANGELOG.md +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-bitbucket-cloud-common +## 0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index e40040081a..41f7f8b49e 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bitbucket-cloud-common", - "version": "0.3.7", + "version": "0.3.8-next.0", "description": "Common functionalities for bitbucket-cloud plugins", "backstage": { "role": "common-library", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 84d0ffe183..104db8bed8 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.4.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.20 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-kubernetes-common@0.9.10 + ## 0.4.20 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index efcb7f76cd..657190a2ac 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", - "version": "0.4.20", + "version": "0.4.21-next.0", "description": "A Backstage catalog backend module that helps integrate towards AWS", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index cd350fb453..71ffc6eb5f 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.3.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-catalog-common@1.1.8 + ## 0.3.14 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index bb5291ef9a..c73d71073a 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", - "version": "0.3.14", + "version": "0.3.15-next.0", "description": "A Backstage catalog backend module that helps integrate towards Azure", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md index b75a3831e4..44454b6645 100644 --- a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-backstage-openapi +## 0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + ## 0.5.11 ### Patch Changes diff --git a/plugins/catalog-backend-module-backstage-openapi/package.json b/plugins/catalog-backend-module-backstage-openapi/package.json index 948e0f0cdf..69a41a3a2a 100644 --- a/plugins/catalog-backend-module-backstage-openapi/package.json +++ b/plugins/catalog-backend-module-backstage-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-backstage-openapi", - "version": "0.5.11", + "version": "0.5.12-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index b1acf9790a..570ed41256 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.5.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-bitbucket-cloud-common@0.3.8-next.0 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.5.8 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index ebfd1104bd..1f7bd194be 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", - "version": "0.5.8", + "version": "0.5.9-next.0", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index 925debc5c0..92bb46d3c0 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.5.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.5.8 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index fb0df6172c..8f4e3b97b5 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-server", - "version": "0.5.8", + "version": "0.5.9-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index d1e8b8cc13..210468d544 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.3.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-kubernetes-common@0.9.10 + ## 0.3.16 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index 446769a5df..51c6791c36 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", - "version": "0.3.16", + "version": "0.3.17-next.0", "description": "A Backstage catalog backend module that helps integrate towards GCP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index 3eaee80cc6..6a14e2fa96 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.8 + ## 0.3.11 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 72d28a5f13..e2b12b1872 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gerrit", - "version": "0.3.11", + "version": "0.3.12-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gitea/CHANGELOG.md b/plugins/catalog-backend-module-gitea/CHANGELOG.md index 92dd71ee81..b7c152b7ec 100644 --- a/plugins/catalog-backend-module-gitea/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-gitea +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.8 + ## 0.1.9 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitea/package.json b/plugins/catalog-backend-module-gitea/package.json index 044f074c9e..9d23fa2ca0 100644 --- a/plugins/catalog-backend-module-gitea/package.json +++ b/plugins/catalog-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitea", - "version": "0.1.9", + "version": "0.1.10-next.0", "description": "The gitea backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index f537919ae8..fd442dab13 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.3.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.12.3-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.3.19 ### Patch Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index b302739caf..be85ed65a3 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", - "version": "0.3.19", + "version": "0.3.20-next.0", "description": "The github-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index d0d993a928..586ea98c43 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-github +## 0.12.3-next.0 + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.12.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 5787333e52..fdef1a8aab 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github", - "version": "0.12.2", + "version": "0.12.3-next.0", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index 8826f23fe9..3584e8ce8c 100644 --- a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-gitlab-org +## 0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.8.1-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.2.18 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index ff0cd55ce0..19d2bd5aa4 100644 --- a/plugins/catalog-backend-module-gitlab-org/package.json +++ b/plugins/catalog-backend-module-gitlab-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab-org", - "version": "0.2.18", + "version": "0.2.19-next.0", "description": "The gitlab-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 2bfa7c354c..e8ee9a0b17 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.8.1-next.0 + +### Patch Changes + +- d933f62: Add configurable throttling and retry mechanism for GitLab integration. +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.8.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index b0dad1bef8..88661729b2 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", - "version": "0.8.0", + "version": "0.8.1-next.0", "description": "A Backstage catalog backend module that helps integrate towards GitLab", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index c7d4c8b3ae..c30dc39d18 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.7.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/plugin-catalog-backend@3.5.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-common@0.9.6 + ## 0.7.9 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index 5ad2ecf499..d03ac523c3 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", - "version": "0.7.9", + "version": "0.7.10-next.0", "description": "An entity provider for streaming large asset sources into the catalog", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 5db5d368e0..b9959ad209 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.12.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + ## 0.12.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index cf3e12c297..9b1ff77957 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", - "version": "0.12.2", + "version": "0.12.3-next.0", "description": "A Backstage catalog backend module that helps integrate towards LDAP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-logs/CHANGELOG.md b/plugins/catalog-backend-module-logs/CHANGELOG.md index 04b21b02b8..9520d32d3f 100644 --- a/plugins/catalog-backend-module-logs/CHANGELOG.md +++ b/plugins/catalog-backend-module-logs/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-logs +## 0.1.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.5.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.1.19 ### Patch Changes diff --git a/plugins/catalog-backend-module-logs/package.json b/plugins/catalog-backend-module-logs/package.json index 5bd890359c..d3c65991a2 100644 --- a/plugins/catalog-backend-module-logs/package.json +++ b/plugins/catalog-backend-module-logs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-logs", - "version": "0.1.19", + "version": "0.1.20-next.0", "description": "A module that subscribes to catalog related events and logs them.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 82a20987bc..5e525e9f0e 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.9.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-catalog-common@1.1.8 + ## 0.9.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 4791527b95..14ccfbef18 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", - "version": "0.9.0", + "version": "0.9.1-next.0", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index f985321a83..69cd786a0a 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + ## 0.2.19 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 42650d9e8e..2ab5d94ff6 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", - "version": "0.2.19", + "version": "0.2.20-next.0", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index ed5a970212..28d5bec889 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.2.19 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index bdeee6b26e..8ae9637017 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", - "version": "0.2.19", + "version": "0.2.20-next.0", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index 7ddee70b70..c5b40cc991 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.2.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + ## 0.2.17 ### Patch Changes diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/package.json b/plugins/catalog-backend-module-scaffolder-entity-model/package.json index 0dcc437ef9..df180ee6bc 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", - "version": "0.2.17", + "version": "0.2.18-next.0", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 748c191e84..31f4f425fa 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.6.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.13 + - @backstage/plugin-permission-common@0.9.6 + ## 0.6.8 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index 2c1ea51ce4..748c6a081e 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", - "version": "0.6.8", + "version": "0.6.9-next.0", "description": "Backstage Catalog module to view unprocessed entities", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 4431c15402..cbca2a2b92 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-catalog-backend +## 3.5.0-next.0 + +### Minor Changes + +- bf71677: Added opentelemetry metrics for SCM events: + + - `catalog.events.scm.messages` with attribute `eventType`: Counter for the number of SCM events actually received by the catalog backend. The `eventType` is currently either `location` or `repository`. + +### Patch Changes + +- 6738cf0: build(deps): bump `minimatch` from 9.0.5 to 10.2.1 +- fbf382f: Minor internal optimisation +- 1ee5b28: Migrates existing catalog metrics to use the alpha MetricsService. This release is a 1:1 migration with no breaking changes. +- 3181973: Changed the `search` table foreign key to point to `final_entities` instead of `refresh_state` +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/filter-predicates@0.1.0 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + ## 3.4.0 ### Minor Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 8cb7b8f64d..b1f0175c81 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "3.4.0", + "version": "3.5.0-next.0", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index e9cfd3fc59..80fcb546b8 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-graph +## 0.5.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/types@1.2.2 + ## 0.5.7 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index e6cef531e1..9b94d69113 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.5.7", + "version": "0.5.8-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-graph", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index f0bc160164..139b565af2 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-import +## 0.13.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-react@0.4.41-next.0 + ## 0.13.10 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index a481fe889c..52b8379ed5 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.13.10", + "version": "0.13.11-next.0", "description": "A Backstage plugin the helps you import entities into your catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 1e2d35507f..22db08b9b9 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-catalog-node +## 2.1.0-next.0 + +### Minor Changes + +- bf71677: Added the ability for SCM events subscribers to mark the fact that they have taken actions based on events, which produces output metrics: + + - `catalog.events.scm.actions` with attribute `action`: Counter for the number of actions actually taken by catalog internals or other subscribers, based on SCM events. The `action` is currently either `create`, `delete`, `refresh`, or `move`. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/backend-test-utils@1.11.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + ## 2.0.0 ### Minor Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index b06bd8ca08..9d37ae31ed 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-node", - "version": "2.0.0", + "version": "2.1.0-next.0", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", "backstage": { "role": "node-library", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 903ce74e04..550c9616ec 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-catalog-react +## 2.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/frontend-test-utils@0.5.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/filter-predicates@0.1.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-react@0.4.41-next.0 + ## 2.0.0 ### Minor Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 9fbd58a3b5..c383c52691 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "2.0.0", + "version": "2.0.1-next.0", "description": "A frontend library that helps other Backstage plugins interact with the catalog", "backstage": { "role": "web-library", diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index 5c3516a3a9..3a5f49f5a9 100644 --- a/plugins/catalog-unprocessed-entities/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-unprocessed-entities +## 0.2.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.13 + - @backstage/plugin-devtools-react@0.1.2-next.0 + ## 0.2.26 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index 3485342d4d..2efd04a781 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-unprocessed-entities", - "version": "0.2.26", + "version": "0.2.27-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-unprocessed-entities", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index d68a9a4209..8242b7f125 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog +## 1.33.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-react@0.4.41-next.0 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + ## 1.33.0 ### Minor Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index b97267d8d4..f04e147638 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.33.0", + "version": "1.33.1-next.0", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index 95fbfd9d0f..b617811885 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-config-schema +## 0.1.78-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.1.77 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index d9a16c4bb7..c643d6b1f4 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-config-schema", - "version": "0.1.77", + "version": "0.1.78-next.0", "description": "A Backstage plugin that lets you browse the configuration schema of your app", "backstage": { "role": "frontend-plugin", diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index eedd6300de..77400b17ed 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-devtools-backend +## 0.5.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.2.0-next.0 + - @backstage/config-loader@1.10.9-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-devtools-common@0.1.22 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + ## 0.5.14 ### Patch Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index 69d87c54fc..3b025b052a 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.5.14", + "version": "0.5.15-next.0", "backstage": { "role": "backend-plugin", "pluginId": "devtools", diff --git a/plugins/devtools-react/CHANGELOG.md b/plugins/devtools-react/CHANGELOG.md index bf8751ffe8..799bbafdda 100644 --- a/plugins/devtools-react/CHANGELOG.md +++ b/plugins/devtools-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-devtools-react +## 0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + ## 0.1.1 ### Patch Changes diff --git a/plugins/devtools-react/package.json b/plugins/devtools-react/package.json index cf6d34b466..0b9e40187f 100644 --- a/plugins/devtools-react/package.json +++ b/plugins/devtools-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-react", - "version": "0.1.1", + "version": "0.1.2-next.0", "description": "Web library for the devtools plugin", "backstage": { "role": "web-library", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index c63d7850a3..b62339220b 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-devtools +## 0.1.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.22 + - @backstage/plugin-devtools-react@0.1.2-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + ## 0.1.36 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index b03eef9dcb..61160edb46 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.36", + "version": "0.1.37-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "devtools", diff --git a/plugins/events-backend-module-aws-sqs/CHANGELOG.md b/plugins/events-backend-module-aws-sqs/CHANGELOG.md index 5cd2e5eac7..2125e080b3 100644 --- a/plugins/events-backend-module-aws-sqs/CHANGELOG.md +++ b/plugins/events-backend-module-aws-sqs/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend-module-aws-sqs +## 0.4.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.4.19 ### Patch Changes diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index 3e6cdb4b57..dd704866a7 100644 --- a/plugins/events-backend-module-aws-sqs/package.json +++ b/plugins/events-backend-module-aws-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-aws-sqs", - "version": "0.4.19", + "version": "0.4.20-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-azure/CHANGELOG.md b/plugins/events-backend-module-azure/CHANGELOG.md index 8191e4faca..27b8884997 100644 --- a/plugins/events-backend-module-azure/CHANGELOG.md +++ b/plugins/events-backend-module-azure/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-azure +## 0.2.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.2.28 ### Patch Changes diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index 5715981263..cadcc17085 100644 --- a/plugins/events-backend-module-azure/package.json +++ b/plugins/events-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-azure", - "version": "0.2.28", + "version": "0.2.29-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md index e83960dc3f..ec5c6f7e51 100644 --- a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-bitbucket-cloud +## 0.2.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.2.28 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index ddd6c64471..85ea3db464 100644 --- a/plugins/events-backend-module-bitbucket-cloud/package.json +++ b/plugins/events-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-cloud", - "version": "0.2.28", + "version": "0.2.29-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-bitbucket-server/CHANGELOG.md b/plugins/events-backend-module-bitbucket-server/CHANGELOG.md index 79ae4419f4..66c8523ed0 100644 --- a/plugins/events-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-bitbucket-server +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.1.9 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-server/package.json b/plugins/events-backend-module-bitbucket-server/package.json index 83d8d470b6..94e6c9f280 100644 --- a/plugins/events-backend-module-bitbucket-server/package.json +++ b/plugins/events-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-server", - "version": "0.1.9", + "version": "0.1.10-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-gerrit/CHANGELOG.md b/plugins/events-backend-module-gerrit/CHANGELOG.md index b3eb366a87..56033f47e8 100644 --- a/plugins/events-backend-module-gerrit/CHANGELOG.md +++ b/plugins/events-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-gerrit +## 0.2.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.2.28 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 7dfff9961e..9d28dc1dc6 100644 --- a/plugins/events-backend-module-gerrit/package.json +++ b/plugins/events-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gerrit", - "version": "0.2.28", + "version": "0.2.29-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-github/CHANGELOG.md b/plugins/events-backend-module-github/CHANGELOG.md index c7656fce90..99906c12cf 100644 --- a/plugins/events-backend-module-github/CHANGELOG.md +++ b/plugins/events-backend-module-github/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-events-backend-module-github +## 0.4.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.4.9 ### Patch Changes diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index b1bb43b902..79e4bf6ec1 100644 --- a/plugins/events-backend-module-github/package.json +++ b/plugins/events-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-github", - "version": "0.4.9", + "version": "0.4.10-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-gitlab/CHANGELOG.md b/plugins/events-backend-module-gitlab/CHANGELOG.md index eff3f72ec6..c81c9a244e 100644 --- a/plugins/events-backend-module-gitlab/CHANGELOG.md +++ b/plugins/events-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-gitlab +## 0.3.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.3.9 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index 3ed8e2eefc..1f05d6de5a 100644 --- a/plugins/events-backend-module-gitlab/package.json +++ b/plugins/events-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gitlab", - "version": "0.3.9", + "version": "0.3.10-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-google-pubsub/CHANGELOG.md b/plugins/events-backend-module-google-pubsub/CHANGELOG.md index c3e7a19b4f..1bbf060b95 100644 --- a/plugins/events-backend-module-google-pubsub/CHANGELOG.md +++ b/plugins/events-backend-module-google-pubsub/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-events-backend-module-google-pubsub +## 0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/filter-predicates@0.1.0 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/events-backend-module-google-pubsub/package.json b/plugins/events-backend-module-google-pubsub/package.json index b9fc748895..29ed2f230b 100644 --- a/plugins/events-backend-module-google-pubsub/package.json +++ b/plugins/events-backend-module-google-pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-google-pubsub", - "version": "0.2.0", + "version": "0.2.1-next.0", "description": "The google-pubsub backend module for the events plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/events-backend-module-kafka/CHANGELOG.md b/plugins/events-backend-module-kafka/CHANGELOG.md index 3e1e527d48..8b601a3f3b 100644 --- a/plugins/events-backend-module-kafka/CHANGELOG.md +++ b/plugins/events-backend-module-kafka/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend-module-kafka +## 0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.3.1 ### Patch Changes diff --git a/plugins/events-backend-module-kafka/package.json b/plugins/events-backend-module-kafka/package.json index 2e795242cf..cb2c1fcde6 100644 --- a/plugins/events-backend-module-kafka/package.json +++ b/plugins/events-backend-module-kafka/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-kafka", - "version": "0.3.1", + "version": "0.3.2-next.0", "description": "The kafka backend module for the events plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/events-backend-test-utils/CHANGELOG.md b/plugins/events-backend-test-utils/CHANGELOG.md index 9bf8093c1a..4e48326968 100644 --- a/plugins/events-backend-test-utils/CHANGELOG.md +++ b/plugins/events-backend-test-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-backend-test-utils +## 0.1.53-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.1.52 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index 9fefe66a44..e5ea9a9e90 100644 --- a/plugins/events-backend-test-utils/package.json +++ b/plugins/events-backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-test-utils", - "version": "0.1.52", + "version": "0.1.53-next.0", "description": "The plugin-events-backend-test-utils for @backstage/plugin-events-node", "backstage": { "role": "node-library", diff --git a/plugins/events-backend/CHANGELOG.md b/plugins/events-backend/CHANGELOG.md index 9b534b57c7..4a60681d58 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-events-backend +## 0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.5.11 ### Patch Changes diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index c4faa91b99..4642f4e92d 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend", - "version": "0.5.11", + "version": "0.5.12-next.0", "backstage": { "role": "backend-plugin", "pluginId": "events", diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index 01834edfb4..8db15b5573 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-node +## 0.4.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.4.19 ### Patch Changes diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index b7dfaa6f00..4bec93ecee 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-node", - "version": "0.4.19", + "version": "0.4.20-next.0", "description": "The plugin-events-node module for @backstage/plugin-events-backend", "backstage": { "role": "node-library", diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index 656a72b24b..2859e0ee97 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/plugin-todo-list-backend +## 1.0.48-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + ## 1.0.47 ### Patch Changes diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index e59787ada2..f2f3d4bba9 100644 --- a/plugins/example-todo-list-backend/package.json +++ b/plugins/example-todo-list-backend/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-backend", - "version": "1.0.47", + "version": "1.0.48-next.0", "backstage": { "role": "backend-plugin", "pluginId": "todo-list", diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index 70f667b7bb..7a17541264 100644 --- a/plugins/example-todo-list/CHANGELOG.md +++ b/plugins/example-todo-list/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/plugin-todo-list +## 1.0.49-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + ## 1.0.48 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index d671bf504a..d6065a1f9c 100644 --- a/plugins/example-todo-list/package.json +++ b/plugins/example-todo-list/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list", - "version": "1.0.48", + "version": "1.0.49-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "todo-list", diff --git a/plugins/gateway-backend/CHANGELOG.md b/plugins/gateway-backend/CHANGELOG.md index 9bd5f6699e..98cec7069e 100644 --- a/plugins/gateway-backend/CHANGELOG.md +++ b/plugins/gateway-backend/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-gateway-backend +## 1.1.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + ## 1.1.2 ### Patch Changes diff --git a/plugins/gateway-backend/package.json b/plugins/gateway-backend/package.json index 8f600ebf02..ef35c22f46 100644 --- a/plugins/gateway-backend/package.json +++ b/plugins/gateway-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gateway-backend", - "version": "1.1.2", + "version": "1.1.3-next.0", "backstage": { "role": "backend-plugin", "pluginId": "gateway", diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md index 5817aa34cd..d80608269d 100644 --- a/plugins/home-react/CHANGELOG.md +++ b/plugins/home-react/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-home-react +## 0.1.36-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + ## 0.1.35 ### Patch Changes diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index 419bf9b770..682beb6edd 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home-react", - "version": "0.1.35", + "version": "0.1.36-next.0", "description": "A Backstage plugin that contains react components helps you build a home page", "backstage": { "role": "web-library", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index ecfd1ea38e..045b8737a4 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-home +## 0.9.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-compat-api@0.5.9-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-home-react@0.1.36-next.0 + ## 0.9.2 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index 561ed37309..4c38991714 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.9.2", + "version": "0.9.3-next.0", "description": "A Backstage plugin that helps you build a home page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 6d6b537cfd..4886067c19 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-kubernetes-backend +## 0.21.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.20 + - @backstage/types@1.2.2 + - @backstage/plugin-kubernetes-common@0.9.10 + - @backstage/plugin-kubernetes-node@0.4.2-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + ## 0.21.1 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index dc65304fb9..d773535a99 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.21.1", + "version": "0.21.2-next.0", "description": "A Backstage backend plugin that integrates towards Kubernetes", "backstage": { "role": "backend-plugin", diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index 94f9e3ee52..38dd2155a5 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.35-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/plugin-kubernetes-common@0.9.10 + - @backstage/plugin-kubernetes-react@0.5.17-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + ## 0.0.34 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 4455308bf4..876986e001 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-cluster", - "version": "0.0.34", + "version": "0.0.35-next.0", "description": "A Backstage plugin that shows details of Kubernetes clusters", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index 0c8087afaf..f902343fae 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kubernetes-node +## 0.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/types@1.2.2 + - @backstage/plugin-kubernetes-common@0.9.10 + ## 0.4.1 ### Patch Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index b56016e044..6b4ff1650d 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-node", - "version": "0.4.1", + "version": "0.4.2-next.0", "description": "Node.js library for the kubernetes plugin", "backstage": { "role": "node-library", diff --git a/plugins/kubernetes-react/CHANGELOG.md b/plugins/kubernetes-react/CHANGELOG.md index a1f5092472..bc698a616c 100644 --- a/plugins/kubernetes-react/CHANGELOG.md +++ b/plugins/kubernetes-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-react +## 0.5.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-kubernetes-common@0.9.10 + ## 0.5.16 ### Patch Changes diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index d4b61ef75a..abed58b6e7 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-react", - "version": "0.5.16", + "version": "0.5.17-next.0", "description": "Web library for the kubernetes-react plugin", "backstage": { "role": "web-library", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 01779f7b6e..8e4cd54658 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-kubernetes +## 0.12.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/plugin-kubernetes-common@0.9.10 + - @backstage/plugin-kubernetes-react@0.5.17-next.0 + - @backstage/plugin-permission-react@0.4.41-next.0 + ## 0.12.16 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 92e2c83244..b43665e711 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.12.16", + "version": "0.12.17-next.0", "description": "A Backstage plugin that integrates towards Kubernetes", "backstage": { "role": "frontend-plugin", diff --git a/plugins/mcp-actions-backend/CHANGELOG.md b/plugins/mcp-actions-backend/CHANGELOG.md index 87e5def870..9cccc03c2f 100644 --- a/plugins/mcp-actions-backend/CHANGELOG.md +++ b/plugins/mcp-actions-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-mcp-actions-backend +## 0.1.10-next.0 + +### Patch Changes + +- dc81af1: Adds two new metrics to track MCP server operations and sessions. + + - `mcp.server.operation.duration`: The duration taken to process an individual MCP operation + - `mcp.server.session.duration`: The duration of the MCP session from the perspective of the server + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.1.9 ### Patch Changes diff --git a/plugins/mcp-actions-backend/package.json b/plugins/mcp-actions-backend/package.json index 71129e8589..6d66add868 100644 --- a/plugins/mcp-actions-backend/package.json +++ b/plugins/mcp-actions-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-mcp-actions-backend", - "version": "0.1.9", + "version": "0.1.10-next.0", "backstage": { "role": "backend-plugin", "pluginId": "mcp-actions", diff --git a/plugins/mui-to-bui/CHANGELOG.md b/plugins/mui-to-bui/CHANGELOG.md index ad24084330..6164b75fd5 100644 --- a/plugins/mui-to-bui/CHANGELOG.md +++ b/plugins/mui-to-bui/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-mui-to-bui +## 0.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.12.1-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + ## 0.2.4 ### Patch Changes diff --git a/plugins/mui-to-bui/package.json b/plugins/mui-to-bui/package.json index de5334e46c..c9d563aa94 100644 --- a/plugins/mui-to-bui/package.json +++ b/plugins/mui-to-bui/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-mui-to-bui", - "version": "0.2.4", + "version": "0.2.5-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "mui-to-bui", diff --git a/plugins/notifications-backend-module-email/CHANGELOG.md b/plugins/notifications-backend-module-email/CHANGELOG.md index 8032c0448a..d39ec31c88 100644 --- a/plugins/notifications-backend-module-email/CHANGELOG.md +++ b/plugins/notifications-backend-module-email/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-notifications-backend-module-email +## 0.3.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/integration-aws-node@0.1.20 + - @backstage/types@1.2.2 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-notifications-node@0.2.24-next.0 + ## 0.3.18 ### Patch Changes diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index cf57cf11bd..515cbe6409 100644 --- a/plugins/notifications-backend-module-email/package.json +++ b/plugins/notifications-backend-module-email/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend-module-email", - "version": "0.3.18", + "version": "0.3.19-next.0", "description": "The email backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend-module-slack/CHANGELOG.md b/plugins/notifications-backend-module-slack/CHANGELOG.md index 39894a62fb..19ab1967ef 100644 --- a/plugins/notifications-backend-module-slack/CHANGELOG.md +++ b/plugins/notifications-backend-module-slack/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-notifications-backend-module-slack +## 0.4.0-next.0 + +### Minor Changes + +- 749ba60: Add an extension for custom Slack message layouts + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-notifications-node@0.2.24-next.0 + ## 0.3.1 ### Patch Changes diff --git a/plugins/notifications-backend-module-slack/package.json b/plugins/notifications-backend-module-slack/package.json index 66a9fe6d6f..f8ced6fce5 100644 --- a/plugins/notifications-backend-module-slack/package.json +++ b/plugins/notifications-backend-module-slack/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend-module-slack", - "version": "0.3.1", + "version": "0.4.0-next.0", "description": "The slack backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend/CHANGELOG.md b/plugins/notifications-backend/CHANGELOG.md index 683d823808..903420657e 100644 --- a/plugins/notifications-backend/CHANGELOG.md +++ b/plugins/notifications-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-notifications-backend +## 0.6.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-notifications-node@0.2.24-next.0 + - @backstage/plugin-signals-node@0.1.29-next.0 + ## 0.6.2 ### Patch Changes diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index be863ed61f..0aebccc5de 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend", - "version": "0.6.2", + "version": "0.6.3-next.0", "backstage": { "role": "backend-plugin", "pluginId": "notifications", diff --git a/plugins/notifications-node/CHANGELOG.md b/plugins/notifications-node/CHANGELOG.md index db2f7c5329..1b569a1493 100644 --- a/plugins/notifications-node/CHANGELOG.md +++ b/plugins/notifications-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-notifications-node +## 0.2.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-signals-node@0.1.29-next.0 + ## 0.2.23 ### Patch Changes diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json index b1cc1ad49f..87f62a7e3b 100644 --- a/plugins/notifications-node/package.json +++ b/plugins/notifications-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-node", - "version": "0.2.23", + "version": "0.2.24-next.0", "description": "Node.js library for the notifications plugin", "backstage": { "role": "node-library", diff --git a/plugins/notifications/CHANGELOG.md b/plugins/notifications/CHANGELOG.md index 49b4f3b174..b838b58087 100644 --- a/plugins/notifications/CHANGELOG.md +++ b/plugins/notifications/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-notifications +## 0.5.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.2 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-signals-react@0.0.20-next.0 + ## 0.5.14 ### Patch Changes diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index 37beefdab5..891b7f38bc 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications", - "version": "0.5.14", + "version": "0.5.15-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "notifications", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index d8963590c5..3f468b6339 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org-react +## 0.1.48-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + ## 0.1.47 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index d96e1b9efe..cc51eb5c1c 100644 --- a/plugins/org-react/package.json +++ b/plugins/org-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org-react", - "version": "0.1.47", + "version": "0.1.48-next.0", "backstage": { "role": "web-library", "pluginId": "org", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index ecdcefeaa5..a77e649b38 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-org +## 0.6.50-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/plugin-catalog-common@1.1.8 + ## 0.6.49 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 9de7bf3536..62a4c9fd1b 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.49", + "version": "0.6.50-next.0", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin", diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index a097f7e4ac..90291ecb49 100644 --- a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md +++ b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-permission-backend-module-allow-all-policy +## 0.2.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + ## 0.2.16 ### Patch Changes diff --git a/plugins/permission-backend-module-policy-allow-all/package.json b/plugins/permission-backend-module-policy-allow-all/package.json index 01cd3cb2e8..9038d05635 100644 --- a/plugins/permission-backend-module-policy-allow-all/package.json +++ b/plugins/permission-backend-module-policy-allow-all/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend-module-allow-all-policy", - "version": "0.2.16", + "version": "0.2.17-next.0", "description": "Allow all policy backend module for the permission plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 3d4730cfbd..53754d5674 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-permission-backend +## 0.7.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + ## 0.7.9 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index 9d1bbcfe1f..9a79b28680 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend", - "version": "0.7.9", + "version": "0.7.10-next.0", "backstage": { "role": "backend-plugin", "pluginId": "permission", diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index 4a6d9f7086..6105e5af1d 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-permission-node +## 0.10.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-permission-common@0.9.6 + ## 0.10.10 ### Patch Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index b9b992cb14..2d9372559b 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-node", - "version": "0.10.10", + "version": "0.10.11-next.0", "description": "Common permission and authorization utilities for backend plugins", "backstage": { "role": "node-library", diff --git a/plugins/permission-react/CHANGELOG.md b/plugins/permission-react/CHANGELOG.md index 5ffd696728..ccdb6b5e22 100644 --- a/plugins/permission-react/CHANGELOG.md +++ b/plugins/permission-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-permission-react +## 0.4.41-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/plugin-permission-common@0.9.6 + ## 0.4.40 ### Patch Changes diff --git a/plugins/permission-react/package.json b/plugins/permission-react/package.json index 3c62e2c853..eb98150e64 100644 --- a/plugins/permission-react/package.json +++ b/plugins/permission-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-react", - "version": "0.4.40", + "version": "0.4.41-next.0", "backstage": { "role": "web-library", "pluginId": "permission", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index c8d4e78489..c98b874891 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-proxy-backend +## 0.6.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-proxy-node@0.1.13-next.0 + ## 0.6.10 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index f813c858d4..e1485befc7 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.6.10", + "version": "0.6.11-next.0", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", "backstage": { "role": "backend-plugin", diff --git a/plugins/proxy-node/CHANGELOG.md b/plugins/proxy-node/CHANGELOG.md index 0a88dfc7fe..e27731f61c 100644 --- a/plugins/proxy-node/CHANGELOG.md +++ b/plugins/proxy-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-proxy-node +## 0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + ## 0.1.12 ### Patch Changes diff --git a/plugins/proxy-node/package.json b/plugins/proxy-node/package.json index 378af6e3ff..e3da75c78c 100644 --- a/plugins/proxy-node/package.json +++ b/plugins/proxy-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-node", - "version": "0.1.12", + "version": "0.1.13-next.0", "description": "The plugin-proxy-node module for @backstage/plugin-proxy-backend", "backstage": { "role": "node-library", diff --git a/plugins/scaffolder-backend-module-azure/CHANGELOG.md b/plugins/scaffolder-backend-module-azure/CHANGELOG.md index 69bf4716e1..27b6f2e934 100644 --- a/plugins/scaffolder-backend-module-azure/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-azure/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-azure +## 0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.2.18 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index 2c88b0d1e3..a5735fa15c 100644 --- a/plugins/scaffolder-backend-module-azure/package.json +++ b/plugins/scaffolder-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-azure", - "version": "0.2.18", + "version": "0.2.19-next.0", "description": "The azure module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md index b707fd65eb..ee15ba6f59 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-cloud +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-bitbucket-cloud-common@0.3.8-next.0 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.3.3 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json index a1701d704f..c57240f97b 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud", - "version": "0.3.3", + "version": "0.3.4-next.0", "description": "The Bitbucket Cloud module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md index 1cd3c24adc..cba6cdc118 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-server +## 0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.2.18 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index 38881b8d44..6b813c33cf 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-server", - "version": "0.2.18", + "version": "0.2.19-next.0", "description": "The Bitbucket Server module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md index 7fe5027422..286dcbece7 100644 --- a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket +## 0.3.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.3.4-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.19-next.0 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.3.19 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index 31ef4d0a94..d048f9dcc9 100644 --- a/plugins/scaffolder-backend-module-bitbucket/package.json +++ b/plugins/scaffolder-backend-module-bitbucket/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket", - "version": "0.3.19", + "version": "0.3.20-next.0", "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index 7d935f288c..585925ca27 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.3.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.3.18 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index 37494f321e..69bcd089c0 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", - "version": "0.3.18", + "version": "0.3.19-next.0", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index aae0ad9a09..55d0e78017 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.3.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.15.3-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.3.20 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 85d9b6a61f..b1cb303441 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", - "version": "0.3.20", + "version": "0.3.21-next.0", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md index 558c8af8a3..bf9c2606fe 100644 --- a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gcp +## 0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.2.18 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gcp/package.json b/plugins/scaffolder-backend-module-gcp/package.json index 2a30a03b54..f2c354feaf 100644 --- a/plugins/scaffolder-backend-module-gcp/package.json +++ b/plugins/scaffolder-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gcp", - "version": "0.2.18", + "version": "0.2.19-next.0", "description": "The GCP Bucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md index f0ca02b932..c300740667 100644 --- a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gerrit +## 0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.2.18 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index 91fb27faad..d16e7fdc29 100644 --- a/plugins/scaffolder-backend-module-gerrit/package.json +++ b/plugins/scaffolder-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gerrit", - "version": "0.2.18", + "version": "0.2.19-next.0", "description": "The gerrit module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md index d9a2504e73..c8d640494f 100644 --- a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gitea +## 0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.2.18 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 28c565b481..693e9560f2 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitea", - "version": "0.2.18", + "version": "0.2.19-next.0", "description": "The gitea module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-github/CHANGELOG.md b/plugins/scaffolder-backend-module-github/CHANGELOG.md index ae8efce7ad..567787aeb7 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.9.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.9.6 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index f1b8bd053d..1b599c8558 100644 --- a/plugins/scaffolder-backend-module-github/package.json +++ b/plugins/scaffolder-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-github", - "version": "0.9.6", + "version": "0.9.7-next.0", "description": "The github module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index 9062c4316a..c6a07bd53a 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.11.4-next.0 + +### Patch Changes + +- 5730c8e: Added `maskedAndHidden` option to `gitlab:projectVariable:create` and `publish:gitlab` action to support creating GitLab project variables that are both masked and hidden. Updated gitbeaker to version 43.8.0 for proper type support. +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.11.3 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index a4e123682e..558de71045 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitlab", - "version": "0.11.3", + "version": "0.11.4-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md index 37ee4d20db..84c719266e 100644 --- a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-notifications +## 0.1.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/plugin-notifications-common@0.2.1 + - @backstage/plugin-notifications-node@0.2.24-next.0 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.1.19 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-notifications/package.json b/plugins/scaffolder-backend-module-notifications/package.json index b364d93ef7..5dd0a94d32 100644 --- a/plugins/scaffolder-backend-module-notifications/package.json +++ b/plugins/scaffolder-backend-module-notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-notifications", - "version": "0.1.19", + "version": "0.1.20-next.0", "description": "The notifications backend module for the scaffolder plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index 5b7036235b..e7590507a9 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.5.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.5.18 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 0ec751cde4..73b18d7daa 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", - "version": "0.5.18", + "version": "0.5.19-next.0", "description": "A module for the scaffolder backend that lets you template projects using Rails", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index 4f53ef6710..b42cf8bb61 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.3.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index d084c92eb6..96484013a8 100644 --- a/plugins/scaffolder-backend-module-sentry/package.json +++ b/plugins/scaffolder-backend-module-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-sentry", - "version": "0.3.1", + "version": "0.3.2-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index 89b05fc273..7f5bf1a9a2 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.4.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + - @backstage/plugin-scaffolder-node-test-utils@0.3.9-next.0 + ## 0.4.19 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 4302bfb29c..8b41d9a053 100644 --- a/plugins/scaffolder-backend-module-yeoman/package.json +++ b/plugins/scaffolder-backend-module-yeoman/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-yeoman", - "version": "0.4.19", + "version": "0.4.20-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index e3e87f80ff..460664fc2e 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-scaffolder-backend +## 3.1.4-next.0 + +### Patch Changes + +- 4e39e63: Removed unused dependencies +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 3.1.3 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index d26941076a..64e8ce29e7 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "3.1.3", + "version": "3.1.4-next.0", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin", diff --git a/plugins/scaffolder-common/CHANGELOG.md b/plugins/scaffolder-common/CHANGELOG.md index 66a2dc78fc..80bc88bbe5 100644 --- a/plugins/scaffolder-common/CHANGELOG.md +++ b/plugins/scaffolder-common/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-common +## 1.7.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.6 + ## 1.7.6 ### Patch Changes diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index 55ca47472e..98520a4381 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-common", - "version": "1.7.6", + "version": "1.7.7-next.0", "description": "Common functionalities for the scaffolder, to be shared between scaffolder and scaffolder-backend plugin", "backstage": { "role": "common-library", diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index 95204f458e..1f93005bfe 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-test-utils@1.11.1-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node@0.12.6-next.0 + ## 0.3.8 ### Patch Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index f9eb2af520..57686d0b6a 100644 --- a/plugins/scaffolder-node-test-utils/package.json +++ b/plugins/scaffolder-node-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node-test-utils", - "version": "0.3.8", + "version": "0.3.9-next.0", "backstage": { "role": "node-library", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index ca84b88546..506640c822 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-node +## 0.12.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + ## 0.12.5 ### Patch Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 1c3147ab2f..44924831cb 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node", - "version": "0.12.5", + "version": "0.12.6-next.0", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library", diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index c8dc8208fa..56e46e55ac 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-scaffolder-react +## 1.19.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-permission-react@0.4.41-next.0 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + ## 1.19.7 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index ad95d45ace..8738088664 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-react", - "version": "1.19.7", + "version": "1.19.8-next.0", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 3fcd488f9a..c321001bd3 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-scaffolder +## 1.35.5-next.0 + +### Patch Changes + +- bd5b842: Added a new `ui:autoSelect` option to the EntityPicker field that controls whether an entity is automatically selected when the field loses focus. When set to `false`, the field will remain empty if the user closes it without explicitly selecting an entity, preventing unintentional selections. Defaults to `true` for backward compatibility. +- ee87720: Added back the `formFieldsApiRef` and `ScaffolderFormFieldsApi` alpha exports that were unintentionally removed. +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-react@0.4.41-next.0 + - @backstage/plugin-scaffolder-common@1.7.7-next.0 + - @backstage/plugin-scaffolder-react@1.19.8-next.0 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + ## 1.35.3 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index b69cea955b..a36b5016e4 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.35.3", + "version": "1.35.5-next.0", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin", diff --git a/plugins/search-backend-module-catalog/CHANGELOG.md b/plugins/search-backend-module-catalog/CHANGELOG.md index 8fb7cf396c..eb5fcd9b28 100644 --- a/plugins/search-backend-module-catalog/CHANGELOG.md +++ b/plugins/search-backend-module-catalog/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend-module-catalog +## 0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + ## 0.3.12 ### Patch Changes diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index 8e3a58a003..d6b3318020 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-catalog", - "version": "0.3.12", + "version": "0.3.13-next.0", "description": "A module for the search backend that exports catalog modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index a5db94bfb0..fa550ad601 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 1.8.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/integration-aws-node@0.1.20 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + ## 1.8.0 ### Minor Changes diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 6cbd4133a5..0f64dda6a9 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", - "version": "1.8.0", + "version": "1.8.1-next.0", "description": "A module for the search backend that implements search using ElasticSearch", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index 1d8db5cf83..501a052f05 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-explore +## 0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + ## 0.3.11 ### Patch Changes diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index 6a2c89e334..5185962af0 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-explore", - "version": "0.3.11", + "version": "0.3.12-next.0", "description": "A module for the search backend that exports explore modules", "backstage": { "moved": "@backstage-community/plugin-search-backend-module-explore", diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index de608e769e..f76197bf7f 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.53-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + ## 0.5.52 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 74688066e1..28ad927826 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-pg", - "version": "0.5.52", + "version": "0.5.53-next.0", "description": "A module for the search backend that implements search using PostgreSQL", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md index 89061fca05..60a6719132 100644 --- a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md +++ b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-stack-overflow-collator +## 0.3.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + ## 0.3.17 ### Patch Changes diff --git a/plugins/search-backend-module-stack-overflow-collator/package.json b/plugins/search-backend-module-stack-overflow-collator/package.json index 8500b396f5..c5c8ba0218 100644 --- a/plugins/search-backend-module-stack-overflow-collator/package.json +++ b/plugins/search-backend-module-stack-overflow-collator/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-stack-overflow-collator", - "version": "0.3.17", + "version": "0.3.18-next.0", "description": "A module for the search backend that exports stack overflow modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index dc4aac2f8a..b445ae0477 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.4.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/plugin-catalog-common@1.1.8 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-techdocs-node@1.14.3-next.0 + ## 0.4.11 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index a9e845030c..b85abc2715 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", - "version": "0.4.11", + "version": "0.4.12-next.0", "description": "A module for the search backend that exports techdocs modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index 35bb537506..ba1c8f1197 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-node +## 1.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-search-common@1.2.22 + ## 1.4.1 ### Patch Changes diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index 4d80beabb9..32bfd43019 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-node", - "version": "1.4.1", + "version": "1.4.2-next.0", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", "backstage": { "role": "node-library", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 36d4330543..0b18e1ccce 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-search-backend +## 2.0.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/backend-openapi-utils@0.6.7-next.0 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.6 + - @backstage/plugin-permission-node@0.10.11-next.0 + - @backstage/plugin-search-backend-node@1.4.2-next.0 + - @backstage/plugin-search-common@1.2.22 + ## 2.0.12 ### Patch Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 28070714b5..4c4739e967 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "2.0.12", + "version": "2.0.13-next.0", "description": "The Backstage backend plugin that provides your backstage app with search", "backstage": { "role": "backend-plugin", diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index e9668c0f6a..53739b9a14 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-search-react +## 1.10.5-next.0 + +### Patch Changes + +- d5eb954: Fixes the search component not registering the first search on navigate to the search page. +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-search-common@1.2.22 + ## 1.10.3 ### Patch Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 76b575b5c9..b7a5a99bff 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "1.10.3", + "version": "1.10.5-next.0", "backstage": { "role": "web-library", "pluginId": "search", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 67d6d87ed6..93711e086f 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search +## 1.6.2-next.0 + +### Patch Changes + +- d5eb954: Fixes the search component not registering the first search on navigate to the search page. +- Updated dependencies + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-search-common@1.2.22 + ## 1.6.0 ### Minor Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 0ab2bd3511..e65a07c145 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "1.6.0", + "version": "1.6.2-next.0", "description": "The Backstage plugin that provides your backstage app with search", "backstage": { "role": "frontend-plugin", diff --git a/plugins/signals-backend/CHANGELOG.md b/plugins/signals-backend/CHANGELOG.md index 9ab368e27d..a9a76f761f 100644 --- a/plugins/signals-backend/CHANGELOG.md +++ b/plugins/signals-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-signals-backend +## 0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.20-next.0 + - @backstage/plugin-signals-node@0.1.29-next.0 + ## 0.3.12 ### Patch Changes diff --git a/plugins/signals-backend/package.json b/plugins/signals-backend/package.json index 456607f2ea..df7ef8c99f 100644 --- a/plugins/signals-backend/package.json +++ b/plugins/signals-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-backend", - "version": "0.3.12", + "version": "0.3.13-next.0", "backstage": { "role": "backend-plugin", "pluginId": "signals", diff --git a/plugins/signals-node/CHANGELOG.md b/plugins/signals-node/CHANGELOG.md index 94b0a03575..cc28371c6a 100644 --- a/plugins/signals-node/CHANGELOG.md +++ b/plugins/signals-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-signals-node +## 0.1.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/config@1.3.6 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-events-node@0.4.20-next.0 + ## 0.1.28 ### Patch Changes diff --git a/plugins/signals-node/package.json b/plugins/signals-node/package.json index 7453a7ed0f..ee254ef5e8 100644 --- a/plugins/signals-node/package.json +++ b/plugins/signals-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-node", - "version": "0.1.28", + "version": "0.1.29-next.0", "description": "Node.js library for the signals plugin", "backstage": { "role": "node-library", diff --git a/plugins/signals-react/CHANGELOG.md b/plugins/signals-react/CHANGELOG.md index 8509cc043d..c21647cc29 100644 --- a/plugins/signals-react/CHANGELOG.md +++ b/plugins/signals-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-signals-react +## 0.0.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/types@1.2.2 + ## 0.0.19 ### Patch Changes diff --git a/plugins/signals-react/package.json b/plugins/signals-react/package.json index a24132d283..12723e0bf3 100644 --- a/plugins/signals-react/package.json +++ b/plugins/signals-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-react", - "version": "0.0.19", + "version": "0.0.20-next.0", "description": "Web library for the signals plugin", "backstage": { "role": "web-library", diff --git a/plugins/signals/CHANGELOG.md b/plugins/signals/CHANGELOG.md index 85073ae23f..d135b27ade 100644 --- a/plugins/signals/CHANGELOG.md +++ b/plugins/signals/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-signals +## 0.0.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-react@0.0.20-next.0 + ## 0.0.28 ### Patch Changes diff --git a/plugins/signals/package.json b/plugins/signals/package.json index 962b1b6fda..4f390f1ddc 100644 --- a/plugins/signals/package.json +++ b/plugins/signals/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals", - "version": "0.0.28", + "version": "0.0.29-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "signals", diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index 988bbd7dbf..c7ceb95718 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-techdocs-addons-test-utils +## 2.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/plugin-catalog@1.33.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/plugin-techdocs@1.17.1-next.0 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/test-utils@1.7.16-next.0 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + ## 2.0.2 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 03e7809f22..639fc90db1 100644 --- a/plugins/techdocs-addons-test-utils/package.json +++ b/plugins/techdocs-addons-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-addons-test-utils", - "version": "2.0.2", + "version": "2.0.3-next.0", "backstage": { "role": "web-library", "pluginId": "techdocs-addons", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 8d7934b086..a75cfe6671 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-techdocs-backend +## 2.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/plugin-catalog-node@2.1.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-techdocs-node@1.14.3-next.0 + ## 2.1.5 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index f128a954db..31d6797f15 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "2.1.5", + "version": "2.1.6-next.0", "description": "The Backstage backend plugin that renders technical documentation for your components", "backstage": { "role": "backend-plugin", diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index 617c97aed0..da2ac1bcb7 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.1.34-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + ## 1.1.33 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index d7819741bc..86ef3743a0 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", - "version": "1.1.33", + "version": "1.1.34-next.0", "description": "Plugin module for contributed TechDocs Addons", "backstage": { "role": "frontend-plugin-module", diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index da459e9bd2..fd02995625 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-techdocs-node +## 1.14.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.21.0-next.0 + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.20 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-techdocs-common@0.1.1 + ## 1.14.2 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index f4e4cd930d..163e8d3c12 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-node", - "version": "1.14.2", + "version": "1.14.3-next.0", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", "backstage": { "role": "node-library", diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index 8981a5df01..bca81bc172 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-techdocs-react +## 1.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/version-bridge@1.0.12 + - @backstage/plugin-techdocs-common@0.1.1 + ## 1.3.8 ### Patch Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index e44b42417c..51acffebcc 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-react", - "version": "1.3.8", + "version": "1.3.9-next.0", "description": "Shared frontend utilities for TechDocs and Addons", "backstage": { "role": "web-library", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 0572da3823..0a636c06f2 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-techdocs +## 1.17.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-react@1.10.5-next.0 + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/integration@1.21.0-next.0 + - @backstage/catalog-client@1.13.1-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/config@1.3.6 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.16-next.0 + - @backstage/theme@0.7.2 + - @backstage/plugin-auth-react@0.1.25-next.0 + - @backstage/plugin-search-common@1.2.22 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.9-next.0 + ## 1.17.0 ### Minor Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 7852273cdb..91c49699ea 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "1.17.0", + "version": "1.17.1-next.0", "description": "The Backstage plugin that renders technical documentation for your components", "backstage": { "role": "frontend-plugin", diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index 1b21b58172..6149ad3704 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-user-settings-backend +## 0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.7.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-auth-node@0.6.14-next.0 + - @backstage/plugin-signals-node@0.1.29-next.0 + - @backstage/plugin-user-settings-common@0.1.0 + ## 0.4.0 ### Minor Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index f96e813330..0dd097def1 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings-backend", - "version": "0.4.0", + "version": "0.4.1-next.0", "description": "The Backstage backend plugin to manage user settings", "backstage": { "role": "backend-plugin", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 0401f61fab..46a1ed955e 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-user-settings +## 0.9.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.14.2-next.0 + - @backstage/plugin-catalog-react@2.0.1-next.0 + - @backstage/catalog-model@1.7.6 + - @backstage/core-app-api@1.19.6-next.0 + - @backstage/core-components@0.18.8-next.0 + - @backstage/core-plugin-api@1.12.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.2 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-react@0.0.20-next.0 + - @backstage/plugin-user-settings-common@0.1.0 + ## 0.9.0 ### Minor Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 4178d04a39..8dbc35fd40 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.9.0", + "version": "0.9.1-next.0", "description": "A Backstage plugin that provides a settings page", "backstage": { "role": "frontend-plugin", From 6a1c9550b5ae34e37a834f3ae7fad0a214e73905 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Feb 2026 21:01:03 +0100 Subject: [PATCH 39/55] repo: migrate to AGENTS.md Move agent instructions from `.github/copilot-instructions.md` to `AGENTS.md` at the repo root for broader AI agent tool compatibility, and remove the Cursor rule that pointed at the old file. Signed-off-by: Patrik Oldsberg Co-authored-by: Cursor Signed-off-by: Patrik Oldsberg Co-authored-by: Cursor --- .cursor/rules/general.mdc | 7 ----- .github/copilot-instructions.md | 46 --------------------------------- AGENTS.md | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 53 deletions(-) delete mode 100644 .cursor/rules/general.mdc create mode 100644 AGENTS.md diff --git a/.cursor/rules/general.mdc b/.cursor/rules/general.mdc deleted file mode 100644 index 3a81a0cb5f..0000000000 --- a/.cursor/rules/general.mdc +++ /dev/null @@ -1,7 +0,0 @@ ---- -description: General project guidelines for Backstage development -globs: -alwaysApply: true ---- - -Follow the instructions at /.github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 2157025b7b..9ded15067a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,49 +1,3 @@ -Backstage is an open platform for building developer portals. This is a TypeScript monorepo using Yarn workspaces. - -## Key Directories - -- `/packages`: Core framework packages (prefixed `@backstage/`) -- `/plugins`: Plugin packages (prefixed `@backstage/plugin-*`) -- `/packages/app` and `/packages/backend`: Example app for local development -- `/packages/app`: Main example app using the new frontend system -- `/packages/app-legacy`: Example app using the old frontend system -- `/docs`: Documentation files - -Packages prefixed with `core-` (e.g., `@backstage/core-plugin-api`) are part of the old frontend system. Packages prefixed with `frontend-` (e.g., `@backstage/frontend-plugin-api`) are part of the new frontend system (NFS). Packages prefixed with `backend-` (e.g., `@backstage/backend-plugin-api`) are part of the backend system. - -## Code Standards - -The following files contain guidelines for the project: - -- `/CONTRIBUTING.md`: comprehensive contribution guidelines. -- `/STYLE.md`: guidelines for code style. -- `/REVIEWING.md`: guidelines for pull requests and writing changesets. -- `/SECURITY.md`: guidelines for security. -- `/docs/architecture-decisions/`: contains the architecture decisions for the project. - -## Development Flow - -Before any of these commands can be run, you need to run `yarn install` in the project root. - -- Build: There is no need to build the project during development, and it is verified automatically in the CI pipeline. -- Test: Use `yarn test --no-watch ` in the project root to run tests. The path can be either a single file or a directory. Always provide a path, avoid running all tests. -- Type checking: Use `yarn tsc` in the project root to run the type checker. -- Code formatting: Use `yarn prettier --write ` to format code. -- Lint: Use `yarn lint --fix` in the project root to run the linter. -- API reports: Before submitting a pull request with changes to any package in the workspace, run `yarn build:api-reports` in the project root to generate API reports for all packages. -- Dev server: Use `yarn start` to run the example app locally (frontend on :3000, backend on :7007). -- Create: Use `yarn new` to scaffold new plugins, packages, or modules. - -You MUST NOT create a release by running `yarn changesets version` or `yarn release` as part of any changes. Releases are created by separate workflows. - -All changes that affect the published version of packages in the `/packages` and `/plugins` directories must be accompanied by a changeset. Only non-private packages require changesets. See the guidelines in `/CONTRIBUTING.md#creating-changesets` for information on how to write good changesets. Changesets are stored in the `/.changeset` directory. Breaking changes must be accompanied by a `minor` version bump for packages below version `1.0.0`, or a `major` version bump for packages at version `1.0.0` or higher. - -Never update ESLint, Prettier, or TypeScript configuration files unless specifically requested. - -## Repository Structure - -See `/docs/contribute/project-structure.md` for a detailed description of the repository structure. - ## Pull Request Reviews When reviewing a pull request you MUST calculate a priority score between 0 and 100, where a higher score indicates higher priority. You MUST include the score as an HTML comment at the very beginning of the "PR Overview" section, using the following format: ``. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..ebf597cecc --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,45 @@ +Backstage is an open platform for building developer portals. This is a TypeScript monorepo using Yarn workspaces. + +## Key Directories + +- `/packages`: Core framework packages (prefixed `@backstage/`) +- `/plugins`: Plugin packages (prefixed `@backstage/plugin-*`) +- `/packages/app` and `/packages/backend`: Example app for local development +- `/packages/app`: Main example app using the new frontend system +- `/packages/app-legacy`: Example app using the old frontend system +- `/docs`: Documentation files + +Packages prefixed with `core-` (e.g., `@backstage/core-plugin-api`) are part of the old frontend system. Packages prefixed with `frontend-` (e.g., `@backstage/frontend-plugin-api`) are part of the new frontend system (NFS). Packages prefixed with `backend-` (e.g., `@backstage/backend-plugin-api`) are part of the backend system. + +## Code Standards + +The following files contain guidelines for the project: + +- `/CONTRIBUTING.md`: comprehensive contribution guidelines. +- `/STYLE.md`: guidelines for code style. +- `/REVIEWING.md`: guidelines for pull requests and writing changesets. +- `/SECURITY.md`: guidelines for security. +- `/docs/architecture-decisions/`: contains the architecture decisions for the project. + +## Development Flow + +Before any of these commands can be run, you need to run `yarn install` in the project root. + +- Build: There is no need to build the project during development, and it is verified automatically in the CI pipeline. +- Test: Use `yarn test --no-watch ` in the project root to run tests. The path can be either a single file or a directory. Always provide a path, avoid running all tests. +- Type checking: Use `yarn tsc` in the project root to run the type checker. +- Code formatting: Use `yarn prettier --write ` to format code. +- Lint: Use `yarn lint --fix` in the project root to run the linter. +- API reports: Before submitting a pull request with changes to any package in the workspace, run `yarn build:api-reports` in the project root to generate API reports for all packages. +- Dev server: Use `yarn start` to run the example app locally (frontend on :3000, backend on :7007). +- Create: Use `yarn new` to scaffold new plugins, packages, or modules. + +You MUST NOT create a release by running `yarn changesets version` or `yarn release` as part of any changes. Releases are created by separate workflows. + +All changes that affect the published version of packages in the `/packages` and `/plugins` directories must be accompanied by a changeset. Only non-private packages require changesets. See the guidelines in `/CONTRIBUTING.md#creating-changesets` for information on how to write good changesets. Changesets are stored in the `/.changeset` directory. Breaking changes must be accompanied by a `minor` version bump for packages below version `1.0.0`, or a `major` version bump for packages at version `1.0.0` or higher. + +Never update ESLint, Prettier, or TypeScript configuration files unless specifically requested. + +## Repository Structure + +See `/docs/contribute/project-structure.md` for a detailed description of the repository structure. From 410e867616953b001383760ab2f68584a38b5dfe Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Feb 2026 21:12:09 +0100 Subject: [PATCH 40/55] AGENTS.md: some more pointers for changesets and code style Signed-off-by: Patrik Oldsberg --- AGENTS.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index ebf597cecc..b4372229d8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,10 @@ The following files contain guidelines for the project: - `/SECURITY.md`: guidelines for security. - `/docs/architecture-decisions/`: contains the architecture decisions for the project. +When writing or generating code, always match the existing coding style of each individual package and file. Different packages in the monorepo may have different conventions — consistency within a package is more important than consistency across the repo. + +When writing or generating tests, prefer fewer thorough tests with multiple assertions over many small tests. When using React Testing Library, prefer using `screen` and `.findBy*` queries over `waitFor`, and avoid adding test IDs to the implementation. + ## Development Flow Before any of these commands can be run, you need to run `yarn install` in the project root. @@ -36,10 +40,14 @@ Before any of these commands can be run, you need to run `yarn install` in the p You MUST NOT create a release by running `yarn changesets version` or `yarn release` as part of any changes. Releases are created by separate workflows. -All changes that affect the published version of packages in the `/packages` and `/plugins` directories must be accompanied by a changeset. Only non-private packages require changesets. See the guidelines in `/CONTRIBUTING.md#creating-changesets` for information on how to write good changesets. Changesets are stored in the `/.changeset` directory. Breaking changes must be accompanied by a `minor` version bump for packages below version `1.0.0`, or a `major` version bump for packages at version `1.0.0` or higher. +All changes that affect the published version of packages in the `/packages` and `/plugins` directories must be accompanied by a changeset. Only non-private packages require changesets. See the guidelines in `/CONTRIBUTING.md#creating-changesets` for information on how to write good changesets. Changesets are stored in the `/.changeset` directory and should be created by writing changeset files directly — never use the changeset CLI. Breaking changes must be accompanied by a `minor` version bump for packages below version `1.0.0`, or a `major` version bump for packages at version `1.0.0` or higher. For non-breaking changes, use `minor` for packages at version `1.0.0` or higher, and `patch` for packages below `1.0.0`. Each changeset message should be relevant to the specific package it targets and written for Backstage adopters as the audience — avoid referencing internal implementation details. If a change spans multiple packages you often need to create separate changesets to make sure they are tailored to each package. + +When creating pull requests, use the template at `/.github/PULL_REQUEST_TEMPLATE.md`. Never update ESLint, Prettier, or TypeScript configuration files unless specifically requested. +Never make changes to the release notes in `/docs/releases` unless explicitly asked. These document past releases and should not be updated based on newer changes. + ## Repository Structure See `/docs/contribute/project-structure.md` for a detailed description of the repository structure. From 60cdf5b80cb59826903b50a6e76f4f8d64938a33 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Feb 2026 21:14:27 +0100 Subject: [PATCH 41/55] repo: expand AGENTS.md with additional guidelines Add coding style, testing, changeset, and PR template guidelines. Remove the now-redundant .claude/CLAUDE.md pointer file. Signed-off-by: Patrik Oldsberg Co-authored-by: Cursor --- .claude/CLAUDE.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .claude/CLAUDE.md diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md deleted file mode 100644 index 170a4eb868..0000000000 --- a/.claude/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -Follow the instructions at /.github/copilot-instructions.md From 614f524ace82d77b28def1fcc0dc8c575f8abc90 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Feb 2026 21:48:12 +0100 Subject: [PATCH 42/55] AGENTS.md: minor for significant changes only Signed-off-by: Patrik Oldsberg --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index b4372229d8..fc8468b98c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,7 +40,7 @@ Before any of these commands can be run, you need to run `yarn install` in the p You MUST NOT create a release by running `yarn changesets version` or `yarn release` as part of any changes. Releases are created by separate workflows. -All changes that affect the published version of packages in the `/packages` and `/plugins` directories must be accompanied by a changeset. Only non-private packages require changesets. See the guidelines in `/CONTRIBUTING.md#creating-changesets` for information on how to write good changesets. Changesets are stored in the `/.changeset` directory and should be created by writing changeset files directly — never use the changeset CLI. Breaking changes must be accompanied by a `minor` version bump for packages below version `1.0.0`, or a `major` version bump for packages at version `1.0.0` or higher. For non-breaking changes, use `minor` for packages at version `1.0.0` or higher, and `patch` for packages below `1.0.0`. Each changeset message should be relevant to the specific package it targets and written for Backstage adopters as the audience — avoid referencing internal implementation details. If a change spans multiple packages you often need to create separate changesets to make sure they are tailored to each package. +All changes that affect the published version of packages in the `/packages` and `/plugins` directories must be accompanied by a changeset. Only non-private packages require changesets. See the guidelines in `/CONTRIBUTING.md#creating-changesets` for information on how to write good changesets. Changesets are stored in the `/.changeset` directory and should be created by writing changeset files directly — never use the changeset CLI. Breaking changes must be accompanied by a `minor` version bump for packages below version `1.0.0`, or a `major` version bump for packages at version `1.0.0` or higher. For non-breaking changes that introduce new APIs or features, use `minor` for packages at version `1.0.0` or higher, and `patch` for packages below `1.0.0`. Each changeset message should be relevant to the specific package it targets and written for Backstage adopters as the audience — avoid referencing internal implementation details. If a change spans multiple packages you often need to create separate changesets to make sure they are tailored to each package. When creating pull requests, use the template at `/.github/PULL_REQUEST_TEMPLATE.md`. From 9657061e648b7241089ddb914369581ef493e96d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Feb 2026 21:50:16 +0100 Subject: [PATCH 43/55] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Patrik Oldsberg --- AGENTS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fc8468b98c..001989d9b6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,15 +30,15 @@ When writing or generating tests, prefer fewer thorough tests with multiple asse Before any of these commands can be run, you need to run `yarn install` in the project root. - Build: There is no need to build the project during development, and it is verified automatically in the CI pipeline. -- Test: Use `yarn test --no-watch ` in the project root to run tests. The path can be either a single file or a directory. Always provide a path, avoid running all tests. -- Type checking: Use `yarn tsc` in the project root to run the type checker. -- Code formatting: Use `yarn prettier --write ` to format code. +- Test: Use `CI=1 yarn test ` in the project root to run tests. The path can be either a single file or a directory. Always provide a path, avoid running all tests. +- Type checking: Use `yarn tsc` in the project root to run the type checker. Do not try to run it somewhere else than the project root and do not supply any options. +- Code formatting: Use `yarn prettier --write <...paths>` to format code. Run it explicitly for file paths that you know are changed, not for entire folders - otherwise it may change formatting of unrelated files. - Lint: Use `yarn lint --fix` in the project root to run the linter. - API reports: Before submitting a pull request with changes to any package in the workspace, run `yarn build:api-reports` in the project root to generate API reports for all packages. - Dev server: Use `yarn start` to run the example app locally (frontend on :3000, backend on :7007). - Create: Use `yarn new` to scaffold new plugins, packages, or modules. -You MUST NOT create a release by running `yarn changesets version` or `yarn release` as part of any changes. Releases are created by separate workflows. +You MUST NOT run builds or create a release by running `yarn build`, `yarn changesets version`, or `yarn release` as part of any changes. Builds and releases are made by separate workflows. All changes that affect the published version of packages in the `/packages` and `/plugins` directories must be accompanied by a changeset. Only non-private packages require changesets. See the guidelines in `/CONTRIBUTING.md#creating-changesets` for information on how to write good changesets. Changesets are stored in the `/.changeset` directory and should be created by writing changeset files directly — never use the changeset CLI. Breaking changes must be accompanied by a `minor` version bump for packages below version `1.0.0`, or a `major` version bump for packages at version `1.0.0` or higher. For non-breaking changes that introduce new APIs or features, use `minor` for packages at version `1.0.0` or higher, and `patch` for packages below `1.0.0`. Each changeset message should be relevant to the specific package it targets and written for Backstage adopters as the audience — avoid referencing internal implementation details. If a change spans multiple packages you often need to create separate changesets to make sure they are tailored to each package. From e44b6a9079a0d64dc9b3020485eebbd63dd7bd0c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Feb 2026 13:21:41 +0100 Subject: [PATCH 44/55] cli-common: Remove unused `findOwnDir` and validate `findOwnRootDir` Addresses review feedback from PR #32939: - Remove the `findOwnDir` function which is no longer needed - Add validation to `findOwnRootDir` to verify the resolved path actually contains a monorepo root with a `package.json` that has a `workspaces` field Signed-off-by: Patrik Oldsberg --- .changeset/clean-up-own-root-dir.md | 5 +++++ packages/cli-common/src/paths.test.ts | 10 +++++----- packages/cli-common/src/paths.ts | 23 +++++++++++++++++------ 3 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 .changeset/clean-up-own-root-dir.md diff --git a/.changeset/clean-up-own-root-dir.md b/.changeset/clean-up-own-root-dir.md new file mode 100644 index 0000000000..e702330140 --- /dev/null +++ b/.changeset/clean-up-own-root-dir.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli-common': patch +--- + +The `findOwnRootDir` utility now searches for the monorepo root by traversing up the directory tree looking for a `package.json` with `workspaces`, instead of assuming a fixed `../..` relative path. diff --git a/packages/cli-common/src/paths.test.ts b/packages/cli-common/src/paths.test.ts index 6ade904e12..d54dd8838b 100644 --- a/packages/cli-common/src/paths.test.ts +++ b/packages/cli-common/src/paths.test.ts @@ -16,18 +16,18 @@ /* eslint-disable no-restricted-syntax */ import { resolve as resolvePath } from 'node:path'; -import { findPaths, findRootPath, findOwnDir, findOwnRootDir } from './paths'; +import { findPaths, findRootPath, findOwnRootDir, findOwnPaths } from './paths'; describe('paths', () => { afterEach(() => { jest.restoreAllMocks(); }); - it('findOwnDir and findOwnRootDir should find owns paths', () => { - const dir = findOwnDir(__dirname); - const root = findOwnRootDir(dir); + it('findOwnPaths and findOwnRootDir should find own paths', () => { + const own = findOwnPaths(__dirname); + const root = findOwnRootDir(own.dir); - expect(dir).toBe(resolvePath(__dirname, '..')); + expect(own.dir).toBe(resolvePath(__dirname, '..')); expect(root).toBe(resolvePath(__dirname, '../../..')); }); diff --git a/packages/cli-common/src/paths.ts b/packages/cli-common/src/paths.ts index 1cd84d4f5b..c6bedc77e4 100644 --- a/packages/cli-common/src/paths.ts +++ b/packages/cli-common/src/paths.ts @@ -119,7 +119,23 @@ export function findOwnRootDir(ownDir: string) { ); } - return resolvePath(ownDir, '../..'); + const rootDir = findRootPath(ownDir, pkgJsonPath => { + try { + const content = fs.readFileSync(pkgJsonPath, 'utf8'); + const data = JSON.parse(content); + return Boolean(data.workspaces); + } catch (error) { + throw new Error( + `Failed to read package.json at '${pkgJsonPath}', ${error}`, + ); + } + }); + + if (!rootDir) { + throw new Error(`No monorepo root found when searching from '${ownDir}'`); + } + + return rootDir; } // Hierarchical directory cache shared across all OwnPathsImpl instances. @@ -199,11 +215,6 @@ class OwnPathsImpl implements OwnPaths { }; } -// Finds the root of a given package -export function findOwnDir(searchDir: string) { - return OwnPathsImpl.findDir(searchDir); -} - // Used by the test utility in testUtils.ts to override targetPaths export let targetPathsOverride: TargetPaths | undefined; From 560f5876453fab5c92d885a9f351218587328262 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Feb 2026 21:57:52 +0100 Subject: [PATCH 45/55] repo: symlink .claude/CLAUDE.md to AGENTS.md Signed-off-by: Patrik Oldsberg Co-authored-by: Cursor --- .claude/CLAUDE.md | 1 + 1 file changed, 1 insertion(+) create mode 120000 .claude/CLAUDE.md diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 120000 index 0000000000..be77ac83a1 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1 @@ +../AGENTS.md \ No newline at end of file From 59824058ff5c61705ec8fdb1ccde85235e778fbd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Feb 2026 22:21:11 +0100 Subject: [PATCH 46/55] retrigger CI Signed-off-by: Patrik Oldsberg Co-authored-by: Cursor From c7eb58261dc2e77be372661b91303783099302d5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Feb 2026 23:55:15 +0100 Subject: [PATCH 47/55] Update .changeset/clean-up-own-root-dir.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Patrik Oldsberg --- .changeset/clean-up-own-root-dir.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/clean-up-own-root-dir.md b/.changeset/clean-up-own-root-dir.md index e702330140..c668073688 100644 --- a/.changeset/clean-up-own-root-dir.md +++ b/.changeset/clean-up-own-root-dir.md @@ -2,4 +2,4 @@ '@backstage/cli-common': patch --- -The `findOwnRootDir` utility now searches for the monorepo root by traversing up the directory tree looking for a `package.json` with `workspaces`, instead of assuming a fixed `../..` relative path. +The `findOwnRootDir` utility now searches for the monorepo root by traversing up the directory tree looking for a `package.json` with `workspaces`, instead of assuming a fixed `../..` relative path. If no workspaces root is found during this traversal, `findOwnRootDir` now throws to enforce stricter validation of the repository layout. From 2e1f98b687ffed78c51c5431acaff2e2718cc34b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 05:24:30 +0000 Subject: [PATCH 48/55] build(deps): bump minimatch from 3.1.2 to 10.2.3 Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.1.2 to 10.2.3. - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v10.2.3) --- updated-dependencies: - dependency-name: minimatch dependency-version: 10.2.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 2a41ca71e4..14a3f7453e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39425,7 +39425,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:10.2.1, minimatch@npm:^10.1.1, minimatch@npm:^10.2.0, minimatch@npm:^10.2.1": +"minimatch@npm:10.2.1": version: 10.2.1 resolution: "minimatch@npm:10.2.1" dependencies: @@ -39452,6 +39452,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.1.1, minimatch@npm:^10.2.0, minimatch@npm:^10.2.1": + version: 10.2.3 + resolution: "minimatch@npm:10.2.3" + dependencies: + brace-expansion: "npm:^5.0.2" + checksum: 10/186c6a6ce9f7a79ae7776efc799c32d1a6670ebbcc2a8756e6cb6ec4aab7439a6ca6e592e1a6aac5f21674eefae5b19821b8fa95072a4f4567da1ae40eb6075d + languageName: node + linkType: hard + "minimatch@npm:^5.0.1, minimatch@npm:^5.1.0": version: 5.1.6 resolution: "minimatch@npm:5.1.6" From b42fcdca2e7c8a520454865645e80274310f23c2 Mon Sep 17 00:00:00 2001 From: Johan Persson Date: Tue, 24 Feb 2026 12:58:10 +0100 Subject: [PATCH 49/55] feat(ui): replace --bui-bg-popover with layered bg approach Remove the --bui-bg-popover token and adopt a two-layer background pattern across overlay components: outer shell uses --bui-bg-app, inner content uses Box bg="neutral-1". Components updated: Popover, Tooltip, Menu, Dialog. Each component gets a local border-radius CSS variable, a Box content wrapper with neutral-1 background, and updated arrow fills where applicable. Story "is open" variants for each component now use a dot-grid background pattern to detect background transparency issues in Chromatic snapshots. Signed-off-by: Johan Persson --- .changeset/early-baboons-roll.md | 16 ++ docs/conf/user-interface/index.md | 1 - packages/ui/report.api.md | 3 + .../src/components/Dialog/Dialog.module.css | 13 +- .../src/components/Dialog/Dialog.stories.tsx | 18 ++ packages/ui/src/components/Dialog/Dialog.tsx | 8 +- .../ui/src/components/Dialog/definition.ts | 1 + .../ui/src/components/Menu/Menu.module.css | 9 +- .../ui/src/components/Menu/Menu.stories.tsx | 18 ++ packages/ui/src/components/Menu/Menu.tsx | 213 ++++++++++-------- packages/ui/src/components/Menu/definition.ts | 1 + .../src/components/Popover/Popover.module.css | 15 +- .../components/Popover/Popover.stories.tsx | 22 ++ .../ui/src/components/Popover/Popover.tsx | 20 +- .../src/components/Tooltip/Tooltip.module.css | 20 +- .../components/Tooltip/Tooltip.stories.tsx | 18 ++ .../ui/src/components/Tooltip/Tooltip.tsx | 22 +- .../ui/src/components/Tooltip/definition.ts | 1 + packages/ui/src/css/tokens.css | 2 - 19 files changed, 300 insertions(+), 121 deletions(-) create mode 100644 .changeset/early-baboons-roll.md diff --git a/.changeset/early-baboons-roll.md b/.changeset/early-baboons-roll.md new file mode 100644 index 0000000000..bdd55ec507 --- /dev/null +++ b/.changeset/early-baboons-roll.md @@ -0,0 +1,16 @@ +--- +'@backstage/ui': minor +--- + +**BREAKING**: Removed `--bui-bg-popover` CSS token. Popover, Tooltip, Menu, and Dialog now use `--bui-bg-app` for their outer shell and `Box bg="neutral-1"` for content areas, providing better theme consistency and eliminating a redundant token. + +**Migration:** + +Replace any usage of `--bui-bg-popover` with `--bui-bg-neutral-1` (for content surfaces) or `--bui-bg-app` (for outer shells): + +```diff +- background: var(--bui-bg-popover); ++ background: var(--bui-bg-neutral-1); +``` + +**Affected components:** Popover, Tooltip, Menu, Dialog diff --git a/docs/conf/user-interface/index.md b/docs/conf/user-interface/index.md index cb53ebe81c..107fde3d57 100644 --- a/docs/conf/user-interface/index.md +++ b/docs/conf/user-interface/index.md @@ -156,7 +156,6 @@ These colors form a layered neutral scale for your application backgrounds. `--b | Token Name | Description | | ----------------------------- | ------------------------------------------------------------ | | `--bui-bg-app` | The base background color of your Backstage instance. | -| `--bui-bg-popover` | The background color used for popovers, tooltips, and menus. | | `--bui-bg-neutral-1` | First elevated layer. Use for cards, dialogs, and panels. | | `--bui-bg-neutral-1-hover` | Hover state for elements on neutral-1. | | `--bui-bg-neutral-1-pressed` | Pressed state for elements on neutral-1. | diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index 5922155e93..25522f1825 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -897,6 +897,7 @@ export const DialogDefinition: { readonly classNames: { readonly overlay: 'bui-DialogOverlay'; readonly dialog: 'bui-Dialog'; + readonly content: 'bui-DialogContent'; readonly header: 'bui-DialogHeader'; readonly headerTitle: 'bui-DialogHeaderTitle'; readonly body: 'bui-DialogBody'; @@ -1331,6 +1332,7 @@ export const MenuDefinition: { readonly classNames: { readonly root: 'bui-Menu'; readonly popover: 'bui-MenuPopover'; + readonly inner: 'bui-MenuInner'; readonly content: 'bui-MenuContent'; readonly section: 'bui-MenuSection'; readonly sectionHeader: 'bui-MenuSectionHeader'; @@ -2218,6 +2220,7 @@ export const Tooltip: ForwardRefExoticComponent< export const TooltipDefinition: { readonly classNames: { readonly tooltip: 'bui-Tooltip'; + readonly content: 'bui-TooltipContent'; readonly arrow: 'bui-TooltipArrow'; }; }; diff --git a/packages/ui/src/components/Dialog/Dialog.module.css b/packages/ui/src/components/Dialog/Dialog.module.css index f5968820a2..da82509134 100644 --- a/packages/ui/src/components/Dialog/Dialog.module.css +++ b/packages/ui/src/components/Dialog/Dialog.module.css @@ -43,8 +43,10 @@ } .bui-Dialog { - background: var(--bui-bg-popover); - border-radius: 0.5rem; + --dialog-border-radius: 0.5rem; + background: var(--bui-bg-app); + box-shadow: var(--bui-shadow); + border-radius: var(--dialog-border-radius); border: 1px solid var(--bui-border-1); color: var(--bui-fg-primary); position: relative; @@ -52,9 +54,14 @@ max-width: calc(100vw - 3rem); height: min(var(--bui-dialog-min-height, auto), calc(100vh - 3rem)); max-height: calc(100vh - 3rem); + outline: none; + } + + .bui-DialogContent { display: flex; flex-direction: column; - outline: none; + border-radius: var(--dialog-border-radius); + height: 100%; } /* Dialog entering animation */ diff --git a/packages/ui/src/components/Dialog/Dialog.stories.tsx b/packages/ui/src/components/Dialog/Dialog.stories.tsx index 0392959a59..9084bfa7e4 100644 --- a/packages/ui/src/components/Dialog/Dialog.stories.tsx +++ b/packages/ui/src/components/Dialog/Dialog.stories.tsx @@ -63,6 +63,24 @@ export const Default = meta.story({ }); export const Open = Default.extend({ + parameters: { layout: 'fullscreen' }, + decorators: [ + Story => ( +
+ +
+ ), + ], args: { defaultOpen: true, }, diff --git a/packages/ui/src/components/Dialog/Dialog.tsx b/packages/ui/src/components/Dialog/Dialog.tsx index 1c33588306..efdc6c4567 100644 --- a/packages/ui/src/components/Dialog/Dialog.tsx +++ b/packages/ui/src/components/Dialog/Dialog.tsx @@ -33,6 +33,7 @@ import { Button } from '../Button'; import { useStyles } from '../../hooks/useStyles'; import { DialogDefinition } from './definition'; import { Flex } from '../Flex'; +import { Box } from '../Box'; import styles from './Dialog.module.css'; /** @public */ @@ -71,7 +72,12 @@ export const Dialog = forwardRef, DialogProps>( ...style, }} > - {children} + + {children} + ); diff --git a/packages/ui/src/components/Dialog/definition.ts b/packages/ui/src/components/Dialog/definition.ts index d2fcb7bb4d..f9aadc9147 100644 --- a/packages/ui/src/components/Dialog/definition.ts +++ b/packages/ui/src/components/Dialog/definition.ts @@ -24,6 +24,7 @@ export const DialogDefinition = { classNames: { overlay: 'bui-DialogOverlay', dialog: 'bui-Dialog', + content: 'bui-DialogContent', header: 'bui-DialogHeader', headerTitle: 'bui-DialogHeaderTitle', body: 'bui-DialogBody', diff --git a/packages/ui/src/components/Menu/Menu.module.css b/packages/ui/src/components/Menu/Menu.module.css index 6dca183395..0717a9468d 100644 --- a/packages/ui/src/components/Menu/Menu.module.css +++ b/packages/ui/src/components/Menu/Menu.module.css @@ -18,12 +18,13 @@ @layer components { .bui-MenuPopover { + --menu-border-radius: var(--bui-radius-2); display: flex; flex-direction: column; box-shadow: var(--bui-shadow); border: 1px solid var(--bui-border-1); - border-radius: var(--bui-radius-2); - background: var(--bui-bg-popover); + border-radius: var(--menu-border-radius); + background: var(--bui-bg-app); color: var(--bui-fg-primary); outline: none; transition: transform 200ms, opacity 200ms; @@ -55,6 +56,10 @@ } } + .bui-MenuInner { + border-radius: var(--menu-border-radius); + } + .bui-MenuContent { max-height: inherit; box-sizing: border-box; diff --git a/packages/ui/src/components/Menu/Menu.stories.tsx b/packages/ui/src/components/Menu/Menu.stories.tsx index ffe184d4ae..982055ade1 100644 --- a/packages/ui/src/components/Menu/Menu.stories.tsx +++ b/packages/ui/src/components/Menu/Menu.stories.tsx @@ -198,6 +198,24 @@ export const PreviewLinks = meta.story({ }); export const Opened = meta.story({ + parameters: { layout: 'fullscreen' }, + decorators: [ + Story => ( +
+ +
+ ), + ], args: { ...Preview.input.args, }, diff --git a/packages/ui/src/components/Menu/Menu.tsx b/packages/ui/src/components/Menu/Menu.tsx index e02292ba44..da4243f643 100644 --- a/packages/ui/src/components/Menu/Menu.tsx +++ b/packages/ui/src/components/Menu/Menu.tsx @@ -58,6 +58,7 @@ import { } from '../InternalLinkProvider'; import styles from './Menu.module.css'; import clsx from 'clsx'; +import { Box } from '../Box'; const { RoutingProvider, useRoutingRegistrationEffect } = createRoutingRegistration(); @@ -119,18 +120,23 @@ export const Menu = (props: MenuProps) => { )} placement={placement} > - {virtualized ? ( - - {menuContent} - - ) : ( - menuContent - )} + + {virtualized ? ( + + {menuContent} + + ) : ( + menuContent + )} + ); @@ -169,18 +175,23 @@ export const MenuListBox = (props: MenuListBoxProps) => { )} placement={placement} > - {virtualized ? ( - - {listBoxContent} - - ) : ( - listBoxContent - )} + + {virtualized ? ( + + {listBoxContent} + + ) : ( + listBoxContent + )} + ); }; @@ -219,43 +230,48 @@ export const MenuAutocomplete = (props: MenuAutocompleteProps) => { )} placement={placement} > - - + + + + + + + + {virtualized ? ( + + {menuContent} + + ) : ( + menuContent )} - aria-label={props.placeholder || 'Search'} - > - - - - - - {virtualized ? ( - - {menuContent} - - ) : ( - menuContent - )} - + + ); @@ -298,43 +314,48 @@ export const MenuAutocompleteListbox = ( )} placement={placement} > - - + + + + + + + + {virtualized ? ( + + {listBoxContent} + + ) : ( + listBoxContent )} - aria-label={props.placeholder || 'Search'} - > - - - - - - {virtualized ? ( - - {listBoxContent} - - ) : ( - listBoxContent - )} - + + ); }; diff --git a/packages/ui/src/components/Menu/definition.ts b/packages/ui/src/components/Menu/definition.ts index e4ba0d83ab..860ad1d729 100644 --- a/packages/ui/src/components/Menu/definition.ts +++ b/packages/ui/src/components/Menu/definition.ts @@ -24,6 +24,7 @@ export const MenuDefinition = { classNames: { root: 'bui-Menu', popover: 'bui-MenuPopover', + inner: 'bui-MenuInner', content: 'bui-MenuContent', section: 'bui-MenuSection', sectionHeader: 'bui-MenuSectionHeader', diff --git a/packages/ui/src/components/Popover/Popover.module.css b/packages/ui/src/components/Popover/Popover.module.css index 69d2acffcf..cdd323817c 100644 --- a/packages/ui/src/components/Popover/Popover.module.css +++ b/packages/ui/src/components/Popover/Popover.module.css @@ -18,9 +18,10 @@ @layer components { .bui-Popover { + --popover-border-radius: var(--bui-radius-3); box-shadow: var(--bui-shadow); - border-radius: var(--bui-radius-3); - background: var(--bui-bg-popover); + border-radius: var(--popover-border-radius); + background: var(--bui-bg-app); border: 1px solid var(--bui-border-1); forced-color-adjust: none; outline: none; @@ -76,6 +77,7 @@ padding: var(--bui-space-4); flex: 1 1 auto; min-height: 0; + border-radius: var(--popover-border-radius); } .bui-PopoverArrow { @@ -89,11 +91,14 @@ we split the stroke and fill across separate elements in order to guarantee that the stroke is always overlaying a consistent color. */ - path:nth-child(1) { - fill: var(--bui-bg-popover); + use:nth-of-type(1) { + fill: var(--bui-bg-app); + } + use:nth-of-type(2) { + fill: var(--bui-bg-neutral-1); } - path:nth-child(2) { + path { fill: var(--bui-border-1); } diff --git a/packages/ui/src/components/Popover/Popover.stories.tsx b/packages/ui/src/components/Popover/Popover.stories.tsx index ea92bf9d5c..e05b352fc9 100644 --- a/packages/ui/src/components/Popover/Popover.stories.tsx +++ b/packages/ui/src/components/Popover/Popover.stories.tsx @@ -19,6 +19,7 @@ import { Button } from '../Button/Button'; import { DialogTrigger } from '../Dialog/Dialog'; import { Text } from '../Text/Text'; import { Flex } from '../Flex/Flex'; +import { Box } from '../Box'; const meta = preview.meta({ title: 'Backstage UI/Popover', @@ -74,6 +75,24 @@ export const Default = meta.story({ }); export const IsOpen = Default.extend({ + parameters: { layout: 'fullscreen' }, + decorators: [ + Story => ( +
+ +
+ ), + ], args: { isOpen: true, }, @@ -189,6 +208,9 @@ export const WithRichContent = Default.extend({ This is a popover with rich content. It can contain multiple elements and formatted text. + + You can also use the automatic bg system inside it. +