Merge remote-tracking branch 'origin/master' into erikengervall/plugin-release-manager-as-a-service

This commit is contained in:
Erik Engervall
2021-04-20 08:30:58 +02:00
29 changed files with 461 additions and 178 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
Forward user token to scaffolder task for subsequent api requests
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/core': patch
'@backstage/plugin-scaffolder': patch
---
Adding Headings for Accessibility on the Scaffolder Plugin
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog': patch
---
SystemDiagramCard UI improvements
@@ -78,6 +78,7 @@ cluster. Valid values are:
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `serviceAccount` | This will use a Kubernetes [service account](https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/) to access the Kubernetes API. When this is used the `serviceAccountToken` field should also be set. |
| `google` | This will use a user's Google auth token from the [Google auth plugin](https://backstage.io/docs/auth/) to access the Kubernetes API. |
| `aws` | This will use AWS credentials to access resources in EKS clusters |
##### `clusters.\*.skipTLSVerify`
+18 -4
View File
@@ -135,7 +135,21 @@ techdocs:
bucketName: 'name-of-techdocs-storage-bucket'
```
**3a. (Recommended) Setup authentication the AWS way, using environment
**3. Create minimal AWS IAM policies to manage TechDocs**
To _write_ TechDocs into the S3 bucket the IAM policy needs to have at a minimum
permissions to:
- `s3:ListBucket` to retrieve bucket metadata
- `s3:PutObject` to upload files to the bucket
To _read_ TechDocs from the S3 bucket the IAM policy needs to have at a minimum
permissions to:
- `s3:ListBucket` - To retrieve bucket metadata
- `s3:GetObject` - To retrieve files from the bucket
**4a. (Recommended) Setup authentication the AWS way, using environment
variables**
You should follow the
@@ -166,7 +180,7 @@ more in
The AWS Region of the bucket is optional since TechDocs uses AWS SDK V2 and not
V3.
**3b. Authentication using app-config.yaml**
**4b. Authentication using app-config.yaml**
AWS credentials and region can be provided to the AWS SDK via `app-config.yaml`.
If the configs below are present, they will be used over existing `AWS_*`
@@ -187,7 +201,7 @@ techdocs:
Refer to the
[official AWS documentation for obtaining the credentials](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/getting-your-credentials.html).
**3c. Authentication using an assumed role** Users with multiple AWS accounts
**4c. Authentication using an assumed role** Users with multiple AWS accounts
may want to use a role for S3 storage that is in a different AWS account. Using
the `roleArn` parameter as seen below, you can instruct the TechDocs publisher
to assume a role before accessing S3.
@@ -207,7 +221,7 @@ Note: Assuming a role requires that primary credentials are already configured
at `AWS.config.credentials`. Read more about
[assuming roles in AWS](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html).
**4. That's it!**
**5. That's it!**
Your Backstage app is now ready to use AWS S3 for TechDocs, to store and read
the static generated documentation files. When you start the backend of the app,
+1
View File
@@ -203,6 +203,7 @@
],
"Deployment": [
"deployment/index",
"deployment/docker",
"deployment/k8s",
"deployment/helm",
"deployment/heroku"
-3
View File
@@ -306,9 +306,6 @@ export class PrivateAppImpl implements BackstageApp {
this.verifyPlugins(this.plugins);
// Initialize APIs once all plugins are available
if (this.apiHolder) {
throw new Error('Plugin holder was initialized too soon');
}
this.getApiHolder();
return result;
@@ -25,7 +25,7 @@ type Props = CSS.Properties & {
const useStyles = makeStyles({
alpha: {
color: '#d00150',
color: '#ffffff',
fontFamily: 'serif',
fontWeight: 'normal',
fontStyle: 'italic',
@@ -66,7 +66,12 @@ const DefaultTitle = ({
title = 'Unknown page',
className,
}: DefaultTitleProps) => (
<Typography variant="h4" className={className} data-testid="header-title">
<Typography
variant="h4"
component="h2"
className={className}
data-testid="header-title"
>
{title}
</Typography>
);
+6 -2
View File
@@ -140,7 +140,7 @@ const TypeFragment = ({
const TitleFragment = ({ pageTitle, classes, tooltip }: TitleFragmentProps) => {
const FinalTitle = (
<Typography className={classes.title} variant="h4">
<Typography className={classes.title} variant="h1">
{pageTitle}
</Typography>
);
@@ -166,7 +166,11 @@ const SubtitleFragment = ({ classes, subtitle }: SubtitleFragmentProps) => {
}
return (
<Typography className={classes.subtitle} variant="subtitle2">
<Typography
className={classes.subtitle}
variant="subtitle2"
component="span"
>
{subtitle}
</Typography>
);
@@ -125,6 +125,7 @@ type Props = {
actionsTopRight?: ReactNode;
className?: string;
noPadding?: boolean;
titleTypographyProps?: object;
};
export const InfoCard = ({
@@ -143,6 +144,7 @@ export const InfoCard = ({
actionsTopRight,
className,
noPadding,
titleTypographyProps,
}: Props): JSX.Element => {
const classes = useStyles();
/**
@@ -183,6 +185,7 @@ export const InfoCard = ({
title={title}
subheader={subheader}
style={{ ...headerStyle }}
titleTypographyProps={titleTypographyProps}
{...headerProps}
/>
)}
@@ -77,8 +77,16 @@ export const ItemCardHeader = (props: ItemCardHeaderProps) => {
const classes = useStyles(props);
return (
<div className={classes.root}>
{subtitle && <Typography variant="subtitle2">{subtitle}</Typography>}
{title && <Typography variant="h6">{title}</Typography>}
{subtitle && (
<Typography variant="subtitle2" component="h3">
{subtitle}
</Typography>
)}
{title && (
<Typography variant="h6" component="h4">
{title}
</Typography>
)}
{children}
</div>
);
@@ -19,6 +19,7 @@ import {
catalogApiRef,
CatalogApi,
EntityProvider,
entityRouteRef,
} from '@backstage/plugin-catalog-react';
import { Entity, RELATION_PART_OF } from '@backstage/catalog-model';
import { renderInTestApp } from '@backstage/test-utils';
@@ -59,13 +60,16 @@ describe('<SystemDiagramCard />', () => {
<SystemDiagramCard />
</EntityProvider>
</ApiProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
expect(queryByText(/System Diagram/)).toBeInTheDocument();
expect(queryByText(/system:my-namespace2\/my-system2/)).toBeInTheDocument();
expect(
queryByText(/component:my-namespace\/my-entity/),
).not.toBeInTheDocument();
expect(queryByText(/my-namespace2\/my-system2/)).toBeInTheDocument();
expect(queryByText(/my-namespace\/my-entity/)).not.toBeInTheDocument();
});
it('shows related systems', async () => {
@@ -115,10 +119,15 @@ describe('<SystemDiagramCard />', () => {
<SystemDiagramCard />
</EntityProvider>
</ApiProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
expect(getByText('System Diagram')).toBeInTheDocument();
expect(getByText('system:my-namespace/my-system')).toBeInTheDocument();
expect(getByText('component:my-namespace/my-entity')).toBeInTheDocument();
expect(getByText('my-namespace/my-system')).toBeInTheDocument();
expect(getByText('my-namespace/my-entity')).toBeInTheDocument();
});
});
@@ -19,10 +19,13 @@ import {
RELATION_DEPENDS_ON,
RELATION_PROVIDES_API,
RELATION_PART_OF,
serializeEntityRef,
stringifyEntityRef,
ENTITY_DEFAULT_NAMESPACE,
parseEntityRef,
} from '@backstage/catalog-model';
import {
catalogApiRef,
entityRouteRef,
getEntityRelations,
useEntity,
} from '@backstage/plugin-catalog-react';
@@ -33,26 +36,107 @@ import {
Progress,
useApi,
ResponseErrorPanel,
Link,
useRouteRef,
} from '@backstage/core';
import { Box, Typography } from '@material-ui/core';
import { Box, makeStyles, Typography } from '@material-ui/core';
import ZoomOutMap from '@material-ui/icons/ZoomOutMap';
import React from 'react';
import { useAsync } from 'react-use';
import { BackstageTheme } from '@backstage/theme';
function simplifiedEntityName(
const useStyles = makeStyles((theme: BackstageTheme) => ({
domainNode: {
fill: theme.palette.primary.main,
stroke: theme.palette.border,
},
systemNode: {
fill: 'coral',
stroke: theme.palette.border,
},
componentNode: {
fill: 'yellowgreen',
stroke: theme.palette.border,
},
apiNode: {
fill: theme.palette.gold,
stroke: theme.palette.border,
},
resourceNode: {
fill: 'grey',
stroke: theme.palette.border,
},
}));
// Simplifies the diagram output by hiding the default namespace and kind
function readableEntityName(
ref:
| Entity
| {
kind?: string;
kind: string;
namespace?: string;
name: string;
},
): string {
// Simplify the diagram output by hiding only the default namespace
return serializeEntityRef(ref)
.toString()
return stringifyEntityRef(ref)
.toLocaleLowerCase('en-US')
.replace(':default/', ':');
.replace(`:${ENTITY_DEFAULT_NAMESPACE}/`, ':')
.split(':')[1];
}
function RenderNode(props: DependencyGraphTypes.RenderNodeProps<any>) {
const classes = useStyles();
const catalogEntityRoute = useRouteRef(entityRouteRef);
const kind = props.node.kind || 'Component';
const ref = parseEntityRef(props.node.id);
let nodeClass = classes.componentNode;
switch (kind) {
case 'Domain':
nodeClass = classes.domainNode;
break;
case 'System':
nodeClass = classes.systemNode;
break;
case 'Component':
nodeClass = classes.componentNode;
break;
case 'API':
nodeClass = classes.apiNode;
break;
case 'Resource':
nodeClass = classes.resourceNode;
break;
default:
nodeClass = classes.componentNode;
}
return (
<g>
<rect width={200} height={100} rx={20} className={nodeClass} />
<Link
to={catalogEntityRoute({
kind: kind,
namespace: ref.namespace,
name: ref.name,
})}
>
<text
x={100}
y={45}
textAnchor="middle"
alignmentBaseline="baseline"
style={{ fontWeight: 'bold' }}
>
{props.node.name}
</text>
</Link>
<text x={100} y={65} textAnchor="middle" alignmentBaseline="hanging">
{props.node.kind}
</text>
</g>
);
}
/**
@@ -62,8 +146,8 @@ function simplifiedEntityName(
export function SystemDiagramCard() {
const { entity } = useEntity();
const currentSystemName = entity.metadata.name;
const currentSystemNode = simplifiedEntityName(entity);
const systemNodes = new Array<{ id: string }>();
const currentSystemNode = stringifyEntityRef(entity);
const systemNodes = new Array<{ id: string; kind: string; name: string }>();
const systemEdges = new Array<{ from: string; to: string; label: string }>();
const catalogApi = useApi(catalogApiRef);
@@ -71,7 +155,10 @@ export function SystemDiagramCard() {
return catalogApi.getEntities({
filter: {
kind: ['Component', 'API', 'Resource', 'System', 'Domain'],
'spec.system': currentSystemName,
'spec.system': [
currentSystemName,
`${ENTITY_DEFAULT_NAMESPACE}/${currentSystemName}`,
],
},
});
}, [catalogApi, currentSystemName]);
@@ -79,22 +166,26 @@ export function SystemDiagramCard() {
// pick out the system itself
systemNodes.push({
id: currentSystemNode,
kind: 'System',
name: readableEntityName(entity),
});
// check if the system has an assigned domain
// even if the domain object doesn't exist in the catalog, display it in the map
const catalogItemDomain = getEntityRelations(entity, RELATION_PART_OF, {
kind: 'domain',
kind: 'Domain',
});
catalogItemDomain.forEach(foundDomain =>
systemNodes.push({
id: simplifiedEntityName(foundDomain),
id: stringifyEntityRef(foundDomain),
kind: foundDomain.kind,
name: readableEntityName(foundDomain),
}),
);
catalogItemDomain.forEach(foundDomain =>
systemEdges.push({
from: currentSystemNode,
to: simplifiedEntityName(foundDomain),
to: stringifyEntityRef(foundDomain),
label: 'part of',
}),
);
@@ -102,7 +193,9 @@ export function SystemDiagramCard() {
if (catalogResponse && catalogResponse.items) {
for (const catalogItem of catalogResponse.items) {
systemNodes.push({
id: simplifiedEntityName(catalogItem),
id: stringifyEntityRef(catalogItem),
kind: catalogItem.kind,
name: readableEntityName(catalogItem),
});
// Check relations of the entity assigned to this system to see
@@ -115,8 +208,8 @@ export function SystemDiagramCard() {
);
catalogItemRelations_partOf.forEach(foundRelation =>
systemEdges.push({
from: simplifiedEntityName(catalogItem),
to: simplifiedEntityName(foundRelation),
from: stringifyEntityRef(catalogItem),
to: stringifyEntityRef(foundRelation),
label: 'part of',
}),
);
@@ -127,9 +220,9 @@ export function SystemDiagramCard() {
);
catalogItemRelations_providesApi.forEach(foundRelation =>
systemEdges.push({
from: simplifiedEntityName(catalogItem),
to: simplifiedEntityName(foundRelation),
label: 'provides API',
from: stringifyEntityRef(catalogItem),
to: stringifyEntityRef(foundRelation),
label: 'provides',
}),
);
@@ -139,8 +232,8 @@ export function SystemDiagramCard() {
);
catalogItemRelations_dependsOn.forEach(foundRelation =>
systemEdges.push({
from: simplifiedEntityName(catalogItem),
to: simplifiedEntityName(foundRelation),
from: stringifyEntityRef(catalogItem),
to: stringifyEntityRef(foundRelation),
label: 'depends on',
}),
);
@@ -160,6 +253,7 @@ export function SystemDiagramCard() {
edges={systemEdges}
nodeMargin={10}
direction={DependencyGraphTypes.Direction.BOTTOM_TOP}
renderNode={RenderNode}
/>
<Box m={1} />
<Typography
@@ -0,0 +1,38 @@
/*
* 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.
*/
// @ts-check
/**
* @param {import('knex').Knex} knex
*/
exports.up = async function up(knex) {
await knex.schema.alterTable('tasks', table => {
table
.text('secrets')
.nullable()
.comment('JSON encoded secrets to authenticate tasks with');
});
};
/**
* @param {import('knex').Knex} knex
*/
exports.down = async function down(knex) {
await knex.schema.alterTable('tasks', table => {
table.dropColumn('secrets');
});
};
@@ -84,10 +84,13 @@ describe('catalog:register', () => {
catalogInfoUrl: 'http://foo/var',
},
});
expect(addLocation).toBeCalledWith({
type: 'url',
target: 'http://foo/var',
});
expect(addLocation).toBeCalledWith(
{
type: 'url',
target: 'http://foo/var',
},
{},
);
expect(mockContext.output).toBeCalledWith(
'entityRef',
@@ -95,10 +95,13 @@ export function createCatalogRegisterAction(options: {
ctx.logger.info(`Registering ${catalogInfoUrl} in the catalog`);
const result = await catalogClient.addLocation({
type: 'url',
target: catalogInfoUrl,
});
const result = await catalogClient.addLocation(
{
type: 'url',
target: catalogInfoUrl,
},
ctx.token ? { token: ctx.token } : {},
);
if (result.entities.length >= 1) {
const { kind, name, namespace } = getEntityName(result.entities[0]);
ctx.output('entityRef', `${kind}:${namespace}/${name}`);
@@ -32,6 +32,10 @@ export type ActionContext<Input extends InputBase> = {
logger: Logger;
logStream: Writable;
/**
* User token forwarded from initial request, for use in subsequent api requests
*/
token?: string | undefined;
workspacePath: string;
input: Input;
output(name: string, value: JsonValue): void;
@@ -24,6 +24,7 @@ import {
DbTaskRow,
Status,
TaskEventType,
TaskSecrets,
TaskSpec,
TaskStore,
TaskStoreEmitOptions,
@@ -42,6 +43,7 @@ export type RawDbTaskRow = {
status: Status;
last_heartbeat_at?: string;
created_at: string;
secrets?: string;
};
export type RawDbTaskEventRow = {
@@ -71,23 +73,29 @@ export class DatabaseTaskStore implements TaskStore {
}
try {
const spec = JSON.parse(result.spec);
const secrets = result.secrets ? JSON.parse(result.secrets) : undefined;
return {
id: result.id,
spec,
status: result.status,
lastHeartbeatAt: result.last_heartbeat_at,
createdAt: result.created_at,
secrets,
};
} catch (error) {
throw new Error(`Failed to parse spec of task '${taskId}', ${error}`);
}
}
async createTask(spec: TaskSpec): Promise<{ taskId: string }> {
async createTask(
spec: TaskSpec,
secrets?: TaskSecrets,
): Promise<{ taskId: string }> {
const taskId = uuid();
await this.db<RawDbTaskRow>('tasks').insert({
id: taskId,
spec: JSON.stringify(spec),
secrets: secrets ? JSON.stringify(secrets) : undefined,
status: 'open',
});
return { taskId };
@@ -119,12 +127,14 @@ export class DatabaseTaskStore implements TaskStore {
try {
const spec = JSON.parse(task.spec);
const secrets = task.secrets ? JSON.parse(task.secrets) : undefined;
return {
id: task.id,
spec,
status: 'processing',
lastHeartbeatAt: task.last_heartbeat_at,
createdAt: task.created_at,
secrets,
};
} catch (error) {
throw new Error(`Failed to parse spec of task '${task.id}', ${error}`);
@@ -209,6 +219,7 @@ export class DatabaseTaskStore implements TaskStore {
})
.update({
status,
secrets: null as any,
});
if (updateCount !== 1) {
throw new ConflictError(
@@ -21,7 +21,7 @@ import {
import { ConfigReader } from '@backstage/config';
import { DatabaseTaskStore } from './DatabaseTaskStore';
import { StorageTaskBroker, TaskAgent } from './StorageTaskBroker';
import { TaskSpec, DbTaskEventRow } from './types';
import { TaskSecrets, TaskSpec, DbTaskEventRow } from './types';
async function createStore(): Promise<DatabaseTaskStore> {
const manager = SingleConnectionDatabaseManager.fromConfig(
@@ -39,6 +39,7 @@ async function createStore(): Promise<DatabaseTaskStore> {
describe('StorageTaskBroker', () => {
let storage: DatabaseTaskStore;
const fakeSecrets = { token: 'secret' } as TaskSecrets;
beforeAll(async () => {
storage = await createStore();
@@ -78,6 +79,13 @@ describe('StorageTaskBroker', () => {
await expect(taskC.spec.steps[0].id).toBe('c');
});
it('should store secrets', async () => {
const broker = new StorageTaskBroker(storage, logger);
await broker.dispatch({} as TaskSpec, fakeSecrets);
const task = await broker.claim();
expect(task.secrets).toEqual(fakeSecrets);
}, 10000);
it('should complete a task', async () => {
const broker = new StorageTaskBroker(storage, logger);
const dispatchResult = await broker.dispatch({} as TaskSpec);
@@ -87,6 +95,16 @@ describe('StorageTaskBroker', () => {
expect(taskRow.status).toBe('completed');
}, 10000);
it('should remove secrets after completing a task', async () => {
const broker = new StorageTaskBroker(storage, logger);
const dispatchResult = await broker.dispatch({} as TaskSpec, fakeSecrets);
const task = await broker.claim();
await task.complete('completed');
const taskRow = await storage.getTask(dispatchResult.taskId);
expect(taskRow.status).toBe('completed');
expect(taskRow.secrets).toBeUndefined();
}, 10000);
it('should fail a task', async () => {
const broker = new StorageTaskBroker(storage, logger);
const dispatchResult = await broker.dispatch({} as TaskSpec);
@@ -96,6 +114,16 @@ describe('StorageTaskBroker', () => {
expect(taskRow.status).toBe('failed');
});
it('should remove secrets after failing a task', async () => {
const broker = new StorageTaskBroker(storage, logger);
const dispatchResult = await broker.dispatch({} as TaskSpec, fakeSecrets);
const task = await broker.claim();
await task.complete('failed');
const taskRow = await storage.getTask(dispatchResult.taskId);
expect(taskRow.status).toBe('failed');
expect(taskRow.secrets).toBeUndefined();
});
it('multiple brokers should be able to observe a single task', async () => {
const broker1 = new StorageTaskBroker(storage, logger);
const broker2 = new StorageTaskBroker(storage, logger);
@@ -18,6 +18,7 @@ import { Logger } from 'winston';
import {
CompletedTaskState,
Task,
TaskSecrets,
TaskSpec,
TaskStore,
TaskBroker,
@@ -48,6 +49,10 @@ export class TaskAgent implements Task {
return this.state.spec;
}
get secrets() {
return this.state.secrets;
}
async getWorkspaceName() {
return this.state.taskId;
}
@@ -101,6 +106,7 @@ export class TaskAgent implements Task {
interface TaskState {
spec: TaskSpec;
taskId: string;
secrets?: TaskSecrets;
}
function defer() {
@@ -126,6 +132,7 @@ export class StorageTaskBroker implements TaskBroker {
{
taskId: pendingTask.id,
spec: pendingTask.spec,
secrets: pendingTask.secrets,
},
this.storage,
this.logger,
@@ -136,8 +143,11 @@ export class StorageTaskBroker implements TaskBroker {
}
}
async dispatch(spec: TaskSpec): Promise<DispatchResult> {
const taskRow = await this.storage.createTask(spec);
async dispatch(
spec: TaskSpec,
secrets?: TaskSecrets,
): Promise<DispatchResult> {
const taskRow = await this.storage.createTask(spec, secrets);
this.signalDispatch();
return {
taskId: taskRow.taskId,
@@ -164,6 +164,7 @@ export class TaskWorker {
logger: taskLogger,
logStream: stream,
input,
token: task.secrets?.token,
workspacePath,
async createTemporaryDirectory() {
const tmpDir = await fs.mkdtemp(
@@ -31,6 +31,7 @@ export type DbTaskRow = {
status: Status;
createdAt: string;
lastHeartbeatAt?: string;
secrets?: TaskSecrets;
};
export type TaskEventType = 'completion' | 'log';
@@ -54,12 +55,17 @@ export type TaskSpec = {
output: { [name: string]: string };
};
export type TaskSecrets = {
token: string | undefined;
};
export type DispatchResult = {
taskId: string;
};
export interface Task {
spec: TaskSpec;
secrets?: TaskSecrets;
done: boolean;
emitLog(message: string, metadata?: JsonValue): Promise<void>;
complete(result: CompletedTaskState, metadata?: JsonValue): Promise<void>;
@@ -68,7 +74,7 @@ export interface Task {
export interface TaskBroker {
claim(): Promise<Task>;
dispatch(spec: TaskSpec): Promise<DispatchResult>;
dispatch(spec: TaskSpec, secrets?: TaskSecrets): Promise<DispatchResult>;
vacuumTasks(timeoutS: { timeoutS: number }): Promise<void>;
observe(
options: {
@@ -93,7 +99,10 @@ export type TaskStoreGetEventsOptions = {
};
export interface TaskStore {
createTask(task: TaskSpec): Promise<{ taskId: string }>;
createTask(
task: TaskSpec,
secrets?: TaskSecrets,
): Promise<{ taskId: string }>;
getTask(taskId: string): Promise<DbTaskRow>;
claimTask(): Promise<DbTaskRow | undefined>;
completeTask(options: {
@@ -301,4 +301,27 @@ describe('createRouter', () => {
expect(response.status).toEqual(201);
});
});
describe('GET /v2/tasks/:taskId', () => {
it('does not divulge secrets', async () => {
const postResponse = await request(app)
.post('/v2/tasks')
.set('Authorization', 'Bearer secret')
.send({
templateName: 'create-react-app-template',
values: {
storePath: 'https://github.com/backstage/backstage',
component_id: '123',
name: 'test',
use_typescript: false,
},
});
const response = await request(app)
.get(`/v2/tasks/${postResponse.body.id}`)
.send();
expect(response.status).toEqual(200);
expect(response.body.secrets).toBeUndefined();
});
});
});
@@ -358,8 +358,9 @@ export async function createRouter(
.post('/v2/tasks', async (req, res) => {
const templateName: string = req.body.templateName;
const values: TemplaterValues = req.body.values;
const token = getBearerToken(req.headers.authorization);
const template = await entityClient.findTemplate(templateName, {
token: getBearerToken(req.headers.authorization),
token,
});
let taskSpec;
@@ -402,7 +403,9 @@ export async function createRouter(
);
}
const result = await taskBroker.dispatch(taskSpec);
const result = await taskBroker.dispatch(taskSpec, {
token: token,
});
res.status(201).json({ id: result.taskId });
})
@@ -412,6 +415,8 @@ export async function createRouter(
if (!task) {
throw new NotFoundError(`Task with id ${taskId} does not exist`);
}
// Do not disclose secrets
delete task.secrets;
res.status(200).json(task);
})
.get('/v2/tasks/:taskId/eventstream', async (req, res) => {
@@ -71,11 +71,17 @@ export const MultistepJsonForm = ({
return (
<>
<Stepper activeStep={activeStep} orientation="vertical">
{steps.map(({ title, schema, ...formProps }) => {
{steps.map(({ title, schema, ...formProps }, index) => {
return (
<StepUI key={title}>
<StepLabel>
<Typography variant="h6">{title}</Typography>
<StepLabel
aria-label={`Step ${index + 1} ${title}`}
aria-disabled="false"
tabIndex={0}
>
<Typography variant="h6" component="h3">
{title}
</Typography>
</StepLabel>
<StepContent key={title}>
<Form
@@ -85,7 +85,7 @@ export const TemplateCard = ({
<Box className={classes.description}>{description}</Box>
</CardContent>
<CardActions>
<Button color="primary" to={href}>
<Button color="primary" to={href} aria-label={`Choose ${title} `}>
Choose
</Button>
</CardActions>
@@ -187,7 +187,11 @@ export const TemplatePage = () => {
<Content>
{loading && <LinearProgress data-testid="loading-progress" />}
{schema && (
<InfoCard title={schema.title} noPadding>
<InfoCard
title={schema.title}
noPadding
titleTypographyProps={{ component: 'h2' }}
>
<MultistepJsonForm
formData={formState}
fields={{ RepoUrlPicker, OwnerPicker }}
+102 -116
View File
@@ -1616,10 +1616,10 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.12.5"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.13.10"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
dependencies:
regenerator-runtime "^0.13.4"
@@ -3720,21 +3720,21 @@
integrity sha512-aIRd0Z9b/HJ/O24KOaP7dNsXypMnXhpWrpLVYvQB/JesVqXYSbioYND200sR+C14a0LSCp+qWnWCnSXmN1hWGw==
"@material-ui/core@^4.11.0", "@material-ui/core@^4.9.1":
version "4.11.0"
resolved "https://registry.npmjs.org/@material-ui/core/-/core-4.11.0.tgz#b69b26e4553c9e53f2bfaf1053e216a0af9be15a"
integrity sha512-bYo9uIub8wGhZySHqLQ833zi4ZML+XCBE1XwJ8EuUVSpTWWG57Pm+YugQToJNFsEyiKFhPh8DPD0bgupz8n01g==
version "4.11.3"
resolved "https://registry.npmjs.org/@material-ui/core/-/core-4.11.3.tgz#f22e41775b0bd075e36a7a093d43951bf7f63850"
integrity sha512-Adt40rGW6Uds+cAyk3pVgcErpzU/qxc7KBR94jFHBYretU4AtWZltYcNsbeMn9tXL86jjVL1kuGcIHsgLgFGRw==
dependencies:
"@babel/runtime" "^7.4.4"
"@material-ui/styles" "^4.10.0"
"@material-ui/system" "^4.9.14"
"@material-ui/styles" "^4.11.3"
"@material-ui/system" "^4.11.3"
"@material-ui/types" "^5.1.0"
"@material-ui/utils" "^4.10.2"
"@material-ui/utils" "^4.11.2"
"@types/react-transition-group" "^4.2.0"
clsx "^1.0.4"
hoist-non-react-statics "^3.3.2"
popper.js "1.16.1-lts"
prop-types "^15.7.2"
react-is "^16.8.0"
react-is "^16.8.0 || ^17.0.0"
react-transition-group "^4.4.0"
"@material-ui/icons@^4.9.1":
@@ -3778,35 +3778,35 @@
react-transition-group "^4.0.0"
rifm "^0.7.0"
"@material-ui/styles@^4.10.0", "@material-ui/styles@^4.9.6":
version "4.10.0"
resolved "https://registry.npmjs.org/@material-ui/styles/-/styles-4.10.0.tgz#2406dc23aa358217aa8cc772e6237bd7f0544071"
integrity sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q==
"@material-ui/styles@^4.10.0", "@material-ui/styles@^4.11.3", "@material-ui/styles@^4.9.6":
version "4.11.3"
resolved "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.3.tgz#1b8d97775a4a643b53478c895e3f2a464e8916f2"
integrity sha512-HzVzCG+PpgUGMUYEJ2rTEmQYeonGh41BYfILNFb/1ueqma+p1meSdu4RX6NjxYBMhf7k+jgfHFTTz+L1SXL/Zg==
dependencies:
"@babel/runtime" "^7.4.4"
"@emotion/hash" "^0.8.0"
"@material-ui/types" "^5.1.0"
"@material-ui/utils" "^4.9.6"
"@material-ui/utils" "^4.11.2"
clsx "^1.0.4"
csstype "^2.5.2"
hoist-non-react-statics "^3.3.2"
jss "^10.0.3"
jss-plugin-camel-case "^10.0.3"
jss-plugin-default-unit "^10.0.3"
jss-plugin-global "^10.0.3"
jss-plugin-nested "^10.0.3"
jss-plugin-props-sort "^10.0.3"
jss-plugin-rule-value-function "^10.0.3"
jss-plugin-vendor-prefixer "^10.0.3"
jss "^10.5.1"
jss-plugin-camel-case "^10.5.1"
jss-plugin-default-unit "^10.5.1"
jss-plugin-global "^10.5.1"
jss-plugin-nested "^10.5.1"
jss-plugin-props-sort "^10.5.1"
jss-plugin-rule-value-function "^10.5.1"
jss-plugin-vendor-prefixer "^10.5.1"
prop-types "^15.7.2"
"@material-ui/system@^4.9.14":
version "4.9.14"
resolved "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz#4b00c48b569340cefb2036d0596b93ac6c587a5f"
integrity sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==
"@material-ui/system@^4.11.3":
version "4.11.3"
resolved "https://registry.npmjs.org/@material-ui/system/-/system-4.11.3.tgz#466bc14c9986798fd325665927c963eb47cc4143"
integrity sha512-SY7otguNGol41Mu2Sg6KbBP1ZRFIbFLHGK81y4KYbsV2yIcaEPOmsCK6zwWlp+2yTV3J/VwT6oSBARtGIVdXPw==
dependencies:
"@babel/runtime" "^7.4.4"
"@material-ui/utils" "^4.9.6"
"@material-ui/utils" "^4.11.2"
csstype "^2.5.2"
prop-types "^15.7.2"
@@ -3815,16 +3815,7 @@
resolved "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2"
integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==
"@material-ui/utils@^4.10.2", "@material-ui/utils@^4.7.1", "@material-ui/utils@^4.9.6":
version "4.10.2"
resolved "https://registry.npmjs.org/@material-ui/utils/-/utils-4.10.2.tgz#3fd5470ca61b7341f1e0468ac8f29a70bf6df321"
integrity sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw==
dependencies:
"@babel/runtime" "^7.4.4"
prop-types "^15.7.2"
react-is "^16.8.0"
"@material-ui/utils@^4.11.2":
"@material-ui/utils@^4.11.2", "@material-ui/utils@^4.7.1":
version "4.11.2"
resolved "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz#f1aefa7e7dff2ebcb97d31de51aecab1bb57540a"
integrity sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==
@@ -10696,12 +10687,12 @@ css-unit-converter@^1.1.2:
resolved "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz#4c77f5a1954e6dbff60695ecb214e3270436ab21"
integrity sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==
css-vendor@^2.0.7:
version "2.0.7"
resolved "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.7.tgz#4e6d53d953c187981576d6a542acc9fb57174bda"
integrity sha512-VS9Rjt79+p7M0WkPqcAza4Yq1ZHrsHrwf7hPL/bjQB+c1lwmAI+1FXxYTYt818D/50fFVflw0XKleiBN5RITkg==
css-vendor@^2.0.8:
version "2.0.8"
resolved "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz#e47f91d3bd3117d49180a3c935e62e3d9f7f449d"
integrity sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==
dependencies:
"@babel/runtime" "^7.6.2"
"@babel/runtime" "^7.8.3"
is-in-browser "^1.0.2"
css-what@2.1:
@@ -10837,12 +10828,12 @@ cssstyle@^2.2.0:
dependencies:
cssom "~0.3.6"
csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.5, csstype@^2.5.7, csstype@^2.6.5, csstype@^2.6.7:
csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.5, csstype@^2.5.7, csstype@^2.6.7:
version "2.6.9"
resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098"
integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==
csstype@^3.0.6:
csstype@^3.0.2, csstype@^3.0.6:
version "3.0.7"
resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz#2a5fb75e1015e84dd15692f71e89a1450290950b"
integrity sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==
@@ -13154,9 +13145,9 @@ fetch-readablestream@^0.2.0:
integrity sha512-qu4mXWf4wus4idBIN/kVH+XSer8IZ9CwHP+Pd7DL7TuKNC1hP7ykon4kkBjwJF3EMX2WsFp4hH7gU7CyL7ucXw==
figgy-pudding@^3.5.1:
version "3.5.1"
resolved "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
version "3.5.2"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
figures@^1.7.0:
version "1.7.0"
@@ -14234,9 +14225,9 @@ google-p12-pem@^3.0.3:
node-forge "^0.10.0"
got@^11.5.2, got@^11.7.0, got@^11.8.0:
version "11.8.0"
resolved "https://registry.npmjs.org/got/-/got-11.8.0.tgz#be0920c3586b07fd94add3b5b27cb28f49e6545f"
integrity sha512-k9noyoIIY9EejuhaBNLyZ31D5328LeqnyPNXJQb2XlJZcKakLqN5m6O/ikhq/0lw56kUYS54fVm+D1x57YC9oQ==
version "11.8.2"
resolved "https://registry.npmjs.org/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599"
integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==
dependencies:
"@sindresorhus/is" "^4.0.0"
"@szmarczak/http-timer" "^4.0.5"
@@ -15214,6 +15205,13 @@ imurmurhash@^0.1.4:
resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
indefinite-observable@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-2.0.1.tgz#574af29bfbc17eb5947793797bddc94c9d859400"
integrity sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==
dependencies:
symbol-observable "1.2.0"
indent-string@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
@@ -16615,9 +16613,9 @@ joi@^17.3.0:
"@sideway/pinpoint" "^2.0.0"
jose@^1.27.1:
version "1.28.0"
resolved "https://registry.npmjs.org/jose/-/jose-1.28.0.tgz#0803f8c71f43cd293a9d931c555c30531f5ca5dc"
integrity sha512-JmfDRzt/HSj8ipd9TsDtEHoLUnLYavG+7e8F6s1mx2jfVSfXOTaFQsJUydbjJpTnTDHP1+yKL9Ke7ktS/a0Eiw==
version "1.28.1"
resolved "https://registry.npmjs.org/jose/-/jose-1.28.1.tgz#34a0f851a534be59ffab82a6e8845f6874e8c128"
integrity sha512-6JK28rFu5ENp/yxMwM+iN7YeaInnY9B9Bggjkz5fuwLiJhbVrl2O4SJr65bdNBPl9y27fdC3Mymh+FVCvozLIg==
dependencies:
"@panva/asn1.js" "^1.0.0"
@@ -16992,72 +16990,74 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
jss-plugin-camel-case@^10.0.3:
version "10.1.1"
resolved "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.1.1.tgz#8e73ecc4f1d0f8dfe4dd31f6f9f2782588970e78"
integrity sha512-MDIaw8FeD5uFz1seQBKz4pnvDLnj5vIKV5hXSVdMaAVq13xR6SVTVWkIV/keyTs5txxTvzGJ9hXoxgd1WTUlBw==
jss-plugin-camel-case@^10.5.1:
version "10.6.0"
resolved "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.6.0.tgz#93d2cd704bf0c4af70cc40fb52d74b8a2554b170"
integrity sha512-JdLpA3aI/npwj3nDMKk308pvnhoSzkW3PXlbgHAzfx0yHWnPPVUjPhXFtLJzgKZge8lsfkUxvYSQ3X2OYIFU6A==
dependencies:
"@babel/runtime" "^7.3.1"
hyphenate-style-name "^1.0.3"
jss "10.1.1"
jss "10.6.0"
jss-plugin-default-unit@^10.0.3:
version "10.1.1"
resolved "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.1.1.tgz#2df86016dfe73085eead843f5794e3890e9c5c47"
integrity sha512-UkeVCA/b3QEA4k0nIKS4uWXDCNmV73WLHdh2oDGZZc3GsQtlOCuiH3EkB/qI60v2MiCq356/SYWsDXt21yjwdg==
jss-plugin-default-unit@^10.5.1:
version "10.6.0"
resolved "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.6.0.tgz#af47972486819b375f0f3a9e0213403a84b5ef3b"
integrity sha512-7y4cAScMHAxvslBK2JRK37ES9UT0YfTIXWgzUWD5euvR+JR3q+o8sQKzBw7GmkQRfZijrRJKNTiSt1PBsLI9/w==
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.1.1"
jss "10.6.0"
jss-plugin-global@^10.0.3:
version "10.1.1"
resolved "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.1.1.tgz#36b0d6d9facb74dfd99590643708a89260747d14"
integrity sha512-VBG3wRyi3Z8S4kMhm8rZV6caYBegsk+QnQZSVmrWw6GVOT/Z4FA7eyMu5SdkorDlG/HVpHh91oFN56O4R9m2VA==
jss-plugin-global@^10.5.1:
version "10.6.0"
resolved "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.6.0.tgz#3e8011f760f399cbadcca7f10a485b729c50e3ed"
integrity sha512-I3w7ji/UXPi3VuWrTCbHG9rVCgB4yoBQLehGDTmsnDfXQb3r1l3WIdcO8JFp9m0YMmyy2CU7UOV6oPI7/Tmu+w==
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.1.1"
jss "10.6.0"
jss-plugin-nested@^10.0.3:
version "10.1.1"
resolved "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.1.1.tgz#5c3de2b8bda344de1ebcef3a4fd30870a29a8a8c"
integrity sha512-ozEu7ZBSVrMYxSDplPX3H82XHNQk2DQEJ9TEyo7OVTPJ1hEieqjDFiOQOxXEj9z3PMqkylnUbvWIZRDKCFYw5Q==
jss-plugin-nested@^10.5.1:
version "10.6.0"
resolved "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.6.0.tgz#5f83c5c337d3b38004834e8426957715a0251641"
integrity sha512-fOFQWgd98H89E6aJSNkEh2fAXquC9aZcAVjSw4q4RoQ9gU++emg18encR4AT4OOIFl4lQwt5nEyBBRn9V1Rk8g==
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.1.1"
jss "10.6.0"
tiny-warning "^1.0.2"
jss-plugin-props-sort@^10.0.3:
version "10.1.1"
resolved "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.1.1.tgz#34bddcbfaf9430ec8ccdf92729f03bb10caf1785"
integrity sha512-g/joK3eTDZB4pkqpZB38257yD4LXB0X15jxtZAGbUzcKAVUHPl9Jb47Y7lYmiGsShiV4YmQRqG1p2DHMYoK91g==
jss-plugin-props-sort@^10.5.1:
version "10.6.0"
resolved "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.6.0.tgz#297879f35f9fe21196448579fee37bcde28ce6bc"
integrity sha512-oMCe7hgho2FllNc60d9VAfdtMrZPo9n1Iu6RNa+3p9n0Bkvnv/XX5San8fTPujrTBScPqv9mOE0nWVvIaohNuw==
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.1.1"
jss "10.6.0"
jss-plugin-rule-value-function@^10.0.3:
version "10.1.1"
resolved "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.1.1.tgz#be00dac6fc394aaddbcef5860b9eca6224d96382"
integrity sha512-ClV1lvJ3laU9la1CUzaDugEcwnpjPTuJ0yGy2YtcU+gG/w9HMInD5vEv7xKAz53Bk4WiJm5uLOElSEshHyhKNw==
jss-plugin-rule-value-function@^10.5.1:
version "10.6.0"
resolved "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.6.0.tgz#3c1a557236a139d0151e70a82c810ccce1c1c5ea"
integrity sha512-TKFqhRTDHN1QrPTMYRlIQUOC2FFQb271+AbnetURKlGvRl/eWLswcgHQajwuxI464uZk91sPiTtdGi7r7XaWfA==
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.1.1"
jss "10.6.0"
tiny-warning "^1.0.2"
jss-plugin-vendor-prefixer@^10.0.3:
version "10.1.1"
resolved "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.1.1.tgz#8348b20749f790beebab3b6a8f7075b07c2cfcfd"
integrity sha512-09MZpQ6onQrhaVSF6GHC4iYifQ7+4YC/tAP6D4ZWeZotvCMq1mHLqNKRIaqQ2lkgANjlEot2JnVi1ktu4+L4pw==
jss-plugin-vendor-prefixer@^10.5.1:
version "10.6.0"
resolved "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.6.0.tgz#e1fcd499352846890c38085b11dbd7aa1c4f2c78"
integrity sha512-doJ7MouBXT1lypLLctCwb4nJ6lDYqrTfVS3LtXgox42Xz0gXusXIIDboeh6UwnSmox90QpVnub7au8ybrb0krQ==
dependencies:
"@babel/runtime" "^7.3.1"
css-vendor "^2.0.7"
jss "10.1.1"
css-vendor "^2.0.8"
jss "10.6.0"
jss@10.1.1, jss@^10.0.3:
version "10.1.1"
resolved "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz#450b27d53761af3e500b43130a54cdbe157ea332"
integrity sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==
jss@10.6.0, jss@^10.5.1:
version "10.6.0"
resolved "https://registry.npmjs.org/jss/-/jss-10.6.0.tgz#d92ff9d0f214f65ca1718591b68e107be4774149"
integrity sha512-n7SHdCozmxnzYGXBHe0NsO0eUf9TvsHVq2MXvi4JmTn3x5raynodDVE/9VQmBdWFyyj9HpHZ2B4xNZ7MMy7lkw==
dependencies:
"@babel/runtime" "^7.3.1"
csstype "^2.6.5"
csstype "^3.0.2"
indefinite-observable "^2.0.1"
is-in-browser "^1.1.3"
tiny-warning "^1.0.2"
@@ -18619,14 +18619,7 @@ minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
safe-buffer "^5.1.2"
yallist "^3.0.0"
minipass@^3.0.0, minipass@^3.1.1:
version "3.1.1"
resolved "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5"
integrity sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==
dependencies:
yallist "^4.0.0"
minipass@^3.1.0, minipass@^3.1.3:
minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3:
version "3.1.3"
resolved "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd"
integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==
@@ -24096,20 +24089,13 @@ sshpk@^1.7.0:
tweetnacl "~0.14.0"
ssri@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==
version "6.0.2"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5"
integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==
dependencies:
figgy-pudding "^3.5.1"
ssri@^8.0.0:
version "8.0.0"
resolved "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808"
integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==
dependencies:
minipass "^3.1.1"
ssri@^8.0.1:
ssri@^8.0.0, ssri@^8.0.1:
version "8.0.1"
resolved "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==
@@ -24788,7 +24774,7 @@ swr@^0.3.0:
dependencies:
fast-deep-equal "2.0.1"
symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0:
symbol-observable@1.2.0, symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0:
version "1.2.0"
resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==