Add summary types and listing methods to CatalogModel
Introduce CatalogModelKindSummary, CatalogModelRelationSummary, CatalogModelAnnotationSummary, CatalogModelLabelSummary, and CatalogModelTagSummary as reduced views of the full model data. Add listKinds(), listRelations(), and getMetadata() methods to CatalogModel for retrieving these summaries. Add a new action for fetching a markdown-formatted catalog model description, useful for informing LLMs about the catalog structure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Fredrik Adelöw <freben@spotify.com>
This commit is contained in:
@@ -31,7 +31,14 @@ export interface CatalogModel {
|
||||
type?: string;
|
||||
};
|
||||
}): CatalogModelKind | undefined;
|
||||
getMetadata(): {
|
||||
annotations: CatalogModelAnnotationSummary[];
|
||||
labels: CatalogModelLabelSummary[];
|
||||
tags: CatalogModelTagSummary[];
|
||||
};
|
||||
getRelations(options: { kind: string }): CatalogModelRelation[] | undefined;
|
||||
listKinds(): CatalogModelKindSummary[];
|
||||
listRelations(): CatalogModelRelationSummary[];
|
||||
}
|
||||
|
||||
// @alpha
|
||||
@@ -44,9 +51,17 @@ export interface CatalogModelAnnotationDefinition {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface CatalogModelAnnotationSummary {
|
||||
description: string;
|
||||
name: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface CatalogModelKind {
|
||||
apiVersions: string[];
|
||||
description: string;
|
||||
jsonSchema: JsonObject;
|
||||
names: {
|
||||
kind: string;
|
||||
@@ -131,6 +146,20 @@ export interface CatalogModelKindRootSchema extends JsonObject {
|
||||
type: 'object';
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface CatalogModelKindSummary {
|
||||
description: string;
|
||||
names: {
|
||||
kind: string;
|
||||
singular: string;
|
||||
plural: string;
|
||||
};
|
||||
versions: Array<{
|
||||
apiVersion: string;
|
||||
specType?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface CatalogModelKindVersionDefinition {
|
||||
description?: string;
|
||||
@@ -153,6 +182,13 @@ export interface CatalogModelLabelDefinition {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface CatalogModelLabelSummary {
|
||||
description: string;
|
||||
name: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface CatalogModelLayer {
|
||||
// (undocumented)
|
||||
@@ -209,6 +245,21 @@ export interface CatalogModelRelationPairDefinition {
|
||||
toKind: string | string[];
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface CatalogModelRelationSummary {
|
||||
description: string;
|
||||
forward: {
|
||||
type: string;
|
||||
title: string;
|
||||
};
|
||||
fromKind: string[];
|
||||
reverse: {
|
||||
type: string;
|
||||
title: string;
|
||||
};
|
||||
toKind: string[];
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface CatalogModelRemoveAnnotationDefinition {
|
||||
name: string;
|
||||
@@ -255,6 +306,13 @@ export interface CatalogModelTagDefinition {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface CatalogModelTagSummary {
|
||||
description: string;
|
||||
name: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface CatalogModelUpdateAnnotationDefinition {
|
||||
description?: string;
|
||||
|
||||
@@ -316,6 +316,7 @@ describe('compileCatalogModel integration', () => {
|
||||
apiVersion: 'example.com/v1alpha1',
|
||||
});
|
||||
expect(kind1).toEqual({
|
||||
description: 'A widget',
|
||||
apiVersions: ['example.com/v1alpha1'],
|
||||
names: { kind: 'Widget', singular: 'widget', plural: 'widgets' },
|
||||
relationFields: [
|
||||
@@ -490,6 +491,7 @@ describe('compileCatalogModel integration', () => {
|
||||
apiVersion: 'example.com/v1alpha1',
|
||||
});
|
||||
expect(kind2).toEqual({
|
||||
description: 'An updated widget',
|
||||
apiVersions: ['example.com/v1alpha1'],
|
||||
names: { kind: 'Widget', singular: 'gizmo', plural: 'gizmos' },
|
||||
relationFields: [
|
||||
@@ -630,6 +632,7 @@ describe('compileCatalogModel integration', () => {
|
||||
apiVersion: 'example.com/v1alpha1',
|
||||
});
|
||||
expect(kind3).toEqual({
|
||||
description: 'An updated widget',
|
||||
apiVersions: ['example.com/v1alpha1'],
|
||||
names: { kind: 'Widget', singular: 'gizmo', plural: 'gizmos' },
|
||||
relationFields: [
|
||||
|
||||
@@ -38,9 +38,14 @@ import { OpUpdateRelationV1 } from './operations/updateRelation';
|
||||
import { OpUpdateTagV1 } from './operations/updateTag';
|
||||
import {
|
||||
CatalogModel,
|
||||
CatalogModelLayer,
|
||||
CatalogModelAnnotationSummary,
|
||||
CatalogModelKind,
|
||||
CatalogModelKindSummary,
|
||||
CatalogModelLabelSummary,
|
||||
CatalogModelLayer,
|
||||
CatalogModelRelation,
|
||||
CatalogModelRelationSummary,
|
||||
CatalogModelTagSummary,
|
||||
OpaqueCatalogModelLayer,
|
||||
} from './types';
|
||||
|
||||
@@ -604,6 +609,7 @@ export function compileCatalogModel(
|
||||
for (const [specType, specificKind] of version.specTypes) {
|
||||
const key = `${kindName}\0${version.apiVersion}\0${specType ?? ''}`;
|
||||
compiledKinds.set(key, {
|
||||
description: specificKind.description ?? kindState.description,
|
||||
apiVersions: [version.apiVersion],
|
||||
names: {
|
||||
kind: kindName,
|
||||
@@ -654,7 +660,85 @@ export function compileCatalogModel(
|
||||
);
|
||||
}
|
||||
|
||||
// Precompute kind summaries, one per unique kind (not per version/specType)
|
||||
const kindSummaries: CatalogModelKindSummary[] = [...kinds.entries()].map(
|
||||
([kindName, kindState]) => ({
|
||||
description: kindState.description,
|
||||
names: {
|
||||
kind: kindName,
|
||||
singular: kindState.singular,
|
||||
plural: kindState.plural,
|
||||
},
|
||||
versions: [...kindState.versions.values()].flatMap(version =>
|
||||
[...version.specTypes.keys()].map(specType => ({
|
||||
apiVersion: version.apiVersion,
|
||||
...(specType !== undefined ? { specType } : undefined),
|
||||
})),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
// Precompute annotation summaries
|
||||
const annotationSummaries: CatalogModelAnnotationSummary[] = [
|
||||
...annotations.entries(),
|
||||
].map(([name, state]) => ({
|
||||
name,
|
||||
...(state.title !== undefined ? { title: state.title } : undefined),
|
||||
description: state.description,
|
||||
}));
|
||||
|
||||
// Precompute label summaries
|
||||
const labelSummaries: CatalogModelLabelSummary[] = [...labels.entries()].map(
|
||||
([name, state]) => ({
|
||||
name,
|
||||
...(state.title !== undefined ? { title: state.title } : undefined),
|
||||
description: state.description,
|
||||
}),
|
||||
);
|
||||
|
||||
// Precompute tag summaries
|
||||
const tagSummaries: CatalogModelTagSummary[] = [...tags.entries()].map(
|
||||
([name, state]) => ({
|
||||
name,
|
||||
...(state.title !== undefined ? { title: state.title } : undefined),
|
||||
description: state.description,
|
||||
}),
|
||||
);
|
||||
|
||||
// Collect all unique relation summaries
|
||||
const relationSummaries: CatalogModelRelationSummary[] = [
|
||||
...relations.values(),
|
||||
].map(r => {
|
||||
const reverseEntry = relations.get(r.reverse.type);
|
||||
return {
|
||||
fromKind: [...r.fromKinds],
|
||||
toKind: [...r.toKinds],
|
||||
description: r.description,
|
||||
forward: r.forward,
|
||||
reverse: {
|
||||
type: r.reverse.type,
|
||||
title: reverseEntry?.forward.title ?? r.reverse.title,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
listKinds() {
|
||||
return kindSummaries;
|
||||
},
|
||||
|
||||
listRelations() {
|
||||
return relationSummaries;
|
||||
},
|
||||
|
||||
getMetadata() {
|
||||
return {
|
||||
annotations: annotationSummaries,
|
||||
labels: labelSummaries,
|
||||
tags: tagSummaries,
|
||||
};
|
||||
},
|
||||
|
||||
getKind(options) {
|
||||
const type = options.spec?.type;
|
||||
|
||||
|
||||
@@ -25,7 +25,12 @@ export * from './modelActions';
|
||||
export * from './sources';
|
||||
export type {
|
||||
CatalogModel,
|
||||
CatalogModelLayer,
|
||||
CatalogModelAnnotationSummary,
|
||||
CatalogModelKind,
|
||||
CatalogModelKindSummary,
|
||||
CatalogModelLabelSummary,
|
||||
CatalogModelLayer,
|
||||
CatalogModelRelation,
|
||||
CatalogModelRelationSummary,
|
||||
CatalogModelTagSummary,
|
||||
} from './types';
|
||||
|
||||
@@ -72,6 +72,26 @@ export const OpaqueCatalogModelLayer = OpaqueType.create<{
|
||||
* @alpha
|
||||
*/
|
||||
export interface CatalogModel {
|
||||
/**
|
||||
* Lists all kinds in the model.
|
||||
*/
|
||||
listKinds(): CatalogModelKindSummary[];
|
||||
|
||||
/**
|
||||
* Lists all relations in the model.
|
||||
*/
|
||||
listRelations(): CatalogModelRelationSummary[];
|
||||
|
||||
/**
|
||||
* Returns summaries of the shared metadata fields in the model, including
|
||||
* all declared annotations, labels, and tags.
|
||||
*/
|
||||
getMetadata(): {
|
||||
annotations: CatalogModelAnnotationSummary[];
|
||||
labels: CatalogModelLabelSummary[];
|
||||
tags: CatalogModelTagSummary[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Look up a kind in the model.
|
||||
*
|
||||
@@ -104,6 +124,11 @@ export interface CatalogModel {
|
||||
* @alpha
|
||||
*/
|
||||
export interface CatalogModelKind {
|
||||
/**
|
||||
* A human-readable description of the kind.
|
||||
*/
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* The API version(s) of the kind that this schema applies to, e.g.
|
||||
* "backstage.io/v1alpha1".
|
||||
@@ -171,6 +196,63 @@ export interface CatalogModelKind {
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region CatalogModelKindSummary
|
||||
|
||||
/**
|
||||
* A summary of a catalog model kind, without version-specific details such as
|
||||
* schemas and relation fields.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface CatalogModelKindSummary {
|
||||
/**
|
||||
* A human-readable description of the kind.
|
||||
*/
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* The names used for this kind.
|
||||
*/
|
||||
names: {
|
||||
/**
|
||||
* The name of the kind with proper casing, e.g. "Component".
|
||||
*/
|
||||
kind: string;
|
||||
|
||||
/**
|
||||
* The singular form of the kind name, e.g. "component".
|
||||
*/
|
||||
singular: string;
|
||||
|
||||
/**
|
||||
* The plural form of the kind name, e.g. "components".
|
||||
*/
|
||||
plural: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* The available versions and spec types for this kind.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Each entry represents a unique apiVersion/specType combination that can be
|
||||
* passed to {@link CatalogModel.getKind} to retrieve the full kind details.
|
||||
*/
|
||||
versions: Array<{
|
||||
/**
|
||||
* The API version, e.g. "backstage.io/v1alpha1".
|
||||
*/
|
||||
apiVersion: string;
|
||||
/**
|
||||
* The spec type, if any, e.g. "service". Undefined means the default
|
||||
* (untyped) version.
|
||||
*/
|
||||
specType?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region CatalogModelRelation
|
||||
|
||||
/**
|
||||
@@ -208,3 +290,113 @@ export interface CatalogModelRelation {
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region CatalogModelAnnotationSummary
|
||||
|
||||
/**
|
||||
* A summary of a catalog model annotation.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface CatalogModelAnnotationSummary {
|
||||
/**
|
||||
* The annotation key, e.g. "backstage.io/managed-by-location".
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* A short human-readable title for the annotation.
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* A human-readable description of the annotation.
|
||||
*/
|
||||
description: string;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region CatalogModelLabelSummary
|
||||
|
||||
/**
|
||||
* A summary of a catalog model label.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface CatalogModelLabelSummary {
|
||||
/**
|
||||
* The label key, e.g. "backstage.io/orphan".
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* A short human-readable title for the label.
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* A human-readable description of the label.
|
||||
*/
|
||||
description: string;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region CatalogModelTagSummary
|
||||
|
||||
/**
|
||||
* A summary of a catalog model tag.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface CatalogModelTagSummary {
|
||||
/**
|
||||
* The tag value, e.g. "java".
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* A short human-readable title for the tag.
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* A human-readable description of the tag.
|
||||
*/
|
||||
description: string;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region CatalogModelRelationSummary
|
||||
|
||||
/**
|
||||
* A summary of a catalog model relation.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface CatalogModelRelationSummary {
|
||||
/**
|
||||
* The kinds that this relation can originate from.
|
||||
*/
|
||||
fromKind: string[];
|
||||
/**
|
||||
* The kinds that this relation can point to.
|
||||
*/
|
||||
toKind: string[];
|
||||
/**
|
||||
* A human-readable description of the relation.
|
||||
*/
|
||||
description: string;
|
||||
/**
|
||||
* The forward direction of this relation.
|
||||
*/
|
||||
forward: {
|
||||
type: string;
|
||||
title: string;
|
||||
};
|
||||
/**
|
||||
* The reverse direction of this relation.
|
||||
*/
|
||||
reverse: {
|
||||
type: string;
|
||||
title: string;
|
||||
};
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
Reference in New Issue
Block a user