fix api reports + create StaticExploreToolProvider

Signed-off-by: Andrew Thauer <athauer@wealthsimple.com>
This commit is contained in:
Andrew Thauer
2022-11-11 07:14:43 -05:00
parent 3eaa4bdfbc
commit ce34320d91
16 changed files with 200 additions and 24 deletions
+8
View File
@@ -32,6 +32,14 @@ export interface RouterOptions {
toolProvider: ExploreToolProvider;
}
// @public
export class StaticExploreToolProvider implements ExploreToolProvider {
// (undocumented)
static fromData(tools: ExploreTool[]): StaticExploreToolProvider;
// (undocumented)
getTools(request: GetExploreToolsRequest): Promise<GetExploreToolsResponse>;
}
// @public
export interface ToolDocument extends IndexableDocument, ExploreTool {}
+2 -1
View File
@@ -22,8 +22,9 @@
export * from './search';
export * from './service';
export * from './tools';
/**
* @internal Example only - do not use in production
*/
export { getExampleTools } from './example/getExampleTools';
export { exampleTools } from './example/exampleTools';
@@ -16,4 +16,3 @@
export { createRouter } from './router';
export type { RouterOptions } from './router';
export * from './types';
@@ -21,9 +21,8 @@ import {
} from '@backstage/plugin-explore-common';
import express from 'express';
import request from 'supertest';
import { ExploreToolProvider } from '../tools';
import { createRouter } from './router';
import { ExploreToolProvider } from './types';
const mockTools: ExploreTool[] = [
{ title: 'Tool 1', url: 'https://example.com/tool1', image: '' },
@@ -19,7 +19,7 @@ import { GetExploreToolsRequest } from '@backstage/plugin-explore-common';
import express from 'express';
import Router from 'express-promise-router';
import { Logger } from 'winston';
import { ExploreToolProvider } from './types';
import { ExploreToolProvider } from '../tools';
/**
* @public
@@ -0,0 +1,18 @@
/*
* 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.
*/
export { StaticExploreToolProvider } from './providers';
export type { ExploreToolProvider } from './types';
@@ -0,0 +1,85 @@
/*
* 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 { ExploreTool } from '@backstage/plugin-explore-common';
import { StaticExploreToolProvider } from './providers';
describe('StaticExploreToolProvider', () => {
const tool1: ExploreTool = {
title: 'Tool 1',
image: 'https://example/image.png',
url: 'https://example.com',
lifecycle: 'production',
tags: ['tag1', 'tag2'],
};
const tool2: ExploreTool = {
...tool1,
title: 'Tool 2',
lifecycle: 'production',
tags: ['tag1'],
};
const tool3: ExploreTool = {
...tool1,
title: 'Tool 3',
lifecycle: 'experimental',
tags: ['tag2'],
};
const allTools: ExploreTool[] = [tool1, tool2, tool3];
describe('getTools', () => {
it('returns a list of all tools', async () => {
const provider = StaticExploreToolProvider.fromData(allTools);
await expect(provider.getTools({})).resolves.toEqual({
tools: allTools,
});
});
it.each([
[['tag1'], [tool1, tool2]],
[['tag2'], [tool1, tool3]],
[[], allTools],
])(
'returns % when filtered by tags %',
async (tagFilter, expectedTools) => {
const provider = StaticExploreToolProvider.fromData(allTools);
await expect(
provider.getTools({ filter: { tags: tagFilter } }),
).resolves.toEqual({
tools: expectedTools,
});
},
);
it.each([
[['production'], [tool1, tool2]],
[['experimental'], [tool3]],
[[], allTools],
])(
'returns % when filtered by lifecycle %',
async (lifecycleFilter, expectedTools) => {
const provider = StaticExploreToolProvider.fromData(allTools);
await expect(
provider.getTools({ filter: { lifecycle: lifecycleFilter } }),
).resolves.toEqual({
tools: expectedTools,
});
},
);
});
});
@@ -1,5 +1,5 @@
/*
* Copyright 2020 The Backstage Authors
* 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.
@@ -15,11 +15,12 @@
*/
import {
ExploreTool,
GetExploreToolsRequest,
GetExploreToolsResponse,
} from '@backstage/plugin-explore-common';
import { intersection, isEmpty } from 'lodash';
import { exampleTools } from './tools';
import { ExploreToolProvider } from './types';
const anyOf = <T>(prop: T | T[], matches: T[]) =>
isEmpty(matches)
@@ -27,18 +28,32 @@ const anyOf = <T>(prop: T | T[], matches: T[]) =>
: intersection([...[prop]].flat(), matches)?.length > 0;
/**
* @private Example only - do not use in production
* A basic ExploreToolProvider implementation using static data.
*
* @public
*/
export async function getExampleTools(
request: GetExploreToolsRequest,
): Promise<GetExploreToolsResponse> {
const { filter } = request ?? {};
const tags = filter?.tags ?? [];
const lifecycles = filter?.lifecycle ?? [];
export class StaticExploreToolProvider implements ExploreToolProvider {
private readonly tools: ExploreTool[];
return {
tools: exampleTools.filter(
static fromData(tools: ExploreTool[]) {
return new StaticExploreToolProvider(tools);
}
private constructor(tools: ExploreTool[]) {
this.tools = tools;
}
async getTools(
request: GetExploreToolsRequest,
): Promise<GetExploreToolsResponse> {
const { filter } = request ?? {};
const tags = filter?.tags ?? [];
const lifecycles = filter?.lifecycle ?? [];
const tools = this.tools.filter(
t => anyOf(t.tags ?? [], tags) && anyOf(t.lifecycle ?? [], lifecycles),
),
};
);
return { tools };
}
}
+46
View File
@@ -5,10 +5,18 @@
```ts
/// <reference types="react" />
import { ApiRef } from '@backstage/core-plugin-api';
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { default as default_2 } from 'react';
import { DiscoveryApi } from '@backstage/core-plugin-api';
import { DomainEntity } from '@backstage/catalog-model';
import { ExternalRouteRef } from '@backstage/core-plugin-api';
import { FetchApi } from '@backstage/core-plugin-api';
import { GetExploreToolsRequest } from '@backstage/plugin-explore-common';
import { GetExploreToolsResponse } from '@backstage/plugin-explore-common';
import { IndexableDocument } from '@backstage/plugin-search-common';
import { ReactNode } from 'react';
import { ResultHighlight } from '@backstage/plugin-search-common';
import { RouteRef } from '@backstage/core-plugin-api';
import { TabProps } from '@material-ui/core';
@@ -30,6 +38,27 @@ export const DomainExplorerContent: (props: {
title?: string | undefined;
}) => JSX.Element;
// @public
export interface ExploreApi {
getTools(request?: GetExploreToolsRequest): Promise<GetExploreToolsResponse>;
}
// @public (undocumented)
export const exploreApiRef: ApiRef<ExploreApi>;
// @public
export class ExploreClient implements ExploreApi {
constructor({
discoveryApi,
fetchApi,
}: {
discoveryApi: DiscoveryApi;
fetchApi: FetchApi;
});
// (undocumented)
getTools(request?: GetExploreToolsRequest): Promise<GetExploreToolsResponse>;
}
// @public
export const ExploreLayout: {
(props: ExploreLayoutProps): JSX.Element;
@@ -91,4 +120,21 @@ export type SubRoute = {
export const ToolExplorerContent: (props: {
title?: string | undefined;
}) => JSX.Element;
// @public (undocumented)
export function ToolSearchResultListItem(
props: ToolSearchResultListItemProps,
): JSX.Element;
// @public
export interface ToolSearchResultListItemProps {
// (undocumented)
highlight?: ResultHighlight;
// (undocumented)
icon?: ReactNode;
// (undocumented)
rank?: number;
// (undocumented)
result: IndexableDocument;
}
```
+3
View File
@@ -34,6 +34,9 @@ export interface ExploreApi {
getTools(request?: GetExploreToolsRequest): Promise<GetExploreToolsResponse>;
}
/**
* @public
*/
export const exploreApiRef = createApiRef<ExploreApi>({
id: 'plugin.explore.service',
});
@@ -28,15 +28,20 @@ import {
WarningPanel,
} from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
// import { exploreApiRef } from '../../api';
const Body = () => {
const exploreToolsConfigApi = useApi(exploreToolsConfigRef);
// const exploreApi = useApi(exploreApiRef);
const {
value: tools,
loading,
error,
} = useAsync(async () => {
return await exploreToolsConfigApi.getTools();
// TODO: Enable the backend ExploreClient
// return (await exploreApi.getTools())?.tools;
}, [exploreToolsConfigApi]);
if (loading) {