Merge pull request #9679 from backstage/emmaindal/stack-overflow-plugin
[Plugin] Stack Overflow
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-home': patch
|
||||
---
|
||||
|
||||
- Adds new `HomePageStackOverflowQuestions` component which renders a list of stack overflow questions on your homepage.
|
||||
|
||||
- Exports `ComponentRenderer` type.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-stack-overflow-backend': minor
|
||||
---
|
||||
|
||||
Add stack overflow backend plugin
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-stack-overflow': minor
|
||||
---
|
||||
|
||||
Add stack overflow plugin
|
||||
@@ -44,6 +44,8 @@
|
||||
/plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka
|
||||
/plugins/search @backstage/reviewers @backstage/techdocs-core
|
||||
/plugins/search-* @backstage/reviewers @backstage/techdocs-core
|
||||
/plugins/stack-overflow @backstage/reviewers @backstage/techdocs-core
|
||||
/plugins/stack-overflow-backend @backstage/reviewers @backstage/techdocs-core
|
||||
/plugins/sonarqube @backstage/reviewers @backstage/sda-se-reviewers
|
||||
/plugins/techdocs @backstage/reviewers @backstage/techdocs-core
|
||||
/plugins/techdocs-* @backstage/reviewers @backstage/techdocs-core
|
||||
|
||||
@@ -39,6 +39,11 @@ export const ComponentAccordion: ({
|
||||
ContextProvider?: ((props: any) => JSX.Element) | undefined;
|
||||
}) => JSX.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ComponentRenderer = {
|
||||
Renderer?: (props: RendererProps) => JSX.Element;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ComponentTab" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
@@ -103,7 +108,6 @@ export const HomepageCompositionRoot: (props: {
|
||||
children?: ReactNode;
|
||||
}) => JSX.Element;
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "ComponentRenderer" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "HomePageRandomJoke" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
@@ -161,5 +165,6 @@ export const WelcomeTitle: () => JSX.Element;
|
||||
|
||||
// Warnings were encountered during analysis:
|
||||
//
|
||||
// src/extensions.d.ts:24:5 - (ae-forgotten-export) The symbol "ComponentParts" needs to be exported by the entry point index.d.ts
|
||||
// src/extensions.d.ts:6:5 - (ae-forgotten-export) The symbol "RendererProps" needs to be exported by the entry point index.d.ts
|
||||
// src/extensions.d.ts:27:5 - (ae-forgotten-export) The symbol "ComponentParts" needs to be exported by the entry point index.d.ts
|
||||
```
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
"@backstage/core-plugin-api": "^1.0.0",
|
||||
"@backstage/plugin-catalog-react": "^1.0.1-next.0",
|
||||
"@backstage/plugin-search": "^0.7.5-next.0",
|
||||
"@backstage/plugin-stack-overflow": "^0.0.0",
|
||||
"@backstage/theme": "^0.2.15",
|
||||
"@backstage/config": "^0.1.15",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
|
||||
@@ -21,6 +21,9 @@ import { InfoCard } from '@backstage/core-components';
|
||||
import { SettingsModal } from './components';
|
||||
import { createReactExtension, useApp } from '@backstage/core-plugin-api';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ComponentRenderer = {
|
||||
Renderer?: (props: RendererProps) => JSX.Element;
|
||||
};
|
||||
|
||||
@@ -35,3 +35,4 @@ export {
|
||||
export { SettingsModal, HeaderWorldClock } from './components';
|
||||
export type { ClockConfig } from './components';
|
||||
export { createCardExtension } from './extensions';
|
||||
export type { ComponentRenderer } from './extensions';
|
||||
|
||||
@@ -28,12 +28,15 @@ import {
|
||||
MockStarredEntitiesApi,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { configApiRef } from '@backstage/core-plugin-api';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
HomePageSearchBar,
|
||||
SearchContextProvider,
|
||||
searchApiRef,
|
||||
searchPlugin,
|
||||
} from '@backstage/plugin-search';
|
||||
import { HomePageStackOverflowQuestions } from '@backstage/plugin-stack-overflow';
|
||||
import { Grid, makeStyles } from '@material-ui/core';
|
||||
import React, { ComponentType } from 'react';
|
||||
|
||||
@@ -56,6 +59,14 @@ export default {
|
||||
starredEntitiesApi,
|
||||
],
|
||||
[searchApiRef, { query: () => Promise.resolve({ results: [] }) }],
|
||||
[
|
||||
configApiRef,
|
||||
new ConfigReader({
|
||||
stackoverflow: {
|
||||
baseUrl: 'https://api.stackexchange.com/2.2',
|
||||
},
|
||||
}),
|
||||
],
|
||||
]}
|
||||
>
|
||||
<Story />
|
||||
@@ -131,14 +142,17 @@ export const DefaultTemplate = () => {
|
||||
<Grid item xs={12} md={6}>
|
||||
<InfoCard title="Composable Section">
|
||||
{/* placeholder for content */}
|
||||
<div style={{ height: 210 }} />
|
||||
<div style={{ height: 370 }} />
|
||||
</InfoCard>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<InfoCard title="Composable Section">
|
||||
{/* placeholder for content */}
|
||||
<div style={{ height: 210 }} />
|
||||
</InfoCard>
|
||||
<HomePageStackOverflowQuestions
|
||||
requestParams={{
|
||||
tagged: 'backstage',
|
||||
site: 'stackoverflow',
|
||||
pagesize: 5,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,44 @@
|
||||
# Stack Overflow
|
||||
|
||||
A plugin that provides stack overflow specific functionality that can be used in different ways (e.g. for search) to compose your Backstage App.
|
||||
|
||||
## Getting started
|
||||
|
||||
Before we begin, make sure:
|
||||
|
||||
- You have created your own standalone Backstage app using @backstage/create-app and not using a fork of the backstage repository. If you haven't setup Backstage already, start [here](https://backstage.io/docs/getting-started/).
|
||||
|
||||
To use any of the functionality this plugin provides, you need to start by configuring your App with the following config:
|
||||
|
||||
```yaml
|
||||
stackoverflow:
|
||||
baseUrl: https://api.stackexchange.com/2.2 # alternative: your internal stack overflow instance
|
||||
```
|
||||
|
||||
## Areas of Responsibility
|
||||
|
||||
This stack overflow backend plugin is primarily responsible for the following:
|
||||
|
||||
- Provides a `StackOverflowQuestionsCollatorFactory`, which can be used in the search backend to index stack overflow questions to your Backstage Search.
|
||||
|
||||
### Index Stack Overflow Questions to search
|
||||
|
||||
Before you are able to start index stack overflow questions to search, you need to go through the [search getting started guide](https://backstage.io/docs/features/search/getting-started).
|
||||
|
||||
When you have your `packages/backend/src/plugins/search.ts` file ready to make modifications, add the following code snippet to add the `StackOverflowQuestionsCollatorFactory`. Note that you can modify the `requestParams`.
|
||||
|
||||
> Note: if your baseUrl is set to the external stack overflow api `https://api.stackexchange.com/2.2`, you can find optional and required parameters under the official API documentation under [`Usage of /questions GET`](https://api.stackexchange.com/docs/questions)
|
||||
|
||||
```ts
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
factory: StackOverflowQuestionsCollatorFactory.fromConfig(env.config, {
|
||||
logger: env.logger,
|
||||
requestParams: {
|
||||
tagged: ['backstage'],
|
||||
site: 'stackoverflow',
|
||||
pagesize: 100,
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
## API Report File for "@backstage/plugin-stack-overflow-backend"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="node" />
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
import { DocumentCollatorFactory } from '@backstage/plugin-search-common';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { Logger } from 'winston';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
// @public
|
||||
export interface StackOverflowDocument extends IndexableDocument {
|
||||
// (undocumented)
|
||||
answers: number;
|
||||
// (undocumented)
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
// @public
|
||||
export class StackOverflowQuestionsCollatorFactory
|
||||
implements DocumentCollatorFactory
|
||||
{
|
||||
// (undocumented)
|
||||
execute(): AsyncGenerator<StackOverflowDocument>;
|
||||
// (undocumented)
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options: StackOverflowQuestionsCollatorFactoryOptions,
|
||||
): StackOverflowQuestionsCollatorFactory;
|
||||
// (undocumented)
|
||||
getCollator(): Promise<Readable>;
|
||||
// (undocumented)
|
||||
protected requestParams: StackOverflowQuestionsRequestParams;
|
||||
// (undocumented)
|
||||
readonly type: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type StackOverflowQuestionsCollatorFactoryOptions = {
|
||||
baseUrl?: string;
|
||||
requestParams: StackOverflowQuestionsRequestParams;
|
||||
logger: Logger;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type StackOverflowQuestionsRequestParams = {
|
||||
[key: string]: string | string[] | number;
|
||||
};
|
||||
```
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 interface Config {
|
||||
/**
|
||||
* Configuration options for the stack overflow plugin
|
||||
*/
|
||||
stackoverflow: {
|
||||
/**
|
||||
* The base url of the Stack Overflow API used for the plugin
|
||||
* @visibility backend
|
||||
*/
|
||||
baseUrl: string;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "@backstage/plugin-stack-overflow-backend",
|
||||
"version": "0.0.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "backend-plugin"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/stack-overflow-backend"
|
||||
},
|
||||
"keywords": [
|
||||
"backstage",
|
||||
"stack-overflow"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
"build": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"clean": "backstage-cli package clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/plugin-search-common": "^0.3.3-next.0",
|
||||
"@backstage/config": "^1.0.0",
|
||||
"qs": "^6.9.4",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"config"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Stack Overflow backend plugin
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export * from './search';
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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 {
|
||||
IndexableDocument,
|
||||
DocumentCollatorFactory,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import { Readable } from 'stream';
|
||||
import fetch from 'cross-fetch';
|
||||
import qs from 'qs';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
/**
|
||||
* Extended IndexableDocument with stack overflow specific properties
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface StackOverflowDocument extends IndexableDocument {
|
||||
answers: number;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Type representing the request parameters accepted by the {@link StackOverflowQuestionsCollatorFactory}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type StackOverflowQuestionsRequestParams = {
|
||||
[key: string]: string | string[] | number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for {@link StackOverflowQuestionsCollatorFactory}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type StackOverflowQuestionsCollatorFactoryOptions = {
|
||||
baseUrl?: string;
|
||||
requestParams: StackOverflowQuestionsRequestParams;
|
||||
logger: Logger;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search collator responsible for collecting stack overflow questions to index.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class StackOverflowQuestionsCollatorFactory
|
||||
implements DocumentCollatorFactory
|
||||
{
|
||||
protected requestParams: StackOverflowQuestionsRequestParams;
|
||||
private readonly baseUrl: string | undefined;
|
||||
private readonly logger: Logger;
|
||||
public readonly type: string = 'stack-overflow';
|
||||
|
||||
private constructor(options: StackOverflowQuestionsCollatorFactoryOptions) {
|
||||
this.baseUrl = options.baseUrl;
|
||||
this.requestParams = options.requestParams;
|
||||
this.logger = options.logger;
|
||||
}
|
||||
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options: StackOverflowQuestionsCollatorFactoryOptions,
|
||||
) {
|
||||
const baseUrl =
|
||||
config.getOptionalString('stackoverflow.baseUrl') ||
|
||||
'https://api.stackexchange.com/2.2';
|
||||
return new StackOverflowQuestionsCollatorFactory({ ...options, baseUrl });
|
||||
}
|
||||
|
||||
async getCollator() {
|
||||
return Readable.from(this.execute());
|
||||
}
|
||||
|
||||
async *execute(): AsyncGenerator<StackOverflowDocument> {
|
||||
if (!this.baseUrl) {
|
||||
this.logger.debug(
|
||||
`No stackoverflow.baseUrl configured in your app-config.yaml`,
|
||||
);
|
||||
}
|
||||
const params = qs.stringify(this.requestParams, {
|
||||
arrayFormat: 'comma',
|
||||
addQueryPrefix: true,
|
||||
});
|
||||
|
||||
const res = await fetch(`${this.baseUrl}/questions${params}`);
|
||||
const data = await res.json();
|
||||
|
||||
for (const question of data.items) {
|
||||
yield {
|
||||
title: question.title,
|
||||
location: question.link,
|
||||
text: question.owner.display_name,
|
||||
tags: question.tags,
|
||||
answers: question.answer_count,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 * from './StackOverflowQuestionsCollatorFactory';
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,52 @@
|
||||
# Stack Overflow
|
||||
|
||||
A plugin that provides stack overflow specific functionality that can be used in different ways (e.g. for homepage and search) to compose your Backstage App.
|
||||
|
||||
## Getting started
|
||||
|
||||
Before we begin, make sure:
|
||||
|
||||
- You have created your own standalone Backstage app using @backstage/create-app and not using a fork of the backstage repository. If you haven't setup Backstage already, start [here](https://backstage.io/docs/getting-started/).
|
||||
|
||||
To use any of the functionality this plugin provides, you need to start by configuring your App with the following config:
|
||||
|
||||
```yaml
|
||||
stackoverflow:
|
||||
baseUrl: https://api.stackexchange.com/2.2 # alternative: your internal stack overflow instance
|
||||
```
|
||||
|
||||
## Areas of Responsibility
|
||||
|
||||
This stack overflow frontend plugin is primarily responsible for the following:
|
||||
|
||||
- Exposing various stack-overflow related components like `<StackOverflowSearchResultListItem />` which can be used for composing the search page, and `<HomePageStackOverflowQuestions/>` which can be used for composing the homepage.
|
||||
|
||||
#### Use specific search result list item for Stack Overflow Question
|
||||
|
||||
When you have your `packages/app/src/components/search/SearchPage.tsx` file ready to make modifications, add the following code snippet to add the `StackOverflowSearchResultListItem` when the type of the search results are `stack-overflow`.
|
||||
|
||||
```tsx
|
||||
case 'stack-overflow':
|
||||
return (
|
||||
<StackOverflowSearchResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
#### Use Stack Overflow Questions on your homepage
|
||||
|
||||
Before you are able to add the stack overflow question component to your homepage, you need to go through the [homepage getting started guide](https://backstage.io/docs/getting-started/homepage). When its ready, add the following code snippet to your `packages/app/src/components/home/HomePage.tsx` file.
|
||||
|
||||
```tsx
|
||||
<Grid item xs={12} md={6}>
|
||||
<HomePageStackOverflowQuestions
|
||||
requestParams={{
|
||||
tagged: 'backstage',
|
||||
site: 'stackoverflow',
|
||||
pagesize: 5,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
```
|
||||
@@ -0,0 +1,46 @@
|
||||
## API Report File for "@backstage/plugin-stack-overflow"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="react" />
|
||||
|
||||
import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import { ComponentRenderer } from '@backstage/plugin-home';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
// @public
|
||||
export const HomePageStackOverflowQuestions: (
|
||||
props: ComponentRenderer & {
|
||||
title?: string | undefined;
|
||||
} & StackOverflowQuestionsContentProps,
|
||||
) => JSX.Element;
|
||||
|
||||
// @public
|
||||
export const stackOverflowPlugin: BackstagePlugin<{}, {}>;
|
||||
|
||||
// @public
|
||||
export type StackOverflowQuestion = {
|
||||
title: string;
|
||||
link: string;
|
||||
owner: Record<string, string>;
|
||||
tags: string[];
|
||||
answer_count: number;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type StackOverflowQuestionsContentProps = {
|
||||
requestParams: StackOverflowQuestionsRequestParams;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type StackOverflowQuestionsRequestParams = {
|
||||
[key: string]: string | string[] | number;
|
||||
};
|
||||
|
||||
// @public
|
||||
export const StackOverflowSearchResultListItem: (props: {
|
||||
result: any;
|
||||
icon?: ReactNode;
|
||||
}) => JSX.Element;
|
||||
```
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 interface Config {
|
||||
/**
|
||||
* Configuration options for the stack overflow plugin
|
||||
*/
|
||||
stackoverflow: {
|
||||
/**
|
||||
* The base url of the Stack Overflow API used for the plugin
|
||||
* @visibility frontend
|
||||
*/
|
||||
baseUrl: string;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "@backstage/plugin-stack-overflow",
|
||||
"version": "0.0.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "frontend-plugin"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "backstage-cli package start",
|
||||
"start": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"diff": "backstage-cli plugin:diff",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"clean": "backstage-cli package clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core-components": "^0.9.3-next.0",
|
||||
"@backstage/plugin-home": "^0.4.20-next.0",
|
||||
"@backstage/core-plugin-api": "^1.0.0",
|
||||
"@backstage/plugin-search-common": "^0.3.3-next.0",
|
||||
"@backstage/config": "^1.0.0",
|
||||
"@backstage/theme": "^0.2.15",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"qs": "^6.9.4",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"lodash": "^4.17.21",
|
||||
"react-use": "^17.2.4",
|
||||
"@testing-library/jest-dom": "^5.10.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.13.1 || ^17.0.0",
|
||||
"react": "^16.13.1 || ^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.16.1-next.0",
|
||||
"@backstage/test-utils": "^1.0.1-next.0",
|
||||
"@testing-library/react": "^12.1.3",
|
||||
"@backstage/core-app-api": "^1.0.1-next.0",
|
||||
"@backstage/dev-utils": "^1.0.1-next.0",
|
||||
"@testing-library/user-event": "^13.1.8",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^14.14.32",
|
||||
"msw": "^0.35.0"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"config"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 { useApi, configApiRef } from '@backstage/core-plugin-api';
|
||||
import { Link } from '@backstage/core-components';
|
||||
import {
|
||||
IconButton,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
ListItemSecondaryAction,
|
||||
} from '@material-ui/core';
|
||||
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import qs from 'qs';
|
||||
import React from 'react';
|
||||
import {
|
||||
StackOverflowQuestion,
|
||||
StackOverflowQuestionsContentProps,
|
||||
} from '../../types';
|
||||
|
||||
/**
|
||||
* A component to display a list of stack overflow questions on the homepage.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
export const Content = (props: StackOverflowQuestionsContentProps) => {
|
||||
const { requestParams } = props;
|
||||
const configApi = useApi(configApiRef);
|
||||
const baseUrl =
|
||||
configApi.getOptionalString('stackoverflow.baseUrl') ||
|
||||
'https://api.stackexchange.com/2.2';
|
||||
|
||||
const { value, loading, error } = useAsync(async (): Promise<
|
||||
StackOverflowQuestion[]
|
||||
> => {
|
||||
const params = qs.stringify(requestParams, { addQueryPrefix: true });
|
||||
const response = await fetch(`${baseUrl}/questions${params}`);
|
||||
const data = await response.json();
|
||||
return data.items;
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return <p>loading...</p>;
|
||||
}
|
||||
|
||||
if (error || !value || !value.length) {
|
||||
return <p>could not load questions</p>;
|
||||
}
|
||||
|
||||
const getSecondaryText = (answer_count: Number) =>
|
||||
answer_count > 1 ? `${answer_count} answers` : `${answer_count} answer`;
|
||||
|
||||
return (
|
||||
<List>
|
||||
{value.map(question => (
|
||||
<ListItem key={question.link}>
|
||||
<Link to={question.link}>
|
||||
<ListItemText
|
||||
primary={question.title}
|
||||
secondary={getSecondaryText(question.answer_count)}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton edge="end" aria-label="external-link">
|
||||
<OpenInNewIcon />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</Link>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
);
|
||||
};
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 { HomePageStackOverflowQuestions } from '../../plugin';
|
||||
import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { configApiRef } from '@backstage/core-plugin-api';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { Grid } from '@material-ui/core';
|
||||
import React, { ComponentType } from 'react';
|
||||
|
||||
export default {
|
||||
title: 'Plugins/Home/Components/StackOverflow',
|
||||
component: HomePageStackOverflowQuestions,
|
||||
decorators: [
|
||||
(Story: ComponentType<{}>) =>
|
||||
wrapInTestApp(
|
||||
<>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
configApiRef,
|
||||
new ConfigReader({
|
||||
stackoverflow: {
|
||||
baseUrl: 'https://api.stackexchange.com/2.2',
|
||||
},
|
||||
}),
|
||||
],
|
||||
]}
|
||||
>
|
||||
<Story />
|
||||
</TestApiProvider>
|
||||
</>,
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<Grid item xs={12} md={6}>
|
||||
<HomePageStackOverflowQuestions
|
||||
requestParams={{
|
||||
tagged: 'backstage',
|
||||
site: 'stackoverflow',
|
||||
pagesize: 5,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 { Content } from './Content';
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* Stack Overflow frontend plugin
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
export {
|
||||
stackOverflowPlugin,
|
||||
StackOverflowSearchResultListItem,
|
||||
HomePageStackOverflowQuestions,
|
||||
} from './plugin';
|
||||
export type {
|
||||
StackOverflowQuestion,
|
||||
StackOverflowQuestionsContentProps,
|
||||
StackOverflowQuestionsRequestParams,
|
||||
} from './types';
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 { stackOverflowPlugin } from './plugin';
|
||||
|
||||
describe('stack-overflow', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(stackOverflowPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 {
|
||||
createPlugin,
|
||||
createComponentExtension,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { createCardExtension } from '@backstage/plugin-home';
|
||||
import { StackOverflowQuestionsContentProps } from './types';
|
||||
|
||||
/**
|
||||
* The Backstage plugin that holds stack overflow specific components
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const stackOverflowPlugin = createPlugin({
|
||||
id: 'stack-overflow',
|
||||
});
|
||||
|
||||
/**
|
||||
* A component to display a stack overflow search result
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const StackOverflowSearchResultListItem = stackOverflowPlugin.provide(
|
||||
createComponentExtension({
|
||||
name: 'StackOverflowResultListItem',
|
||||
component: {
|
||||
lazy: () =>
|
||||
import('./search/StackOverflowSearchResultListItem').then(
|
||||
m => m.StackOverflowSearchResultListItem,
|
||||
),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* A component to display a list of stack overflow questions on the homepage.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const HomePageStackOverflowQuestions = stackOverflowPlugin.provide(
|
||||
createCardExtension<StackOverflowQuestionsContentProps>({
|
||||
name: 'HomePageStackOverflowQuestions',
|
||||
title: 'Stack Overflow Questions',
|
||||
components: () => import('./home/StackOverflowQuestions'),
|
||||
}),
|
||||
);
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 { StackOverflowSearchResultListItem } from '../../plugin';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import React, { ComponentType } from 'react';
|
||||
|
||||
export default {
|
||||
title: 'Plugins/Search/StackOverflowResultListItem',
|
||||
component: StackOverflowSearchResultListItem,
|
||||
decorators: [(Story: ComponentType<{}>) => wrapInTestApp(<Story />)],
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<StackOverflowSearchResultListItem
|
||||
result={{
|
||||
title: 'Customizing Spotify backstage UI',
|
||||
text: 'Name of Author',
|
||||
location: 'stackoverflow.question/1',
|
||||
answers: 0,
|
||||
tags: ['backstage'],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { StackOverflowSearchResultListItem } from './StackOverflowSearchResultListItem';
|
||||
|
||||
describe('<StackOverflowSearchResultListItem/>', () => {
|
||||
it('should render without exploding', async () => {
|
||||
await renderInTestApp(
|
||||
<StackOverflowSearchResultListItem
|
||||
result={{
|
||||
title: 'Customizing Spotify backstage UI',
|
||||
text: 'Name of Author',
|
||||
location: 'https://stackoverflow.com/questions/7',
|
||||
answers: 0,
|
||||
tags: ['backstage'],
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(
|
||||
screen.getByText(/Customizing Spotify backstage UI/i),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText(/Tag: backstage/i)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/Customizing Spotify backstage UI/i).closest('a'),
|
||||
).toHaveAttribute('href', 'https://stackoverflow.com/questions/7');
|
||||
});
|
||||
});
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
import _unescape from 'lodash/unescape';
|
||||
import { Link } from '@backstage/core-components';
|
||||
import {
|
||||
Divider,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
ListItemIcon,
|
||||
Box,
|
||||
Chip,
|
||||
} from '@material-ui/core';
|
||||
|
||||
type StackOverflowSearchResultListItemProps = {
|
||||
result: any; // TODO(emmaindal): type to StackOverflowDocument.
|
||||
icon?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const StackOverflowSearchResultListItem = (
|
||||
props: StackOverflowSearchResultListItemProps,
|
||||
) => {
|
||||
const { location, title, text, answers, tags } = props.result;
|
||||
|
||||
return (
|
||||
<Link to={location}>
|
||||
<ListItem alignItems="center">
|
||||
{props.icon && <ListItemIcon>{props.icon}</ListItemIcon>}
|
||||
<Box flexWrap="wrap">
|
||||
<ListItemText
|
||||
primaryTypographyProps={{ variant: 'h6' }}
|
||||
primary={_unescape(title)}
|
||||
secondary={`Author: ${text}`}
|
||||
/>
|
||||
<Chip label={`Answer(s): ${answers}`} size="small" />
|
||||
{tags &&
|
||||
tags.map((tag: string) => (
|
||||
<Chip key={tag} label={`Tag: ${tag}`} size="small" />
|
||||
))}
|
||||
</Box>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 { StackOverflowSearchResultListItem } from './StackOverflowSearchResultListItem';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 '@testing-library/jest-dom';
|
||||
import 'cross-fetch/polyfill';
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Type representing a stack overflow question
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type StackOverflowQuestion = {
|
||||
title: string;
|
||||
link: string;
|
||||
owner: Record<string, string>;
|
||||
tags: string[];
|
||||
answer_count: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Props for HomePageStackOverflowQuestions
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type StackOverflowQuestionsContentProps = {
|
||||
requestParams: StackOverflowQuestionsRequestParams;
|
||||
};
|
||||
|
||||
/**
|
||||
* Type representing the request parameters accepted by the HomePageStackOverflowQuestions component
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type StackOverflowQuestionsRequestParams = {
|
||||
[key: string]: string | string[] | number;
|
||||
};
|
||||
@@ -9,6 +9,7 @@ const BACKSTAGE_CORE_STORIES = [
|
||||
'plugins/org',
|
||||
'plugins/search',
|
||||
'plugins/home',
|
||||
'plugins/stack-overflow',
|
||||
];
|
||||
|
||||
module.exports = ({ args }) => {
|
||||
|
||||
@@ -1480,6 +1480,14 @@
|
||||
lodash "^4.17.21"
|
||||
uuid "^8.0.0"
|
||||
|
||||
"@backstage/config@^0.1.15":
|
||||
version "0.1.15"
|
||||
resolved "https://registry.npmjs.org/@backstage/config/-/config-0.1.15.tgz#4bad122ad861be5bd61a60639f92d2494fa245c5"
|
||||
integrity sha512-eNJEYYSEu9MkrkBYiMpUBWEc3Bu64YgB9pZZGCMW7/9350tV2wbylEdoBJHslilJlJhiUyTXBckn8Ua7DOH7rw==
|
||||
dependencies:
|
||||
"@backstage/types" "^0.1.3"
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@backstage/core-components@^0.9.0", "@backstage/core-components@^0.9.2":
|
||||
version "0.9.2"
|
||||
resolved "https://registry.npmjs.org/@backstage/core-components/-/core-components-0.9.2.tgz#9a3d79a15039256bbc007e5daa08c983050e0238"
|
||||
@@ -1580,6 +1588,11 @@
|
||||
yaml "^1.10.0"
|
||||
zen-observable "^0.8.15"
|
||||
|
||||
"@backstage/types@^0.1.3":
|
||||
version "0.1.3"
|
||||
resolved "https://registry.npmjs.org/@backstage/types/-/types-0.1.3.tgz#6613d8cbdf97d42d31cd1e66a833df533e7ccf14"
|
||||
integrity sha512-fJVi4oVrlO+G3PRv1fYSll9/X4pE11HLnkI//Geare9sP6wSfp/2zXpLYfKVsG0e24jOl7Swkc8lwLkQ90zMaQ==
|
||||
|
||||
"@bcoe/v8-coverage@^0.2.3":
|
||||
version "0.2.3"
|
||||
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
|
||||
Reference in New Issue
Block a user