Merge branch 'master' of github.com:spotify/backstage into mob/prepare-from-catalog

* 'master' of github.com:spotify/backstage: (60 commits)
  packages,plugins: remove main:src and fix some main fields
  packages/config: added get and getOptional
  changed  down caret to be up caret on user profile in the sidebar
  packages/config: add getOptionalConfig and getOptionalConfigArray to mirror other accessors
  packages/config-loader,core: update config usage to include context
  build(deps): bump @types/react from 16.9.25 to 16.9.37 (#1342)
  build(deps): bump rollup-plugin-esbuild from 2.0.0 to 2.1.0 (#1361)
  build(deps-dev): bump lint-staged from 10.2.9 to 10.2.11 (#1359)
  Polishing the Create page (#1353)
  Add some air between sidebar sections (#1355)
  Starred icon is yellow (#1351)
  Updated FAQ with Gitlab link (#1352)
  build(deps): bump react-helmet from 6.0.0 to 6.1.0 (#1327)
  packages/config: added context to keep track of where config is from for error messages
  packages/config: added .keys()
  packages/config: keep track of key prefix to display better error messages
  packages/config: optimize some error message handling
  packages/config: allow config readers to be backed by undefined data
  packages/config: flip around must* and get* to get* and getOptional*
  packages/core-api: fix invocation order when continuing to app after sign-in
  ...
This commit is contained in:
blam
2020-06-18 14:00:29 +02:00
108 changed files with 2045 additions and 1379 deletions
+13 -10
View File
@@ -1,10 +1,10 @@
{
"name": "@backstage/plugin-auth-backend",
"version": "0.1.1-alpha.8",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
@@ -20,12 +20,10 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/backend-common": "^0.1.1-alpha.8",
"@types/cookie-parser": "^1.4.2",
"@types/jwt-decode": "2.2.1",
"@types/passport": "^1.0.3",
"@types/passport-github2": "^1.2.4",
"@types/passport-google-oauth20": "^2.0.3",
"@backstage/backend-common": "^0.1.1-alpha.9",
"@backstage/config": "^0.1.1-alpha.9",
"@backstage/config-loader": "^0.1.1-alpha.9",
"@types/express": "^4.17.6",
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
@@ -44,12 +42,17 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@types/body-parser": "^1.19.0",
"@types/cookie-parser": "^1.4.2",
"@types/jwt-decode": "2.2.1",
"@types/passport-github2": "^1.2.4",
"@types/passport-google-oauth20": "^2.0.3",
"@types/passport-saml": "^1.1.2",
"@types/passport": "^1.0.3",
"jest-fetch-mock": "^3.0.3"
},
"files": [
"dist"
"dist/**/*.{js,d.ts}"
]
}
@@ -37,7 +37,7 @@ export class EnvironmentHandler implements AuthProviderRouteHandlers {
async start(req: express.Request, res: express.Response): Promise<void> {
const provider = this.getProviderForEnv(req);
provider.start(req, res);
await provider.start(req, res);
}
async frameHandler(
@@ -45,18 +45,18 @@ export class EnvironmentHandler implements AuthProviderRouteHandlers {
res: express.Response,
): Promise<void> {
const provider = this.getProviderForEnv(req);
provider.frameHandler(req, res);
await provider.frameHandler(req, res);
}
async refresh(req: express.Request, res: express.Response): Promise<void> {
const provider = this.getProviderForEnv(req);
if (provider.refresh) {
provider.refresh(req, res);
await provider.refresh(req, res);
}
}
async logout(req: express.Request, res: express.Response): Promise<void> {
const provider = this.getProviderForEnv(req);
provider.logout(req, res);
await provider.logout(req, res);
}
}
@@ -96,6 +96,7 @@ export function createGithubProvider(
}
envProviders[env] = new OAuthProvider(new GithubAuthProvider(opts), {
disableRefresh: true,
providerId: 'github',
secure,
baseUrl,
@@ -143,6 +143,7 @@ export function createGoogleProvider(
}
envProviders[env] = new OAuthProvider(new GoogleAuthProvider(opts), {
disableRefresh: false,
providerId: 'google',
secure,
baseUrl,
+4 -2
View File
@@ -20,9 +20,11 @@ import cookieParser from 'cookie-parser';
import bodyParser from 'body-parser';
import { Logger } from 'winston';
import { createAuthProviderRouter } from '../providers';
import { Config } from '@backstage/config';
export interface RouterOptions {
logger: Logger;
config: Config;
}
export async function createRouter(
@@ -38,7 +40,7 @@ export async function createRouter(
// TODO: read from app config
const config = {
backend: {
baseUrl: 'http://localhost:7000',
baseUrl: options.config.getString('backend.baseUrl'),
},
auth: {
providers: {
@@ -77,7 +79,7 @@ export async function createRouter(
const providerConfigs = config.auth.providers;
for (const [providerId, providerConfig] of Object.entries(providerConfigs)) {
const baseUrl = `${config.backend.baseUrl}/auth`;
const baseUrl = `${options.config.getString('backend.baseUrl')}/auth`;
logger.info(`Configuring provider, ${providerId}`);
try {
const providerRouter = createAuthProviderRouter(
@@ -19,6 +19,7 @@ import {
notFoundHandler,
requestLoggingHandler,
} from '@backstage/backend-common';
import { Config } from '@backstage/config';
import compression from 'compression';
import cors from 'cors';
import express from 'express';
@@ -29,12 +30,13 @@ import { createRouter } from './router';
export interface ApplicationOptions {
enableCors: boolean;
logger: Logger;
config: Config;
}
export async function createStandaloneApplication(
options: ApplicationOptions,
): Promise<express.Application> {
const { enableCors, logger } = options;
const { enableCors, logger, config } = options;
const app = express();
app.use(helmet());
@@ -44,7 +46,7 @@ export async function createStandaloneApplication(
app.use(compression());
app.use(express.json());
app.use(requestLoggingHandler());
app.use('/', await createRouter({ logger }));
app.use('/', await createRouter({ logger, config }));
app.use(notFoundHandler());
app.use(errorHandler());
@@ -17,6 +17,8 @@
import { Server } from 'http';
import { Logger } from 'winston';
import { createStandaloneApplication } from './standaloneApplication';
import { ConfigReader } from '@backstage/config';
import { loadConfig } from '@backstage/config-loader';
export interface ServerOptions {
port: number;
@@ -28,11 +30,13 @@ export async function startStandaloneServer(
options: ServerOptions,
): Promise<Server> {
const logger = options.logger.child({ service: 'auth-backend' });
const config = ConfigReader.fromConfigs(await loadConfig());
logger.debug('Creating application...');
const app = await createStandaloneApplication({
enableCors: options.enableCors,
logger,
config,
});
logger.debug('Starting application server...');
+8 -7
View File
@@ -1,10 +1,10 @@
{
"name": "@backstage/plugin-catalog-backend",
"version": "0.1.1-alpha.8",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
@@ -21,8 +21,9 @@
"mock-data": "./scripts/mock-data"
},
"dependencies": {
"@backstage/backend-common": "^0.1.1-alpha.8",
"@backstage/catalog-model": "^0.1.1-alpha.8",
"@backstage/backend-common": "^0.1.1-alpha.9",
"@backstage/catalog-model": "^0.1.1-alpha.9",
"@types/express": "^4.17.6",
"express": "^4.17.1",
"express-promise-router": "^3.0.3",
"fs-extra": "^9.0.0",
@@ -38,7 +39,7 @@
"yup": "^0.28.5"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@types/lodash": "^4.14.151",
"@types/node-fetch": "^2.5.7",
"@types/supertest": "^2.0.8",
@@ -48,7 +49,7 @@
"supertest": "^4.0.2"
},
"files": [
"dist",
"migrations"
"dist/**/*.{js,d.ts}",
"migrations/**/*.{js,d.ts}"
]
}
+11 -12
View File
@@ -1,11 +1,10 @@
{
"name": "@backstage/plugin-catalog",
"version": "0.1.1-alpha.8",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
@@ -22,11 +21,11 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/plugin-scaffolder": "^0.1.1-alpha.8",
"@backstage/plugin-sentry": "^0.1.1-alpha.8",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/catalog-model": "^0.1.1-alpha.9",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/plugin-scaffolder": "^0.1.1-alpha.9",
"@backstage/plugin-sentry": "^0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -39,9 +38,9 @@
"swr": "^0.2.2"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/test-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@backstage/test-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/react-hooks": "^3.3.0",
@@ -25,15 +25,26 @@ import {
} from '@backstage/core';
import CatalogLayout from './CatalogLayout';
import { rootRoute as scaffolderRootRoute } from '@backstage/plugin-scaffolder';
import { Button, Link, makeStyles, Typography } from '@material-ui/core';
import {
Button,
Link,
makeStyles,
Typography,
withStyles,
} from '@material-ui/core';
import Edit from '@material-ui/icons/Edit';
import GitHub from '@material-ui/icons/GitHub';
import Star from '@material-ui/icons/Star';
import StarOutline from '@material-ui/icons/StarBorder';
import React, { FC, useCallback, useState } from 'react';
import React, { FC, useCallback, useState, useMemo } from 'react';
import { Link as RouterLink } from 'react-router-dom';
import { catalogApiRef } from '../..';
import { defaultFilter, entityFilters, filterGroups } from '../../data/filters';
import {
defaultFilter,
entityFilters,
filterGroups,
EntityFilterType,
} from '../../data/filters';
import { findLocationForEntityMeta } from '../../data/utils';
import { useStarredEntities } from '../../hooks/useStarredEntites';
import {
@@ -43,6 +54,30 @@ import {
import { CatalogTable } from '../CatalogTable/CatalogTable';
import useStaleWhileRevalidate from 'swr';
// TODO: replace me with the proper tabs implemntation
const tabs = [
{
id: 'service',
label: 'Services',
},
{
id: 'website',
label: 'Websites',
},
{
id: 'lib',
label: 'Libraries',
},
{
id: 'documentation',
label: 'Documentation',
},
{
id: 'other',
label: 'Other',
},
];
const useStyles = makeStyles(theme => ({
contentWrapper: {
display: 'grid',
@@ -58,8 +93,8 @@ const useStyles = makeStyles(theme => ({
export const CatalogPage: FC<{}> = () => {
const catalogApi = useApi(catalogApiRef);
const [selectedTab, setSelectedTab] = useState<string>(tabs[0].id);
const { toggleStarredEntity, isStarredEntity } = useStarredEntities();
const [selectedFilter, setSelectedFilter] = useState<CatalogFilterItem>(
defaultFilter,
);
@@ -69,18 +104,27 @@ export const CatalogPage: FC<{}> = () => {
async () => catalogApi.getEntities(),
);
const data =
entities?.filter(e =>
entityFilters[selectedFilter.id](e, { isStarred: isStarredEntity(e) }),
) ?? [];
const onFilterSelected = useCallback(
selected => setSelectedFilter(selected),
[],
);
const filteredEntities = useMemo(() => {
const typeFilter = entityFilters[EntityFilterType.TYPE];
const leftMenuFilter = entityFilters[selectedFilter.id];
return entities
?.filter(e => leftMenuFilter(e, { isStarred: isStarredEntity(e) }))
.filter(e => typeFilter(e, { type: selectedTab }));
}, [selectedFilter.id, selectedTab, isStarredEntity, entities?.filter]);
const styles = useStyles();
const YellowStar = withStyles({
root: {
color: '#f3ba37',
},
})(Star);
const actions = [
(rowData: Entity) => {
const location = findLocationForEntityMeta(rowData.metadata);
@@ -120,40 +164,21 @@ export const CatalogPage: FC<{}> = () => {
(rowData: Entity) => {
const isStarred = isStarredEntity(rowData);
return {
icon: isStarred ? Star : StarOutline,
icon: isStarred ? YellowStar : StarOutline,
tooltip: isStarred ? 'Remove from favorites' : 'Add to favorites',
onClick: () => toggleStarredEntity(rowData),
};
},
];
// TODO: replace me with the proper tabs implemntation
const tabs = [
{
id: 'services',
label: 'Services',
},
{
id: 'websites',
label: 'Websites',
},
{
id: 'libs',
label: 'Libraries',
},
{
id: 'documentation',
label: 'Documentation',
},
{
id: 'other',
label: 'Other',
},
];
return (
<CatalogLayout>
<HeaderTabs tabs={tabs} />
<HeaderTabs
tabs={tabs}
onChange={index => {
setSelectedTab(tabs[index as number].id);
}}
/>
<Content>
<DismissableBanner
variant="info"
@@ -193,8 +218,8 @@ export const CatalogPage: FC<{}> = () => {
</div>
<CatalogTable
titlePreamble={selectedFilter.label}
entities={data || []}
loading={!data && !error}
entities={filteredEntities || []}
loading={!entities && !error}
error={error}
actions={actions}
/>
@@ -88,7 +88,7 @@
"Swedish": "God afton",
"Tagalog": "Magandang gabi",
"Tatar": "Xäyerle kiç",
"Telugu" : "శుభ సాయంత్రం",
"Telugu": "శుభ సాయంత్రం",
"Thai": "Sawat-dii torn khum",
"Turkish": "İyi akşamlar",
"Ukrainian": "Dobry vechir",
+6 -3
View File
@@ -28,6 +28,7 @@ export enum EntityFilterType {
ALL = 'ALL',
STARRED = 'STARRED',
OWNED = 'OWNED',
TYPE = 'TYPE',
}
export const filterGroups: CatalogFilterGroup[] = [
@@ -54,7 +55,7 @@ export const filterGroups: CatalogFilterGroup[] = [
items: [
{
id: EntityFilterType.ALL,
label: 'All Services',
label: 'All Entities',
count: AllServicesCount,
},
],
@@ -64,13 +65,15 @@ export const filterGroups: CatalogFilterGroup[] = [
type EntityFilter = (entity: Entity, options: EntityFilterOptions) => boolean;
type EntityFilterOptions = {
isStarred: boolean;
isStarred?: boolean;
type?: string;
};
export const entityFilters: Record<string, EntityFilter> = {
[EntityFilterType.OWNED]: () => false,
[EntityFilterType.ALL]: () => true,
[EntityFilterType.STARRED]: (_, { isStarred }) => isStarred,
[EntityFilterType.STARRED]: (_, { isStarred }) => !!isStarred,
[EntityFilterType.TYPE]: (e, { type }) => (e.spec as any)?.type === type,
};
export const defaultFilter: CatalogFilterItem = filterGroups[0].items[0];
+8 -9
View File
@@ -1,11 +1,10 @@
{
"name": "@backstage/plugin-circleci",
"version": "0.1.1-alpha.8",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
@@ -31,12 +30,11 @@
"postpack": "backstage-cli postpack"
},
"dependencies": {
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
"@types/react-lazylog": "^4.5.0",
"circleci-api": "^4.0.0",
"moment": "^2.25.3",
"react": "^16.13.1",
@@ -47,13 +45,14 @@
"react-use": "^14.2.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
"@types/jest": "^25.2.2",
"@types/node": "^12.0.0",
"@types/react-lazylog": "^4.5.0",
"@types/testing-library__jest-dom": "^5.0.4",
"jest-fetch-mock": "^3.0.3"
},
+8 -9
View File
@@ -1,11 +1,10 @@
{
"name": "@backstage/plugin-explore",
"version": "0.1.1-alpha.8",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
@@ -22,8 +21,8 @@
"start": "backstage-cli plugin:serve"
},
"dependencies": {
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -33,9 +32,9 @@
"react-use": "^14.2.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/test-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@backstage/test-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
+7 -8
View File
@@ -1,11 +1,10 @@
{
"name": "@backstage/plugin-gitops-profiles",
"version": "0.1.1-alpha.8",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
@@ -22,8 +21,8 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -33,8 +32,8 @@
"react-use": "^14.2.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
+1 -1
View File
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createApiRef } from '@backstage/core-api';
import { createApiRef } from '@backstage/core';
export interface CloneFromTemplateRequest {
templateRepository: string;
@@ -20,7 +20,7 @@ import mockFetch from 'jest-fetch-mock';
import ProfileCatalog from './ProfileCatalog';
import { ThemeProvider } from '@material-ui/core';
import { lightTheme } from '@backstage/theme';
import { ApiProvider, ApiRegistry } from '@backstage/core-api';
import { ApiProvider, ApiRegistry } from '@backstage/core';
import { gitOpsApiRef, GitOpsRestApi } from '../../api';
describe('ProfileCatalog', () => {
+7 -8
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/plugin-graphiql",
"description": "Backstage plugin for browsing GraphQL APIs",
"version": "0.1.1-alpha.8",
"version": "0.1.1-alpha.9",
"private": false,
"publishConfig": {
"access": "public",
@@ -18,8 +18,7 @@
"backstage"
],
"license": "Apache-2.0",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"build": "backstage-cli plugin:build",
@@ -32,8 +31,8 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -44,9 +43,9 @@
"react-use": "^14.2.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/test-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@backstage/test-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
+6 -5
View File
@@ -1,10 +1,10 @@
{
"name": "@backstage/plugin-identity-backend",
"version": "0.1.1-alpha.8",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
@@ -20,7 +20,8 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/backend-common": "^0.1.1-alpha.8",
"@backstage/backend-common": "^0.1.1-alpha.9",
"@types/express": "^4.17.6",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.1",
@@ -32,10 +33,10 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"jest-fetch-mock": "^3.0.3"
},
"files": [
"dist"
"dist/**/*.{js,d.ts}"
]
}
@@ -0,0 +1,82 @@
/*
* Copyright 2020 Spotify AB
*
* 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 {
Group,
GroupsResponse,
IdentityApi,
GroupsRequest,
GroupsJson,
} from './types';
export class StaticJsonAdapter implements IdentityApi {
private readonly groups: Group[];
constructor(userGroups: GroupsJson) {
this.groups = userGroups.groups;
}
getUserGroups(req: GroupsRequest): Promise<GroupsResponse> {
return new Promise(resolve => {
const { user, type } = req;
const userGroups = this._getUserGroups(this.groups, user);
const groups = this.filterGroupsByType(userGroups, type);
resolve({ groups });
});
}
_getUserGroups(groups: Group[], user: string) {
const userGroups: Set<Group> = new Set();
groups.forEach(group => {
if (this.isUserInGroup(group, user)) {
userGroups.add(group);
}
if (group.children) {
const userSubGroups = this._getUserGroups(group.children, user) ?? [];
const isUserInSubGroup = Boolean(userSubGroups.length);
if (isUserInSubGroup) {
userGroups.add(group);
}
userSubGroups.forEach(subGroup => userGroups.add(subGroup));
}
});
return Array.from(userGroups);
}
private filterGroupsByType = (userGroups: Group[], type: string) => {
const groups = type
? userGroups
.filter((group: Group) => group.type === type)
.map(group => ({ name: group.name, type: group.type }))
: userGroups.map(group => ({
name: group.name,
type: group.type,
}));
return groups;
};
private isUserInGroup = (group: Group, user: string): boolean => {
if (group.members) {
const groupMembers = group.members;
const groupsWithUser = groupMembers.filter(
member => member.name === user,
);
return Boolean(groupsWithUser.length);
}
return false;
};
}
@@ -0,0 +1,17 @@
/*
* Copyright 2020 Spotify AB
*
* 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 './StaticJsonAdapter';
@@ -0,0 +1,42 @@
/*
* Copyright 2020 Spotify AB
*
* 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 type User = {
name: string;
};
export type Group = {
name: string;
type: string;
members?: User[];
children?: Group[];
};
export type GroupsJson = {
groups: Group[];
};
export type GroupsResponse = {
groups: Group[];
};
export type GroupsRequest = {
user: string;
type: string;
};
export interface IdentityApi {
getUserGroups(req: GroupsRequest): Promise<GroupsResponse>;
}
@@ -0,0 +1,35 @@
/*
* Copyright 2020 Spotify AB
*
* 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 const userGroups = {
groups: [
{
name: 'engineering',
type: 'org',
children: [
{
name: 'authentication',
type: 'team',
members: [{ name: 'kent' }, { name: 'dobbs' }],
},
{
name: 'checkout',
type: 'team',
members: [{ name: 'don' }, { name: 'abramev' }],
},
],
},
],
};
+18 -7
View File
@@ -17,21 +17,32 @@
import express from 'express';
import Router from 'express-promise-router';
import { Logger } from 'winston';
import { StaticJsonAdapter } from '../adapters';
import { IdentityApi } from '../adapters/types';
import { userGroups } from '../adapters/userGroups';
export interface RouterOptions {
logger: Logger;
}
const makeRouter = (adapter: IdentityApi): express.Router => {
const router = Router();
router.get('/users/:user/groups', async (req, res) => {
const user = req.params.user;
const type = req.query.type?.toString() ?? '';
const response = await adapter.getUserGroups({ user, type });
res.send(response);
});
return router;
};
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const router = Router();
const logger = options.logger;
router.use('/ping', (_, res) => {
logger.info('heartbeat for identity service');
res.send('pong');
});
return router;
logger.info('Initializing identity API backend');
const adapter = new StaticJsonAdapter(userGroups);
return makeRouter(adapter);
}
+7 -8
View File
@@ -1,8 +1,7 @@
{
"name": "@backstage/plugin-lighthouse",
"version": "0.1.1-alpha.8",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": false,
@@ -22,8 +21,8 @@
"start": "backstage-cli plugin:serve"
},
"dependencies": {
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -34,9 +33,9 @@
"react-use": "^14.2.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/test-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@backstage/test-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
+9 -10
View File
@@ -1,11 +1,10 @@
{
"name": "@backstage/plugin-register-component",
"version": "0.1.1-alpha.8",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
@@ -22,10 +21,10 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/plugin-catalog": "^0.1.1-alpha.8",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/catalog-model": "^0.1.1-alpha.9",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/plugin-catalog": "^0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -37,8 +36,8 @@
"react-use": "^14.2.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
+10 -6
View File
@@ -1,10 +1,10 @@
{
"name": "@backstage/plugin-scaffolder-backend",
"version": "0.1.1-alpha.8",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
@@ -21,8 +21,8 @@
"mock-data": "./scripts/mock-data"
},
"dependencies": {
"@backstage/backend-common": "^0.1.1-alpha.8",
"@backstage/catalog-model": "^0.1.1-alpha.8",
"@backstage/backend-common": "^0.1.1-alpha.9",
"@backstage/catalog-model": "^0.1.1-alpha.9",
"compression": "^1.7.4",
"cors": "^2.8.5",
"dockerode": "^3.2.0",
@@ -35,10 +35,14 @@
"winston": "^3.2.1"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@types/fs-extra": "^9.0.1",
"@types/express": "^4.17.6",
"@types/supertest": "^2.0.8",
"supertest": "^4.0.2",
"yaml": "^1.10.0"
}
},
"files": [
"dist/**/*.{js,d.ts}"
]
}
+7 -8
View File
@@ -1,11 +1,10 @@
{
"name": "@backstage/plugin-scaffolder",
"version": "0.1.1-alpha.8",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
@@ -22,8 +21,8 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -33,8 +32,8 @@
"react-use": "^14.2.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
@@ -20,23 +20,33 @@ import {
Content,
ContentHeader,
Header,
SupportButton,
Page,
pageTheme,
} from '@backstage/core';
import { Typography, Link, Button } from '@material-ui/core';
import { Button, Grid, Link, Typography } from '@material-ui/core';
import { Link as RouterLink } from 'react-router-dom';
import TemplateCard from '../TemplateCard';
// TODO(blam): Connect to backend
const STATIC_DATA = [
{
id: 'springboot-template',
type: 'service',
name: 'Spring Boot Service',
tags: ['Recommended', 'Java'],
description:
'Standard Spring Boot (Java) microservice with recommended configuration.',
ownerId: 'spotify',
},
{
id: 'react-ssr-template',
type: 'web-infra',
type: 'website',
name: 'SSR React Website',
tags: ['Experimental'],
tags: ['Recommended', 'React'],
description:
'Next.js application skeleton for creating isomorphic web applications.',
ownerId: 'something',
ownerId: 'spotify',
},
];
const ScaffolderPage: React.FC<{}> = () => {
@@ -46,7 +56,7 @@ const ScaffolderPage: React.FC<{}> = () => {
pageTitleOverride="Create a new component"
title={
<>
Create a new component <Lifecycle alpha shorthand />{' '}
Create a new component <Lifecycle alpha shorthand />
</>
}
subtitle="Create new software components using standard templates"
@@ -61,6 +71,11 @@ const ScaffolderPage: React.FC<{}> = () => {
>
Register existing component
</Button>
<SupportButton>
Create new software components using standard templates. Different
templates create different kinds of components (services, websites,
documentation, ...).
</SupportButton>
</ContentHeader>
<Typography variant="body2" paragraph style={{ fontStyle: 'italic' }}>
<strong>NOTE!</strong> This feature is WIP. You can follow progress{' '}
@@ -69,7 +84,7 @@ const ScaffolderPage: React.FC<{}> = () => {
</Link>
.
</Typography>
<div style={{ display: 'flex' }}>
<Grid container>
{STATIC_DATA.map(item => {
return (
<TemplateCard
@@ -81,7 +96,7 @@ const ScaffolderPage: React.FC<{}> = () => {
/>
);
})}
</div>
</Grid>
</Content>
</Page>
);
+6 -5
View File
@@ -1,10 +1,10 @@
{
"name": "@backstage/plugin-sentry-backend",
"version": "0.1.1-alpha.8",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
@@ -20,7 +20,8 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/backend-common": "^0.1.1-alpha.8",
"@backstage/backend-common": "^0.1.1-alpha.9",
"@types/express": "^4.17.6",
"axios": "^0.19.2",
"compression": "^1.7.4",
"cors": "^2.8.5",
@@ -33,10 +34,10 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"jest-fetch-mock": "^3.0.3"
},
"files": [
"dist"
"dist/**/*.{js,d.ts}"
]
}
+8 -8
View File
@@ -1,11 +1,10 @@
{
"name": "@backstage/plugin-sentry",
"version": "0.1.1-alpha.8",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
@@ -22,11 +21,12 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
"@types/react": "^16.9",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-sparklines": "^1.7.0",
@@ -34,8 +34,8 @@
"timeago.js": "^4.0.2"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
+7 -8
View File
@@ -1,8 +1,7 @@
{
"name": "@backstage/plugin-tech-radar",
"version": "0.1.1-alpha.8",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": false,
@@ -22,9 +21,9 @@
"start": "backstage-cli plugin:serve"
},
"dependencies": {
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/test-utils-core": "^0.1.1-alpha.7",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/test-utils-core": "0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -37,8 +36,8 @@
"react-use": "^14.2.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
+7 -8
View File
@@ -1,10 +1,9 @@
{
"name": "@backstage/plugin-welcome",
"version": "0.1.1-alpha.8",
"main": "dist/index.esm.js",
"main:src": "src/index.ts",
"version": "0.1.1-alpha.9",
"main": "src/index.ts",
"types": "src/index.ts",
"private": true,
"private": false,
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
@@ -22,8 +21,8 @@
"start": "backstage-cli plugin:serve"
},
"dependencies": {
"@backstage/core": "^0.1.1-alpha.8",
"@backstage/theme": "^0.1.1-alpha.8",
"@backstage/core": "^0.1.1-alpha.9",
"@backstage/theme": "^0.1.1-alpha.9",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -33,8 +32,8 @@
"react-use": "^14.2.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.8",
"@backstage/dev-utils": "^0.1.1-alpha.8",
"@backstage/cli": "^0.1.1-alpha.9",
"@backstage/dev-utils": "^0.1.1-alpha.9",
"@testing-library/jest-dom": "^5.7.0",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^10.2.4",
@@ -39,7 +39,8 @@ import {
} from '@backstage/core';
const WelcomePage: FC<{}> = () => {
const appTitle = useApi(configApiRef).getString('app.title') ?? 'Backstage';
const appTitle =
useApi(configApiRef).getOptionalString('app.title') ?? 'Backstage';
const profile = { givenName: '' };
return (