libs/designer/src/lib/ui/panel/recommendation/SearchOpeationsService.tsx (82 lines of code) (raw):
import type { DiscoveryOpArray, DiscoveryOperation, DiscoveryResultTypes, ISearchService } from '@microsoft/logic-apps-shared';
import { isBuiltInConnector, isCustomConnector } from '@microsoft/designer-ui';
import Fuse from 'fuse.js';
export class DefaultSearchOperationsService implements Pick<ISearchService, 'searchOperations'> {
constructor(
private allOperations: DiscoveryOpArray,
private showParseDocWithMetadata: boolean
) {}
// Comparison function for sorting Fuse results
private compareItems(
a: Fuse.FuseResult<DiscoveryOperation<DiscoveryResultTypes>>,
b: Fuse.FuseResult<DiscoveryOperation<DiscoveryResultTypes>>
): number {
const isACustom = a.item.properties.isCustomApi ?? false;
const isBCustom = b.item.properties.isCustomApi ?? false;
if (isACustom !== isBCustom) {
return isACustom ? 1 : -1;
}
if (a.score !== undefined && b.score !== undefined) {
return a.score < b.score ? -1 : a.score > b.score ? 1 : 0;
}
return a.score === undefined ? 1 : -1;
}
// Fuse search options configuration
private searchOptions() {
return {
includeScore: true,
threshold: 0.4,
ignoreLocation: true,
keys: [
{ name: 'properties.summary', weight: 2.1 },
{
name: 'displayName',
getFn: (operation: DiscoveryOperation<DiscoveryResultTypes>) => operation.properties.api.displayName,
weight: 2,
},
{
name: 'description',
getFn: (operation: DiscoveryOperation<DiscoveryResultTypes>) => operation.properties.api.description ?? '',
weight: 1.9,
},
],
};
}
// Search operation method with various filters
public async searchOperations(
searchTerm: string,
actionType?: string,
runtimeFilter?: string,
additionalFilter?: (operation: DiscoveryOperation<DiscoveryResultTypes>) => boolean
): Promise<DiscoveryOpArray> {
if (!this.allOperations) {
return [];
}
const filterItems = (result: Fuse.FuseResult<DiscoveryOperation<DiscoveryResultTypes>>): boolean => {
const { item } = result;
const { api } = item.properties;
// Skip if document with metadata should not be shown
if (!this.showParseDocWithMetadata && item.id === 'parsedocumentwithmetadata') {
return false;
}
// Apply runtime filters
if (runtimeFilter) {
if (
(runtimeFilter === 'inapp' && !isBuiltInConnector(api)) ||
(runtimeFilter === 'custom' && !isCustomConnector(api)) ||
(runtimeFilter === 'shared' && (isBuiltInConnector(api) || isCustomConnector(api)))
) {
return false;
}
}
// Apply action type filters (actions vs triggers)
if (actionType) {
const isTrigger = item.properties?.trigger !== undefined;
if ((actionType.toLowerCase() === 'actions' && isTrigger) || (actionType.toLowerCase() === 'triggers' && !isTrigger)) {
return false;
}
}
// Apply any additional filter provided
return additionalFilter ? additionalFilter(item) : true;
};
const fuse = new Fuse(this.allOperations, this.searchOptions());
return fuse
.search(searchTerm, { limit: 100 })
.filter(filterItems)
.sort(this.compareItems)
.map((result) => result.item);
}
}