Merge branch 'mob/create-vcs-step' of github.com:spotify/backstage into shmidt-i/scaffolder-flow-frontend
This commit is contained in:
@@ -76,13 +76,6 @@
|
||||
"pathRewrite": {
|
||||
"^/circleci/api/": "/"
|
||||
}
|
||||
},
|
||||
"/catalog/api": {
|
||||
"target": "http://localhost:7000",
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": {
|
||||
"^/catalog/api/": "/catalog/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ describe('App', () => {
|
||||
{
|
||||
data: {
|
||||
app: { title: 'Test' },
|
||||
backend: { baseUrl: 'http://localhost:7000' },
|
||||
},
|
||||
context: 'test',
|
||||
},
|
||||
|
||||
@@ -27,11 +27,13 @@ import {
|
||||
GoogleAuth,
|
||||
GithubAuth,
|
||||
OktaAuth,
|
||||
GitlabAuth,
|
||||
oauthRequestApiRef,
|
||||
OAuthRequestManager,
|
||||
googleAuthApiRef,
|
||||
githubAuthApiRef,
|
||||
oktaAuthApiRef,
|
||||
gitlabAuthApiRef,
|
||||
storageApiRef,
|
||||
WebStorage,
|
||||
} from '@backstage/core';
|
||||
@@ -51,15 +53,14 @@ import {
|
||||
graphQlBrowseApiRef,
|
||||
GraphQLEndpoints,
|
||||
} from '@backstage/plugin-graphiql';
|
||||
import {
|
||||
scaffolderApiRef,
|
||||
ScaffolderApi,
|
||||
} from '@backstage/plugin-scaffolder/src/api';
|
||||
import { scaffolderApiRef, ScaffolderApi } from '@backstage/plugin-scaffolder';
|
||||
|
||||
export const apis = (config: ConfigApi) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Creating APIs for ${config.getString('app.title')}`);
|
||||
|
||||
const backendUrl = config.getString('backend.baseUrl');
|
||||
|
||||
const builder = ApiRegistry.builder();
|
||||
|
||||
const alertApi = builder.add(alertApiRef, new AlertApiForwarder());
|
||||
@@ -82,7 +83,7 @@ export const apis = (config: ConfigApi) => {
|
||||
builder.add(
|
||||
googleAuthApiRef,
|
||||
GoogleAuth.create({
|
||||
apiOrigin: 'http://localhost:7000',
|
||||
apiOrigin: backendUrl,
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
@@ -91,7 +92,7 @@ export const apis = (config: ConfigApi) => {
|
||||
const githubAuthApi = builder.add(
|
||||
githubAuthApiRef,
|
||||
GithubAuth.create({
|
||||
apiOrigin: 'http://localhost:7000',
|
||||
apiOrigin: backendUrl,
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
@@ -100,6 +101,15 @@ export const apis = (config: ConfigApi) => {
|
||||
builder.add(
|
||||
oktaAuthApiRef,
|
||||
OktaAuth.create({
|
||||
apiOrigin: backendUrl,
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
gitlabAuthApiRef,
|
||||
GitlabAuth.create({
|
||||
apiOrigin: 'http://localhost:7000',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
@@ -117,7 +127,7 @@ export const apis = (config: ConfigApi) => {
|
||||
builder.add(
|
||||
catalogApiRef,
|
||||
new CatalogClient({
|
||||
apiOrigin: 'http://localhost:7000',
|
||||
apiOrigin: backendUrl,
|
||||
basePath: '/catalog',
|
||||
}),
|
||||
);
|
||||
@@ -125,7 +135,7 @@ export const apis = (config: ConfigApi) => {
|
||||
builder.add(
|
||||
scaffolderApiRef,
|
||||
new ScaffolderApi({
|
||||
apiOrigin: 'http://localhost:7000',
|
||||
apiOrigin: backendUrl,
|
||||
basePath: '/scaffolder/v1',
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -242,11 +242,24 @@ export const githubAuthApiRef = createApiRef<
|
||||
*/
|
||||
export const oktaAuthApiRef = createApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionStateApi
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionStateApi
|
||||
>({
|
||||
id: 'core.auth.okta',
|
||||
description: 'Provides authentication towards Okta APIs',
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Gitlab APIs.
|
||||
*
|
||||
* See https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const gitlabAuthApiRef = createApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionStateApi
|
||||
>({
|
||||
id: 'core.auth.gitlab',
|
||||
description: 'Provides authentication towards Gitlab APIs',
|
||||
});
|
||||
|
||||
@@ -27,7 +27,10 @@ import {
|
||||
} from '../../../definitions/auth';
|
||||
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
|
||||
import { SessionManager } from '../../../../lib/AuthSessionManager/types';
|
||||
import { StaticAuthSessionManager } from '../../../../lib/AuthSessionManager';
|
||||
import {
|
||||
AuthSessionStore,
|
||||
StaticAuthSessionManager,
|
||||
} from '../../../../lib/AuthSessionManager';
|
||||
import { Observable } from '../../../../types';
|
||||
|
||||
type CreateOptions = {
|
||||
@@ -91,7 +94,13 @@ class GithubAuth implements OAuthApi, SessionStateApi {
|
||||
sessionScopes: (session: GithubSession) => session.providerInfo.scopes,
|
||||
});
|
||||
|
||||
return new GithubAuth(sessionManager);
|
||||
const authSessionStore = new AuthSessionStore<GithubSession>({
|
||||
manager: sessionManager,
|
||||
storageKey: 'githubSession',
|
||||
sessionScopes: (session: GithubSession) => session.providerInfo.scopes,
|
||||
});
|
||||
|
||||
return new GithubAuth(authSessionStore);
|
||||
}
|
||||
|
||||
sessionState$(): Observable<SessionState> {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 GitlabAuth from './GitlabAuth';
|
||||
|
||||
describe('GitlabAuth', () => {
|
||||
it('should get access token', async () => {
|
||||
const getSession = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ providerInfo: { accessToken: 'access-token' } });
|
||||
const gitlabAuth = new GitlabAuth({ getSession } as any);
|
||||
|
||||
expect(await gitlabAuth.getAccessToken()).toBe('access-token');
|
||||
expect(getSession).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should normalize scope', () => {
|
||||
const tests = [
|
||||
{
|
||||
arguments: ['read_user api write_repository'],
|
||||
expect: new Set(['read_user', 'api', 'write_repository']),
|
||||
},
|
||||
{
|
||||
arguments: ['read_repository sudo'],
|
||||
expect: new Set(['read_repository', 'sudo']),
|
||||
},
|
||||
];
|
||||
|
||||
for (const test of tests) {
|
||||
expect(GitlabAuth.normalizeScope(...test.arguments)).toEqual(test.expect);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 GitlabIcon from '@material-ui/icons/AcUnit';
|
||||
import { DefaultAuthConnector } from '../../../../lib/AuthConnector';
|
||||
import { GitlabSession } from './types';
|
||||
import {
|
||||
OAuthApi,
|
||||
SessionStateApi,
|
||||
SessionState,
|
||||
ProfileInfo,
|
||||
BackstageIdentity,
|
||||
AuthRequestOptions,
|
||||
} from '../../../definitions/auth';
|
||||
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
|
||||
import { SessionManager } from '../../../../lib/AuthSessionManager/types';
|
||||
import { StaticAuthSessionManager } from '../../../../lib/AuthSessionManager';
|
||||
import { Observable } from '../../../../types';
|
||||
|
||||
type CreateOptions = {
|
||||
apiOrigin: string;
|
||||
basePath: string;
|
||||
|
||||
oauthRequestApi: OAuthRequestApi;
|
||||
|
||||
environment?: string;
|
||||
provider?: AuthProvider & { id: string };
|
||||
};
|
||||
|
||||
export type GitlabAuthResponse = {
|
||||
providerInfo: {
|
||||
accessToken: string;
|
||||
scope: string;
|
||||
expiresInSeconds: number;
|
||||
};
|
||||
profile: ProfileInfo;
|
||||
backstageIdentity: BackstageIdentity;
|
||||
};
|
||||
|
||||
const DEFAULT_PROVIDER = {
|
||||
id: 'gitlab',
|
||||
title: 'Gitlab',
|
||||
icon: GitlabIcon,
|
||||
};
|
||||
|
||||
class GitlabAuth implements OAuthApi, SessionStateApi {
|
||||
static create({
|
||||
apiOrigin,
|
||||
basePath,
|
||||
environment = 'development',
|
||||
provider = DEFAULT_PROVIDER,
|
||||
oauthRequestApi,
|
||||
}: CreateOptions) {
|
||||
const connector = new DefaultAuthConnector({
|
||||
apiOrigin,
|
||||
basePath,
|
||||
environment,
|
||||
provider,
|
||||
oauthRequestApi,
|
||||
sessionTransform(res: GitlabAuthResponse): GitlabSession {
|
||||
return {
|
||||
...res,
|
||||
providerInfo: {
|
||||
accessToken: res.providerInfo.accessToken,
|
||||
scopes: GitlabAuth.normalizeScope(res.providerInfo.scope),
|
||||
expiresAt: new Date(
|
||||
Date.now() + res.providerInfo.expiresInSeconds * 1000,
|
||||
),
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const sessionManager = new StaticAuthSessionManager({
|
||||
connector,
|
||||
defaultScopes: new Set(['read_user']),
|
||||
sessionScopes: (session: GitlabSession) => session.providerInfo.scopes,
|
||||
});
|
||||
|
||||
return new GitlabAuth(sessionManager);
|
||||
}
|
||||
|
||||
sessionState$(): Observable<SessionState> {
|
||||
return this.sessionManager.sessionState$();
|
||||
}
|
||||
|
||||
constructor(private readonly sessionManager: SessionManager<GitlabSession>) {}
|
||||
|
||||
async getAccessToken(scope?: string, options?: AuthRequestOptions) {
|
||||
const session = await this.sessionManager.getSession({
|
||||
...options,
|
||||
scopes: GitlabAuth.normalizeScope(scope),
|
||||
});
|
||||
return session?.providerInfo.accessToken ?? '';
|
||||
}
|
||||
|
||||
async getBackstageIdentity(
|
||||
options: AuthRequestOptions = {},
|
||||
): Promise<BackstageIdentity | undefined> {
|
||||
const session = await this.sessionManager.getSession(options);
|
||||
return session?.backstageIdentity;
|
||||
}
|
||||
|
||||
async getProfile(options: AuthRequestOptions = {}) {
|
||||
const session = await this.sessionManager.getSession(options);
|
||||
return session?.profile;
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this.sessionManager.removeSession();
|
||||
}
|
||||
|
||||
static normalizeScope(scope?: string): Set<string> {
|
||||
if (!scope) {
|
||||
return new Set();
|
||||
}
|
||||
|
||||
const scopeList = Array.isArray(scope) ? scope : scope.split(' ');
|
||||
return new Set(scopeList);
|
||||
}
|
||||
}
|
||||
export default GitlabAuth;
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 './types';
|
||||
export { default as GitlabAuth } from './GitlabAuth';
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 { ProfileInfo, BackstageIdentity } from '../../../definitions';
|
||||
|
||||
export type GitlabSession = {
|
||||
providerInfo: {
|
||||
accessToken: string;
|
||||
scopes: Set<string>;
|
||||
expiresAt: Date;
|
||||
};
|
||||
profile: ProfileInfo;
|
||||
backstageIdentity: BackstageIdentity;
|
||||
};
|
||||
@@ -16,4 +16,5 @@
|
||||
|
||||
export * from './google';
|
||||
export * from './github';
|
||||
export * from './gitlab';
|
||||
export * from './okta';
|
||||
|
||||
@@ -36,6 +36,8 @@ type Options<T> = {
|
||||
/**
|
||||
* AuthSessionStore decorates another SessionManager with a functionality
|
||||
* to store the session in local storage.
|
||||
*
|
||||
* Session is serialized to JSON with special support for following types: Set.
|
||||
*/
|
||||
export class AuthSessionStore<T> implements SessionManager<T> {
|
||||
private readonly manager: SessionManager<T>;
|
||||
@@ -90,7 +92,12 @@ export class AuthSessionStore<T> implements SessionManager<T> {
|
||||
try {
|
||||
const sessionJson = localStorage.getItem(this.storageKey);
|
||||
if (sessionJson) {
|
||||
const session = JSON.parse(sessionJson);
|
||||
const session = JSON.parse(sessionJson, (_key, value) => {
|
||||
if (value?.__type === 'Set') {
|
||||
return new Set(value.__value);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -105,7 +112,18 @@ export class AuthSessionStore<T> implements SessionManager<T> {
|
||||
if (session === undefined) {
|
||||
localStorage.removeItem(this.storageKey);
|
||||
} else {
|
||||
localStorage.setItem(this.storageKey, JSON.stringify(session));
|
||||
localStorage.setItem(
|
||||
this.storageKey,
|
||||
JSON.stringify(session, (_key, value) => {
|
||||
if (value instanceof Set) {
|
||||
return {
|
||||
__type: 'Set',
|
||||
__value: Array.from(value),
|
||||
};
|
||||
}
|
||||
return value;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,4 +16,5 @@
|
||||
|
||||
export { RefreshingAuthSessionManager } from './RefreshingAuthSessionManager';
|
||||
export { StaticAuthSessionManager } from './StaticAuthSessionManager';
|
||||
export { AuthSessionStore } from './AuthSessionStore';
|
||||
export * from './types';
|
||||
|
||||
@@ -185,3 +185,38 @@ export const SubvalueTable = () => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const DenseTable = () => {
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'Column 1',
|
||||
field: 'col1',
|
||||
highlight: true,
|
||||
},
|
||||
{
|
||||
title: 'Column 2',
|
||||
field: 'col2',
|
||||
},
|
||||
{
|
||||
title: 'Numeric value',
|
||||
field: 'number',
|
||||
type: 'numeric',
|
||||
},
|
||||
{
|
||||
title: 'A Date',
|
||||
field: 'date',
|
||||
type: 'date',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<Table
|
||||
options={{ paging: false, padding: 'dense' }}
|
||||
data={testData10}
|
||||
columns={columns}
|
||||
title="Backstage Table"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -35,7 +35,6 @@ import ViewColumn from '@material-ui/icons/ViewColumn';
|
||||
import MTable, {
|
||||
Column,
|
||||
MaterialTableProps,
|
||||
MTableCell,
|
||||
MTableHeader,
|
||||
MTableToolbar,
|
||||
Options,
|
||||
@@ -96,14 +95,6 @@ const tableIcons = {
|
||||
)),
|
||||
};
|
||||
|
||||
const useCellStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
root: {
|
||||
color: theme.palette.grey[500],
|
||||
padding: theme.spacing(0, 2, 0, 2.5),
|
||||
height: '56px',
|
||||
},
|
||||
}));
|
||||
|
||||
const useHeaderStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
header: {
|
||||
padding: theme.spacing(1, 2, 1, 2.5),
|
||||
@@ -169,7 +160,6 @@ export function Table<T extends object = {}>({
|
||||
subtitle,
|
||||
...props
|
||||
}: TableProps<T>) {
|
||||
const cellClasses = useCellStyles();
|
||||
const headerClasses = useHeaderStyles();
|
||||
const toolbarClasses = useToolbarStyles();
|
||||
const theme = useTheme<BackstageTheme>();
|
||||
@@ -185,9 +175,6 @@ export function Table<T extends object = {}>({
|
||||
return (
|
||||
<MTable<T>
|
||||
components={{
|
||||
Cell: cellProps => (
|
||||
<MTableCell className={cellClasses.root} {...cellProps} />
|
||||
),
|
||||
Header: headerProps => (
|
||||
<MTableHeader classes={headerClasses} {...headerProps} />
|
||||
),
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
import { ItemCard } from '.';
|
||||
import { Grid } from '@material-ui/core';
|
||||
|
||||
export default {
|
||||
title: 'Item Card',
|
||||
component: ItemCard,
|
||||
};
|
||||
|
||||
export const Default = () => (
|
||||
<Grid container spacing={4}>
|
||||
<Grid item xs={6} sm={4} md={2}>
|
||||
<ItemCard
|
||||
title="Item Card"
|
||||
description="This is the description of an Item Card"
|
||||
label="Button"
|
||||
type="Pretitle"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6} sm={4} md={2}>
|
||||
<ItemCard
|
||||
title="Item Card"
|
||||
description="This is the description of an Item Card"
|
||||
label="Button"
|
||||
type="Pretitle"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
|
||||
export const Tags = () => (
|
||||
<Grid container spacing={4}>
|
||||
<Grid item xs={6} sm={4} md={2}>
|
||||
<ItemCard
|
||||
title="Item Card"
|
||||
description="This is a Item Card"
|
||||
tags={['one tag', 'two tag']}
|
||||
label="Button"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6} sm={4} md={2}>
|
||||
<ItemCard
|
||||
title="Item Card"
|
||||
description="This is a Item Card"
|
||||
tags={['one tag', 'two tag']}
|
||||
label="Button"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 React, { FC } from 'react';
|
||||
import { Button, Card, Chip, Typography, makeStyles } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
header: {
|
||||
color: theme.palette.common.white,
|
||||
padding: theme.spacing(2, 2, 6),
|
||||
backgroundImage:
|
||||
'linear-gradient(-137deg, rgb(25, 230, 140) 0%, rgb(29, 127, 110) 100%)',
|
||||
},
|
||||
content: {
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
description: {
|
||||
height: 175,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
},
|
||||
footer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row-reverse',
|
||||
},
|
||||
}));
|
||||
|
||||
type ItemCardProps = {
|
||||
description: string;
|
||||
tags?: string[];
|
||||
title: string;
|
||||
type?: string;
|
||||
label: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
export const ItemCard: FC<ItemCardProps> = ({
|
||||
description,
|
||||
tags,
|
||||
title,
|
||||
type,
|
||||
label,
|
||||
onClick,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className={classes.header}>
|
||||
{type ?? <Typography variant="subtitle2">{type}</Typography>}
|
||||
<Typography variant="h6">{title}</Typography>
|
||||
</div>
|
||||
<div className={classes.content}>
|
||||
{tags?.map(tag => (
|
||||
<Chip label={tag} key={tag} />
|
||||
))}
|
||||
<Typography variant="body2" paragraph className={classes.description}>
|
||||
{description}
|
||||
</Typography>
|
||||
<div className={classes.footer}>
|
||||
<Button onClick={onClick} color="primary">
|
||||
{label}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
@@ -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 { ItemCard } from './ItemCard';
|
||||
@@ -22,6 +22,7 @@ import { SidebarContext } from './config';
|
||||
import {
|
||||
googleAuthApiRef,
|
||||
githubAuthApiRef,
|
||||
gitlabAuthApiRef,
|
||||
identityApiRef,
|
||||
oktaAuthApiRef,
|
||||
useApi,
|
||||
@@ -57,6 +58,11 @@ export function SidebarUserSettings() {
|
||||
apiRef={githubAuthApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
<OAuthProviderSettings
|
||||
title="Gitlab"
|
||||
apiRef={gitlabAuthApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
<OIDCProviderSettings
|
||||
title="Okta"
|
||||
apiRef={oktaAuthApiRef}
|
||||
|
||||
@@ -26,3 +26,4 @@ export * from './Sidebar';
|
||||
export * from './SignInPage';
|
||||
export * from './TabbedCard';
|
||||
export * from './HeaderTabs';
|
||||
export * from './ItemCard';
|
||||
|
||||
@@ -28,6 +28,8 @@ import {
|
||||
googleAuthApiRef,
|
||||
GithubAuth,
|
||||
githubAuthApiRef,
|
||||
GitlabAuth,
|
||||
gitlabAuthApiRef,
|
||||
} from '@backstage/core';
|
||||
|
||||
// TODO(rugvip): We should likely figure out how to reuse all of these between apps
|
||||
@@ -75,3 +77,14 @@ export const githubAuthApiFactory = createApiFactory({
|
||||
oauthRequestApi,
|
||||
}),
|
||||
});
|
||||
|
||||
export const gitlabAuthApiFactory = createApiFactory({
|
||||
implements: gitlabAuthApiRef,
|
||||
deps: { oauthRequestApi: oauthRequestApiRef },
|
||||
factory: ({ oauthRequestApi }) =>
|
||||
GitlabAuth.create({
|
||||
apiOrigin: 'http://localhost:7000',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -6,12 +6,14 @@ import {
|
||||
OAuthRequestManager,
|
||||
googleAuthApiRef,
|
||||
githubAuthApiRef,
|
||||
gitlabAuthApiRef,
|
||||
oktaAuthApiRef,
|
||||
AlertApiForwarder,
|
||||
ErrorApiForwarder,
|
||||
ErrorAlerter,
|
||||
GoogleAuth,
|
||||
GithubAuth,
|
||||
GitlabAuth,
|
||||
OktaAuth,
|
||||
identityApiRef,
|
||||
} from '@backstage/core';
|
||||
@@ -52,6 +54,15 @@ builder.add(
|
||||
}),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
gitlabAuthApiRef,
|
||||
GitlabAuth.create({
|
||||
apiOrigin: 'http://localhost:7000',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
oktaAuthApiRef,
|
||||
OktaAuth.create({
|
||||
|
||||
@@ -118,9 +118,12 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides {
|
||||
verticalAlign: 'middle',
|
||||
lineHeight: '1',
|
||||
margin: 0,
|
||||
padding: '8px',
|
||||
padding: theme.spacing(3, 2, 3, 2.5),
|
||||
borderBottom: 0,
|
||||
},
|
||||
sizeSmall: {
|
||||
padding: theme.spacing(1, 2, 1, 2.5),
|
||||
},
|
||||
head: {
|
||||
wordBreak: 'break-word',
|
||||
overflow: 'hidden',
|
||||
|
||||
Reference in New Issue
Block a user