Merge pull request #10532 from brethubbard/index-group-display-name

Search: Index group display name in DefaultCatalogCollatorFactory
This commit is contained in:
Fredrik Adelöw
2022-04-09 14:25:55 +02:00
committed by GitHub
4 changed files with 191 additions and 24 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Added `spec.profile.displayName` to search index for Group kinds
@@ -23,11 +23,7 @@ import {
CatalogClient,
GetEntitiesRequest,
} from '@backstage/catalog-client';
import {
Entity,
stringifyEntityRef,
UserEntity,
} from '@backstage/catalog-model';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import { DocumentCollatorFactory } from '@backstage/plugin-search-common';
import {
@@ -36,6 +32,7 @@ import {
} from '@backstage/plugin-catalog-common';
import { Permission } from '@backstage/plugin-permission-common';
import { Readable } from 'stream';
import { getDocumentText } from './util';
/** @public */
export type DefaultCatalogCollatorFactoryOptions = {
@@ -100,24 +97,6 @@ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory {
return formatted.toLowerCase();
}
private isUserEntity(entity: Entity): entity is UserEntity {
return entity.kind.toLocaleUpperCase('en-US') === 'USER';
}
private getDocumentText(entity: Entity): string {
let documentText = entity.metadata.description || '';
if (this.isUserEntity(entity)) {
if (entity.spec?.profile?.displayName && documentText) {
// combine displayName and description
const displayName = entity.spec?.profile?.displayName;
documentText = displayName.concat(' : ', documentText);
} else {
documentText = entity.spec?.profile?.displayName || documentText;
}
}
return documentText;
}
private async *execute(): AsyncGenerator<CatalogEntityDocument> {
const { token } = await this.tokenManager.getToken();
let entitiesRetrieved = 0;
@@ -150,7 +129,7 @@ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory {
kind: entity.kind,
name: entity.metadata.name,
}),
text: this.getDocumentText(entity),
text: getDocumentText(entity),
componentType: entity.spec?.type?.toString() || 'other',
type: entity.spec?.type?.toString() || 'other',
namespace: entity.metadata.namespace || 'default',
@@ -0,0 +1,146 @@
/*
* Copyright 2022 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 {
ComponentEntity,
GroupEntity,
UserEntity,
} from '@backstage/catalog-model';
import { getDocumentText } from './util';
describe('getDocumentText', () => {
describe('kind is not User or Group', () => {
test('contains description if set', () => {
const entity = createComponent();
entity.metadata.description = 'The expected description';
const actual = getDocumentText(entity);
expect(actual).toContain(entity.metadata.description);
});
test('is empty if description is not set', () => {
const entity = createComponent();
const actual = getDocumentText(entity);
expect(actual).toEqual('');
});
});
describe('kind is User', () => {
test('contains display name if set', () => {
const entity = createUser();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.spec.profile?.displayName);
});
test('contains description if set', () => {
const entity = createUser();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.metadata.description);
});
test('contains both description and display name if both are set', () => {
const entity = createUser();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.spec.profile?.displayName);
expect(actual).toContain(entity.metadata.description);
});
test('is empty if description and display name are not set', () => {
const entity = createUser();
delete entity.metadata.description;
delete entity.spec.profile?.displayName;
const actual = getDocumentText(entity);
expect(actual).toEqual('');
});
});
describe('kind is Group', () => {
test('contains display name if set', () => {
const entity = createGroup();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.spec.profile?.displayName);
});
test('contains description if set', () => {
const entity = createGroup();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.metadata.description);
});
test('contains both description and display name if both are set', () => {
const entity = createGroup();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.spec.profile?.displayName);
expect(actual).toContain(entity.metadata.description);
});
test('is empty if description and display name are not set', () => {
const entity = createGroup();
delete entity.metadata.description;
delete entity.spec.profile?.displayName;
const actual = getDocumentText(entity);
expect(actual).toEqual('');
});
});
});
function createGroup(): GroupEntity {
return {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'group-1',
description: 'The expected description',
},
spec: {
type: 'team',
profile: {
displayName: 'Group 1',
},
children: [],
},
};
}
function createUser(): UserEntity {
return {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
name: 'user-1',
description: 'The expected description',
},
spec: {
profile: {
displayName: 'User 1',
},
},
};
}
function createComponent(): ComponentEntity {
return {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'component-1',
},
spec: {
lifecycle: 'experimental',
owner: 'someone',
type: 'service',
},
};
}
@@ -0,0 +1,37 @@
/*
* Copyright 2022 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 { Entity, UserEntity } from '@backstage/catalog-model';
function isUserEntity(entity: Entity): entity is UserEntity {
return entity.kind.toLocaleUpperCase('en-US') === 'USER';
}
function isGroupEntity(entity: Entity): entity is UserEntity {
return entity.kind.toLocaleUpperCase('en-US') === 'GROUP';
}
export function getDocumentText(entity: Entity): string {
const documentTexts: string[] = [];
documentTexts.push(entity.metadata.description || '');
if (isUserEntity(entity) || isGroupEntity(entity)) {
if (entity.spec?.profile?.displayName) {
documentTexts.push(entity.spec.profile.displayName);
}
}
return documentTexts.join(' : ');
}