Merge branch 'master' into generic-grid--page
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Fix `EntityTypeFilter` so it produces unique case-insensitive set of available types
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Scaffolder: Added an 'eq' handlebars helper for use in software template YAML files. This can be used to execute a step depending on the value of an input, e.g.:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
id: 'conditional-step'
|
||||
action: 'custom-action'
|
||||
if: '{{ eq parameters.myvalue "custom" }}',
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-search-backend-node': minor
|
||||
---
|
||||
|
||||
Change return value of `SearchEngine.index` to `Promise<void>` to support
|
||||
implementation of external search engines.
|
||||
@@ -132,4 +132,4 @@ Integrating infrastructure of this size and complexity can seem overwhelming. It
|
||||
|
||||
## More questions about adopting Backstage?
|
||||
|
||||
[Contact the Backstage team at Spotify.](https://calendly.com/spotify-backstage) We’ll share more about what we’ve learned from our experience here at Spotify — and from other companies who are already using Backstage to transform their developer experience.
|
||||
[Contact the Backstage team at Spotify.](https://backstage.spotify.com/) We’ll share more about what we’ve learned from our experience here at Spotify — and from other companies who are already using Backstage to transform their developer experience.
|
||||
|
||||
@@ -86,10 +86,11 @@ export function useEntityTypeFilter(): EntityTypeReturn {
|
||||
const countByType = entities.reduce((acc, entity) => {
|
||||
if (typeof entity.spec?.type !== 'string') return acc;
|
||||
|
||||
if (!acc[entity.spec.type]) {
|
||||
acc[entity.spec.type] = 0;
|
||||
const entityType = entity.spec.type.toLocaleLowerCase('en-US');
|
||||
if (!acc[entityType]) {
|
||||
acc[entityType] = 0;
|
||||
}
|
||||
acc[entity.spec.type] += 1;
|
||||
acc[entityType] += 1;
|
||||
return acc;
|
||||
}, {} as Record<string, number>);
|
||||
|
||||
|
||||
@@ -203,6 +203,39 @@ describe('TaskWorker', () => {
|
||||
expect((event?.body?.output as JsonObject).result).toBe('winning');
|
||||
});
|
||||
|
||||
it('should execute steps conditionally with eq helper', async () => {
|
||||
const broker = new StorageTaskBroker(storage, logger);
|
||||
const taskWorker = new TaskWorker({
|
||||
logger,
|
||||
workingDirectory: os.tmpdir(),
|
||||
actionRegistry,
|
||||
taskBroker: broker,
|
||||
});
|
||||
|
||||
const { taskId } = await broker.dispatch({
|
||||
steps: [
|
||||
{ id: 'test', name: 'test', action: 'test-action' },
|
||||
{
|
||||
id: 'conditional',
|
||||
name: 'conditional',
|
||||
action: 'test-action',
|
||||
if: '{{ eq steps.test.output.testOutput "winning" }}',
|
||||
},
|
||||
],
|
||||
output: {
|
||||
result: '{{ steps.conditional.output.testOutput }}',
|
||||
},
|
||||
values: {},
|
||||
});
|
||||
|
||||
const task = await broker.claim();
|
||||
await taskWorker.runOneTask(task);
|
||||
|
||||
const { events } = await storage.listEvents({ taskId });
|
||||
const event = events.find(e => e.type === 'completion');
|
||||
expect((event?.body?.output as JsonObject).result).toBe('winning');
|
||||
});
|
||||
|
||||
it('should skip steps conditionally', async () => {
|
||||
const broker = new StorageTaskBroker(storage, logger);
|
||||
const taskWorker = new TaskWorker({
|
||||
|
||||
@@ -56,6 +56,8 @@ export class TaskWorker {
|
||||
this.handlebars.registerHelper('json', obj => JSON.stringify(obj));
|
||||
|
||||
this.handlebars.registerHelper('not', value => !isTruthy(value));
|
||||
|
||||
this.handlebars.registerHelper('eq', (a, b) => a === b);
|
||||
}
|
||||
|
||||
start() {
|
||||
|
||||
@@ -32,7 +32,7 @@ export class LunrSearchEngine implements SearchEngine {
|
||||
// (undocumented)
|
||||
protected docStore: Record<string, IndexableDocument>;
|
||||
// (undocumented)
|
||||
index(type: string, documents: IndexableDocument[]): void;
|
||||
index(type: string, documents: IndexableDocument[]): Promise<void>;
|
||||
// (undocumented)
|
||||
protected logger: Logger_2;
|
||||
// (undocumented)
|
||||
@@ -57,7 +57,7 @@ export class Scheduler {
|
||||
|
||||
// @public
|
||||
export interface SearchEngine {
|
||||
index(type: string, documents: IndexableDocument[]): void;
|
||||
index(type: string, documents: IndexableDocument[]): Promise<void>;
|
||||
query(query: SearchQuery): Promise<SearchResultSet>;
|
||||
setTranslator(translator: QueryTranslator): void;
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ export class IndexBuilder {
|
||||
}
|
||||
|
||||
// pushing documents to index to a configured search engine.
|
||||
this.searchEngine.index(type, documents);
|
||||
await this.searchEngine.index(type, documents);
|
||||
}, this.collators[type].refreshInterval * 1000);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { ConcreteLunrQuery, LunrSearchEngine } from './LunrSearchEngine';
|
||||
import { SearchEngine } from '../types';
|
||||
import lunr from 'lunr';
|
||||
import { SearchEngine } from '../types';
|
||||
import { ConcreteLunrQuery, LunrSearchEngine } from './LunrSearchEngine';
|
||||
|
||||
/**
|
||||
* Just used to test the default translator shipped with LunrSearchEngine.
|
||||
@@ -45,7 +45,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine.setTranslator(translatorSpy);
|
||||
|
||||
// When: querying the search engine
|
||||
testLunrSearchEngine.query({
|
||||
await testLunrSearchEngine.query({
|
||||
term: 'testTerm',
|
||||
filters: {},
|
||||
pageCursor: '',
|
||||
@@ -259,7 +259,7 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock indexing of 1 document
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
|
||||
// Perform search query
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
@@ -282,7 +282,7 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock indexing of 1 document
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
|
||||
// Perform search query
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
@@ -315,7 +315,7 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock indexing of 1 document
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
|
||||
// Perform search query
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
@@ -347,7 +347,7 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock indexing of 1 document
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
|
||||
// Perform search query
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
@@ -379,7 +379,7 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock indexing of 1 document
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
|
||||
// Perform search query
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
@@ -412,7 +412,7 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock indexing of 1 document
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
|
||||
// Perform search query
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
@@ -445,7 +445,7 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock indexing of 1 document
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
|
||||
// Perform search query
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
@@ -483,7 +483,7 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock indexing of 2 documents
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
|
||||
// Perform search query
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
@@ -527,8 +527,8 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock 2 indices with 1 document each
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
testLunrSearchEngine.index('test-index-2', mockDocuments2);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index-2', mockDocuments2);
|
||||
// Perform search query scoped to "test-index-2" with a filter on the field "extraField"
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
term: 'testTitle',
|
||||
@@ -565,7 +565,7 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock indexing of 2 documents
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
|
||||
// Perform search query
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
@@ -618,8 +618,8 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// Mock 2 indices with 2 documents each
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
testLunrSearchEngine.index('test-index-2', mockDocuments2);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index-2', mockDocuments2);
|
||||
|
||||
// Perform search query scoped to "test-index-2"
|
||||
const mockedSearchResult = await testLunrSearchEngine.query({
|
||||
@@ -661,7 +661,7 @@ describe('LunrSearchEngine', () => {
|
||||
];
|
||||
|
||||
// call index func and ensure the index func was invoked.
|
||||
testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
await testLunrSearchEngine.index('test-index', mockDocuments);
|
||||
expect(indexSpy).toHaveBeenCalled();
|
||||
expect(indexSpy).toHaveBeenCalledWith('test-index', [
|
||||
{ title: 'testTerm', text: 'testText', location: 'test/location' },
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
SearchQuery,
|
||||
IndexableDocument,
|
||||
SearchQuery,
|
||||
SearchResultSet,
|
||||
} from '@backstage/search-common';
|
||||
import lunr from 'lunr';
|
||||
import { Logger } from 'winston';
|
||||
import { SearchEngine, QueryTranslator } from '../types';
|
||||
import { QueryTranslator, SearchEngine } from '../types';
|
||||
|
||||
export type ConcreteLunrQuery = {
|
||||
lunrQueryBuilder: lunr.Index.QueryBuilder;
|
||||
@@ -113,7 +113,7 @@ export class LunrSearchEngine implements SearchEngine {
|
||||
this.translator = translator;
|
||||
}
|
||||
|
||||
index(type: string, documents: IndexableDocument[]): void {
|
||||
async index(type: string, documents: IndexableDocument[]): Promise<void> {
|
||||
const lunrBuilder = new lunr.Builder();
|
||||
|
||||
lunrBuilder.pipeline.add(lunr.trimmer, lunr.stopWordFilter, lunr.stemmer);
|
||||
@@ -139,7 +139,7 @@ export class LunrSearchEngine implements SearchEngine {
|
||||
this.lunrIndices[type] = lunrBuilder.build();
|
||||
}
|
||||
|
||||
query(query: SearchQuery): Promise<SearchResultSet> {
|
||||
async query(query: SearchQuery): Promise<SearchResultSet> {
|
||||
const { lunrQueryBuilder, documentTypes } = this.translator(
|
||||
query,
|
||||
) as ConcreteLunrQuery;
|
||||
@@ -183,6 +183,6 @@ export class LunrSearchEngine implements SearchEngine {
|
||||
}),
|
||||
};
|
||||
|
||||
return Promise.resolve(realResultSet);
|
||||
return realResultSet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ export interface SearchEngine {
|
||||
/**
|
||||
* Add the given documents to the SearchEngine index of the given type.
|
||||
*/
|
||||
index(type: string, documents: IndexableDocument[]): void;
|
||||
index(type: string, documents: IndexableDocument[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Perform a search query against the SearchEngine.
|
||||
|
||||
Reference in New Issue
Block a user