diff --git a/.changeset/afraid-weeks-sort.md b/.changeset/afraid-weeks-sort.md deleted file mode 100644 index c5ec608daf..0000000000 --- a/.changeset/afraid-weeks-sort.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -k8s plugin now surfaces k8s components with only label selector query annotation. -Previously backstage.io/kubernetes-label-selector catalog entity annotation would only work if you also included backstage.io/kubernetes-id. -But backstage.io/kubernetes-id value was ignored diff --git a/.changeset/blue-lemons-reflect.md b/.changeset/blue-lemons-reflect.md deleted file mode 100644 index 8b2fc144fd..0000000000 --- a/.changeset/blue-lemons-reflect.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-circleci': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-github-actions': patch ---- - -Use Breadcrumbs from @backstage/core rather than material-ui diff --git a/.changeset/brown-hotels-study.md b/.changeset/brown-hotels-study.md deleted file mode 100644 index 2b9e6a86b8..0000000000 --- a/.changeset/brown-hotels-study.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -Move logic for constructing the template form to the backend, using a new `./parameter-schema` endpoint that returns the form schema for a given template. diff --git a/.changeset/chatty-turkeys-fold.md b/.changeset/chatty-turkeys-fold.md deleted file mode 100644 index bbc083cd85..0000000000 --- a/.changeset/chatty-turkeys-fold.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Bump `@svgr/webpack` from `5.4.x` to `5.5.x`. diff --git a/.changeset/clean-plums-crash.md b/.changeset/clean-plums-crash.md deleted file mode 100644 index 77cd94f097..0000000000 --- a/.changeset/clean-plums-crash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs-backend': patch ---- - -Forward authorization header on backend request if present diff --git a/.changeset/curvy-pugs-type.md b/.changeset/curvy-pugs-type.md deleted file mode 100644 index 7e94a5b35f..0000000000 --- a/.changeset/curvy-pugs-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -GithubDiscoveryProcessor outputs locations as optional to avoid outputting errors for missing locations (see https://github.com/backstage/backstage/issues/4730). diff --git a/.changeset/empty-taxis-search.md b/.changeset/empty-taxis-search.md deleted file mode 100644 index ad45f290d3..0000000000 --- a/.changeset/empty-taxis-search.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch -'@backstage/plugin-catalog': patch ---- - -Update messaging when no entities are in a table. diff --git a/.changeset/empty-windows-nail.md b/.changeset/empty-windows-nail.md deleted file mode 100644 index 0b2eeb08cc..0000000000 --- a/.changeset/empty-windows-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Make the external `createComponent` route optional, hiding the "Create Component" button if it isn't bound. diff --git a/.changeset/fluffy-elephants-suffer.md b/.changeset/fluffy-elephants-suffer.md deleted file mode 100644 index cd118829ed..0000000000 --- a/.changeset/fluffy-elephants-suffer.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/catalog-model': patch -'@backstage/plugin-catalog-backend': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -Add version `backstage.io/v1beta2` schema for Template entities. diff --git a/.changeset/gentle-bears-fry.md b/.changeset/gentle-bears-fry.md deleted file mode 100644 index 43966311c7..0000000000 --- a/.changeset/gentle-bears-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-sonarqube': patch ---- - -Collect all available metric types if the number exceeds the default page size of 100. diff --git a/.changeset/honest-moose-eat.md b/.changeset/honest-moose-eat.md deleted file mode 100644 index be42e9e26b..0000000000 --- a/.changeset/honest-moose-eat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Add optional `logClientErrors` to errorHandler to log 4XX errors diff --git a/.changeset/hot-islands-kick.md b/.changeset/hot-islands-kick.md deleted file mode 100644 index 67222245de..0000000000 --- a/.changeset/hot-islands-kick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-proxy-backend': patch ---- - -Added a verification for well formed URLs when processing proxy targets. Otherwise users gets a cryptic error message thrown from Express which makes it hard to debug. diff --git a/.changeset/hungry-frogs-flow.md b/.changeset/hungry-frogs-flow.md deleted file mode 100644 index 91a6ab0892..0000000000 --- a/.changeset/hungry-frogs-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Consider both authentication methods for both `onprem` and `cloud` BitBucket diff --git a/.changeset/itchy-items-serve.md b/.changeset/itchy-items-serve.md deleted file mode 100644 index df39751e9d..0000000000 --- a/.changeset/itchy-items-serve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs-backend': patch ---- - -Use external url for static docs diff --git a/.changeset/kind-pens-deliver.md b/.changeset/kind-pens-deliver.md deleted file mode 100644 index e4937cb396..0000000000 --- a/.changeset/kind-pens-deliver.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@backstage/techdocs-common': patch -'@backstage/plugin-auth-backend': patch -'@backstage/plugin-kafka-backend': patch -'@backstage/plugin-kubernetes-backend': patch -'@backstage/plugin-scaffolder-backend': patch -'@backstage/plugin-techdocs-backend': patch ---- - -remove usage of res.send() for res.json() and res.end() to ensure content types are more consistently application/json on backend responses and error cases diff --git a/.changeset/late-singers-speak.md b/.changeset/late-singers-speak.md deleted file mode 100644 index 5e58e09c23..0000000000 --- a/.changeset/late-singers-speak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Change debug log format to print as color grey diff --git a/.changeset/late-tables-provide.md b/.changeset/late-tables-provide.md deleted file mode 100644 index 125d3bc3ac..0000000000 --- a/.changeset/late-tables-provide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Tweak the template cards to be even more compliant with MUI examples, and a little bit more dense. diff --git a/.changeset/light-vans-hide.md b/.changeset/light-vans-hide.md deleted file mode 100644 index ccc1339959..0000000000 --- a/.changeset/light-vans-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -Add organization name to the API Explorer page title diff --git a/.changeset/long-bikes-check.md b/.changeset/long-bikes-check.md deleted file mode 100644 index 4fbc28d5aa..0000000000 --- a/.changeset/long-bikes-check.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -The api-docs plugin has been migrated to use an [external route reference](https://backstage.io/docs/plugins/composability#binding-external-routes-in-the-app) to dynamically link to the create component page. - -If you want to have a button that links to the scaffolder plugin from the API explorer, apply the following changes to `packages/app/src/App.tsx`: - -```diff -+ import { apiDocsPlugin } from '@backstage/plugin-api-docs'; - import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; - - const app = createApp({ - // ... - bindRoutes({ bind }) { -+ bind(apiDocsPlugin.externalRoutes, { -+ createComponent: scaffolderPlugin.routes.root, -+ }); - }, - }); -``` - -If you choose to not bind the routes, the button to create new APIs is not displayed. diff --git a/.changeset/metal-pianos-repeat.md b/.changeset/metal-pianos-repeat.md deleted file mode 100644 index e24320938a..0000000000 --- a/.changeset/metal-pianos-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -Support AWS auth provider on kubernetes FE plugin diff --git a/.changeset/nervous-dogs-pull.md b/.changeset/nervous-dogs-pull.md deleted file mode 100644 index fd62b751d8..0000000000 --- a/.changeset/nervous-dogs-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-proxy-backend': patch ---- - -Fix for proxy-backend plugin when global-agent is enabled diff --git a/.changeset/new-forks-perform.md b/.changeset/new-forks-perform.md deleted file mode 100644 index 04d6d59be1..0000000000 --- a/.changeset/new-forks-perform.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Use entity relation for the owner of an entity in the catalog entity page header. diff --git a/.changeset/nice-spies-beam.md b/.changeset/nice-spies-beam.md deleted file mode 100644 index 0ac956709a..0000000000 --- a/.changeset/nice-spies-beam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Bump `@testing-library/react` in the plugin template from `^10.4.1` to `^11.2.5`. To apply this to an existing plugin, update the dependency in your `package.json`. diff --git a/.changeset/nine-comics-smash.md b/.changeset/nine-comics-smash.md new file mode 100644 index 0000000000..147944930d --- /dev/null +++ b/.changeset/nine-comics-smash.md @@ -0,0 +1,7 @@ +--- +'@backstage/techdocs-common': patch +'@backstage/plugin-techdocs-backend': patch +'@backstage/plugin-techdocs': patch +--- + +OpenStack Swift publisher added for tech-docs. diff --git a/.changeset/old-coats-suffer.md b/.changeset/old-coats-suffer.md deleted file mode 100644 index 5938c60b1e..0000000000 --- a/.changeset/old-coats-suffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': minor ---- - -Add authorization header on techdocs api requests. Breaking change as clients now needs the Identity API. diff --git a/.changeset/pink-ads-shave.md b/.changeset/pink-ads-shave.md deleted file mode 100644 index 50d0ddef22..0000000000 --- a/.changeset/pink-ads-shave.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -'@backstage/plugin-scaffolder': minor -'@backstage/plugin-scaffolder-backend': minor ---- - -The scaffolder has been updated to support the new `v1beta2` template schema which allows for custom template actions! - -See documentation for more information how to create and register new template actions. - -**Breaking changes** - -The backend scaffolder plugin now needs a `UrlReader` which can be pulled from the PluginEnvironment. - -The following change is required in `backend/src/plugins/scaffolder.ts` - -```diff - export default async function createPlugin({ - logger, - config, - database, -+ reader, - }: PluginEnvironment): Promise { - - // omitted code - - return await createRouter({ - preparers, - templaters, - publishers, - logger, - config, - dockerClient, - database, - catalogClient, -+ reader, - }); -``` diff --git a/.changeset/popular-sheep-act.md b/.changeset/popular-sheep-act.md deleted file mode 100644 index 885d9f0e6b..0000000000 --- a/.changeset/popular-sheep-act.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-api': patch -'@backstage/core': patch ---- - -Added support for optional external route references. By setting `optional: true` when creating an `ExternalRouteRef` it is no longer a requirement to bind the route in the app. If the app isn't bound `useRouteRef` will return `undefined`. diff --git a/.changeset/red-taxis-confess.md b/.changeset/red-taxis-confess.md deleted file mode 100644 index 4fa40d8f17..0000000000 --- a/.changeset/red-taxis-confess.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -The api-docs plugin has been migrated to use an [external route reference](https://backstage.io/docs/plugins/composability#binding-external-routes-in-the-app) to dynamically link to the create component page. This means you need to migrate the api docs plugin to use the new extension components, as well as bind the external route. - -To bind the external route from the api docs plugin to the scaffolder template index page, make sure you have the appropriate imports and add the following to the `createApp` call: - -```ts -import { apiDocsPlugin } from '@backstage/plugin-api-docs'; -import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; - -const app = createApp({ - // ... - bindRoutes({ bind }) { - bind(apiDocsPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - }); - }, -}); -``` - -If you choose to not bind the routes, the button to create new APIs is not displayed. diff --git a/.changeset/rich-socks-move.md b/.changeset/rich-socks-move.md deleted file mode 100644 index 5aeb3121f1..0000000000 --- a/.changeset/rich-socks-move.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-api': patch -'@backstage/core': patch ---- - -Internal refactor of how component data is access to avoid polluting components and make it possible to bridge across versions. diff --git a/.changeset/rude-roses-jam.md b/.changeset/rude-roses-jam.md new file mode 100644 index 0000000000..44fc771791 --- /dev/null +++ b/.changeset/rude-roses-jam.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-splunk-on-call': minor +--- + +Updated splunk-on-call plugin to use the REST endpoint (incident creation-acknowledgement-resolution). +It implies switching from `splunkOnCall.username` configuration to `splunkOnCall.eventsRestEndpoint` configuration, this is a breaking change. diff --git a/.changeset/shaggy-spiders-shop.md b/.changeset/shaggy-spiders-shop.md deleted file mode 100644 index ae500f1e81..0000000000 --- a/.changeset/shaggy-spiders-shop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-sonarqube': patch ---- - -Display '-' instead of 'undefined' if no code coverage is reported. diff --git a/.changeset/short-geese-double.md b/.changeset/short-geese-double.md deleted file mode 100644 index 1c63321be7..0000000000 --- a/.changeset/short-geese-double.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -New config command to export the configuration schema. When running backstage-cli with yarn, consider using `yarn --silent backstage-cli config:schema` to get a clean output on `stdout`. diff --git a/.changeset/silly-dolphins-repair.md b/.changeset/silly-dolphins-repair.md deleted file mode 100644 index 43c6aed230..0000000000 --- a/.changeset/silly-dolphins-repair.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -Added the color prop to TrendLine from the Sparklines props types to be able to have custom colors. diff --git a/.changeset/silver-balloons-play.md b/.changeset/silver-balloons-play.md deleted file mode 100644 index 24a5c8ac46..0000000000 --- a/.changeset/silver-balloons-play.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -Added a new useSupportConfig hook that reads a new `app.support` config key. Also updated the SupportButton and ErrorPage components to use the new config. diff --git a/.changeset/slimy-singers-return.md b/.changeset/slimy-singers-return.md deleted file mode 100644 index c5fc60aaa2..0000000000 --- a/.changeset/slimy-singers-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Fixed file path resolution for templates with a file location diff --git a/.changeset/slow-jobs-live.md b/.changeset/slow-jobs-live.md new file mode 100644 index 0000000000..dd640a54f3 --- /dev/null +++ b/.changeset/slow-jobs-live.md @@ -0,0 +1,6 @@ +--- +'@backstage/core': patch +'@backstage/theme': patch +--- + +Deprecated `ItemCard`. Added `ItemCardGrid` and `ItemCardHeader` instead, that can be used to compose functionality around regular Material-UI `Card` components instead. diff --git a/.changeset/sour-insects-marry.md b/.changeset/sour-insects-marry.md deleted file mode 100644 index 27e72977d5..0000000000 --- a/.changeset/sour-insects-marry.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -'@backstage/core': minor -'@backstage/plugin-pagerduty': patch ---- - -- Adds onClick and other props to IconLinkVertical; -- Allows TriggerButton component to render when pager duty key is missing; -- Refactors TriggerButton and PagerDutyCard not to have shared state; -- Removes the `action` prop of the IconLinkVertical component while adding `onClick`. - - Instead of having an action including a button with onClick, now the whole component can be clickable making it easier to implement and having a better UX. - - Before: - - ```ts - const myLink: IconLinkVerticalProps = { - label: 'Click me', - action: + + + ))} + ); + +export const Styling = () => { + const classes = useStyles(); + return ( + + + Both the grid and the header can be styled, using the{' '} + classes property. This lets + you for example tweak the column sizes and the background of the header. + + + {[...Array(10).keys()].map(index => ( + + + + + + {text + .split(' ') + .slice(0, 5 + Math.floor(Math.random() * 30)) + .join(' ')} + + + + + + ))} + + + ); +}; diff --git a/packages/core/src/layout/ItemCard/ItemCard.tsx b/packages/core/src/layout/ItemCard/ItemCard.tsx index 9e16def5d3..8c26e6a9f1 100644 --- a/packages/core/src/layout/ItemCard/ItemCard.tsx +++ b/packages/core/src/layout/ItemCard/ItemCard.tsx @@ -13,33 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Button, Card, Chip, makeStyles, Typography } from '@material-ui/core'; -import clsx from 'clsx'; +import { + Box, + Card, + CardActions, + CardContent, + CardMedia, + Chip, +} from '@material-ui/core'; import React, { ReactNode } from 'react'; -import { Link } from '../../components'; - -const useStyles = makeStyles(theme => ({ - header: { - color: theme.palette.common.white, - padding: theme.spacing(2, 2, 6), - backgroundImage: 'linear-gradient(-137deg, #4BB8A5 0%, #187656 100%)', - }, - content: { - padding: theme.spacing(2), - }, - description: { - height: 175, - overflow: 'hidden', - textOverflow: 'ellipsis', - }, - withTags: { - height: 'calc(175px - 32px - 8px)', - }, - footer: { - display: 'flex', - flexDirection: 'row-reverse', - }, -})); +import { Button } from '../../components'; +import { ItemCardHeader } from './ItemCardHeader'; type ItemCardProps = { description?: string; @@ -53,6 +37,31 @@ type ItemCardProps = { href?: string; }; +/** + * This card type has been deprecated. Instead use plain MUI Card and helpers + * where appropriate. + * + * + * + * + * + * @deprecated Use plain MUI and composable helpers instead. + * @see https://material-ui.com/components/cards/ + */ export const ItemCard = ({ description, tags, @@ -63,43 +72,33 @@ export const ItemCard = ({ onClick, href, }: ItemCardProps) => { - const classes = useStyles(); - return ( -
- {(subtitle || type) && ( - {subtitle ?? type} + + + + + {tags?.length ? ( + + {tags.map((tag, i) => ( + + ))} + + ) : null} + {description} + + + {!href && ( + )} - {title} -
-
- {tags?.map((tag, i) => ( - - ))} - 0 && classes.withTags, - )} - > - {description} - -
- {!href && ( - - )} - {href && ( - - )} -
-
+ {href && ( + + )} +
); }; diff --git a/packages/core/src/layout/ItemCard/ItemCardGrid.test.tsx b/packages/core/src/layout/ItemCard/ItemCardGrid.test.tsx new file mode 100644 index 0000000000..51ef8c544f --- /dev/null +++ b/packages/core/src/layout/ItemCard/ItemCardGrid.test.tsx @@ -0,0 +1,50 @@ +/* + * 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 { renderInTestApp } from '@backstage/test-utils'; +import { Card } from '@material-ui/core'; +import { screen } from '@testing-library/react'; +import React from 'react'; +import { ItemCardGrid } from './ItemCardGrid'; + +describe('', () => { + it('renders default without exploding', async () => { + await renderInTestApp( + + Hello! + , + ); + expect(screen.getByRole('grid')).toBeInTheDocument(); + expect(screen.getByText('Hello!')).toBeInTheDocument(); + }); + + it('renders custom styles', async () => { + await renderInTestApp( + <> + + Hello! + + + Goodbye! + + , + ); + expect(screen.getAllByRole('grid')[0]).toHaveStyle({ + gridTemplateColumns: 'repeat(auto-fill, minmax(22em, 1fr))', + }); + expect(screen.getAllByRole('grid')[1]).toHaveClass('my-css-class'); + }); +}); diff --git a/packages/core/src/layout/ItemCard/ItemCardGrid.tsx b/packages/core/src/layout/ItemCard/ItemCardGrid.tsx new file mode 100644 index 0000000000..551c3c67d5 --- /dev/null +++ b/packages/core/src/layout/ItemCard/ItemCardGrid.tsx @@ -0,0 +1,62 @@ +/* + * Copyright 2021 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 { createStyles, makeStyles, Theme, WithStyles } from '@material-ui/core'; +import React from 'react'; + +const styles = (theme: Theme) => + createStyles({ + root: { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(22em, 1fr))', + gridAutoRows: '1fr', + gridGap: theme.spacing(2), + }, + }); + +const useStyles = makeStyles(styles); + +export type ItemCardGridProps = Partial> & { + /** + * The Card items of the grid. + */ + children?: React.ReactNode; +}; + +/** + * A default grid to use when arranging "item cards" - cards that let users + * select among several options. + * + * The immediate children are expected to be MUI Card components. + * + * Styles for the grid can be overridden using the `classes` prop, e.g.: + * + * + * + * + * + * This can be useful for e.g. overriding gridTemplateColumns to adapt the + * minimum size of the cells to fit the content better. + */ +export const ItemCardGrid = (props: ItemCardGridProps) => { + const { children, ...otherProps } = props; + const classes = useStyles(otherProps); + return ( +
+ {children} +
+ ); +}; diff --git a/packages/core/src/layout/ItemCard/ItemCardHeader.test.tsx b/packages/core/src/layout/ItemCard/ItemCardHeader.test.tsx new file mode 100644 index 0000000000..ec9af6436e --- /dev/null +++ b/packages/core/src/layout/ItemCard/ItemCardHeader.test.tsx @@ -0,0 +1,60 @@ +/* + * 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 { renderInTestApp } from '@backstage/test-utils'; +import { Card, CardMedia } from '@material-ui/core'; +import { screen } from '@testing-library/react'; +import React from 'react'; +import { ItemCardHeader } from './ItemCardHeader'; + +describe('', () => { + it('renders default without exploding', async () => { + await renderInTestApp( + + + + + , + ); + expect(screen.getByText('My Title')).toBeInTheDocument(); + expect(screen.getByText('My Subtitle')).toBeInTheDocument(); + }); + + it('renders custom children', async () => { + await renderInTestApp( + + + My Custom Text + + , + ); + expect(screen.getByText('My Title')).toBeInTheDocument(); + expect(screen.getByText('My Custom Text')).toBeInTheDocument(); + }); + + it('renders custom styles', async () => { + await renderInTestApp( + + + + My Custom Text + + + , + ); + expect(screen.getByText('My Custom Text')).toHaveClass('my-css-class'); + }); +}); diff --git a/packages/core/src/layout/ItemCard/ItemCardHeader.tsx b/packages/core/src/layout/ItemCard/ItemCardHeader.tsx new file mode 100644 index 0000000000..17c4a19e58 --- /dev/null +++ b/packages/core/src/layout/ItemCard/ItemCardHeader.tsx @@ -0,0 +1,85 @@ +/* + * Copyright 2021 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 { + createStyles, + makeStyles, + Theme, + Typography, + WithStyles, +} from '@material-ui/core'; +import React from 'react'; + +const styles = (theme: Theme) => + createStyles({ + root: { + color: theme.palette.common.white, + padding: theme.spacing(2, 2, 3), + backgroundImage: 'linear-gradient(-137deg, #4BB8A5 0%, #187656 100%)', + backgroundPosition: 0, + backgroundSize: 'inherit', + }, + }); + +const useStyles = makeStyles(styles); + +export type ItemCardHeaderProps = Partial> & { + /** + * A large title to show in the header, providing the main heading. + * + * Use this if you want to have the default styling and placement of a title. + */ + title?: React.ReactNode; + /** + * A slightly smaller title to show in the header, providing additional + * details. + * + * Use this if you want to have the default styling and placement of a + * subtitle. + */ + subtitle?: React.ReactNode; + /** + * Custom children to draw in the header. + * + * If the title and/or subtitle were specified, the children are drawn below + * those. + */ + children?: React.ReactNode; +}; + +/** + * A simple card header, rendering a default look for "item cards" - cards that + * are arranged in a grid for users to select among several options. + * + * This component expects to be placed within a MUI . + * + * Styles for the header can be overridden using the `classes` prop, e.g.: + * + * + * + * + */ +export const ItemCardHeader = (props: ItemCardHeaderProps) => { + const { title, subtitle, children } = props; + const classes = useStyles(props); + return ( +
+ {subtitle && {subtitle}} + {title && {title}} + {children} +
+ ); +}; diff --git a/packages/core/src/layout/ItemCard/index.ts b/packages/core/src/layout/ItemCard/index.ts index b38dd2acc2..da2c1dd546 100644 --- a/packages/core/src/layout/ItemCard/index.ts +++ b/packages/core/src/layout/ItemCard/index.ts @@ -15,3 +15,7 @@ */ export { ItemCard } from './ItemCard'; +export { ItemCardGrid } from './ItemCardGrid'; +export type { ItemCardGridProps } from './ItemCardGrid'; +export { ItemCardHeader } from './ItemCardHeader'; +export type { ItemCardHeaderProps } from './ItemCardHeader'; diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index de59a343ed..52fbdea6e2 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,167 @@ # @backstage/create-app +## 0.3.12 + +### Patch Changes + +- f71589800: The api-docs plugin has been migrated to use an [external route reference](https://backstage.io/docs/plugins/composability#binding-external-routes-in-the-app) to dynamically link to the create component page. + + If you want to have a button that links to the scaffolder plugin from the API explorer, apply the following changes to `packages/app/src/App.tsx`: + + ```diff + + import { apiDocsPlugin } from '@backstage/plugin-api-docs'; + import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; + + const app = createApp({ + // ... + bindRoutes({ bind }) { + + bind(apiDocsPlugin.externalRoutes, { + + createComponent: scaffolderPlugin.routes.root, + + }); + }, + }); + ``` + + If you choose to not bind the routes, the button to create new APIs is not displayed. + +- 7a1b2ba0e: Migrated away from using deprecated routes and router components at top-level in the app, and instead use routable extension pages. + + To apply this change to an existing app, make the following changes to `packages/app/src/App.tsx`: + + Update imports and remove the usage of the deprecated `app.getRoutes()`. + + ```diff + -import { Router as DocsRouter } from '@backstage/plugin-techdocs'; + +import { TechdocsPage } from '@backstage/plugin-techdocs'; + import { CatalogImportPage } from '@backstage/plugin-catalog-import'; + -import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; + -import { SearchPage as SearchRouter } from '@backstage/plugin-search'; + -import { Router as SettingsRouter } from '@backstage/plugin-user-settings'; + +import { TechRadarPage } from '@backstage/plugin-tech-radar'; + +import { SearchPage } from '@backstage/plugin-search'; + +import { UserSettingsPage } from '@backstage/plugin-user-settings'; + +import { ApiExplorerPage } from '@backstage/plugin-api-docs'; + import { EntityPage } from './components/catalog/EntityPage'; + import { scaffolderPlugin, ScaffolderPage } from '@backstage/plugin-scaffolder'; + ``` + +const AppProvider = app.getProvider(); +const AppRouter = app.getRouter(); +-const deprecatedAppRoutes = app.getRoutes(); + +```` + +As well as update or add the following routes: + +```diff + } /> +- } /> ++ } /> ++ } /> + } ++ element={} + /> + } /> +- } +- /> +- } /> +- {deprecatedAppRoutes} ++ } /> ++ } /> +```` + +If you have added additional plugins with registered routes or are using `Router` components from other plugins, these should be migrated to use the `*Page` components as well. See [this commit](https://github.com/backstage/backstage/commit/abd655e42d4ed416b70848ffdb1c4b99d189f13b) for more examples of how to migrate. + +For more information and the background to this change, see the [composability system migration docs](https://backstage.io/docs/plugins/composability). + +- 415a3a42d: Updated the default `App` test to work better on Windows. + + To apply this change to an existing app, replace the `process.env.APP_CONFIG` definition in `packages/app/src/App.test.tsx` with the following: + + ```ts + process.env = { + NODE_ENV: 'test', + APP_CONFIG: [ + { + data: { + app: { title: 'Test' }, + backend: { baseUrl: 'http://localhost:7000' }, + techdocs: { + storageUrl: 'http://localhost:7000/api/techdocs/static/docs', + }, + }, + context: 'test', + }, + ] as any, + }; + ``` + +- Updated dependencies [b2a5320a4] +- Updated dependencies [12d8f27a6] +- Updated dependencies [507513fed] +- Updated dependencies [52b5bc3e2] +- Updated dependencies [ecdd407b1] +- Updated dependencies [32a003973] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [12d8f27a6] +- Updated dependencies [497859088] +- Updated dependencies [1987c9341] +- Updated dependencies [f31b76b44] +- Updated dependencies [15eee03bc] +- Updated dependencies [f43192207] +- Updated dependencies [cfc83cac1] +- Updated dependencies [8adb48df4] +- Updated dependencies [bc327dc42] +- Updated dependencies [2386de1d3] +- Updated dependencies [9ce68b677] +- Updated dependencies [10362e9eb] +- Updated dependencies [e37d2de99] +- Updated dependencies [813c6a4f2] +- Updated dependencies [11c6208fe] +- Updated dependencies [8106c9528] +- Updated dependencies [05183f202] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [f71589800] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [d4f0a1406] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [d0ed25196] +- Updated dependencies [4c049a1a1] +- Updated dependencies [96ccc8f69] +- Updated dependencies [3af994c81] +- Updated dependencies [b33e553b2] +- Updated dependencies [04667f571] +- Updated dependencies [b93538acc] +- Updated dependencies [8871e7523] +- Updated dependencies [dbea11072] + - @backstage/plugin-circleci@0.2.11 + - @backstage/plugin-github-actions@0.3.5 + - @backstage/plugin-scaffolder@0.7.0 + - @backstage/plugin-scaffolder-backend@0.9.0 + - @backstage/cli@0.6.3 + - @backstage/plugin-techdocs-backend@0.6.3 + - @backstage/plugin-catalog-backend@0.6.4 + - @backstage/plugin-api-docs@0.4.8 + - @backstage/plugin-catalog@0.4.1 + - @backstage/catalog-model@0.7.3 + - @backstage/backend-common@0.5.5 + - @backstage/plugin-proxy-backend@0.2.5 + - @backstage/plugin-auth-backend@0.3.3 + - @backstage/plugin-explore@0.3.0 + - @backstage/plugin-techdocs@0.6.0 + - @backstage/plugin-catalog-import@0.4.3 + - @backstage/core@0.7.0 + - @backstage/plugin-lighthouse@0.2.13 + - @backstage/plugin-search@0.3.3 + - @backstage/plugin-tech-radar@0.3.7 + - @backstage/plugin-user-settings@0.2.7 + ## 0.3.11 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 05615cc78d..ba69da9c8c 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.3.11", + "version": "0.3.12", "private": false, "publishConfig": { "access": "public" @@ -56,7 +56,6 @@ "@backstage/plugin-catalog": "*", "@backstage/plugin-catalog-backend": "*", "@backstage/plugin-catalog-import": "*", - "@backstage/plugin-circleci": "*", "@backstage/plugin-explore": "*", "@backstage/plugin-github-actions": "*", "@backstage/plugin-lighthouse": "*", diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index c7561fe091..d42e19eb1f 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -15,7 +15,6 @@ "@backstage/plugin-scaffolder": "^{{version '@backstage/plugin-scaffolder'}}", "@backstage/plugin-techdocs": "^{{version '@backstage/plugin-techdocs'}}", "@backstage/catalog-model": "^{{version '@backstage/catalog-model'}}", - "@backstage/plugin-circleci": "^{{version '@backstage/plugin-circleci'}}", "@backstage/plugin-tech-radar": "^{{version '@backstage/plugin-tech-radar'}}", "@backstage/plugin-github-actions": "^{{version '@backstage/plugin-github-actions'}}", "@backstage/plugin-user-settings": "^{{version '@backstage/plugin-user-settings'}}", diff --git a/packages/create-app/templates/default-app/packages/app/src/App.test.tsx b/packages/create-app/templates/default-app/packages/app/src/App.test.tsx index 21a2eaa703..82bc479858 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.test.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.test.tsx @@ -4,9 +4,9 @@ import App from './App'; describe('App', () => { it('should render', async () => { - Object.defineProperty(process.env, 'APP_CONFIG', { - configurable: true, - value: [ + process.env = { + NODE_ENV: 'test', + APP_CONFIG: [ { data: { app: { title: 'Test' }, @@ -17,8 +17,8 @@ describe('App', () => { }, context: 'test', }, - ], - }); + ] as any, + }; const rendered = await renderWithEffects(); expect(rendered.baseElement).toBeInTheDocument(); diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index a3a3715ab0..de6ab2b6b5 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -17,9 +17,9 @@ import { ApiEntity, Entity } from '@backstage/catalog-model'; import { WarningPanel } from '@backstage/core'; import { ApiDefinitionCard, - ConsumedApisCard, - ConsumingComponentsCard, - ProvidedApisCard, + ConsumedApisCard, + ConsumingComponentsCard, + ProvidedApisCard, ProvidingComponentsCard } from '@backstage/plugin-api-docs'; import { @@ -28,9 +28,6 @@ import { import { useEntity } from '@backstage/plugin-catalog-react'; -import { - isPluginApplicableToEntity as isCircleCIAvailable, Router as CircleCIRouter -} from '@backstage/plugin-circleci'; import { isPluginApplicableToEntity as isGitHubActionsAvailable, Router as GitHubActionsRouter } from '@backstage/plugin-github-actions'; @@ -45,8 +42,6 @@ const CICDSwitcher = ({ entity }: { entity: Entity }) => { switch (true) { case isGitHubActionsAvailable(entity): return ; - case isCircleCIAvailable(entity): - return ; default: return ( diff --git a/packages/create-app/templates/default-app/packages/app/src/plugins.ts b/packages/create-app/templates/default-app/packages/app/src/plugins.ts index 28b42d5be2..df53885723 100644 --- a/packages/create-app/templates/default-app/packages/app/src/plugins.ts +++ b/packages/create-app/templates/default-app/packages/app/src/plugins.ts @@ -1,7 +1,6 @@ export { plugin as ApiDocs } from '@backstage/plugin-api-docs'; export { plugin as CatalogPlugin } from '@backstage/plugin-catalog'; export { plugin as CatalogImport } from '@backstage/plugin-catalog-import'; -export { plugin as Circleci } from '@backstage/plugin-circleci'; export { plugin as GithubActions } from '@backstage/plugin-github-actions'; export { plugin as ScaffolderPlugin } from '@backstage/plugin-scaffolder'; export { plugin as TechDocsPlugin } from '@backstage/plugin-techdocs'; diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index f02ce3ae23..3e8b8a96a0 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/dev-utils +## 0.1.13 + +### Patch Changes + +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.1.12 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 54c42ac70c..f17da4c1be 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "0.1.12", + "version": "0.1.13", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.6.3", - "@backstage/catalog-model": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/core": "^0.7.0", + "@backstage/catalog-model": "^0.7.3", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/test-utils": "^0.1.8", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", @@ -47,7 +47,7 @@ "react-router-dom": "6.0.0-beta.0" }, "devDependencies": { - "@backstage/cli": "^0.6.2", + "@backstage/cli": "^0.6.3", "@types/jest": "^26.0.7", "@types/node": "^12.0.0" }, diff --git a/packages/e2e-test/src/commands/run.ts b/packages/e2e-test/src/commands/run.ts index 5f4894f9e9..851dccae16 100644 --- a/packages/e2e-test/src/commands/run.ts +++ b/packages/e2e-test/src/commands/run.ts @@ -42,33 +42,44 @@ const templatePackagePaths = [ ]; export async function run() { - const rootDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'backstage-e2e-')); - print(`CLI E2E test root: ${rootDir}\n`); + try { + const rootDir = await fs.mkdtemp( + resolvePath(os.tmpdir(), 'backstage-e2e-'), + ); + print(`CLI E2E test root: ${rootDir}\n`); - print('Building dist workspace'); - const workspaceDir = await buildDistWorkspace('workspace', rootDir); + print('Building dist workspace'); + const workspaceDir = await buildDistWorkspace('workspace', rootDir); - const isPostgres = Boolean(process.env.POSTGRES_USER); - print('Creating a Backstage App'); - const appDir = await createApp('test-app', isPostgres, workspaceDir, rootDir); + const isPostgres = Boolean(process.env.POSTGRES_USER); + print('Creating a Backstage App'); + const appDir = await createApp( + 'test-app', + isPostgres, + workspaceDir, + rootDir, + ); - print('Creating a Backstage Plugin'); - const pluginName = await createPlugin('test-plugin', appDir); + print('Creating a Backstage Plugin'); + const pluginName = await createPlugin('test-plugin', appDir); - print('Creating a Backstage Backend Plugin'); - await createPlugin('test-plugin', appDir, ['--backend']); + print('Creating a Backstage Backend Plugin'); + await createPlugin('test-plugin', appDir, ['--backend']); - print('Starting the app'); - await testAppServe(pluginName, appDir); + print('Starting the app'); + await testAppServe(pluginName, appDir); - print('Testing the backend startup'); - await testBackendStart(appDir, isPostgres); + print('Testing the backend startup'); + await testBackendStart(appDir, isPostgres); - print('All tests successful, removing test dir'); - await fs.remove(rootDir); + print('All tests successful, removing test dir'); + await fs.remove(rootDir); - // Just in case some child process was left hanging - process.exit(0); + // Just in case some child process was left hanging + process.exit(0); + } catch { + process.exit(1); + } } /** diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md index 60a10c924e..bb694c9a6b 100644 --- a/packages/techdocs-common/CHANGELOG.md +++ b/packages/techdocs-common/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/techdocs-common +## 0.4.3 + +### Patch Changes + +- f43192207: remove usage of res.send() for res.json() and res.end() to ensure content types are more consistently application/json on backend responses and error cases +- 61299519f: Remove read-store-upload loop when uploading S3 objects for TechDocs +- Updated dependencies [12d8f27a6] +- Updated dependencies [497859088] +- Updated dependencies [8adb48df4] + - @backstage/catalog-model@0.7.3 + - @backstage/backend-common@0.5.5 + ## 0.4.2 ### Patch Changes diff --git a/packages/techdocs-common/__mocks__/@azure/storage-blob.ts b/packages/techdocs-common/__mocks__/@azure/storage-blob.ts index 4fc1e4c374..0d480a7fa9 100644 --- a/packages/techdocs-common/__mocks__/@azure/storage-blob.ts +++ b/packages/techdocs-common/__mocks__/@azure/storage-blob.ts @@ -17,11 +17,13 @@ import type { BlobUploadCommonResponse, ContainerGetPropertiesResponse, } from '@azure/storage-blob'; +import { EventEmitter } from 'events'; import fs from 'fs-extra'; import os from 'os'; import path from 'path'; const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; + /** * @param sourceFile Relative path to entity root dir. Contains either / or \ as file separator * depending upon the OS. @@ -48,6 +50,9 @@ export class BlockBlobClient { } uploadFile(source: string): Promise { + if (!fs.existsSync(source)) { + return Promise.reject(new Error(`The file ${source} does not exist`)); + } return Promise.resolve({ _response: { request: { @@ -62,6 +67,25 @@ export class BlockBlobClient { exists() { return checkFileExists(this.blobName); } + + download() { + const filePath = path.join(rootDir, this.blobName); + const emitter = new EventEmitter(); + process.nextTick(() => { + if (fs.existsSync(filePath)) { + emitter.emit('data', Buffer.from(fs.readFileSync(filePath))); + emitter.emit('end'); + } else { + emitter.emit( + 'error', + new Error(`The file ${filePath} does not exist !`), + ); + } + }); + return Promise.resolve({ + readableStreamBody: emitter, + }); + } } class BlockBlobClientFailUpload extends BlockBlobClient { diff --git a/packages/techdocs-common/__mocks__/pkgcloud.ts b/packages/techdocs-common/__mocks__/pkgcloud.ts new file mode 100644 index 0000000000..5d9f81cda0 --- /dev/null +++ b/packages/techdocs-common/__mocks__/pkgcloud.ts @@ -0,0 +1,109 @@ +/* + * 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 fs from 'fs-extra'; +import os from 'os'; +import path from 'path'; +import { EventEmitter } from 'events'; + +const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; + +const checkFileExists = async (Key: string): Promise => { + // Key will always have / as file separator irrespective of OS since cloud providers expects /. + // Normalize Key to OS specific path before checking if file exists. + const filePath = path.join(rootDir, Key); + + try { + fs.accessSync(filePath, fs.constants.F_OK); + return true; + } catch (err) { + return false; + } +}; + +class PkgCloudStorageClient { + getFile( + containerName: string, + file: string, + callback: (err: any, file: string) => any, + ) { + checkFileExists(file).then(res => { + if (!res) { + callback('File does not exist', file); + throw new Error('File does not exist'); + } else { + callback(undefined, 'success'); + } + }); + } + + getContainer( + containerName: string, + callback: (err: string, container: string) => any, + ) { + if (containerName !== 'mock') { + callback('Container does not exist', containerName); + throw new Error('Container does not exist'); + } else { + callback('Container does not exist', 'success'); + } + } + + upload({ remote }: { remote: string }) { + const filePath = path.join(rootDir, remote); + + const emitter = new EventEmitter(); + + process.nextTick(() => { + if (fs.existsSync(filePath)) { + emitter.emit('success'); + (emitter as any).end = () => true; + } else { + emitter.emit( + 'error', + new Error(`The file ${filePath} does not exist !`), + ); + } + }); + + return emitter; + } + + download({ remote }: { remote: string }) { + const filePath = path.join(rootDir, remote); + + const emitter = new EventEmitter(); + + process.nextTick(() => { + if (fs.existsSync(filePath)) { + emitter.emit('data', Buffer.from(fs.readFileSync(filePath))); + emitter.emit('end'); + } else { + emitter.emit( + 'error', + new Error(`The file ${filePath} does not exist !`), + ); + } + }); + + return emitter; + } +} + +export class storage { + static createClient() { + return new PkgCloudStorageClient(); + } +} diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index c8ae633c97..8e3c28e9ea 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/techdocs-common", "description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "0.4.2", + "version": "0.4.3", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -38,8 +38,8 @@ "dependencies": { "@azure/identity": "^1.2.2", "@azure/storage-blob": "^12.4.0", - "@backstage/backend-common": "^0.5.4", - "@backstage/catalog-model": "^0.7.2", + "@backstage/backend-common": "^0.5.5", + "@backstage/catalog-model": "^0.7.3", "@backstage/config": "^0.1.3", "@backstage/integration": "^0.5.0", "@google-cloud/storage": "^5.6.0", @@ -56,16 +56,18 @@ "mime-types": "^2.1.27", "mock-fs": "^4.13.0", "p-limit": "^3.1.0", + "pkgcloud": "^2.2.0", "recursive-readdir": "^2.2.2", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.6.2", + "@backstage/cli": "^0.6.3", "@types/fs-extra": "^9.0.5", "@types/git-url-parse": "^9.0.0", "@types/js-yaml": "^3.12.5", "@types/mime-types": "^2.1.0", "@types/mock-fs": "^4.13.0", + "@types/pkgcloud": "^1.7.4", "@types/recursive-readdir": "^2.2.0" }, "jest": { diff --git a/packages/techdocs-common/src/helpers.ts b/packages/techdocs-common/src/helpers.ts index ec58bb7b40..16f2876efb 100644 --- a/packages/techdocs-common/src/helpers.ts +++ b/packages/techdocs-common/src/helpers.ts @@ -107,7 +107,7 @@ export const getGitRepositoryTempFolder = async ( // fs.realpathSync fixes a problem with macOS returning a path that is a symlink fs.realpathSync(os.tmpdir()), 'backstage-repo', - parsedGitLocation.source, + parsedGitLocation.resource, parsedGitLocation.owner, parsedGitLocation.name, parsedGitLocation.ref, @@ -216,12 +216,12 @@ export const getDocFilesFromRepository = async ( entity, ); - opts?.logger?.info(`Reading files from ${target}`); + opts?.logger?.debug(`Reading files from ${target}`); // readTree will throw NotModifiedError if etag has not changed. const readTreeResponse = await reader.readTree(target, { etag: opts?.etag }); const preparedDir = await readTreeResponse.dir(); - opts?.logger?.info(`Tree downloaded and stored at ${preparedDir}`); + opts?.logger?.debug(`Tree downloaded and stored at ${preparedDir}`); return { preparedDir, diff --git a/packages/techdocs-common/src/stages/generate/helpers.ts b/packages/techdocs-common/src/stages/generate/helpers.ts index a88ed13231..2035eb8da9 100644 --- a/packages/techdocs-common/src/stages/generate/helpers.ts +++ b/packages/techdocs-common/src/stages/generate/helpers.ts @@ -252,7 +252,7 @@ export const patchMkdocsYmlPreBuild = async ( mkdocsYmlFileString = await fs.readFile(mkdocsYmlPath, 'utf8'); } catch (error) { logger.warn( - `Could not read file ${mkdocsYmlPath} before running the generator. ${error.message}`, + `Could not read MkDocs YAML config file ${mkdocsYmlPath} before running the generator: ${error.message}`, ); return; } diff --git a/packages/techdocs-common/src/stages/prepare/commonGit.ts b/packages/techdocs-common/src/stages/prepare/commonGit.ts index 46b96675f3..c38f5ced9d 100644 --- a/packages/techdocs-common/src/stages/prepare/commonGit.ts +++ b/packages/techdocs-common/src/stages/prepare/commonGit.ts @@ -40,9 +40,9 @@ export class CommonGitPreparer implements PreparerBase { options?: { etag?: string }, ): Promise { this.logger.warn( - 'You are using the legacy git preparer in TechDocs which will be removed in near future (March 2021). ' + - 'Migrate to URL reader by updating `backstage.io/techdocs-ref` annotation in `catalog-info.yaml` ' + - 'to be prefixed with `url:`. Read the migration guide and benefits at https://github.com/backstage/backstage/issues/4409 ', + `You are using the legacy git preparer in TechDocs for \`${entity.metadata.name}\` which will be removed in near future (March 2021). ` + + `Migrate to URL reader by updating \`backstage.io/techdocs-ref\` annotation in \`catalog-info.yaml\` ` + + `to be prefixed with \`url:\`. Read the migration guide and benefits at https://github.com/backstage/backstage/issues/4409 `, ); const { target } = parseReferenceAnnotation( diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts index cf1ea60592..44262768a5 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts @@ -14,13 +14,17 @@ * limitations under the License. */ import { getVoidLogger } from '@backstage/backend-common'; -import { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model'; +import { + Entity, + EntityName, + ENTITY_DEFAULT_NAMESPACE, +} from '@backstage/catalog-model'; import { ConfigReader } from '@backstage/config'; import mockFs from 'mock-fs'; import os from 'os'; import path from 'path'; import { AzureBlobStoragePublish } from './azureBlobStorage'; -import { PublisherBase } from './types'; +import { PublisherBase, TechDocsMetadata } from './types'; // NOTE: /packages/techdocs-common/__mocks__ is being used to mock Azure client library @@ -38,7 +42,14 @@ const createMockEntity = (annotations = {}) => { }; }; +const createMockEntityName = (): EntityName => ({ + kind: 'TestKind', + name: 'test-component-name', + namespace: 'test-namespace', +}); + const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; + const getEntityRootDir = (entity: Entity) => { const { kind, @@ -163,6 +174,72 @@ describe('publishing with valid credentials', () => { expect(await publisher.hasDocsBeenGenerated(entity)).toBe(false); }); }); + + describe('fetchTechDocsMetadata', () => { + it('should return tech docs metadata', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': + '{"site_name": "backstage", "site_description": "site_content"}', + }, + }); + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return tech docs metadata when json encoded with single quotes', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': `{'site_name': 'backstage', 'site_description': 'site_content'}`, + }, + }); + + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return an error if the techdocs_metadata.json file is not present', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + let error; + try { + await publisher.fetchTechDocsMetadata(entityNameMock); + } catch (e) { + error = e; + } + + expect(error).toEqual( + new Error( + `TechDocs metadata fetch failed, The file ${path.join( + entityRootDir, + 'techdocs_metadata.json', + )} does not exist !`, + ), + ); + }); + }); }); describe('error reporting', () => { @@ -255,4 +332,63 @@ describe('error reporting', () => { mockFs.restore(); }); + + describe('fetchTechDocsMetadata', () => { + it('should return tech docs metadata', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': + '{"site_name": "backstage", "site_description": "site_content"}', + }, + }); + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return tech docs metadata when json encoded with single quotes', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': `{'site_name': 'backstage', 'site_description': 'site_content'}`, + }, + }); + + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return an error if the techdocs_metadata.json file is not present', async () => { + const entityNameMock = createMockEntityName(); + + let error; + try { + await publisher.fetchTechDocsMetadata(entityNameMock); + } catch (e) { + error = e; + } + + expect(error.message).toEqual( + expect.stringContaining('TechDocs metadata fetch'), + ); + }); + }); }); diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts index 2136ee2bc2..155d14fbb5 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts @@ -201,10 +201,7 @@ export class AzureBlobStoragePublish implements PublisherBase { return; } body - .on('error', e => { - this.logger.error(e.message); - reject(e.message); - }) + .on('error', reject) .on('data', chunk => { fileStreamChunks.push(chunk); }) @@ -220,16 +217,21 @@ export class AzureBlobStoragePublish implements PublisherBase { ): Promise { const entityRootDir = `${entityName.namespace}/${entityName.kind}/${entityName.name}`; try { - return await new Promise(resolve => { - const download = this.download( - this.containerName, - `${entityRootDir}/techdocs_metadata.json`, + const techdocsMetadataJson = await this.download( + this.containerName, + `${entityRootDir}/techdocs_metadata.json`, + ); + if (!techdocsMetadataJson) { + throw new Error( + `Unable to parse the techdocs metadata file ${entityRootDir}/techdocs_metadata.json.`, ); - resolve(JSON5.parse(download.toString())); - }); + } + const techdocsMetadata = JSON5.parse( + techdocsMetadataJson.toString('utf-8'), + ); + return techdocsMetadata; } catch (e) { - this.logger.error(e.message); - throw e; + throw new Error(`TechDocs metadata fetch failed, ${e.message}`); } } diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts new file mode 100644 index 0000000000..b831012acf --- /dev/null +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts @@ -0,0 +1,248 @@ +/* + * 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 { + Entity, + EntityName, + ENTITY_DEFAULT_NAMESPACE, +} from '@backstage/catalog-model'; +import { ConfigReader } from '@backstage/config'; +import mockFs from 'mock-fs'; +import os from 'os'; +import path from 'path'; +import * as winston from 'winston'; +import { OpenStackSwiftPublish } from './openStackSwift'; +import { PublisherBase, TechDocsMetadata } from './types'; + +// NOTE: /packages/techdocs-common/__mocks__ is being used to mock pkgcloud client library + +const createMockEntity = (annotations = {}): Entity => { + return { + apiVersion: 'version', + kind: 'TestKind', + metadata: { + name: 'test-component-name', + namespace: 'test-namespace', + annotations: { + ...annotations, + }, + }, + }; +}; + +const createMockEntityName = (): EntityName => ({ + kind: 'TestKind', + name: 'test-component-name', + namespace: 'test-namespace', +}); + +const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; + +const getEntityRootDir = (entity: Entity) => { + const { + kind, + metadata: { namespace, name }, + } = entity; + + return path.join(rootDir, namespace || ENTITY_DEFAULT_NAMESPACE, kind, name); +}; + +const logger = winston.createLogger(); +jest.spyOn(logger, 'info').mockReturnValue(logger); +jest.spyOn(logger, 'error').mockReturnValue(logger); + +let publisher: PublisherBase; + +beforeEach(() => { + mockFs.restore(); + const mockConfig = new ConfigReader({ + techdocs: { + requestUrl: 'http://localhost:7000', + publisher: { + type: 'openStackSwift', + openStackSwift: { + credentials: { + username: 'mockuser', + password: 'verystrongpass', + }, + authUrl: 'mockauthurl', + region: 'mockregion', + containerName: 'mock', + }, + }, + }, + }); + + publisher = OpenStackSwiftPublish.fromConfig(mockConfig, logger); +}); + +describe('OpenStackSwiftPublish', () => { + describe('publish', () => { + beforeEach(() => { + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'index.html': '', + '404.html': '', + assets: { + 'main.css': '', + }, + }, + }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + it('should publish a directory', async () => { + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + expect( + await publisher.publish({ + entity, + directory: entityRootDir, + }), + ).toBeUndefined(); + }); + + it('should fail to publish a directory', async () => { + expect.assertions(3); + const wrongPathToGeneratedDirectory = path.join( + rootDir, + 'wrong', + 'path', + 'to', + 'generatedDirectory', + ); + + const entity = createMockEntity(); + await expect( + publisher.publish({ + entity, + directory: wrongPathToGeneratedDirectory, + }), + ).rejects.toThrowError(); + + await publisher + .publish({ + entity, + directory: wrongPathToGeneratedDirectory, + }) + .catch(error => { + expect(error.message).toEqual( + // Can not do exact error message match due to mockFs adding unexpected characters in the path when throwing the error + // Issue reported https://github.com/tschaub/mock-fs/issues/118 + expect.stringContaining( + `Unable to upload file(s) to OpenStack Swift. Error: Failed to read template directory: ENOENT, no such file or directory`, + ), + ); + expect(error.message).toEqual( + expect.stringContaining(wrongPathToGeneratedDirectory), + ); + }); + mockFs.restore(); + }); + }); + + describe('hasDocsBeenGenerated', () => { + it('should return true if docs has been generated', async () => { + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'index.html': 'file-content', + }, + }); + + expect(await publisher.hasDocsBeenGenerated(entity)).toBe(true); + mockFs.restore(); + }); + + it('should return false if docs has not been generated', async () => { + const entity = createMockEntity(); + + expect(await publisher.hasDocsBeenGenerated(entity)).toBe(false); + }); + }); + + describe('fetchTechDocsMetadata', () => { + it('should return tech docs metadata', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': + '{"site_name": "backstage", "site_description": "site_content"}', + }, + }); + + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return tech docs metadata when json encoded with single quotes', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': `{'site_name': 'backstage', 'site_description': 'site_content'}`, + }, + }); + + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return an error if the techdocs_metadata.json file is not present', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + await publisher + .fetchTechDocsMetadata(entityNameMock) + .catch(error => + expect(error).toEqual( + new Error( + `TechDocs metadata fetch failed, The file ${path.join( + entityRootDir, + 'techdocs_metadata.json', + )} does not exist !`, + ), + ), + ); + }); + }); +}); diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts new file mode 100644 index 0000000000..0e60bc5796 --- /dev/null +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -0,0 +1,263 @@ +/* + * 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 { Entity, EntityName } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { storage } from 'pkgcloud'; +import express from 'express'; +import fs from 'fs-extra'; +import JSON5 from 'json5'; +import createLimiter from 'p-limit'; +import path from 'path'; +import { Readable } from 'stream'; +import { Logger } from 'winston'; +import { getFileTreeRecursively, getHeadersForFileExtension } from './helpers'; +import { PublisherBase, PublishRequest, TechDocsMetadata } from './types'; + +const streamToBuffer = (stream: Readable): Promise => { + return new Promise((resolve, reject) => { + try { + const chunks: any[] = []; + stream.on('data', chunk => chunks.push(chunk)); + stream.on('error', reject); + stream.on('end', () => resolve(Buffer.concat(chunks))); + } catch (e) { + throw new Error(`Unable to parse the response data ${e.message}`); + } + }); +}; + +export class OpenStackSwiftPublish implements PublisherBase { + static fromConfig(config: Config, logger: Logger): PublisherBase { + let containerName = ''; + try { + containerName = config.getString( + 'techdocs.publisher.openStackSwift.containerName', + ); + } catch (error) { + throw new Error( + "Since techdocs.publisher.type is set to 'openStackSwift' in your app config, " + + 'techdocs.publisher.openStackSwift.containerName is required.', + ); + } + + const openStackSwiftConfig = config.getConfig( + 'techdocs.publisher.openStackSwift', + ); + + const storageClient = storage.createClient({ + provider: 'openstack', + username: openStackSwiftConfig.getString('credentials.username'), + password: openStackSwiftConfig.getString('credentials.password'), + authUrl: openStackSwiftConfig.getString('authUrl'), + keystoneAuthVersion: + openStackSwiftConfig.getOptionalString('keystoneAuthVersion') || 'v3', + domainId: openStackSwiftConfig.getOptionalString('domainId') || 'default', + domainName: + openStackSwiftConfig.getOptionalString('domainName') || 'Default', + region: openStackSwiftConfig.getString('region'), + }); + + // Check if the defined container exists. Being able to connect means the configuration is good + // and the storage client will work. + storageClient.getContainer(containerName, (err, container) => { + if (container) { + logger.info( + `Successfully connected to the OpenStack Swift container ${containerName}.`, + ); + } else { + logger.error( + `Could not retrieve metadata about the OpenStack Swift container ${containerName}. ` + + 'Make sure the container exists. Also make sure that authentication is setup either by ' + + 'explicitly defining credentials and region in techdocs.publisher.openStackSwift in app config or ' + + 'by using environment variables. Refer to https://backstage.io/docs/features/techdocs/using-cloud-storage', + ); + + logger.error(`from OpenStack client library: ${err.message}`); + } + }); + + return new OpenStackSwiftPublish(storageClient, containerName, logger); + } + + constructor( + private readonly storageClient: storage.Client, + private readonly containerName: string, + private readonly logger: Logger, + ) { + this.storageClient = storageClient; + this.containerName = containerName; + this.logger = logger; + } + + /** + * Upload all the files from the generated `directory` to the OpenStack Swift container. + * Directory structure used in the bucket is - entityNamespace/entityKind/entityName/index.html + */ + async publish({ entity, directory }: PublishRequest): Promise { + try { + // Note: OpenStack Swift manages creation of parent directories if they do not exist. + // So collecting path of only the files is good enough. + const allFilesToUpload = await getFileTreeRecursively(directory); + const limiter = createLimiter(10); + const uploadPromises: Array> = []; + for (const filePath of allFilesToUpload) { + // Remove the absolute path prefix of the source directory + // Path of all files to upload, relative to the root of the source directory + // e.g. ['index.html', 'sub-page/index.html', 'assets/images/favicon.png'] + const relativeFilePath = path.relative(directory, filePath); + + // Convert destination file path to a POSIX path for uploading. + // Swift expects / as path separator and relativeFilePath will contain \\ on Windows. + // https://docs.openstack.org/python-openstackclient/pike/cli/man/openstack.html + const relativeFilePathPosix = relativeFilePath + .split(path.sep) + .join(path.posix.sep); + + // The / delimiter is intentional since it represents the cloud storage and not the local file system. + const entityRootDir = `${entity.metadata.namespace}/${entity.kind}/${entity.metadata.name}`; + const destination = `${entityRootDir}/${relativeFilePathPosix}`; // Swift container file relative path + + const params = { + container: this.containerName, + remote: destination, + }; + + // Rate limit the concurrent execution of file uploads to batches of 10 (per publish) + const uploadFile = limiter( + () => + new Promise((res, rej) => { + const readStream = fs.createReadStream(filePath, 'utf8'); + + const writeStream = this.storageClient.upload(params); + + writeStream.on('error', rej); + + writeStream.on('success', res); + + readStream.pipe(writeStream); + }), + ); + uploadPromises.push(uploadFile); + } + await Promise.all(uploadPromises); + this.logger.info( + `Successfully uploaded all the generated files for Entity ${entity.metadata.name}. Total number of files: ${allFilesToUpload.length}`, + ); + return; + } catch (e) { + const errorMessage = `Unable to upload file(s) to OpenStack Swift. ${e}`; + this.logger.error(errorMessage); + throw new Error(errorMessage); + } + } + + async fetchTechDocsMetadata( + entityName: EntityName, + ): Promise { + try { + return await new Promise(async (resolve, reject) => { + const entityRootDir = `${entityName.namespace}/${entityName.kind}/${entityName.name}`; + + const stream = this.storageClient.download({ + container: this.containerName, + remote: `${entityRootDir}/techdocs_metadata.json`, + }); + + try { + const techdocsMetadataJson = await streamToBuffer(stream); + if (!techdocsMetadataJson) { + throw new Error( + `Unable to parse the techdocs metadata file ${entityRootDir}/techdocs_metadata.json.`, + ); + } + + const techdocsMetadata = JSON5.parse( + techdocsMetadataJson.toString('utf-8'), + ); + + resolve(techdocsMetadata); + } catch (err) { + this.logger.error(err.message); + reject(new Error(err.message)); + } + }); + } catch (e) { + throw new Error(`TechDocs metadata fetch failed, ${e.message}`); + } + } + + /** + * Express route middleware to serve static files on a route in techdocs-backend. + */ + docsRouter(): express.Handler { + return async (req, res) => { + // Trim the leading forward slash + // filePath example - /default/Component/documented-component/index.html + + const filePath = req.path.replace(/^\//, ''); + + // Files with different extensions (CSS, HTML) need to be served with different headers + const fileExtension = path.extname(filePath); + const responseHeaders = getHeadersForFileExtension(fileExtension); + + const stream = this.storageClient.download({ + container: this.containerName, + remote: filePath, + }); + + try { + // Inject response headers + for (const [headerKey, headerValue] of Object.entries( + responseHeaders, + )) { + res.setHeader(headerKey, headerValue); + } + + res.send(await streamToBuffer(stream)); + } catch (err) { + this.logger.warn(err.message); + res.status(404).send(err.message); + } + }; + } + + /** + * A helper function which checks if index.html of an Entity's docs site is available. This + * can be used to verify if there are any pre-generated docs available to serve. + */ + async hasDocsBeenGenerated(entity: Entity): Promise { + try { + const entityRootDir = `${entity.metadata.namespace}/${entity.kind}/${entity.metadata.name}`; + + return new Promise(res => { + this.storageClient.getFile( + this.containerName, + `${entityRootDir}/index.html`, + (err, file) => { + if (!err && file) { + res(true); + } else { + res(false); + this.logger.warn(err.message); + } + }, + ); + }); + } catch (e) { + return Promise.resolve(false); + } + } +} diff --git a/packages/techdocs-common/src/stages/publish/publish.test.ts b/packages/techdocs-common/src/stages/publish/publish.test.ts index d1faf82b78..948b92aaa6 100644 --- a/packages/techdocs-common/src/stages/publish/publish.test.ts +++ b/packages/techdocs-common/src/stages/publish/publish.test.ts @@ -23,6 +23,7 @@ import { LocalPublish } from './local'; import { GoogleGCSPublish } from './googleStorage'; import { AwsS3Publish } from './awsS3'; import { AzureBlobStoragePublish } from './azureBlobStorage'; +import { OpenStackSwiftPublish } from './openStackSwift'; const logger = getVoidLogger(); const discovery: jest.Mocked = { @@ -161,4 +162,30 @@ describe('Publisher', () => { }); expect(publisher).toBeInstanceOf(AzureBlobStoragePublish); }); + + it('should create Open Stack Swift publisher from config', async () => { + const mockConfig = new ConfigReader({ + techdocs: { + requestUrl: 'http://localhost:7000', + publisher: { + type: 'openStackSwift', + openStackSwift: { + credentials: { + username: 'mockuser', + password: 'verystrongpass', + }, + authUrl: 'mockauthurl', + region: 'mockregion', + containerName: 'mock', + }, + }, + }, + }); + + const publisher = await Publisher.fromConfig(mockConfig, { + logger, + discovery, + }); + expect(publisher).toBeInstanceOf(OpenStackSwiftPublish); + }); }); diff --git a/packages/techdocs-common/src/stages/publish/publish.ts b/packages/techdocs-common/src/stages/publish/publish.ts index a4e33a2d9c..f70a5f7626 100644 --- a/packages/techdocs-common/src/stages/publish/publish.ts +++ b/packages/techdocs-common/src/stages/publish/publish.ts @@ -22,6 +22,7 @@ import { LocalPublish } from './local'; import { GoogleGCSPublish } from './googleStorage'; import { AwsS3Publish } from './awsS3'; import { AzureBlobStoragePublish } from './azureBlobStorage'; +import { OpenStackSwiftPublish } from './openStackSwift'; type factoryOptions = { logger: Logger; @@ -53,6 +54,11 @@ export class Publisher { 'Creating Azure Blob Storage Container publisher for TechDocs', ); return AzureBlobStoragePublish.fromConfig(config, logger); + case 'openStackSwift': + logger.info( + 'Creating OpenStack Swift Container publisher for TechDocs', + ); + return OpenStackSwiftPublish.fromConfig(config, logger); case 'local': logger.info('Creating Local publisher for TechDocs'); return new LocalPublish(config, logger, discovery); diff --git a/packages/techdocs-common/src/stages/publish/types.ts b/packages/techdocs-common/src/stages/publish/types.ts index 5e953deb81..f0ecb3cb2b 100644 --- a/packages/techdocs-common/src/stages/publish/types.ts +++ b/packages/techdocs-common/src/stages/publish/types.ts @@ -23,7 +23,8 @@ export type PublisherType = | 'local' | 'googleGcs' | 'awsS3' - | 'azureBlobStorage'; + | 'azureBlobStorage' + | 'openStackSwift'; export type PublishRequest = { entity: Entity; diff --git a/packages/theme/src/baseTheme.ts b/packages/theme/src/baseTheme.ts index 37b8e3b47f..e7bb7e58b6 100644 --- a/packages/theme/src/baseTheme.ts +++ b/packages/theme/src/baseTheme.ts @@ -234,12 +234,31 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides { margin: `0 ${theme.spacing(0.5)}px 0 -${theme.spacing(0.5)}px`, }, }, + MuiCard: { + root: { + // When cards have a forced size, such as when they are arranged in a + // CSS grid, the content needs to flex such that the actions (buttons + // etc) end up at the bottom of the card instead of just below the body + // contents. + display: 'flex', + flexDirection: 'column', + }, + }, MuiCardHeader: { root: { // Reduce padding between header and content paddingBottom: 0, }, }, + MuiCardContent: { + root: { + // When cards have a forced size, such as when they are arranged in a + // CSS grid, the content needs to flex such that the actions (buttons + // etc) end up at the bottom of the card instead of just below the body + // contents. + flexGrow: 1, + }, + }, MuiCardActions: { root: { // We default to putting the card actions at the end diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 480beeff16..cd4cd82831 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/plugin-api-docs +## 0.4.8 + +### Patch Changes + +- 32a003973: Update messaging when no entities are in a table. +- 2386de1d3: Add organization name to the API Explorer page title +- f71589800: The api-docs plugin has been migrated to use an [external route reference](https://backstage.io/docs/plugins/composability#binding-external-routes-in-the-app) to dynamically link to the create component page. This means you need to migrate the api docs plugin to use the new extension components, as well as bind the external route. + + To bind the external route from the api docs plugin to the scaffolder template index page, make sure you have the appropriate imports and add the following to the `createApp` call: + + ```ts + import { apiDocsPlugin } from '@backstage/plugin-api-docs'; + import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; + + const app = createApp({ + // ... + bindRoutes({ bind }) { + bind(apiDocsPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + }); + }, + }); + ``` + + If you choose to not bind the routes, the button to create new APIs is not displayed. + +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.4.7 ### Patch Changes diff --git a/plugins/api-docs/README.md b/plugins/api-docs/README.md index 182ee5a882..0b8d658f89 100644 --- a/plugins/api-docs/README.md +++ b/plugins/api-docs/README.md @@ -23,6 +23,68 @@ Other formats are displayed as plain text, but this can easily be extended. To fill the catalog with APIs, [provide entities of kind API](https://backstage.io/docs/features/software-catalog/descriptor-format#kind-api). To link that a component provides or consumes an API, see the [`providesApis`](https://backstage.io/docs/features/software-catalog/descriptor-format#specprovidesapis-optional) and [`consumesApis`](https://backstage.io/docs/features/software-catalog/descriptor-format#specconsumesapis-optional) properties on the Component kind. +## Implementing OAuth 2 Authorization Code flow with Swagger UI + +### Adding `oauth2-redirect.html` to support OAuth2 `redirect_uri` route + +The Swagger UI package by expects to have a route to `/oauth2-redirect.html` which processes +the redirect callback for the OAuth2 Authorization Code flow, however, this file is not installed +by this plugin. + +Grab a copy of [oauth2-redirect.html](https://github.com/swagger-api/swagger-ui/blob/master/dist/oauth2-redirect.html) +and put it in the `app/public/` directory in order to enable Swagger UI to complete this redirection. + +### Configuring your OAuth2 Client + +You'll need to make sure your OAuth2 client has been registered in your OAuth2 Authentication Server (AS) +with the appropriate `redirect_uris`, `scopes` and `grant_types`. For example, if your AS supports +the [OAuth 2.0 Dynamic Client Registration Protocol](https://tools.ietf.org/html/rfc7591), an example +POST request body would look like this: + +```json +{ + "client_name": "Example Backstage api-docs plugin Swagger UI Client", + "redirect_uris": [ + "https://www.getpostman.com/oauth2/callback", + "http://localhost:3000/oauth2-redirect.html" + "https:///oauth2-redirect.html" + ], + "scope": "read_pets write_pets", + "grant_types": [ + "authorization_code" + ] +} +``` + +The above `redirect_uris` are: + +- [Postman](https://www.postman.com/) testing: `https://www.getpostman.com/oauth2/callback` +- Local Backstage app development: `http://localhost:3000/oauth2-redirect.html` +- Backstage app production: `https:///oauth2-redirect.html` + +### Configuring OAuth2 in your OpenAPI 3.0 schema + +To configure [OAuth 2 Authorization Code](https://swagger.io/docs/specification/authentication/oauth2/) flow +in your OpenAPI 3.0 schema you'll need something like this snippet: + +```yaml +components: + securitySchemes: + oauth: + type: oauth2 + description: OAuth2 service + flows: + authorizationCode: + authorizationUrl: https://api.example.com/oauth2/authorize + tokenUrl: https://api.example.com/oauth2/token + scopes: + read_pets: read your pets + write_pets: modify pets in your account +security: + oauth: + - [read_pets, write_pets] +``` + ## Links - [The Backstage homepage](https://backstage.io) diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 6d0fd2fdec..abb5de92c8 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.4.7", + "version": "0.4.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ }, "dependencies": { "@asyncapi/react-component": "^0.19.2", - "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.6.3", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/catalog-model": "^0.7.3", + "@backstage/core": "^0.7.0", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.3", "@material-icons/font": "^1.0.2", "@material-ui/core": "^4.11.0", @@ -49,8 +49,8 @@ "swagger-ui-react": "^3.37.2" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 14d70ba1f5..0e24237181 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-auth-backend +## 0.3.3 + +### Patch Changes + +- f43192207: remove usage of res.send() for res.json() and res.end() to ensure content types are more consistently application/json on backend responses and error cases +- 3af994c81: Expose a configuration option for the oidc scope +- Updated dependencies [12d8f27a6] +- Updated dependencies [497859088] +- Updated dependencies [8adb48df4] + - @backstage/catalog-model@0.7.3 + - @backstage/backend-common@0.5.5 + ## 0.3.2 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 9742e872f6..5be148c984 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.3.2", + "version": "0.3.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.5.4", + "@backstage/backend-common": "^0.5.5", "@backstage/catalog-client": "^0.3.6", - "@backstage/catalog-model": "^0.7.2", + "@backstage/catalog-model": "^0.7.3", "@backstage/config": "^0.1.3", "@backstage/test-utils": "^0.1.8", "@types/express": "^4.17.6", @@ -67,7 +67,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.2", + "@backstage/cli": "^0.6.3", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", diff --git a/plugins/auth-backend/src/identity/router.ts b/plugins/auth-backend/src/identity/router.ts index de05e8a06c..2ddde31b22 100644 --- a/plugins/auth-backend/src/identity/router.ts +++ b/plugins/auth-backend/src/identity/router.ts @@ -31,7 +31,7 @@ export function createOidcRouter(options: Options) { issuer: baseUrl, token_endpoint: `${baseUrl}/v1/token`, userinfo_endpoint: `${baseUrl}/v1/userinfo`, - jwks_uri: `${baseUrl}/v1/certs`, + jwks_uri: `${baseUrl}/.well-known/jwks.json`, response_types_supported: ['id_token'], subject_types_supported: ['public'], id_token_signing_alg_values_supported: ['RS256'], diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 42315b3802..42bcd3bab6 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend +## 0.6.4 + +### Patch Changes + +- ecdd407b1: GithubDiscoveryProcessor outputs locations as optional to avoid outputting errors for missing locations (see https://github.com/backstage/backstage/issues/4730). +- 12d8f27a6: Add version `backstage.io/v1beta2` schema for Template entities. +- Updated dependencies [12d8f27a6] +- Updated dependencies [497859088] +- Updated dependencies [8adb48df4] + - @backstage/catalog-model@0.7.3 + - @backstage/backend-common@0.5.5 + ## 0.6.3 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 0925870506..5ffc789635 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.6.3", + "version": "0.6.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,8 +30,8 @@ }, "dependencies": { "@azure/msal-node": "^1.0.0-beta.3", - "@backstage/backend-common": "^0.5.4", - "@backstage/catalog-model": "^0.7.2", + "@backstage/backend-common": "^0.5.5", + "@backstage/catalog-model": "^0.7.3", "@backstage/config": "^0.1.3", "@backstage/integration": "^0.5.0", "@octokit/graphql": "^4.5.8", @@ -59,7 +59,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", + "@backstage/cli": "^0.6.3", "@backstage/test-utils": "^0.1.8", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index fce40aacfc..ed48368ee9 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-import +## 0.4.3 + +### Patch Changes + +- 05183f202: Bump react-hook-form version constraint to "^6.15.4" +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.4.2 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 83c9a0394e..f9cea55a3b 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.4.2", + "version": "0.4.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,11 +30,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", + "@backstage/catalog-model": "^0.7.3", "@backstage/catalog-client": "^0.3.6", - "@backstage/core": "^0.6.3", + "@backstage/core": "^0.7.0", "@backstage/integration": "^0.5.0", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -44,15 +44,15 @@ "git-url-parse": "^11.4.4", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-hook-form": "^6.6.0", + "react-hook-form": "^6.15.4", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3", "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/AutocompleteTextField.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/AutocompleteTextField.tsx index 3231c3c6a1..a3743dc580 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/AutocompleteTextField.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/AutocompleteTextField.tsx @@ -22,7 +22,7 @@ import { Control, Controller, FieldErrors, - ValidationRules, + UseControllerOptions, } from 'react-hook-form'; type Props = { @@ -32,7 +32,7 @@ type Props = { control?: Control>; errors?: FieldErrors>; - rules?: ValidationRules; + rules?: UseControllerOptions>['rules']; loading?: boolean; loadingText?: string; diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx index 0bf7746e6a..4693535d24 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx @@ -25,7 +25,7 @@ describe('', () => { const onSubmitFn = jest.fn(); const { getByRole } = render( - defaultValues={{ main: 'default' }} render={({ register }) => ( <> @@ -49,7 +49,7 @@ describe('', () => { const onSubmitFn = jest.fn(); const { getByRole, getByLabelText } = render( - defaultValues={{ main: 'default' }} render={({ register }) => ( <> @@ -80,7 +80,7 @@ describe('', () => { const onSubmitFn = jest.fn(); const { queryByText, getByRole } = render( - defaultValues={{}} render={({ errors, register }) => ( <> diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 85035880eb..65ebd5a39a 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-react +## 0.1.1 + +### Patch Changes + +- 9d455f69a: Introduce parameters for namespace, kind, and name to `entityRouteRef`. +- 02816ecd7: Fixed EntityProvider setting 'loading' bool erroneously to true +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [4c049a1a1] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 11f5abe074..5e7540d1c7 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "0.1.0", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,8 +29,8 @@ }, "dependencies": { "@backstage/catalog-client": "^0.3.6", - "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.6.3", + "@backstage/catalog-model": "^0.7.3", + "@backstage/core": "^0.7.0", "@material-ui/core": "^4.11.0", "@types/react": "^16.9", "react": "^16.13.1", @@ -39,8 +39,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 129bb98346..a4ccb50e60 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-catalog +## 0.4.1 + +### Patch Changes + +- 32a003973: Update messaging when no entities are in a table. +- 40c0fdbaa: Make the external `createComponent` route optional, hiding the "Create Component" button if it isn't bound. +- 10362e9eb: Use entity relation for the owner of an entity in the catalog entity page header. +- b33e553b2: Removed fullScreen property from UnregisterEntity Dialog modal. +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.4.0 ### Minor Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 06fe95cab6..a61bc6fa68 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "0.4.0", + "version": "0.4.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ }, "dependencies": { "@backstage/catalog-client": "^0.3.6", - "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.6.3", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/catalog-model": "^0.7.3", + "@backstage/core": "^0.7.0", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -50,8 +50,8 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@microsoft/microsoft-graph-types": "^1.25.0", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md index b849abe379..c97dd8b091 100644 --- a/plugins/circleci/CHANGELOG.md +++ b/plugins/circleci/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-circleci +## 0.2.11 + +### Patch Changes + +- b2a5320a4: Use Breadcrumbs from @backstage/core rather than material-ui +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.2.10 ### Patch Changes diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 63fee1c0fd..5a78c1a7d6 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-circleci", - "version": "0.2.10", + "version": "0.2.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ "postpack": "backstage-cli postpack" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.6.3", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/catalog-model": "^0.7.3", + "@backstage/core": "^0.7.0", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -50,8 +50,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md index f79281be18..799a8a4bc6 100644 --- a/plugins/cloudbuild/CHANGELOG.md +++ b/plugins/cloudbuild/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-cloudbuild +## 0.2.12 + +### Patch Changes + +- b2a5320a4: Use Breadcrumbs from @backstage/core rather than material-ui +- d21058c34: Change terminology for cloudbuild +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.2.11 ### Patch Changes diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index f0e60eea87..4a7c060448 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cloudbuild", - "version": "0.2.11", + "version": "0.2.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.1.0", - "@backstage/core": "^0.6.3", + "@backstage/catalog-model": "^0.7.3", + "@backstage/plugin-catalog-react": "^0.1.1", + "@backstage/core": "^0.7.0", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -47,8 +47,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md index 622454d2a9..de901bea2b 100644 --- a/plugins/cost-insights/CHANGELOG.md +++ b/plugins/cost-insights/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-cost-insights +## 0.8.3 + +### Patch Changes + +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [4c049a1a1] + - @backstage/core@0.7.0 + ## 0.8.2 ### Patch Changes diff --git a/plugins/cost-insights/README.md b/plugins/cost-insights/README.md index c10d483f3f..a103b58a77 100644 --- a/plugins/cost-insights/README.md +++ b/plugins/cost-insights/README.md @@ -25,6 +25,8 @@ yarn add @backstage/plugin-cost-insights 2. Create a CostInsights client. Clients must implement the CostInsightsApi interface. See the [API file](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts) for required methods and documentation. +**Note:** We've briefly explored using the AWS Cost Explorer API to implement a CostInsights client. Learn more about our findings [here](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/contrib/aws-cost-explorer-api.md). + ```ts // path/to/CostInsightsClient.ts import { CostInsightsApi } from '@backstage/plugin-cost-insights'; diff --git a/plugins/cost-insights/contrib/aws-cost-explorer-api.md b/plugins/cost-insights/contrib/aws-cost-explorer-api.md new file mode 100644 index 0000000000..10622b7917 --- /dev/null +++ b/plugins/cost-insights/contrib/aws-cost-explorer-api.md @@ -0,0 +1,328 @@ +# Using the AWS Cost Explorer API with Cost Insights + +Cost Insights currently does not provide a CostInsightsApi client out of the box, this is left up to the implementer. We plan to provide an open-source client and backend eventually that can pull cost data from different cloud providers. In the meantime, we briefly explored the AWS Cost Explorer API and wanted to share our findings. Please contribute to this documentation if you try any experiments with AWS Cost Explorer. + +**Note:** Each request using the Cost Explorer API will incur a cost of \$0.01. If you anticipate high usage, adding some caching of Cost Explorer API responses could prove worthwhile over time. + +## Authentication + +The CostExplorerClient needs to be initialized with access keys tied to an IAM account. You'll need to attach the Cost Explorer policy to the IAM account to be able to make requests to the Cost Explorer API. + +Cost Explorer permission policy: + +```JSON +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ce:*" + ], + "Resource": [ + "*" + ] + } + ] +} + +``` + +## Setup + +Install the AWS Cost Explorer SDK. The AWS docs recommend using the SDK over making calls to the API directly as it simplifies authentication and provides direct access to commands. + +```bash +yarn add @aws-sdk/client-cost-explorer +``` + +## Usage of the SDK + +1. Initiate a CostExplorerClient with the appropriate configuration (e.g. credentials, region). + +```ts +const client = new CostExplorerClient({ + region: 'REGION', + credentials: { + accessKeyId: 'ACCESSKEYID', + secretAccessKey: 'SECRETACCESSKEY', + }, +}); +``` + +2. Initiate a command with the relevant input parameters. The SDK provides a variety of commands, but you can access most cost data using the [GetCostAndUsageCommand](https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_GetCostAndUsage.html) commands with the relevant [parameters](#GetCostAndUsageCommand-Parameters). + +```ts +const command = new GetCostAndUsageCommand(params); +``` + +3. Call the `send` operation on the Cost Explorer client with the command object. + +```ts +// async/await. +try { + const data = await client.send(command); + // process data. +} catch (error) { + // error handling. +} finally { + // finally. +} +``` + +## Implementing methods on [CostInsightsApi](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts) + +We can use the data provided by the Cost Explorer API to implement CostInsightsApi methods such as `getGroupDailyCost`, `getProjectDailyCost`, and `getProductInsights`. You'll still need to provide external data for methods such as `getUserGroups` and `getAlerts`. + +### 1. [getGroupDailyCost](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts#L93) + +The `getGroupDailyCost` method is expected to return daily cost aggregations for a given group and interval time frame as a [`Cost`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Cost.ts). + +#### Total Daily cost + +By using the `GetCostAndUsageCommand` command with some simple parameters, you should be able to use the response to construct a `Cost` object to represent daily cost aggregations. + +Sample command: + +```ts +const command = new GetCostAndUsageCommand({ + TimePeriod: { Start: '2020-12-01', End: '2021-01-02' }, + Metrics: ['Unblended Cost'], + Filter: { + Dimensions: { Key: 'LINKED_ACCOUNT', Values: ['ACCOUNT_ID'] }, + }, + Granularity: 'DAILY', +}); +``` + +The response provides a `ResultsByTime` array, where each [object](https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_ResultByTime.html) contains the time period and costs for a single day. This should give you enough data to represent the `aggregation` field with some minor reformatting. + +Sample response: + +```ts + { + ... + ResultsByTime: [ + { + Estimated: false, + TimePeriod: { + End: '2020-12-02', + Start: '2020-12-01', + }, + Total: { UnblendedCost: { Amount: '9392.30', Unit: 'USD' } }, + }, + { + Estimated: false, + TimePeriod: { + End: '2020-12-03', + Start: '2020-12-02', + }, + Total: { UnblendedCost: { Amount: '1543.93', Unit: 'USD' } }, + }, + ], + } +``` + +#### Daily cost breakdown + +You can optionally provide a `groupedCosts` field on the `Cost` object to view daily costs broken down by specific groupings. Each key-value pair on `groupedCosts` will result in an additional tab in the top panel that displays an area chart of costs broken down by that key. + +You can get grouped daily costs from the API by using the `GetCostAndUsageCommand` command with a [`GroupBy`](https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_GetCostAndUsage.html#awscostmanagement-GetCostAndUsage-request-GroupBy) parameter. For example, you can use the `SERVICE` dimension to group costs by cloud product. + +Sample command: + +```ts +const command = new GetCostAndUsageCommand({ + TimePeriod: { Start: '2020-12-01', End: '2021-01-02' }, + Metrics: ['Unblended Cost'], + Filter: { + Dimensions: { Key: 'LINKED_ACCOUNT', Values: ['ACCOUNT_ID'] }, + }, + Granularity: 'DAILY', + GroupBy: [ + { + Type: 'DIMENSION', + Key: 'SERVICE', + }, + ], +}); +``` + +The `ResultsByTime` objects provide a `Groups` array with an entry for each group and its costs for the given day. Since `groupedCosts` expects a separate `Cost` object per group, you'll need to aggregate daily cost data for each group from the response. + +Sample Response: + +```ts +{ + ... + ResultsByTime: [ + { + TimePeriod: { + End: '2020-12-02', + Start: '2020-12-01', + }, + Estimated: false, + Groups: [ + { + Keys: ['Amazon Simple Storage Service'], + Metrics: { + UnblendedCost: { Amount: '169.442', Unit: 'USD' }, + }, + }, + { + Keys: ['Amazon Relational Database Service'], + Metrics: { + UnblendedCost: { Amount: '74.11115', Unit: 'USD' }, + }, + }, + ... + ], + }, + ], +} +``` + +Sample `groupedCosts` based on the response: + +```ts +{ + product: [ + { + id: 'Amazon Simple Storage Service', + aggregation: [ + { date: '2020-12-01', amount: 169.442 }, + ..., + ] + }, + { + id: 'Amazon Relational Database Service', + aggregation: [ + { date: '2020-12-01', amount: 74.11115 }, + ... + ] + }, + ] +} +``` + +### 2. [getProjectDailyCost](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts#L111) + +The `getProjectDailyCost` method is expected to return daily cost aggregations for a given billing entity and interval time frame as a [`Cost`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Cost.ts). + +This should be similar to the `getGroupDailyCost` method implementation, but with an updated `LINKED_ACCOUNT` filter to get narrower cost data for a lower-level linked account. + +### 3. [getProductInsights](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts#L111) + +The `getProductInsights` method is expected to return cost aggregations for a particular cloud product and interval time frame as an [`Entity`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Entity.ts). + +#### Cloud product cost by resource + +The Cost Explorer API doesn't provide resource level costs by default. To achieve this, you need to add [cost allocation tags](#cost-allocation-tags) to each resource and use those tags to group costs. + +Once resources have been tagged, we can filter by the service (ex: S3) and group by the tag key (ex: S3-Bucket-Name). Any resources that do not have a value for the given tag will be grouped into a single cost group where the key is not the bucket name, but the tag key with a $ appended (ex: S3-Bucket-Name$). We can aggregate the daily costs for each resource and provide the data for the comparison in the bar chart. + +Sample command: + +```ts +const command = new GetCostAndUsageCommand({ + TimePeriod: { + End: '2020-12-02', + Start: '2020-12-01', + }, + Metrics: ['UnblendedCost'], + Granularity: 'DAILY', + Filter: { + And: [ + { Dimensions: { Key: 'LINKED_ACCOUNT', Values: ['ACCOUNT_ID'] } }, + { + Dimensions: { + Key: 'SERVICE', + Values: ['Amazon Simple Storage Service'], + }, + }, + ], + }, + GroupBy: [{ Type: 'TAG', Key: 'S3-Bucket-Name' }], +}); +``` + +The `ResultsByTime` objects provide a `Groups` array with an entry for each resource and its costs for the given day. You'll need to aggregate cost data into two bucketed time periods (e.g. month vs month, or quarter vs quarter) for each resource since this is the expected data type for the `aggregation` field on `Entity`. + +Sample response: + +```ts +{ + ... + ResultsByTime: [ + { + TimePeriod: { + End: '2020-12-02', + Start: '2020-12-01', + }, + Estimated: false, + Groups: [ + { + Keys: ['S3-Bucket-Name$'], + Metrics: { + UnblendedCost: { Amount: '345.23', Unit: 'USD' }, + }, + }, + { + Keys: ['Bucket1'], + Metrics: { + UnblendedCost: { Amount: '111.09', Unit: 'USD' }, + }, + }, + { + Keys: ['Bucket2'], + Metrics: { + UnblendedCost: { Amount: '254.1111', Unit: 'USD' }, + }, + }, + ], + }, + ], + }; + +``` + +#### Resource cost breakdown + +There are a couple of options for breaking down costs for a resource. + +1. We can use `USAGE_TYPE` (units used to measure usage for each service) supported by the `GroupBy` field. This requires the lowest amount of effort as we can easily add it to the existing resource request. Examples usage types for EC2: `BoxUsage:c1.medium(Hrs)`, `BoxUsage:m3.xlarge(Hrs)`, and `BoxUsage:t1.micro(Hrs)` + +2. Similar to resources, we can use custom tags to group the data. For example, you’re able to tag objects within a bucket so that would be one way to get more granularity within a resource. More info on object tagging [here](https://docs.aws.amazon.com/AmazonS3/latest/dev/object-tagging.html). + +## Cost Allocation Tags + +Tags are labels that you can assign to an AWS resource, each with a key and value. You can use cost allocation tags to track your AWS costs on a detailed level. You can tag resources through the AWS UI by using the Tags tab on the relevant resource screen. You can also add tags programmatically via the relevant cloud product API. + +After tagging your resources (either through UI or API), you still need to activate the tags through the UI. Once tags are activated there’s a 24-hour delay until you see the data in Cost Explorer. The tags don’t apply to historical data, so you only get cost allocation for dates after the tag activation date. More info on activating tags [here](https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/activating-tags.html). + +### Adding Tags + +We've looked into how tagging works for some popular AWS products, but unfortunately the methods seem to be different depending on the service. You'd also need to implement the client and get permissions for each service to be able to add tags programmatically. + +S3 Tagging: +There's currently no capability to specify a tag during bucket creation. You must first create the bucket and then perform a second action to add the tag. You can tag buckets through the AWS console, but in order to do this programmatically, you'll need to use and have access to the S3 API. Additionally, there's no way to specify multiple buckets when tagging, so you would need to make a request to tag each bucket. More on tagging buckets through the API [here](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketTagging.html). + +Similar to S3, you can add tags through the UI as well the EC2 API itself. The [`CreateTags`](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateTags.html) method allows you to tag multiple resources (up to a 1000) at a time. You can also specify tags when launching instances or include them in the launch templates. More on EC2 tagging [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html). + +## [GetCostAndUsageCommand Parameters](https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_GetCostAndUsage.html#API_GetCostAndUsage_RequestParameters) + +1. TimePeriod (required): + Expects an inclusive `Start` and exclusive `End` date in YYYY-MM-DD format. + +2. Metrics (required): + Valid values: AmortizedCost, BlendedCost, NetAmortizedCost, NetUnblendedCost, NormalizedUsageAmount, UnblendedCost, and UsageQuantity + + While you can choose any cost dataset, we've used 'UnblendedCost' in the examples above as it is used by the majority of AWS users. More information on different cost types [here](https://aws.amazon.com/blogs/aws-cost-management/understanding-your-aws-cost-datasets-a-cheat-sheet/). + +3. Granularity: + Valid values: DAILY, MONTHLY, or HOURLY. + + We've used the 'DAILY' granularity above as it allows for the most flexibility depending on the requested time period. + +4. GroupBy: + Valid values: AZ, INSTANCE_TYPE, LEGAL_ENTITY_NAME, LINKED_ACCOUNT, OPERATION, PLATFORM, PURCHASE_TYPE, SERVICE, TAGS, TENANCY, RECORD_TYPE, and USAGE_TYPE. diff --git a/plugins/cost-insights/dev/index.tsx b/plugins/cost-insights/dev/index.tsx index e4732fc364..8cfa12fc18 100644 --- a/plugins/cost-insights/dev/index.tsx +++ b/plugins/cost-insights/dev/index.tsx @@ -14,8 +14,8 @@ * limitations under the License. */ import { createDevApp } from '@backstage/dev-utils'; +import { ExampleCostInsightsClient } from '../src/example'; import { costInsightsApiRef } from '../src/api'; -import { ExampleCostInsightsClient } from '../src/client'; import { costInsightsPlugin } from '../src/plugin'; createDevApp() diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 5125dac83b..c03952c25c 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cost-insights", - "version": "0.8.2", + "version": "0.8.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/config": "^0.1.3", - "@backstage/core": "^0.6.3", + "@backstage/core": "^0.7.0", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -55,8 +55,8 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/cost-insights/src/alerts/ProjectGrowthAlert.test.tsx b/plugins/cost-insights/src/alerts/ProjectGrowthAlert.test.tsx index 05a10bb2f0..35a0248ef5 100644 --- a/plugins/cost-insights/src/alerts/ProjectGrowthAlert.test.tsx +++ b/plugins/cost-insights/src/alerts/ProjectGrowthAlert.test.tsx @@ -23,7 +23,7 @@ import { MockCurrencyProvider, MockConfigProvider, MockBillingDateProvider, -} from '../utils/tests'; +} from '../testUtils'; const mockData: ProjectGrowthData = { project: 'test-project', diff --git a/plugins/cost-insights/src/alerts/UnlabeledDataflowAlert.test.tsx b/plugins/cost-insights/src/alerts/UnlabeledDataflowAlert.test.tsx index 95e213f06a..00b8332cd9 100644 --- a/plugins/cost-insights/src/alerts/UnlabeledDataflowAlert.test.tsx +++ b/plugins/cost-insights/src/alerts/UnlabeledDataflowAlert.test.tsx @@ -23,7 +23,7 @@ import { MockCurrencyProvider, MockConfigProvider, MockBillingDateProvider, -} from '../utils/tests'; +} from '../testUtils'; const mockData: UnlabeledDataflowData = { periodStart: '2021-02-01', diff --git a/plugins/cost-insights/src/alerts/index.ts b/plugins/cost-insights/src/alerts/index.ts index b55c012a60..a732083454 100644 --- a/plugins/cost-insights/src/alerts/index.ts +++ b/plugins/cost-insights/src/alerts/index.ts @@ -16,6 +16,3 @@ export { ProjectGrowthAlert } from './ProjectGrowthAlert'; export { UnlabeledDataflowAlert } from './UnlabeledDataflowAlert'; -export { KubernetesMigrationAlert } from './KubernetesMigrationAlert'; -export type { MigrationAlert } from './KubernetesMigrationAlert'; -export type { MigrationData } from './KubernetesMigrationAlert'; diff --git a/plugins/cost-insights/src/components/ActionItems/ActionItemCard.test.tsx b/plugins/cost-insights/src/components/ActionItems/ActionItemCard.test.tsx index b72f050e5b..70ae44bcdc 100644 --- a/plugins/cost-insights/src/components/ActionItems/ActionItemCard.test.tsx +++ b/plugins/cost-insights/src/components/ActionItems/ActionItemCard.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; import { ActionItemCard } from './ActionItemCard'; -import { MockScrollProvider } from '../../utils/tests'; +import { MockScrollProvider } from '../../testUtils'; import { ProjectGrowthAlert } from '../../alerts'; import { ProjectGrowthData } from '../../types'; diff --git a/plugins/cost-insights/src/components/ActionItems/ActionItems.test.tsx b/plugins/cost-insights/src/components/ActionItems/ActionItems.test.tsx index 677ba99c6a..39b8d1ad05 100644 --- a/plugins/cost-insights/src/components/ActionItems/ActionItems.test.tsx +++ b/plugins/cost-insights/src/components/ActionItems/ActionItems.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { ActionItems } from './ActionItems'; -import { MockScrollProvider } from '../../utils/tests'; +import { MockScrollProvider } from '../../testUtils'; function renderInContext(children: JSX.Element) { return render({children}); diff --git a/plugins/cost-insights/src/components/AlertInsights/AlertInsights.test.tsx b/plugins/cost-insights/src/components/AlertInsights/AlertInsights.test.tsx index 98f75b1d41..5509d8f1ea 100644 --- a/plugins/cost-insights/src/components/AlertInsights/AlertInsights.test.tsx +++ b/plugins/cost-insights/src/components/AlertInsights/AlertInsights.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { render, fireEvent, waitFor } from '@testing-library/react'; import { AlertInsights } from './AlertInsights'; -import { MockScrollProvider, MockLoadingProvider } from '../../utils/tests'; +import { MockScrollProvider, MockLoadingProvider } from '../../testUtils'; function renderInContext(children: JSX.Element) { return render( diff --git a/plugins/cost-insights/src/components/AlertInsights/AlertInsightsSection.test.tsx b/plugins/cost-insights/src/components/AlertInsights/AlertInsightsSection.test.tsx index 937e099356..093ca1ff0c 100644 --- a/plugins/cost-insights/src/components/AlertInsights/AlertInsightsSection.test.tsx +++ b/plugins/cost-insights/src/components/AlertInsights/AlertInsightsSection.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { AlertInsightsSection } from './AlertInsightsSection'; import { render } from '@testing-library/react'; import { Alert } from '../../types'; -import { MockScrollProvider } from '../../utils/tests'; +import { MockScrollProvider } from '../../testUtils'; const mockAlert: Alert = { subtitle: diff --git a/plugins/cost-insights/src/components/AlertInsights/AlertStatusSummary.test.tsx b/plugins/cost-insights/src/components/AlertInsights/AlertStatusSummary.test.tsx index 830db19fcd..ab39e0364e 100644 --- a/plugins/cost-insights/src/components/AlertInsights/AlertStatusSummary.test.tsx +++ b/plugins/cost-insights/src/components/AlertInsights/AlertStatusSummary.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { AlertStatusSummary } from './AlertStatusSummary'; import { render } from '@testing-library/react'; import { Alert, AlertStatus } from '../../types'; -import { MockScrollProvider } from '../../utils/tests'; +import { MockScrollProvider } from '../../testUtils'; const mockSnoozed: Alert = { title: 'snoozed-title', diff --git a/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx b/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx index 4e40a5ddda..cd031f9d1b 100644 --- a/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx +++ b/plugins/cost-insights/src/components/BarChart/BarChart.test.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; import { BarChart, BarChartProps } from './BarChart'; import { ResourceData } from '../../types'; -import { createMockEntity } from '../../utils/mockData'; +import { createMockEntity } from '../../testUtils'; import { resourceSort } from '../../utils/sort'; import { renderInTestApp } from '@backstage/test-utils'; diff --git a/plugins/cost-insights/src/components/CostGrowth/CostGrowth.test.tsx b/plugins/cost-insights/src/components/CostGrowth/CostGrowth.test.tsx index 12668140b6..f8c06845c9 100644 --- a/plugins/cost-insights/src/components/CostGrowth/CostGrowth.test.tsx +++ b/plugins/cost-insights/src/components/CostGrowth/CostGrowth.test.tsx @@ -19,7 +19,7 @@ import { renderInTestApp } from '@backstage/test-utils'; import { CostGrowth } from './CostGrowth'; import { Currency, CurrencyType, Duration } from '../../types'; import { findAlways } from '../../utils/assert'; -import { MockConfigProvider, MockCurrencyProvider } from '../../utils/tests'; +import { MockConfigProvider, MockCurrencyProvider } from '../../testUtils'; import { defaultCurrencies } from '../../utils/currency'; const engineers = findAlways(defaultCurrencies, c => c.kind === null); diff --git a/plugins/cost-insights/src/components/CostInsightsNavigation/CostInsightsNavigation.test.tsx b/plugins/cost-insights/src/components/CostInsightsNavigation/CostInsightsNavigation.test.tsx index f94ab48c6f..9b53cb9d1a 100644 --- a/plugins/cost-insights/src/components/CostInsightsNavigation/CostInsightsNavigation.test.tsx +++ b/plugins/cost-insights/src/components/CostInsightsNavigation/CostInsightsNavigation.test.tsx @@ -19,7 +19,7 @@ import { default as HappyFace } from '@material-ui/icons/SentimentSatisfiedAlt'; import { renderInTestApp } from '@backstage/test-utils'; import { CostInsightsNavigation } from './CostInsightsNavigation'; import { Product, Icon } from '../../types'; -import { MockConfigProvider, MockScrollProvider } from '../../utils/tests'; +import { MockConfigProvider, MockScrollProvider } from '../../testUtils'; import { getDefaultNavigationItems } from '../../utils/navigation'; const mockIcons: Icon[] = [ diff --git a/plugins/cost-insights/src/components/CostInsightsTabs/CostInsightsTabs.test.tsx b/plugins/cost-insights/src/components/CostInsightsTabs/CostInsightsTabs.test.tsx index a910d43c22..24204abd95 100644 --- a/plugins/cost-insights/src/components/CostInsightsTabs/CostInsightsTabs.test.tsx +++ b/plugins/cost-insights/src/components/CostInsightsTabs/CostInsightsTabs.test.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { CostInsightsTabs } from './CostInsightsTabs'; import UserEvent from '@testing-library/user-event'; import { Group } from '../../types'; -import { MockFilterProvider, MockLoadingProvider } from '../../utils/tests'; +import { MockFilterProvider, MockLoadingProvider } from '../../testUtils'; import { renderInTestApp } from '@backstage/test-utils'; const mockSetPageFilters = jest.fn(); diff --git a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.test.tsx b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.test.tsx index 6fca8f67c9..3ff4d31135 100644 --- a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.test.tsx +++ b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.test.tsx @@ -24,13 +24,11 @@ import { getGroupedProjects, MockAggregatedDailyCosts, trendlineOf, -} from '../../utils/mockData'; -import { MockBillingDateProvider, MockConfigProvider, MockFilterProvider, MockScrollProvider, -} from '../../utils/tests'; +} from '../../testUtils'; import { CostInsightsThemeProvider } from '../CostInsightsPage/CostInsightsThemeProvider'; const mockGroupDailyCost: Cost = { diff --git a/plugins/cost-insights/src/components/PeriodSelect/PeriodSelect.test.tsx b/plugins/cost-insights/src/components/PeriodSelect/PeriodSelect.test.tsx index ae513c04e0..0c43e4d403 100644 --- a/plugins/cost-insights/src/components/PeriodSelect/PeriodSelect.test.tsx +++ b/plugins/cost-insights/src/components/PeriodSelect/PeriodSelect.test.tsx @@ -20,7 +20,7 @@ import { renderInTestApp } from '@backstage/test-utils'; import UserEvent from '@testing-library/user-event'; import { PeriodSelect, getDefaultOptions } from './PeriodSelect'; import { getDefaultPageFilters } from '../../utils/filters'; -import { MockBillingDateProvider } from '../../utils/tests'; +import { MockBillingDateProvider } from '../../testUtils'; import { Group, Duration } from '../../types'; const DefaultPageFilters = getDefaultPageFilters([{ id: 'tools' }] as Group[]); diff --git a/plugins/cost-insights/src/components/ProductInsights/ProductInsights.test.tsx b/plugins/cost-insights/src/components/ProductInsights/ProductInsights.test.tsx index 3719bc8f6c..f782659ff2 100644 --- a/plugins/cost-insights/src/components/ProductInsights/ProductInsights.test.tsx +++ b/plugins/cost-insights/src/components/ProductInsights/ProductInsights.test.tsx @@ -18,8 +18,8 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; import { ProductInsights } from './ProductInsights'; import { ProductInsightsOptions } from '../../api'; -import { mockDefaultLoadingState } from '../../utils/mockData'; import { + mockDefaultLoadingState, MockConfigProvider, MockCostInsightsApiProvider, MockCurrencyProvider, @@ -27,7 +27,7 @@ import { MockBillingDateProvider, MockScrollProvider, MockLoadingProvider, -} from '../../utils/tests'; +} from '../../testUtils'; import { Entity, Product } from '../../types'; // suppress recharts componentDidUpdate warnings diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.test.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.test.tsx index a2f87b1d86..cd171feba6 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.test.tsx +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductInsightsCard.test.tsx @@ -22,15 +22,13 @@ import { createMockEntity, mockDefaultLoadingState, MockComputeEngine, -} from '../../utils/mockData'; -import { MockConfigProvider, MockCostInsightsApiProvider, MockCurrencyProvider, MockBillingDateProvider, MockScrollProvider, MockLoadingProvider, -} from '../../utils/tests'; +} from '../../testUtils'; import { Duration, Entity, Product } from '../../types'; // suppress recharts componentDidUpdate warnings diff --git a/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.test.tsx b/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.test.tsx index 0e24f042bc..52f334c4cb 100644 --- a/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.test.tsx +++ b/plugins/cost-insights/src/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.test.tsx @@ -17,12 +17,12 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; import { ProjectGrowthAlertCard } from './ProjectGrowthAlertCard'; -import { createMockProjectGrowthData } from '../../utils/mockData'; import { + createMockProjectGrowthData, MockCurrencyProvider, MockConfigProvider, MockBillingDateProvider, -} from '../../utils/tests'; +} from '../../testUtils'; import { AlertCost } from '../../types'; import { defaultCurrencies } from '../../utils/currency'; import { findAlways } from '../../utils/assert'; diff --git a/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx b/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx index 16270429e8..1bd57a9eed 100644 --- a/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx +++ b/plugins/cost-insights/src/components/ProjectSelect/ProjectSelect.test.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { getByRole, waitFor } from '@testing-library/react'; import UserEvent from '@testing-library/user-event'; import { ProjectSelect } from './ProjectSelect'; -import { MockFilterProvider } from '../../utils/tests'; +import { MockFilterProvider } from '../../testUtils'; import { renderInTestApp } from '@backstage/test-utils'; const mockProjects = [ diff --git a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.test.tsx b/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.test.tsx index df38c78a03..5aea9be548 100644 --- a/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.test.tsx +++ b/plugins/cost-insights/src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.test.tsx @@ -19,7 +19,7 @@ import { UnlabeledDataflowAlertCard } from './UnlabeledDataflowAlertCard'; import { createMockUnlabeledDataflowData, createMockUnlabeledDataflowAlertProject, -} from '../../utils/mockData'; +} from '../../testUtils'; import { renderInTestApp } from '@backstage/test-utils'; const MockUnlabeledDataflowAlertMultipleProjects = createMockUnlabeledDataflowData( diff --git a/plugins/cost-insights/src/components/index.ts b/plugins/cost-insights/src/components/index.ts index c390666419..4388250d31 100644 --- a/plugins/cost-insights/src/components/index.ts +++ b/plugins/cost-insights/src/components/index.ts @@ -14,6 +14,28 @@ * limitations under the License. */ +export * from './ActionItems'; +export * from './AlertInsights'; +export * from './AlertInstructionsLayout'; export * from './BarChart'; +export * from './CopyUrlToClipboard'; export * from './CostGrowth'; +export * from './CostInsightsHeader'; +export * from './CostInsightsLayout'; +export * from './CostInsightsNavigation'; +export * from './CostInsightsPage'; +export * from './CostInsightsSupportButton'; +export * from './CostInsightsTabs'; +export * from './CostOverviewCard'; +export * from './CurrencySelect'; +export * from './LabelDataflowInstructionsPage'; export * from './LegendItem'; +export * from './MetricSelect'; +export * from './PeriodSelect'; +export * from './ProductInsights'; +export * from './ProductInsightsCard'; +export * from './ProjectGrowthAlertCard'; +export * from './ProjectGrowthInstructionsPage'; +export * from './ProjectSelect'; +export * from './UnlabeledDataflowAlertCard'; +export * from './WhyCostsMatter'; diff --git a/plugins/cost-insights/src/alerts/KubernetesMigrationAlert.tsx b/plugins/cost-insights/src/example/alerts/KubernetesMigrationAlert.tsx similarity index 86% rename from plugins/cost-insights/src/alerts/KubernetesMigrationAlert.tsx rename to plugins/cost-insights/src/example/alerts/KubernetesMigrationAlert.tsx index 31a4d7e0e9..4878462f62 100644 --- a/plugins/cost-insights/src/alerts/KubernetesMigrationAlert.tsx +++ b/plugins/cost-insights/src/example/alerts/KubernetesMigrationAlert.tsx @@ -16,8 +16,8 @@ import React from 'react'; import pluralize from 'pluralize'; -import { MigrationAlertCard } from '../components/MigrationAlertCard'; -import { CostInsightsApi } from '../api'; +import { KubernetesMigrationAlertCard } from '../components'; +import { CostInsightsApi } from '../../api'; import { Alert, AlertForm, @@ -26,19 +26,22 @@ import { AlertSnoozeFormData, ChangeStatistic, Entity, -} from '../types'; -import { MigrationDismissForm, MigrationDismissFormData } from '../forms'; +} from '../../types'; +import { + KubernetesMigrationDismissForm, + KubernetesMigrationDismissFormData, +} from '../forms'; -export interface MigrationData { +export interface KubernetesMigrationData { startDate: string; endDate: string; change: ChangeStatistic; services: Array; } -export interface MigrationAlert extends Alert { +export interface KubernetesMigrationApi extends Alert { api: CostInsightsApi; - data: MigrationData; + data: KubernetesMigrationData; } /** @@ -63,9 +66,9 @@ export interface MigrationAlert extends Alert { * Custom forms must implement the corresponding event hook. See /forms for example implementations. */ -export class KubernetesMigrationAlert implements MigrationAlert { +export class KubernetesMigrationAlert implements KubernetesMigrationApi { api: CostInsightsApi; - data: MigrationData; + data: KubernetesMigrationData; subtitle = 'Services running on Kubernetes are estimated to save 50% or more compared to Compute Engine.'; @@ -74,11 +77,11 @@ export class KubernetesMigrationAlert implements MigrationAlert { AcceptForm = null; // Overrides default Dismiss form with a custom form component. DismissForm: AlertForm< - MigrationAlert, - MigrationDismissFormData - > = MigrationDismissForm; + KubernetesMigrationAlert, + KubernetesMigrationDismissFormData + > = KubernetesMigrationDismissForm; - constructor(api: CostInsightsApi, data: MigrationData) { + constructor(api: CostInsightsApi, data: KubernetesMigrationData) { this.api = api; this.data = data; } @@ -98,7 +101,7 @@ export class KubernetesMigrationAlert implements MigrationAlert { true, )}, sorted by cost`; return ( - , + options: AlertOptions, ): Promise { const alerts = await this.api.getAlerts(options.group); return new Promise(resolve => diff --git a/plugins/cost-insights/src/example/alerts/index.ts b/plugins/cost-insights/src/example/alerts/index.ts new file mode 100644 index 0000000000..eadd8d7ef4 --- /dev/null +++ b/plugins/cost-insights/src/example/alerts/index.ts @@ -0,0 +1,19 @@ +/* + * 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 { KubernetesMigrationAlert } from './KubernetesMigrationAlert'; +export type { KubernetesMigrationApi } from './KubernetesMigrationAlert'; +export type { KubernetesMigrationData } from './KubernetesMigrationAlert'; diff --git a/plugins/cost-insights/src/client.ts b/plugins/cost-insights/src/example/client.ts similarity index 96% rename from plugins/cost-insights/src/client.ts rename to plugins/cost-insights/src/example/client.ts index 2e70170fc1..00d0a25233 100644 --- a/plugins/cost-insights/src/client.ts +++ b/plugins/cost-insights/src/example/client.ts @@ -16,7 +16,7 @@ /* eslint-disable no-restricted-imports */ import dayjs from 'dayjs'; -import { CostInsightsApi, ProductInsightsOptions } from '../src/api'; +import { CostInsightsApi, ProductInsightsOptions } from '../api'; import { Alert, Cost, @@ -27,12 +27,9 @@ import { Project, ProjectGrowthData, UnlabeledDataflowData, -} from '../src/types'; -import { - ProjectGrowthAlert, - UnlabeledDataflowAlert, - KubernetesMigrationAlert, -} from '../src/alerts'; +} from '../types'; +import { KubernetesMigrationAlert } from './alerts'; +import { ProjectGrowthAlert, UnlabeledDataflowAlert } from '../alerts'; import { aggregationFor, changeOf, @@ -40,7 +37,7 @@ import { getGroupedProducts, getGroupedProjects, trendlineOf, -} from './utils/mockData'; +} from '../testUtils'; export class ExampleCostInsightsClient implements CostInsightsApi { private request(_: any, res: any): Promise { diff --git a/plugins/cost-insights/src/components/MigrationAlertCard/MigrationAlertCard.tsx b/plugins/cost-insights/src/example/components/KubernetesMigrationAlertCard/KubernetesMigrationAlertCard.tsx similarity index 75% rename from plugins/cost-insights/src/components/MigrationAlertCard/MigrationAlertCard.tsx rename to plugins/cost-insights/src/example/components/KubernetesMigrationAlertCard/KubernetesMigrationAlertCard.tsx index 56d636d8aa..3cba2b7e4d 100644 --- a/plugins/cost-insights/src/components/MigrationAlertCard/MigrationAlertCard.tsx +++ b/plugins/cost-insights/src/example/components/KubernetesMigrationAlertCard/KubernetesMigrationAlertCard.tsx @@ -17,30 +17,30 @@ import React from 'react'; import { Box } from '@material-ui/core'; import { InfoCard } from '@backstage/core'; -import { MigrationBarChartLegend } from './MigrationBarChartLegend'; -import { MigrationBarChart } from './MigrationBarChart'; -import { MigrationData } from '../../alerts'; +import { KubernetesMigrationBarChartLegend } from './KubernetesMigrationBarChartLegend'; +import { KubernetesMigrationBarChart } from './KubernetesMigrationBarChart'; +import { KubernetesMigrationData } from '../../alerts'; -type MigrationAlertProps = { - data: MigrationData; +type KubernetesMigrationAlertProps = { + data: KubernetesMigrationData; title: string; subheader: string; currentProduct: string; comparedProduct: string; }; -export const MigrationAlertCard = ({ +export const KubernetesMigrationAlertCard = ({ data, title, subheader, currentProduct, comparedProduct, -}: MigrationAlertProps) => { +}: KubernetesMigrationAlertProps) => { return ( - - ; }; -export const MigrationBarChart = ({ +export const KubernetesMigrationBarChart = ({ currentProduct, comparedProduct, services, diff --git a/plugins/cost-insights/src/components/MigrationAlertCard/MigrationBarChartLegend.tsx b/plugins/cost-insights/src/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChartLegend.tsx similarity index 89% rename from plugins/cost-insights/src/components/MigrationAlertCard/MigrationBarChartLegend.tsx rename to plugins/cost-insights/src/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChartLegend.tsx index a84e865e28..2c74535d0d 100644 --- a/plugins/cost-insights/src/components/MigrationAlertCard/MigrationBarChartLegend.tsx +++ b/plugins/cost-insights/src/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChartLegend.tsx @@ -16,10 +16,9 @@ import React from 'react'; import { Box, useTheme } from '@material-ui/core'; -import { CostGrowth } from '../CostGrowth'; -import { LegendItem } from '../LegendItem'; -import { ChangeStatistic, CostInsightsTheme, Duration } from '../../types'; -import { monthOf } from '../../utils/formatters'; +import { CostGrowth, LegendItem } from '../../../components'; +import { ChangeStatistic, CostInsightsTheme, Duration } from '../../../types'; +import { monthOf } from '../../../utils/formatters'; export type MigrationBarChartLegendProps = { change: ChangeStatistic; @@ -28,7 +27,7 @@ export type MigrationBarChartLegendProps = { comparedProduct: string; }; -export const MigrationBarChartLegend = ({ +export const KubernetesMigrationBarChartLegend = ({ currentProduct, comparedProduct, change, diff --git a/plugins/cost-insights/src/components/MigrationAlertCard/index.ts b/plugins/cost-insights/src/example/components/KubernetesMigrationAlertCard/index.ts similarity index 90% rename from plugins/cost-insights/src/components/MigrationAlertCard/index.ts rename to plugins/cost-insights/src/example/components/KubernetesMigrationAlertCard/index.ts index 4feb749c27..65f0c097f3 100644 --- a/plugins/cost-insights/src/components/MigrationAlertCard/index.ts +++ b/plugins/cost-insights/src/example/components/KubernetesMigrationAlertCard/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { MigrationAlertCard } from './MigrationAlertCard'; +export * from './KubernetesMigrationAlertCard'; diff --git a/plugins/cost-insights/src/example/components/index.ts b/plugins/cost-insights/src/example/components/index.ts new file mode 100644 index 0000000000..65f0c097f3 --- /dev/null +++ b/plugins/cost-insights/src/example/components/index.ts @@ -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 './KubernetesMigrationAlertCard'; diff --git a/plugins/cost-insights/src/forms/MigrationDismissForm.tsx b/plugins/cost-insights/src/example/forms/KubernetesMigrationDismissForm.tsx similarity index 86% rename from plugins/cost-insights/src/forms/MigrationDismissForm.tsx rename to plugins/cost-insights/src/example/forms/KubernetesMigrationDismissForm.tsx index ce71af3a37..835577787c 100644 --- a/plugins/cost-insights/src/forms/MigrationDismissForm.tsx +++ b/plugins/cost-insights/src/example/forms/KubernetesMigrationDismissForm.tsx @@ -28,22 +28,22 @@ import { FormGroup, Typography, } from '@material-ui/core'; -import { AlertFormProps, Entity } from '../types'; -import { MigrationAlert } from '../alerts'; -import { findAlways } from '../utils/assert'; +import { AlertFormProps, Entity } from '../../types'; +import { KubernetesMigrationAlert } from '../alerts'; +import { findAlways } from '../../utils/assert'; -export type MigrationDismissFormData = { +export type KubernetesMigrationDismissFormData = { services: Entity[]; }; -export type MigrationDismissFormProps = AlertFormProps< - MigrationAlert, - MigrationDismissFormData +export type KubernetesMigrationDismissFormProps = AlertFormProps< + KubernetesMigrationAlert, + KubernetesMigrationDismissFormData >; -export const MigrationDismissForm = forwardRef< +export const KubernetesMigrationDismissForm = forwardRef< HTMLFormElement, - MigrationDismissFormProps + KubernetesMigrationDismissFormProps >(({ onSubmit, disableSubmit, alert }, ref) => { const [services, setServices] = useState(alert.data.services); diff --git a/plugins/cost-insights/src/example/forms/index.ts b/plugins/cost-insights/src/example/forms/index.ts new file mode 100644 index 0000000000..2ad52201f9 --- /dev/null +++ b/plugins/cost-insights/src/example/forms/index.ts @@ -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 './KubernetesMigrationDismissForm'; diff --git a/plugins/cost-insights/src/example/index.ts b/plugins/cost-insights/src/example/index.ts new file mode 100644 index 0000000000..c27f03bdda --- /dev/null +++ b/plugins/cost-insights/src/example/index.ts @@ -0,0 +1,20 @@ +/* + * 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 './alerts'; +export * from './components'; +export * from './forms'; +export * from './client'; diff --git a/plugins/cost-insights/src/forms/index.ts b/plugins/cost-insights/src/forms/index.ts index 5b8384a293..4692bc1de3 100644 --- a/plugins/cost-insights/src/forms/index.ts +++ b/plugins/cost-insights/src/forms/index.ts @@ -17,5 +17,3 @@ export { AlertAcceptForm } from './AlertAcceptForm'; export { AlertSnoozeForm } from './AlertSnoozeForm'; export { AlertDismissForm } from './AlertDismissForm'; -export { MigrationDismissForm } from './MigrationDismissForm'; -export type { MigrationDismissFormData } from './MigrationDismissForm'; diff --git a/plugins/cost-insights/src/index.ts b/plugins/cost-insights/src/index.ts index 0ae9d05b09..2d12eec9d9 100644 --- a/plugins/cost-insights/src/index.ts +++ b/plugins/cost-insights/src/index.ts @@ -21,10 +21,9 @@ export { CostInsightsProjectGrowthInstructionsPage, CostInsightsLabelDataflowInstructionsPage, } from './plugin'; -export * from './client'; +export { ExampleCostInsightsClient } from './example'; +export { BarChart, LegendItem, CostGrowth } from './components'; +export { MockConfigProvider, MockCurrencyProvider } from './testUtils'; export * from './api'; -export { ProjectGrowthAlert, UnlabeledDataflowAlert } from './alerts'; -export * from './components'; -export { useCurrency } from './hooks'; +export * from './alerts'; export * from './types'; -export * from './utils/tests'; diff --git a/plugins/cost-insights/src/testUtils/alerts.ts b/plugins/cost-insights/src/testUtils/alerts.ts new file mode 100644 index 0000000000..2e8e1b114a --- /dev/null +++ b/plugins/cost-insights/src/testUtils/alerts.ts @@ -0,0 +1,78 @@ +/* + * 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 { + ProjectGrowthData, + UnlabeledDataflowAlertProject, + UnlabeledDataflowData, +} from '../types'; + +type mockAlertRenderer = (alert: T) => T; +type mockEntityRenderer = (entity: T) => T; + +export const createMockUnlabeledDataflowData = ( + callback?: mockAlertRenderer, +): UnlabeledDataflowData => { + const data: UnlabeledDataflowData = { + periodStart: '2020-05-01', + periodEnd: '2020-06-1', + projects: [], + unlabeledCost: 0, + labeledCost: 0, + }; + + if (typeof callback === 'function') { + return callback({ ...data }); + } + + return { ...data }; +}; + +export const createMockUnlabeledDataflowAlertProject = ( + callback?: mockEntityRenderer, +): UnlabeledDataflowAlertProject => { + const defaultProject: UnlabeledDataflowAlertProject = { + id: 'test-alert-project', + unlabeledCost: 2000.0, + labeledCost: 3200.0, + }; + if (typeof callback === 'function') { + return callback({ ...defaultProject }); + } + return { ...defaultProject }; +}; + +export const createMockProjectGrowthData = ( + callback?: mockAlertRenderer, +): ProjectGrowthData => { + const data: ProjectGrowthData = { + project: 'test-project-growth-alert', + periodStart: '2019-Q4', + periodEnd: '2020-Q1', + aggregation: [670532.1, 970502.8], + change: { + ratio: 0.5, + amount: 180000, + }, + products: [], + }; + + if (typeof callback === 'function') { + return callback({ ...data }); + } + + return { ...data }; +}; diff --git a/plugins/cost-insights/src/testUtils/config.ts b/plugins/cost-insights/src/testUtils/config.ts new file mode 100644 index 0000000000..7d48121506 --- /dev/null +++ b/plugins/cost-insights/src/testUtils/config.ts @@ -0,0 +1,33 @@ +/* + * 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 { Config } from '@backstage/config'; +import { ConfigApi } from '@backstage/core'; +import { MockProductTypes } from './products'; + +export const MockProductsConfig: Partial = { + keys: () => Object.keys(MockProductTypes), +}; + +export const MockMetricsConfig: Partial = { + getOptionalString: () => 'daily-cost', + keys: () => ['daily-cost'], +}; + +export const MockCostInsightsConfig: Partial = { + getConfig: () => MockProductsConfig as Config, + getOptionalConfig: () => MockMetricsConfig as Config, +}; diff --git a/plugins/explore/src/components/DomainCard/DomainCardGrid.tsx b/plugins/cost-insights/src/testUtils/filters.ts similarity index 53% rename from plugins/explore/src/components/DomainCard/DomainCardGrid.tsx rename to plugins/cost-insights/src/testUtils/filters.ts index b4a13aa39f..e401b1f2e7 100644 --- a/plugins/explore/src/components/DomainCard/DomainCardGrid.tsx +++ b/plugins/cost-insights/src/testUtils/filters.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021 Spotify AB + * 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. @@ -13,21 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { DomainEntity } from '@backstage/catalog-model'; -import { Grid } from '@material-ui/core'; -import React from 'react'; -import { DomainCard } from '.'; -type DomainCardGridProps = { - entities: DomainEntity[]; -}; +import { Duration, ProductFilters } from '../types'; +import { MockProductTypes } from './products'; -export const DomainCardGrid = ({ entities }: DomainCardGridProps) => ( - - {entities.map((e, i) => ( - - - - ))} - -); +export const MockProductFilters: ProductFilters = Object.keys( + MockProductTypes, +).map(productType => ({ duration: Duration.P30D, productType })); diff --git a/plugins/cost-insights/src/testUtils/index.ts b/plugins/cost-insights/src/testUtils/index.ts new file mode 100644 index 0000000000..a1748b15b2 --- /dev/null +++ b/plugins/cost-insights/src/testUtils/index.ts @@ -0,0 +1,23 @@ +/* + * 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 './alerts'; +export * from './config'; +export * from './filters'; +export * from './loading'; +export * from './mockData'; +export * from './products'; +export * from './providers'; +export * from './testUtils'; diff --git a/plugins/cost-insights/src/testUtils/loading.ts b/plugins/cost-insights/src/testUtils/loading.ts new file mode 100644 index 0000000000..6c6f761ee8 --- /dev/null +++ b/plugins/cost-insights/src/testUtils/loading.ts @@ -0,0 +1,30 @@ +/* + * 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 { MockProducts } from './products'; +import { + DefaultLoadingAction, + getDefaultState as getDefaultLoadingState, +} from '../utils/loading'; + +export const MockDefaultLoadingActions = ([ + DefaultLoadingAction.UserGroups, + DefaultLoadingAction.CostInsightsInitial, + DefaultLoadingAction.CostInsightsPage, +] as string[]).concat(MockProducts.map(product => product.kind)); + +export const mockDefaultLoadingState = getDefaultLoadingState( + MockDefaultLoadingActions, +); diff --git a/plugins/cost-insights/src/utils/mockData.ts b/plugins/cost-insights/src/testUtils/mockData.ts similarity index 58% rename from plugins/cost-insights/src/utils/mockData.ts rename to plugins/cost-insights/src/testUtils/mockData.ts index d4ac4cd2df..5149da278d 100644 --- a/plugins/cost-insights/src/utils/mockData.ts +++ b/plugins/cost-insights/src/testUtils/mockData.ts @@ -14,264 +14,7 @@ * limitations under the License. */ -import dayjs from 'dayjs'; -import regression, { DataPoint } from 'regression'; -import { Config } from '@backstage/config'; -import { ConfigApi } from '@backstage/core'; -import { - ChangeStatistic, - Duration, - Entity, - Product, - ProductFilters, - ProjectGrowthData, - Trendline, - UnlabeledDataflowAlertProject, - UnlabeledDataflowData, - DateAggregation, - DEFAULT_DATE_FORMAT, -} from '../types'; -import { - DefaultLoadingAction, - getDefaultState as getDefaultLoadingState, -} from '../utils/loading'; -import { findAlways } from '../utils/assert'; -import { inclusiveEndDateOf, inclusiveStartDateOf } from './duration'; - -type mockAlertRenderer = (alert: T) => T; -type mockEntityRenderer = (entity: T) => T; - -type IntervalFields = { - duration: Duration; - endDate: string; -}; - -export const createMockEntity = ( - callback?: mockEntityRenderer, -): Entity => { - const defaultEntity: Entity = { - id: 'test-entity', - aggregation: [100, 200], - entities: {}, - change: { - ratio: 0, - amount: 0, - }, - }; - - if (typeof callback === 'function') { - return callback({ ...defaultEntity }); - } - return { ...defaultEntity }; -}; - -export const createMockProduct = ( - callback?: mockEntityRenderer, -): Product => { - const defaultProduct: Product = { - kind: 'compute-engine', - name: 'Compute Engine', - }; - if (typeof callback === 'function') { - return callback({ ...defaultProduct }); - } - return { ...defaultProduct }; -}; - -export const createMockProjectGrowthData = ( - callback?: mockAlertRenderer, -): ProjectGrowthData => { - const data: ProjectGrowthData = { - project: 'test-project-growth-alert', - periodStart: '2019-Q4', - periodEnd: '2020-Q1', - aggregation: [670532.1, 970502.8], - change: { - ratio: 0.5, - amount: 180000, - }, - products: [], - }; - - if (typeof callback === 'function') { - return callback({ ...data }); - } - - return { ...data }; -}; - -export const createMockUnlabeledDataflowData = ( - callback?: mockAlertRenderer, -): UnlabeledDataflowData => { - const data: UnlabeledDataflowData = { - periodStart: '2020-05-01', - periodEnd: '2020-06-1', - projects: [], - unlabeledCost: 0, - labeledCost: 0, - }; - - if (typeof callback === 'function') { - return callback({ ...data }); - } - - return { ...data }; -}; - -export const createMockUnlabeledDataflowAlertProject = ( - callback?: mockEntityRenderer, -): UnlabeledDataflowAlertProject => { - const defaultProject: UnlabeledDataflowAlertProject = { - id: 'test-alert-project', - unlabeledCost: 2000.0, - labeledCost: 3200.0, - }; - if (typeof callback === 'function') { - return callback({ ...defaultProject }); - } - return { ...defaultProject }; -}; - -export const MockProductTypes: Record = { - 'compute-engine': 'Compute Engine', - 'cloud-dataflow': 'Cloud Dataflow', - 'cloud-storage': 'Cloud Storage', - 'big-query': 'Big Query', - 'big-table': 'BigTable', - 'cloud-pub-sub': 'Cloud Pub/Sub', -}; - -export const MockProductFilters: ProductFilters = Object.keys( - MockProductTypes, -).map(productType => ({ duration: Duration.P30D, productType })); - -export const MockProducts: Product[] = Object.keys(MockProductTypes).map( - productType => - createMockProduct(() => ({ - kind: productType, - name: MockProductTypes[productType], - })), -); - -export const MockDefaultLoadingActions = ([ - DefaultLoadingAction.UserGroups, - DefaultLoadingAction.CostInsightsInitial, - DefaultLoadingAction.CostInsightsPage, -] as string[]).concat(MockProducts.map(product => product.kind)); - -export const mockDefaultLoadingState = getDefaultLoadingState( - MockDefaultLoadingActions, -); - -export const MockComputeEngine = findAlways( - MockProducts, - p => p.kind === 'compute-engine', -); -export const MockCloudDataflow = findAlways( - MockProducts, - p => p.kind === 'cloud-dataflow', -); -export const MockCloudStorage = findAlways( - MockProducts, - p => p.kind === 'cloud-storage', -); -export const MockBigQuery = findAlways( - MockProducts, - p => p.kind === 'big-query', -); -export const MockBigtable = findAlways( - MockProducts, - p => p.kind === 'big-table', -); - -export const MockProductsConfig: Partial = { - keys: () => Object.keys(MockProductTypes), -}; - -export const MockMetricsConfig: Partial = { - getOptionalString: () => 'daily-cost', - keys: () => ['daily-cost'], -}; - -export const MockCostInsightsConfig: Partial = { - getConfig: () => MockProductsConfig as Config, - getOptionalConfig: () => MockMetricsConfig as Config, -}; - -export function trendlineOf(aggregation: DateAggregation[]): Trendline { - const data: ReadonlyArray = aggregation.map(a => [ - Date.parse(a.date) / 1000, - a.amount, - ]); - const result = regression.linear(data, { precision: 5 }); - return { - slope: result.equation[0], - intercept: result.equation[1], - }; -} - -export function changeOf(aggregation: DateAggregation[]): ChangeStatistic { - const firstAmount = aggregation.length ? aggregation[0].amount : 0; - const lastAmount = aggregation.length - ? aggregation[aggregation.length - 1].amount - : 0; - const ratio = - firstAmount !== 0 ? (lastAmount - firstAmount) / firstAmount : 0; - return { - ratio: ratio, - amount: lastAmount - firstAmount, - }; -} - -export function aggregationFor( - intervals: string, - baseline: number, -): DateAggregation[] { - const { duration, endDate } = parseIntervals(intervals); - const inclusiveEndDate = inclusiveEndDateOf(duration, endDate); - const days = dayjs(endDate).diff( - inclusiveStartDateOf(duration, inclusiveEndDate), - 'day', - ); - - function nextDelta(): number { - const varianceFromBaseline = 0.15; - // Let's give positive vibes in trendlines - higher change for positive delta with >0.5 value - const positiveTrendChance = 0.55; - const normalization = positiveTrendChance - 1; - return baseline * (Math.random() + normalization) * varianceFromBaseline; - } - - return [...Array(days).keys()].reduce( - (values: DateAggregation[], i: number): DateAggregation[] => { - const last = values.length ? values[values.length - 1].amount : baseline; - const date = dayjs(inclusiveStartDateOf(duration, inclusiveEndDate)) - .add(i, 'day') - .format(DEFAULT_DATE_FORMAT); - const amount = Math.max(0, last + nextDelta()); - values.push({ - date: date, - amount: amount, - }); - return values; - }, - [], - ); -} - -function parseIntervals(intervals: string): IntervalFields { - const match = intervals.match( - /\/(?P\d+[DM])\/(?\d{4}-\d{2}-\d{2})/, - ); - if (Object.keys(match?.groups || {}).length !== 2) { - throw new Error(`Invalid intervals: ${intervals}`); - } - const { duration, date } = match!.groups!; - return { - duration: duration as Duration, - endDate: date, - }; -} +import { DateAggregation, Entity } from '../types'; export const MockAggregatedDailyCosts: DateAggregation[] = [ { @@ -520,7 +263,7 @@ export const MockAggregatedDailyCosts: DateAggregation[] = [ }, ]; -export const SampleBigQueryInsights: Entity = { +export const MockBigQueryInsights: Entity = { id: 'bigQuery', aggregation: [10_000, 30_000], change: { @@ -560,7 +303,7 @@ export const SampleBigQueryInsights: Entity = { }, }; -export const SampleCloudDataflowInsights: Entity = { +export const MockCloudDataflowInsights: Entity = { id: 'cloudDataflow', aggregation: [100_000, 158_000], change: { @@ -579,7 +322,7 @@ export const SampleCloudDataflowInsights: Entity = { entities: { SKU: [ { - id: 'Sample SKU A', + id: 'Mock SKU A', aggregation: [3_000, 4_000], change: { ratio: 0.333333, @@ -588,7 +331,7 @@ export const SampleCloudDataflowInsights: Entity = { entities: {}, }, { - id: 'Sample SKU B', + id: 'Mock SKU B', aggregation: [7_000, 8_000], change: { ratio: 0.14285714, @@ -609,7 +352,7 @@ export const SampleCloudDataflowInsights: Entity = { entities: { SKU: [ { - id: 'Sample SKU A', + id: 'Mock SKU A', aggregation: [20_000, 15_000], change: { ratio: -0.25, @@ -618,7 +361,7 @@ export const SampleCloudDataflowInsights: Entity = { entities: {}, }, { - id: 'Sample SKU B', + id: 'Mock SKU B', aggregation: [30_000, 35_000], change: { ratio: -0.16666666666666666, @@ -627,7 +370,7 @@ export const SampleCloudDataflowInsights: Entity = { entities: {}, }, { - id: 'Sample SKU C', + id: 'Mock SKU C', aggregation: [10_000, 20_000], change: { ratio: 1, @@ -648,7 +391,7 @@ export const SampleCloudDataflowInsights: Entity = { entities: { SKU: [ { - id: 'Sample SKU A', + id: 'Mock SKU A', aggregation: [4_000, 4_000], change: { ratio: 0, @@ -657,7 +400,7 @@ export const SampleCloudDataflowInsights: Entity = { entities: {}, }, { - id: 'Sample SKU B', + id: 'Mock SKU B', aggregation: [8_000, 4_000], change: { ratio: -0.5, @@ -681,7 +424,7 @@ export const SampleCloudDataflowInsights: Entity = { }, }; -export const SampleCloudStorageInsights: Entity = { +export const MockCloudStorageInsights: Entity = { id: 'cloudStorage', aggregation: [45_000, 45_000], change: { @@ -700,7 +443,7 @@ export const SampleCloudStorageInsights: Entity = { entities: { SKU: [ { - id: 'Sample SKU A', + id: 'Mock SKU A', aggregation: [10_000, 11_000], change: { ratio: 0.1, @@ -709,7 +452,7 @@ export const SampleCloudStorageInsights: Entity = { entities: {}, }, { - id: 'Sample SKU B', + id: 'Mock SKU B', aggregation: [2_000, 5_000], change: { ratio: 1.5, @@ -718,7 +461,7 @@ export const SampleCloudStorageInsights: Entity = { entities: {}, }, { - id: 'Sample SKU C', + id: 'Mock SKU C', aggregation: [3_000, 4_000], change: { ratio: 0.3333, @@ -739,7 +482,7 @@ export const SampleCloudStorageInsights: Entity = { entities: { SKU: [ { - id: 'Sample SKU A', + id: 'Mock SKU A', aggregation: [12_000, 13_000], change: { ratio: 0.08333333333333333, @@ -748,7 +491,7 @@ export const SampleCloudStorageInsights: Entity = { entities: {}, }, { - id: 'Sample SKU B', + id: 'Mock SKU B', aggregation: [16_000, 12_000], change: { ratio: -0.25, @@ -757,7 +500,7 @@ export const SampleCloudStorageInsights: Entity = { entities: {}, }, { - id: 'Sample SKU C', + id: 'Mock SKU C', aggregation: [2_000, 0], change: { ratio: -1, @@ -781,7 +524,7 @@ export const SampleCloudStorageInsights: Entity = { }, }; -export const SampleComputeEngineInsights: Entity = { +export const MockComputeEngineInsights: Entity = { id: 'computeEngine', aggregation: [80_000, 90_000], change: { @@ -800,7 +543,7 @@ export const SampleComputeEngineInsights: Entity = { entities: { SKU: [ { - id: 'Sample SKU A', + id: 'Mock SKU A', aggregation: [4_000, 2_000], change: { ratio: -0.5, @@ -809,7 +552,7 @@ export const SampleComputeEngineInsights: Entity = { entities: {}, }, { - id: 'Sample SKU B', + id: 'Mock SKU B', aggregation: [7_000, 6_000], change: { ratio: -0.14285714285714285, @@ -818,7 +561,7 @@ export const SampleComputeEngineInsights: Entity = { entities: {}, }, { - id: 'Sample SKU C', + id: 'Mock SKU C', aggregation: [9_000, 2_000], change: { ratio: -0.7777777777777778, @@ -859,7 +602,7 @@ export const SampleComputeEngineInsights: Entity = { entities: { SKU: [ { - id: 'Sample SKU A', + id: 'Mock SKU A', aggregation: [1_000, 2_000], change: { ratio: 1, @@ -868,7 +611,7 @@ export const SampleComputeEngineInsights: Entity = { entities: {}, }, { - id: 'Sample SKU B', + id: 'Mock SKU B', aggregation: [4_000, 8_000], change: { ratio: 1, @@ -877,7 +620,7 @@ export const SampleComputeEngineInsights: Entity = { entities: {}, }, { - id: 'Sample SKU C', + id: 'Mock SKU C', aggregation: [5_000, 10_000], change: { ratio: 1, @@ -921,7 +664,7 @@ export const SampleComputeEngineInsights: Entity = { }, }; -export const SampleEventsInsights: Entity = { +export const MockEventsInsights: Entity = { id: 'events', aggregation: [20_000, 10_000], change: { @@ -940,7 +683,7 @@ export const SampleEventsInsights: Entity = { entities: { product: [ { - id: 'Sample Product A', + id: 'Mock Product A', aggregation: [5_000, 2_000], change: { ratio: -0.6, @@ -949,7 +692,7 @@ export const SampleEventsInsights: Entity = { entities: {}, }, { - id: 'Sample Product B', + id: 'Mock Product B', aggregation: [7_000, 2_500], change: { ratio: -0.64285714285, @@ -958,7 +701,7 @@ export const SampleEventsInsights: Entity = { entities: {}, }, { - id: 'Sample Product C', + id: 'Mock Product C', aggregation: [3_000, 2_500], change: { ratio: -0.16666666666, @@ -979,7 +722,7 @@ export const SampleEventsInsights: Entity = { entities: { product: [ { - id: 'Sample Product A', + id: 'Mock Product A', aggregation: [2_000, 1_000], change: { ratio: -0.5, @@ -988,7 +731,7 @@ export const SampleEventsInsights: Entity = { entities: {}, }, { - id: 'Sample Product B', + id: 'Mock Product B', aggregation: [1_000, 1_500], change: { ratio: 0.5, @@ -997,7 +740,7 @@ export const SampleEventsInsights: Entity = { entities: {}, }, { - id: 'Sample Product C', + id: 'Mock Product C', aggregation: [2_000, 500], change: { ratio: -0.75, @@ -1011,72 +754,3 @@ export const SampleEventsInsights: Entity = { ], }, }; - -export function entityOf(product: string): Entity { - switch (product) { - case 'computeEngine': - return SampleComputeEngineInsights; - case 'cloudDataflow': - return SampleCloudDataflowInsights; - case 'cloudStorage': - return SampleCloudStorageInsights; - case 'bigQuery': - return SampleBigQueryInsights; - case 'events': - return SampleEventsInsights; - default: - throw new Error( - `Cannot get insights for ${product}. Make sure product matches product property in app-info.yaml`, - ); - } -} - -export const getGroupedProducts = (intervals: string) => [ - { - id: 'Cloud Dataflow', - aggregation: aggregationFor(intervals, 1_700), - }, - { - id: 'Compute Engine', - aggregation: aggregationFor(intervals, 350), - }, - { - id: 'Cloud Storage', - aggregation: aggregationFor(intervals, 1_300), - }, - { - id: 'BigQuery', - aggregation: aggregationFor(intervals, 2_000), - }, - { - id: 'Cloud SQL', - aggregation: aggregationFor(intervals, 750), - }, - { - id: 'Cloud Spanner', - aggregation: aggregationFor(intervals, 50), - }, - { - id: 'Cloud Pub/Sub', - aggregation: aggregationFor(intervals, 1_000), - }, - { - id: 'Cloud Bigtable', - aggregation: aggregationFor(intervals, 250), - }, -]; - -export const getGroupedProjects = (intervals: string) => [ - { - id: 'project-a', - aggregation: aggregationFor(intervals, 1_700), - }, - { - id: 'project-b', - aggregation: aggregationFor(intervals, 350), - }, - { - id: 'project-c', - aggregation: aggregationFor(intervals, 1_300), - }, -]; diff --git a/plugins/cost-insights/src/testUtils/products.ts b/plugins/cost-insights/src/testUtils/products.ts new file mode 100644 index 0000000000..0ecac46244 --- /dev/null +++ b/plugins/cost-insights/src/testUtils/products.ts @@ -0,0 +1,90 @@ +/* + * 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 { Entity, Product } from '../types'; +import { findAlways } from '../utils/assert'; + +type mockEntityRenderer = (entity: T) => T; + +export const createMockEntity = ( + callback?: mockEntityRenderer, +): Entity => { + const defaultEntity: Entity = { + id: 'test-entity', + aggregation: [100, 200], + entities: {}, + change: { + ratio: 0, + amount: 0, + }, + }; + + if (typeof callback === 'function') { + return callback({ ...defaultEntity }); + } + return { ...defaultEntity }; +}; + +export const createMockProduct = ( + callback?: mockEntityRenderer, +): Product => { + const defaultProduct: Product = { + kind: 'compute-engine', + name: 'Compute Engine', + }; + if (typeof callback === 'function') { + return callback({ ...defaultProduct }); + } + return { ...defaultProduct }; +}; + +export const MockProductTypes: Record = { + 'compute-engine': 'Compute Engine', + 'cloud-dataflow': 'Cloud Dataflow', + 'cloud-storage': 'Cloud Storage', + 'big-query': 'Big Query', + 'big-table': 'BigTable', + 'cloud-pub-sub': 'Cloud Pub/Sub', +}; + +export const MockProducts: Product[] = Object.keys(MockProductTypes).map( + productType => + createMockProduct(() => ({ + kind: productType, + name: MockProductTypes[productType], + })), +); + +export const MockComputeEngine = findAlways( + MockProducts, + p => p.kind === 'compute-engine', +); +export const MockCloudDataflow = findAlways( + MockProducts, + p => p.kind === 'cloud-dataflow', +); +export const MockCloudStorage = findAlways( + MockProducts, + p => p.kind === 'cloud-storage', +); +export const MockBigQuery = findAlways( + MockProducts, + p => p.kind === 'big-query', +); +export const MockBigtable = findAlways( + MockProducts, + p => p.kind === 'big-table', +); diff --git a/plugins/cost-insights/src/utils/tests.tsx b/plugins/cost-insights/src/testUtils/providers.tsx similarity index 92% rename from plugins/cost-insights/src/utils/tests.tsx rename to plugins/cost-insights/src/testUtils/providers.tsx index aae2800573..1097d86be4 100644 --- a/plugins/cost-insights/src/utils/tests.tsx +++ b/plugins/cost-insights/src/testUtils/providers.tsx @@ -34,18 +34,6 @@ import { import { ScrollContext, ScrollContextProps } from '../hooks/useScroll'; import { Group, Duration } from '../types'; -/* - Mock Providers and types are exposed publicly to allow users to test custom implementations - such as alerts, which may require context. - - Utility functions such as getDefaultPageFilters, etc. are intentionally - omitted as we do not want to expose explictly or implicitly internal implementations - that may be subject to change. - - Each Mock Provider provides minimal defaults which can be overridden, allowing users to define - context props only when necessary. -*/ - type PartialPropsWithChildren = PropsWithChildren>; export const MockGroups: Group[] = [{ id: 'tech' }, { id: 'mock-group' }]; diff --git a/plugins/cost-insights/src/testUtils/testUtils.ts b/plugins/cost-insights/src/testUtils/testUtils.ts new file mode 100644 index 0000000000..37913f635e --- /dev/null +++ b/plugins/cost-insights/src/testUtils/testUtils.ts @@ -0,0 +1,183 @@ +/* + * 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 dayjs from 'dayjs'; +import regression, { DataPoint } from 'regression'; +import { + ChangeStatistic, + Duration, + Entity, + Trendline, + DateAggregation, + DEFAULT_DATE_FORMAT, +} from '../types'; +import { inclusiveEndDateOf, inclusiveStartDateOf } from '../utils/duration'; +import { + MockComputeEngineInsights, + MockCloudDataflowInsights, + MockCloudStorageInsights, + MockBigQueryInsights, + MockEventsInsights, +} from './mockData'; + +type IntervalFields = { + duration: Duration; + endDate: string; +}; + +function parseIntervals(intervals: string): IntervalFields { + const match = intervals.match( + /\/(?P\d+[DM])\/(?\d{4}-\d{2}-\d{2})/, + ); + if (Object.keys(match?.groups || {}).length !== 2) { + throw new Error(`Invalid intervals: ${intervals}`); + } + const { duration, date } = match!.groups!; + return { + duration: duration as Duration, + endDate: date, + }; +} + +export function aggregationFor( + intervals: string, + baseline: number, +): DateAggregation[] { + const { duration, endDate } = parseIntervals(intervals); + const inclusiveEndDate = inclusiveEndDateOf(duration, endDate); + const days = dayjs(endDate).diff( + inclusiveStartDateOf(duration, inclusiveEndDate), + 'day', + ); + + function nextDelta(): number { + const varianceFromBaseline = 0.15; + // Let's give positive vibes in trendlines - higher change for positive delta with >0.5 value + const positiveTrendChance = 0.55; + const normalization = positiveTrendChance - 1; + return baseline * (Math.random() + normalization) * varianceFromBaseline; + } + + return [...Array(days).keys()].reduce( + (values: DateAggregation[], i: number): DateAggregation[] => { + const last = values.length ? values[values.length - 1].amount : baseline; + const date = dayjs(inclusiveStartDateOf(duration, inclusiveEndDate)) + .add(i, 'day') + .format(DEFAULT_DATE_FORMAT); + const amount = Math.max(0, last + nextDelta()); + values.push({ + date: date, + amount: amount, + }); + return values; + }, + [], + ); +} + +export function changeOf(aggregation: DateAggregation[]): ChangeStatistic { + const firstAmount = aggregation.length ? aggregation[0].amount : 0; + const lastAmount = aggregation.length + ? aggregation[aggregation.length - 1].amount + : 0; + const ratio = + firstAmount !== 0 ? (lastAmount - firstAmount) / firstAmount : 0; + return { + ratio: ratio, + amount: lastAmount - firstAmount, + }; +} + +export function trendlineOf(aggregation: DateAggregation[]): Trendline { + const data: ReadonlyArray = aggregation.map(a => [ + Date.parse(a.date) / 1000, + a.amount, + ]); + const result = regression.linear(data, { precision: 5 }); + return { + slope: result.equation[0], + intercept: result.equation[1], + }; +} + +export function entityOf(product: string): Entity { + switch (product) { + case 'computeEngine': + return MockComputeEngineInsights; + case 'cloudDataflow': + return MockCloudDataflowInsights; + case 'cloudStorage': + return MockCloudStorageInsights; + case 'bigQuery': + return MockBigQueryInsights; + case 'events': + return MockEventsInsights; + default: + throw new Error( + `Cannot get insights for ${product}. Make sure product matches product property in app-info.yaml`, + ); + } +} + +export const getGroupedProducts = (intervals: string) => [ + { + id: 'Cloud Dataflow', + aggregation: aggregationFor(intervals, 1_700), + }, + { + id: 'Compute Engine', + aggregation: aggregationFor(intervals, 350), + }, + { + id: 'Cloud Storage', + aggregation: aggregationFor(intervals, 1_300), + }, + { + id: 'BigQuery', + aggregation: aggregationFor(intervals, 2_000), + }, + { + id: 'Cloud SQL', + aggregation: aggregationFor(intervals, 750), + }, + { + id: 'Cloud Spanner', + aggregation: aggregationFor(intervals, 50), + }, + { + id: 'Cloud Pub/Sub', + aggregation: aggregationFor(intervals, 1_000), + }, + { + id: 'Cloud Bigtable', + aggregation: aggregationFor(intervals, 250), + }, +]; + +export const getGroupedProjects = (intervals: string) => [ + { + id: 'project-a', + aggregation: aggregationFor(intervals, 1_700), + }, + { + id: 'project-b', + aggregation: aggregationFor(intervals, 350), + }, + { + id: 'project-c', + aggregation: aggregationFor(intervals, 1_300), + }, +]; diff --git a/plugins/cost-insights/src/utils/change.test.ts b/plugins/cost-insights/src/utils/change.test.ts index 9516dfe270..7208e9bc4f 100644 --- a/plugins/cost-insights/src/utils/change.test.ts +++ b/plugins/cost-insights/src/utils/change.test.ts @@ -22,7 +22,7 @@ import { Duration, Cost, } from '../types'; -import { MockAggregatedDailyCosts, trendlineOf, changeOf } from './mockData'; +import { MockAggregatedDailyCosts, trendlineOf, changeOf } from '../testUtils'; const GrowthMap = { [GrowthType.Negligible]: 'negligible growth', diff --git a/plugins/explore-react/CHANGELOG.md b/plugins/explore-react/CHANGELOG.md index 1b58da0f04..03374d1805 100644 --- a/plugins/explore-react/CHANGELOG.md +++ b/plugins/explore-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-explore-react +## 0.0.3 + +### Patch Changes + +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [4c049a1a1] + - @backstage/core@0.7.0 + ## 0.0.2 ### Patch Changes diff --git a/plugins/explore-react/package.json b/plugins/explore-react/package.json index 4979882ed3..321b5b557f 100644 --- a/plugins/explore-react/package.json +++ b/plugins/explore-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore-react", - "version": "0.0.2", + "version": "0.0.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,11 +28,11 @@ "postpack": "backstage-cli postpack" }, "dependencies": { - "@backstage/core": "^0.6.0" + "@backstage/core": "^0.7.0" }, "devDependencies": { - "@backstage/cli": "^0.6.0", - "@backstage/dev-utils": "^0.1.9", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/explore/CHANGELOG.md b/plugins/explore/CHANGELOG.md index be284dfa64..7e2345fb5e 100644 --- a/plugins/explore/CHANGELOG.md +++ b/plugins/explore/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-explore +## 0.3.0 + +### Minor Changes + +- cfc83cac1: Introduce external route for linking to the entity page from the explore plugin. + + To use the explore plugin you have to bind the external route in your app: + + ```typescript + const app = createApp({ + ... + bindRoutes({ bind }) { + ... + bind(explorePlugin.externalRoutes, { + catalogEntity: catalogPlugin.routes.catalogEntity, + }); + }, + }); + ``` + +### Patch Changes + +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + - @backstage/plugin-explore-react@0.0.3 + ## 0.2.7 ### Patch Changes diff --git a/plugins/explore/package.json b/plugins/explore/package.json index dfd68207f2..e0165cccef 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore", - "version": "0.2.7", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,10 +30,10 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.6.3", - "@backstage/plugin-catalog-react": "^0.1.0", - "@backstage/plugin-explore-react": "^0.0.2", + "@backstage/catalog-model": "^0.7.3", + "@backstage/core": "^0.7.0", + "@backstage/plugin-catalog-react": "^0.1.1", + "@backstage/plugin-explore-react": "^0.0.3", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -45,8 +45,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/explore/src/components/DomainCard/DomainCard.test.tsx b/plugins/explore/src/components/DomainCard/DomainCard.test.tsx index 9d409051f6..56a02a3642 100644 --- a/plugins/explore/src/components/DomainCard/DomainCard.test.tsx +++ b/plugins/explore/src/components/DomainCard/DomainCard.test.tsx @@ -15,13 +15,13 @@ */ import { DomainEntity } from '@backstage/catalog-model'; -import { render } from '@testing-library/react'; +import { renderInTestApp } from '@backstage/test-utils'; import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; +import { catalogEntityRouteRef } from '../../routes'; import { DomainCard } from './DomainCard'; describe('', () => { - it('renders a domain card', () => { + it('renders a domain card', async () => { const entity: DomainEntity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Domain', @@ -34,9 +34,14 @@ describe('', () => { owner: 'guest', }, }; - const { getByText } = render(, { - wrapper: MemoryRouter, - }); + const { getByText } = await renderInTestApp( + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': catalogEntityRouteRef, + }, + }, + ); expect(getByText('artists')).toBeInTheDocument(); expect(getByText('Everything about artists')).toBeInTheDocument(); diff --git a/plugins/explore/src/components/DomainCard/DomainCard.tsx b/plugins/explore/src/components/DomainCard/DomainCard.tsx index e5efc543b2..ee06fd39fd 100644 --- a/plugins/explore/src/components/DomainCard/DomainCard.tsx +++ b/plugins/explore/src/components/DomainCard/DomainCard.tsx @@ -14,41 +14,61 @@ * limitations under the License. */ import { DomainEntity, RELATION_OWNED_BY } from '@backstage/catalog-model'; -import { ItemCard } from '@backstage/core'; +import { Button, ItemCardHeader, useRouteRef } from '@backstage/core'; import { EntityRefLinks, - entityRoute, entityRouteParams, getEntityRelations, } from '@backstage/plugin-catalog-react'; +import { + Box, + Card, + CardActions, + CardContent, + CardMedia, + Chip, +} from '@material-ui/core'; import React from 'react'; -import { generatePath } from 'react-router-dom'; +import { catalogEntityRouteRef } from '../../routes'; type DomainCardProps = { entity: DomainEntity; }; export const DomainCard = ({ entity }: DomainCardProps) => { - const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); + const catalogEntityRoute = useRouteRef(catalogEntityRouteRef); - return ( - - } - label="Explore" - // TODO: Use useRouteRef here to generate the path - href={generatePath( - `/catalog/${entityRoute.path}`, - entityRouteParams(entity), - )} + const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); + const url = catalogEntityRoute(entityRouteParams(entity)); + + const owner = ( + ); + + return ( + + + + + + {entity.metadata.tags?.length ? ( + + {entity.metadata.tags.map(tag => ( + + ))} + + ) : null} + {entity.metadata.description} + + + + + + ); }; diff --git a/plugins/explore/src/components/DomainCard/DomainCardGrid.test.tsx b/plugins/explore/src/components/DomainCard/DomainCardGrid.test.tsx deleted file mode 100644 index bad3ca581b..0000000000 --- a/plugins/explore/src/components/DomainCard/DomainCardGrid.test.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 { DomainEntity } from '@backstage/catalog-model'; -import { render } from '@testing-library/react'; -import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { DomainCardGrid } from './DomainCardGrid'; - -describe('', () => { - it('renders a grid of domain cards', () => { - const entities: DomainEntity[] = [ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Domain', - metadata: { - name: 'playback', - }, - spec: { - owner: 'guest', - }, - }, - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Domain', - metadata: { - name: 'artists', - }, - spec: { - owner: 'guest', - }, - }, - ]; - const { getByText } = render(, { - wrapper: MemoryRouter, - }); - - expect(getByText('artists')).toBeInTheDocument(); - expect(getByText('playback')).toBeInTheDocument(); - }); -}); diff --git a/plugins/explore/src/components/DomainCard/index.ts b/plugins/explore/src/components/DomainCard/index.ts index 6acf10800a..3511ff023d 100644 --- a/plugins/explore/src/components/DomainCard/index.ts +++ b/plugins/explore/src/components/DomainCard/index.ts @@ -14,4 +14,3 @@ * limitations under the License. */ export { DomainCard } from './DomainCard'; -export { DomainCardGrid } from './DomainCardGrid'; diff --git a/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.test.tsx b/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.test.tsx index f29ad2cd6a..cd3f2847c6 100644 --- a/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.test.tsx +++ b/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.test.tsx @@ -20,6 +20,7 @@ import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; +import { catalogEntityRouteRef } from '../../routes'; import { DomainExplorerContent } from './DomainExplorerContent'; describe('', () => { @@ -71,6 +72,11 @@ describe('', () => { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': catalogEntityRouteRef, + }, + }, ); await waitFor(() => { @@ -86,6 +92,11 @@ describe('', () => { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': catalogEntityRouteRef, + }, + }, ); await waitFor(() => @@ -101,6 +112,11 @@ describe('', () => { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': catalogEntityRouteRef, + }, + }, ); await waitFor(() => diff --git a/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.tsx b/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.tsx index 182adf70ea..a12811e486 100644 --- a/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.tsx +++ b/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.tsx @@ -18,6 +18,7 @@ import { Content, ContentHeader, EmptyState, + ItemCardGrid, Progress, SupportButton, useApi, @@ -27,47 +28,64 @@ import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { Button } from '@material-ui/core'; import React from 'react'; import { useAsync } from 'react-use'; -import { DomainCardGrid } from '../DomainCard'; +import { DomainCard } from '../DomainCard'; -export const DomainExplorerContent = () => { +const Body = () => { const catalogApi = useApi(catalogApiRef); const { value: entities, loading, error } = useAsync(async () => { const response = await catalogApi.getEntities({ filter: { kind: 'domain' }, }); - return response.items as DomainEntity[]; }, [catalogApi]); + if (loading) { + return ; + } + + if (error) { + return ( + + {error.message} + + ); + } + + if (!entities?.length) { + return ( + + Read more + + } + /> + ); + } + + return ( + + {entities.map((entity, index) => ( + + ))} + + ); +}; + +export const DomainExplorerContent = () => { return ( Discover the domains in your ecosystem. - - {loading && } - {error && ( - - {error.message} - - )} - {!loading && !error && (!entities || entities.length === 0) && ( - - Read more - - } - /> - )} - {!loading && entities && } + ); }; diff --git a/plugins/explore/src/plugin.ts b/plugins/explore/src/plugin.ts index 27573eaeb9..c5dd6644e7 100644 --- a/plugins/explore/src/plugin.ts +++ b/plugins/explore/src/plugin.ts @@ -16,7 +16,7 @@ import { createApiFactory, createPlugin } from '@backstage/core'; import { exploreToolsConfigRef } from '@backstage/plugin-explore-react'; -import { exploreRouteRef } from './routes'; +import { catalogEntityRouteRef, exploreRouteRef } from './routes'; import { exampleTools } from './util/examples'; export const explorePlugin = createPlugin({ @@ -37,4 +37,7 @@ export const explorePlugin = createPlugin({ routes: { explore: exploreRouteRef, }, + externalRoutes: { + catalogEntity: catalogEntityRouteRef, + }, }); diff --git a/plugins/explore/src/routes.ts b/plugins/explore/src/routes.ts index c41a53128f..fc7868c4c9 100644 --- a/plugins/explore/src/routes.ts +++ b/plugins/explore/src/routes.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createRouteRef } from '@backstage/core'; +import { createExternalRouteRef, createRouteRef } from '@backstage/core'; const NoIcon = () => null; @@ -22,3 +22,8 @@ export const exploreRouteRef = createRouteRef({ icon: NoIcon, title: 'Explore', }); + +export const catalogEntityRouteRef = createExternalRouteRef({ + id: 'catalog-entity', + params: ['namespace', 'kind', 'name'], +}); diff --git a/plugins/fossa/CHANGELOG.md b/plugins/fossa/CHANGELOG.md index 23129f83f9..001b926c6d 100644 --- a/plugins/fossa/CHANGELOG.md +++ b/plugins/fossa/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-fossa +## 0.2.4 + +### Patch Changes + +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.2.3 ### Patch Changes diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index 0d28f2285b..90dc8c52b4 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-fossa", - "version": "0.2.3", + "version": "0.2.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.6.3", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/catalog-model": "^0.7.3", + "@backstage/core": "^0.7.0", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -44,8 +44,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/gcp-projects/CHANGELOG.md b/plugins/gcp-projects/CHANGELOG.md index 92f1da463e..8445cbb02b 100644 --- a/plugins/gcp-projects/CHANGELOG.md +++ b/plugins/gcp-projects/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-gcp-projects +## 0.2.5 + +### Patch Changes + +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [4c049a1a1] + - @backstage/core@0.7.0 + ## 0.2.4 ### Patch Changes diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 10615878ed..b6fa8d7135 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gcp-projects", - "version": "0.2.4", + "version": "0.2.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.6.3", + "@backstage/core": "^0.7.0", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,8 +41,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index ac717c1059..4e027251b4 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-github-actions +## 0.3.5 + +### Patch Changes + +- b2a5320a4: Use Breadcrumbs from @backstage/core rather than material-ui +- 04667f571: Use Breadcrumbs from @backstage/core +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.3.4 ### Patch Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index e377432db6..4741fa2d4d 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.3.4", + "version": "0.3.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,9 +32,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.1.0", - "@backstage/core": "^0.6.3", + "@backstage/catalog-model": "^0.7.3", + "@backstage/plugin-catalog-react": "^0.1.1", + "@backstage/core": "^0.7.0", "@backstage/integration": "^0.5.0", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", @@ -50,8 +50,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/gitops-profiles/CHANGELOG.md b/plugins/gitops-profiles/CHANGELOG.md index bb0834a03d..b72e3c23c5 100644 --- a/plugins/gitops-profiles/CHANGELOG.md +++ b/plugins/gitops-profiles/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-gitops-profiles +## 0.2.6 + +### Patch Changes + +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [4c049a1a1] + - @backstage/core@0.7.0 + ## 0.2.5 ### Patch Changes diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index fa149aa659..6b51f338dc 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gitops-profiles", - "version": "0.2.5", + "version": "0.2.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.6.3", + "@backstage/core": "^0.7.0", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -42,8 +42,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md index d6f4864a2f..60b1865427 100644 --- a/plugins/graphiql/CHANGELOG.md +++ b/plugins/graphiql/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-graphiql +## 0.2.8 + +### Patch Changes + +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [4c049a1a1] + - @backstage/core@0.7.0 + ## 0.2.7 ### Patch Changes diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 493378bf5f..ee6a834c4f 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.2.7", + "version": "0.2.8", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.6.3", + "@backstage/core": "^0.7.0", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -43,8 +43,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index 8ba44c30f5..7f69ba133e 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-jenkins +## 0.3.12 + +### Patch Changes + +- 0452ba238: - Add Jenkins logo to project page. + - Move to new Backstage breadcrumb component. + - Change references of deprecated "Job" terminology to "Project" per [Jenkins Glossary](https://www.jenkins.io/doc/book/glossary/). +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.3.11 ### Patch Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 04c653d17a..8c9eff6322 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins", - "version": "0.3.11", + "version": "0.3.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.6.3", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/catalog-model": "^0.7.3", + "@backstage/core": "^0.7.0", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -47,8 +47,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/jenkins/src/assets/JenkinsLogo.svg b/plugins/jenkins/src/assets/JenkinsLogo.svg index 4a4538daf6..bd714dcf52 100644 --- a/plugins/jenkins/src/assets/JenkinsLogo.svg +++ b/plugins/jenkins/src/assets/JenkinsLogo.svg @@ -1,283 +1 @@ - - - -image/svg+xml \ No newline at end of file + \ No newline at end of file diff --git a/plugins/kafka-backend/CHANGELOG.md b/plugins/kafka-backend/CHANGELOG.md index 1e4701af16..be1b53f710 100644 --- a/plugins/kafka-backend/CHANGELOG.md +++ b/plugins/kafka-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kafka-backend +## 0.2.1 + +### Patch Changes + +- 4fbc9df79: Fixed `config.d.ts` not being included in the published package. +- f43192207: remove usage of res.send() for res.json() and res.end() to ensure content types are more consistently application/json on backend responses and error cases +- Updated dependencies [12d8f27a6] +- Updated dependencies [497859088] +- Updated dependencies [8adb48df4] + - @backstage/catalog-model@0.7.3 + - @backstage/backend-common@0.5.5 + ## 0.2.0 ### Minor Changes diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index 1280e30633..ce9e03a617 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kafka-backend", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.5.2", - "@backstage/catalog-model": "^0.7.1", + "@backstage/backend-common": "^0.5.5", + "@backstage/catalog-model": "^0.7.3", "@backstage/config": "^0.1.2", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -42,7 +42,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.6.0", + "@backstage/cli": "^0.6.3", "@types/jest-when": "^2.7.2", "@types/lodash": "^4.14.151", "jest-when": "^3.1.0", diff --git a/plugins/kafka/CHANGELOG.md b/plugins/kafka/CHANGELOG.md index a8153002c9..f555144b4e 100644 --- a/plugins/kafka/CHANGELOG.md +++ b/plugins/kafka/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-kafka +## 0.2.5 + +### Patch Changes + +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.2.4 ### Patch Changes diff --git a/plugins/kafka/README.md b/plugins/kafka/README.md index c41c328741..395b3240ef 100644 --- a/plugins/kafka/README.md +++ b/plugins/kafka/README.md @@ -12,7 +12,7 @@ yarn add @backstage/plugin-kafka @backstage/plugin-kafka-backend 2. Add the plugin backend: -In a new file named `kafka.js` under `backend/src/plugins`: +In a new file named `kafka.ts` under `backend/src/plugins`: ```js import { createRouter } from '@backstage/plugin-kafka-backend'; @@ -29,6 +29,9 @@ export default async function createPlugin({ And then add to `packages/backend/src/index.ts`: ```js +// ... +import kafka from './plugins/kafka'; +// ... async function main() { // ... const kafkaEnv = useHotMemoize(module, () => createEnv('kafka')); diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 3e72ac5fca..cda91a1b33 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kafka", - "version": "0.2.4", + "version": "0.2.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,9 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.6.3", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/catalog-model": "^0.7.3", + "@backstage/core": "^0.7.0", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -33,8 +33,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index e02c3e14a0..3e0d7e891b 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-backend +## 0.2.8 + +### Patch Changes + +- f43192207: remove usage of res.send() for res.json() and res.end() to ensure content types are more consistently application/json on backend responses and error cases +- e3adec2bd: Allow apps to pass in a KubernetesClustersSupplier +- Updated dependencies [12d8f27a6] +- Updated dependencies [497859088] +- Updated dependencies [8adb48df4] + - @backstage/catalog-model@0.7.3 + - @backstage/backend-common@0.5.5 + ## 0.2.7 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 9d5a0c25e0..2938d00f9d 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.2.7", + "version": "0.2.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,8 +32,8 @@ }, "dependencies": { "@aws-sdk/credential-provider-node": "^3.3.0", - "@backstage/backend-common": "^0.5.2", - "@backstage/catalog-model": "^0.7.2", + "@backstage/backend-common": "^0.5.5", + "@backstage/catalog-model": "^0.7.3", "@backstage/config": "^0.1.3", "@kubernetes/client-node": "^0.13.2", "@types/express": "^4.17.6", @@ -51,7 +51,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.2", + "@backstage/cli": "^0.6.3", "@types/aws4": "^1.5.1", "supertest": "^4.0.2" }, diff --git a/plugins/kubernetes-backend/src/service/router.ts b/plugins/kubernetes-backend/src/service/router.ts index 333f48a623..90bea82077 100644 --- a/plugins/kubernetes-backend/src/service/router.ts +++ b/plugins/kubernetes-backend/src/service/router.ts @@ -27,6 +27,7 @@ import { ServiceLocatorMethod, ClusterLocatorMethod, ClusterDetails, + KubernetesClustersSupplier, } from '..'; import { getCombinedClusterDetails } from '../cluster-locator'; import { KubernetesFanOutHandler } from './KubernetesFanOutHandler'; @@ -34,6 +35,7 @@ import { KubernetesFanOutHandler } from './KubernetesFanOutHandler'; export interface RouterOptions { logger: Logger; config: Config; + clusterSupplier?: KubernetesClustersSupplier; } const getServiceLocator = ( @@ -98,10 +100,16 @@ export async function createRouter( 'kubernetes.clusterLocatorMethods', ) as ClusterLocatorMethod[]; - const clusterDetails = await getCombinedClusterDetails( - clusterLocatorMethods, - options.config, - ); + let clusterDetails: ClusterDetails[]; + + if (options.clusterSupplier) { + clusterDetails = await options.clusterSupplier.getClusters(); + } else { + clusterDetails = await getCombinedClusterDetails( + clusterLocatorMethods, + options.config, + ); + } const serviceLocator = getServiceLocator(options.config, clusterDetails); diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 75df5943dd..99416f12cd 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-kubernetes +## 0.3.12 + +### Patch Changes + +- ba21797ca: k8s plugin now surfaces k8s components with only label selector query annotation. + Previously backstage.io/kubernetes-label-selector catalog entity annotation would only work if you also included backstage.io/kubernetes-id. + But backstage.io/kubernetes-id value was ignored +- 42c8ebb79: Support AWS auth provider on kubernetes FE plugin +- Updated dependencies [12d8f27a6] +- Updated dependencies [f43192207] +- Updated dependencies [e3adec2bd] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/plugin-kubernetes-backend@0.2.8 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.3.11 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 24bfc1c11f..47be4bcc84 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.3.11", + "version": "0.3.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,11 +31,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/catalog-model": "^0.7.3", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/config": "^0.1.3", - "@backstage/core": "^0.6.3", - "@backstage/plugin-kubernetes-backend": "^0.2.7", + "@backstage/core": "^0.7.0", + "@backstage/plugin-kubernetes-backend": "^0.2.8", "@backstage/theme": "^0.2.3", "@kubernetes/client-node": "^0.13.2", "@material-ui/core": "^4.11.0", @@ -48,8 +48,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/kubernetes/src/api/KubernetesBackendClient.ts b/plugins/kubernetes/src/api/KubernetesBackendClient.ts index dcb2cca142..353f5e8800 100644 --- a/plugins/kubernetes/src/api/KubernetesBackendClient.ts +++ b/plugins/kubernetes/src/api/KubernetesBackendClient.ts @@ -14,23 +14,27 @@ * limitations under the License. */ -import { DiscoveryApi, IdentityApi } from '@backstage/core'; +import { ConfigApi, DiscoveryApi, IdentityApi } from '@backstage/core'; import { KubernetesApi } from './types'; import { KubernetesRequestBody, ObjectsByEntityResponse, } from '@backstage/plugin-kubernetes-backend'; +import { Config } from '@backstage/config'; export class KubernetesBackendClient implements KubernetesApi { private readonly discoveryApi: DiscoveryApi; private readonly identityApi: IdentityApi; + private readonly configApi: ConfigApi; constructor(options: { discoveryApi: DiscoveryApi; identityApi: IdentityApi; + configApi: ConfigApi; }) { this.discoveryApi = options.discoveryApi; this.identityApi = options.identityApi; + this.configApi = options.configApi; } private async getRequired( @@ -73,4 +77,8 @@ export class KubernetesBackendClient implements KubernetesApi { requestBody, ); } + + getClusters(): Config[] { + return this.configApi.getConfigArray('kubernetes.clusters'); + } } diff --git a/plugins/kubernetes/src/api/types.ts b/plugins/kubernetes/src/api/types.ts index 498e74a626..d9ed79e720 100644 --- a/plugins/kubernetes/src/api/types.ts +++ b/plugins/kubernetes/src/api/types.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Config } from '@backstage/config'; import { createApiRef } from '@backstage/core'; import { KubernetesRequestBody, @@ -30,4 +31,5 @@ export interface KubernetesApi { getObjectsByEntity( requestBody: KubernetesRequestBody, ): Promise; + getClusters(): Config[]; } diff --git a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx index 18351c7455..ec08267579 100644 --- a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx +++ b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx @@ -25,7 +25,6 @@ import { } from '@material-ui/core'; import { Config } from '@backstage/config'; import { - configApiRef, Content, Page, Progress, @@ -169,8 +168,7 @@ export const KubernetesContent = ({ entity }: KubernetesContentProps) => { >(undefined); const [error, setError] = useState(undefined); - const configApi = useApi(configApiRef); - const clusters: Config[] = configApi.getConfigArray('kubernetes.clusters'); + const clusters: Config[] = kubernetesApi.getClusters(); const allAuthProviders: string[] = clusters.map(c => c.getString('authProvider'), ); diff --git a/plugins/kubernetes/src/plugin.ts b/plugins/kubernetes/src/plugin.ts index 24f51fe64a..6da1abb4b6 100644 --- a/plugins/kubernetes/src/plugin.ts +++ b/plugins/kubernetes/src/plugin.ts @@ -21,6 +21,7 @@ import { identityApiRef, googleAuthApiRef, createRoutableExtension, + configApiRef, } from '@backstage/core'; import { KubernetesBackendClient } from './api/KubernetesBackendClient'; import { kubernetesApiRef } from './api/types'; @@ -37,9 +38,13 @@ export const kubernetesPlugin = createPlugin({ apis: [ createApiFactory({ api: kubernetesApiRef, - deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef }, - factory: ({ discoveryApi, identityApi }) => - new KubernetesBackendClient({ discoveryApi, identityApi }), + deps: { + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, identityApi, configApi }) => + new KubernetesBackendClient({ discoveryApi, identityApi, configApi }), }), createApiFactory({ api: kubernetesAuthProvidersApiRef, diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index 20b1a5c1e1..a026aee2ca 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-lighthouse +## 0.2.13 + +### Patch Changes + +- dbea11072: Change setup wording of intro card to latest +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.2.12 ### Patch Changes diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index e3e1570b53..050a894b19 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-lighthouse", - "version": "0.2.12", + "version": "0.2.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", + "@backstage/catalog-model": "^0.7.3", "@backstage/config": "^0.1.3", - "@backstage/core": "^0.6.3", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/core": "^0.7.0", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -46,8 +46,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/lighthouse/src/components/Intro/index.tsx b/plugins/lighthouse/src/components/Intro/index.tsx index b140141c9f..1756f441a5 100644 --- a/plugins/lighthouse/src/components/Intro/index.tsx +++ b/plugins/lighthouse/src/components/Intro/index.tsx @@ -41,31 +41,37 @@ To get started, you will need a running instance of [lighthouse-audit-service](h _It's likely you will need to enable CORS when running lighthouse-audit-service. Initialize the app with the environment variable \`LAS_CORS\` set to \`true\`._ -When you have an instance running that Backstage can hook into, make sure to export the plugin in -your app's [\`plugins.ts\`](https://github.com/backstage/backstage/blob/master/packages/app/src/plugins.ts) -to enable the plugin: +When you have an instance running that Backstage can hook into, first install the plugin into your app: + +\`\`\`sh +$ yarn add @backstage/plugin-lighthouse +\`\`\` + +Then make sure to export the plugin in your app's [\`plugins.ts\`](https://github.com/backstage/backstage/blob/master/packages/app/src/plugins.ts) to enable the plugin: \`\`\`js export { plugin as LighthousePlugin } from '@backstage/plugin-lighthouse'; \`\`\` -Then, you need to use the \`lighthouseApiRef\` exported from the plugin to initialize the Rest API in -your [\`apis.ts\`](https://github.com/backstage/backstage/blob/master/packages/app/src/apis.ts). +Modify your app routes in \`App.tsx\` or \`App.jsx\` to include the Router component exported from the plugin, for example: \`\`\`js -import { ApiHolder, ApiRegistry } from '@backstage/core'; -import { - lighthouseApiRef, - LighthouseRestApi, -} from '@backstage/plugin-lighthouse'; +// At the top imports +import { LighthousePage } from '@backstage/plugin-lighthouse'; -const builder = ApiRegistry.builder(); +// Inside App component + + // ... + } /> + // ... +; +\`\`\` -export const lighthouseApi = - new LighthouseRestApi(/* your service url here! */); -builder.add(lighthouseApiRef, lighthouseApi); +Then configure the \`lighthouse-audit-service\` URL in your [\`app-config.yaml\`](https://github.com/backstage/backstage/blob/master/app-config.yaml). -export default builder.build() as ApiHolder; +\`\`\`yaml +lighthouse: + baseUrl: http://your-service-url \`\`\` `; diff --git a/plugins/newrelic/CHANGELOG.md b/plugins/newrelic/CHANGELOG.md index ab8ad1c289..f2745796d8 100644 --- a/plugins/newrelic/CHANGELOG.md +++ b/plugins/newrelic/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-newrelic +## 0.2.6 + +### Patch Changes + +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [4c049a1a1] + - @backstage/core@0.7.0 + ## 0.2.5 ### Patch Changes diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index e8a6786cd8..dba1531b02 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic", - "version": "0.2.5", + "version": "0.2.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.6.3", + "@backstage/core": "^0.7.0", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,8 +41,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 474d44a735..22cea18cdb 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-org +## 0.3.9 + +### Patch Changes + +- Updated dependencies [12d8f27a6] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] + - @backstage/catalog-model@0.7.3 + - @backstage/core-api@0.2.12 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + ## 0.3.8 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index c7cf35dcd7..fe94402bc5 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.3.8", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.6.3", - "@backstage/core-api": "^0.2.11", - "@backstage/plugin-catalog-react": "^0.1.0", + "@backstage/catalog-model": "^0.7.3", + "@backstage/core": "^0.7.0", + "@backstage/core-api": "^0.2.12", + "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -35,8 +35,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/pagerduty/CHANGELOG.md b/plugins/pagerduty/CHANGELOG.md index 85547d08c7..0cd9d0cfb8 100644 --- a/plugins/pagerduty/CHANGELOG.md +++ b/plugins/pagerduty/CHANGELOG.md @@ -1,5 +1,50 @@ # @backstage/plugin-pagerduty +## 0.3.2 + +### Patch Changes + +- 4c049a1a1: - Adds onClick and other props to IconLinkVertical; + + - Allows TriggerButton component to render when pager duty key is missing; + - Refactors TriggerButton and PagerDutyCard not to have shared state; + - Removes the `action` prop of the IconLinkVertical component while adding `onClick`. + + Instead of having an action including a button with onClick, now the whole component can be clickable making it easier to implement and having a better UX. + + Before: + + ```ts + const myLink: IconLinkVerticalProps = { + label: 'Click me', + action: + +
+ ))} + ); diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index d35b347e16..7c9e5f74fc 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-user-settings +## 0.2.7 + +### Patch Changes + +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [4c049a1a1] + - @backstage/core@0.7.0 + ## 0.2.6 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 547644c8bd..1435961141 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.2.6", + "version": "0.2.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.6.3", + "@backstage/core": "^0.7.0", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,8 +41,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/welcome/CHANGELOG.md b/plugins/welcome/CHANGELOG.md index 594d30226a..68b8c358c2 100644 --- a/plugins/welcome/CHANGELOG.md +++ b/plugins/welcome/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-welcome +## 0.2.6 + +### Patch Changes + +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [4c049a1a1] + - @backstage/core@0.7.0 + ## 0.2.5 ### Patch Changes diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 9ed7c219cf..ae0d066029 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-welcome", - "version": "0.2.5", + "version": "0.2.6", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -30,7 +30,7 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.6.3", + "@backstage/core": "^0.7.0", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,8 +41,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.2", - "@backstage/dev-utils": "^0.1.12", + "@backstage/cli": "^0.6.3", + "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.8", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/yarn.lock b/yarn.lock index cc49902ba9..b11c5eb0b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1662,10 +1662,10 @@ core-js "^2.6.5" regenerator-runtime "^0.13.4" -"@babel/runtime-corejs3@^7.10.2", "@babel/runtime-corejs3@^7.11.2": - version "7.12.5" - resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz#ffee91da0eb4c6dae080774e94ba606368e414f4" - integrity sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ== +"@babel/runtime-corejs3@^7.10.2", "@babel/runtime-corejs3@^7.11.2", "@babel/runtime-corejs3@^7.13.6": + version "7.13.9" + resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.9.tgz#b2fa9a6e5690ef8d4c4f2d30cac3ec1a8bb633ce" + integrity sha512-p6WSr71+5u/VBf1KDS/Y4dK3ZwbV+DD6wQO3X2EbUVluEOiyXUk09DzcwSaUH4WomYXrEPC+i2rqzuthhZhOJw== dependencies: core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" @@ -1816,7 +1816,7 @@ to-fast-properties "^2.0.0" "@backstage/catalog-model@^0.2.0": - version "0.7.2" + version "0.7.3" dependencies: "@backstage/config" "^0.1.3" "@types/json-schema" "^7.0.5" @@ -1828,7 +1828,7 @@ yup "^0.29.3" "@backstage/catalog-model@^0.3.0": - version "0.7.2" + version "0.7.3" dependencies: "@backstage/config" "^0.1.3" "@types/json-schema" "^7.0.5" @@ -1840,10 +1840,92 @@ yup "^0.29.3" "@backstage/core@^0.3.0": - version "0.6.3" + version "0.7.0" dependencies: "@backstage/config" "^0.1.3" - "@backstage/core-api" "^0.2.11" + "@backstage/core-api" "^0.2.12" + "@backstage/theme" "^0.2.3" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@testing-library/react-hooks" "^3.4.2" + "@types/dagre" "^0.7.44" + "@types/prop-types" "^15.7.3" + "@types/react" "^16.9" + "@types/react-sparklines" "^1.7.0" + "@types/react-text-truncate" "^0.14.0" + classnames "^2.2.6" + clsx "^1.1.0" + d3-selection "^2.0.0" + d3-shape "^2.0.0" + d3-zoom "^2.0.0" + dagre "^0.8.5" + immer "^8.0.1" + lodash "^4.17.15" + material-table "^1.69.1" + prop-types "^15.7.2" + qs "^6.9.4" + rc-progress "^3.0.0" + react "^16.12.0" + react-dom "^16.12.0" + react-helmet "6.1.0" + react-hook-form "^6.6.0" + react-markdown "^5.0.2" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-sparklines "^1.7.0" + react-syntax-highlighter "^13.5.1" + react-text-truncate "^0.16.0" + react-use "^15.3.3" + remark-gfm "^1.0.0" + zen-observable "^0.8.15" + +"@backstage/core@^0.6.0": + version "0.7.0" + dependencies: + "@backstage/config" "^0.1.3" + "@backstage/core-api" "^0.2.12" + "@backstage/theme" "^0.2.3" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@testing-library/react-hooks" "^3.4.2" + "@types/dagre" "^0.7.44" + "@types/prop-types" "^15.7.3" + "@types/react" "^16.9" + "@types/react-sparklines" "^1.7.0" + "@types/react-text-truncate" "^0.14.0" + classnames "^2.2.6" + clsx "^1.1.0" + d3-selection "^2.0.0" + d3-shape "^2.0.0" + d3-zoom "^2.0.0" + dagre "^0.8.5" + immer "^8.0.1" + lodash "^4.17.15" + material-table "^1.69.1" + prop-types "^15.7.2" + qs "^6.9.4" + rc-progress "^3.0.0" + react "^16.12.0" + react-dom "^16.12.0" + react-helmet "6.1.0" + react-hook-form "^6.6.0" + react-markdown "^5.0.2" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-sparklines "^1.7.0" + react-syntax-highlighter "^13.5.1" + react-text-truncate "^0.16.0" + react-use "^15.3.3" + remark-gfm "^1.0.0" + zen-observable "^0.8.15" + +"@backstage/core@^0.6.1": + version "0.7.0" + dependencies: + "@backstage/config" "^0.1.3" + "@backstage/core-api" "^0.2.12" "@backstage/theme" "^0.2.3" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" @@ -1881,12 +1963,12 @@ zen-observable "^0.8.15" "@backstage/plugin-catalog@^0.2.1": - version "0.4.0" + version "0.4.1" dependencies: "@backstage/catalog-client" "^0.3.6" - "@backstage/catalog-model" "^0.7.2" - "@backstage/core" "^0.6.3" - "@backstage/plugin-catalog-react" "^0.1.0" + "@backstage/catalog-model" "^0.7.3" + "@backstage/core" "^0.7.0" + "@backstage/plugin-catalog-react" "^0.1.1" "@backstage/theme" "^0.2.3" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" @@ -1903,12 +1985,12 @@ swr "^0.3.0" "@backstage/plugin-catalog@^0.3.1": - version "0.4.0" + version "0.4.1" dependencies: "@backstage/catalog-client" "^0.3.6" - "@backstage/catalog-model" "^0.7.2" - "@backstage/core" "^0.6.3" - "@backstage/plugin-catalog-react" "^0.1.0" + "@backstage/catalog-model" "^0.7.3" + "@backstage/core" "^0.7.0" + "@backstage/plugin-catalog-react" "^0.1.1" "@backstage/theme" "^0.2.3" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" @@ -2336,6 +2418,23 @@ query-string "^6.13.3" xcase "^2.0.1" +"@google-cloud/common@^0.32.0": + version "0.32.1" + resolved "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz#6a32c340172cea3db6674d0e0e34e78740a0073f" + integrity sha512-bLdPzFvvBMtVkwsoBtygE9oUm3yrNmPa71gvOgucYI/GqvNP2tb6RYsDHPq98kvignhcgHGDI5wyNgxaCo8bKQ== + dependencies: + "@google-cloud/projectify" "^0.3.3" + "@google-cloud/promisify" "^0.4.0" + "@types/request" "^2.48.1" + arrify "^2.0.0" + duplexify "^3.6.0" + ent "^2.2.0" + extend "^3.0.2" + google-auth-library "^3.1.1" + pify "^4.0.1" + retry-request "^4.0.0" + teeny-request "^3.11.3" + "@google-cloud/common@^3.5.0": version "3.5.0" resolved "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz#0959e769e8075a06eb0823cc567eef00fd0c2d02" @@ -2351,6 +2450,16 @@ retry-request "^4.1.1" teeny-request "^7.0.0" +"@google-cloud/paginator@^0.2.0": + version "0.2.0" + resolved "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-0.2.0.tgz#eab2e6aa4b81df7418f6c51e2071f64dab2c2fa5" + integrity sha512-2ZSARojHDhkLvQ+CS32K+iUhBsWg3AEw+uxtqblA7xoCABDyhpj99FPp35xy6A+XlzMhOSrHHaxFE+t6ZTQq0w== + dependencies: + arrify "^1.0.1" + extend "^3.0.1" + split-array-stream "^2.0.0" + stream-events "^1.0.4" + "@google-cloud/paginator@^3.0.0": version "3.0.5" resolved "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.5.tgz#9d6b96c421a89bd560c1bc2c197c7611ef21db6c" @@ -2359,16 +2468,53 @@ arrify "^2.0.0" extend "^3.0.2" +"@google-cloud/projectify@^0.3.3": + version "0.3.3" + resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz#bde9103d50b20a3ea3337df8c6783a766e70d41d" + integrity sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw== + "@google-cloud/projectify@^2.0.0": version "2.0.1" resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz#13350ee609346435c795bbfe133a08dfeab78d65" integrity sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ== +"@google-cloud/promisify@^0.4.0": + version "0.4.0" + resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz#4fbfcf4d85bb6a2e4ccf05aa63d2b10d6c9aad9b" + integrity sha512-4yAHDC52TEMCNcMzVC8WlqnKKKq+Ssi2lXoUg9zWWkZ6U6tq9ZBRYLHHCRdfU+EU9YJsVmivwGcKYCjRGjnf4Q== + "@google-cloud/promisify@^2.0.0": version "2.0.3" resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz#f934b5cdc939e3c7039ff62b9caaf59a9d89e3a8" integrity sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw== +"@google-cloud/storage@^2.4.3": + version "2.5.0" + resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-2.5.0.tgz#9dd3566d8155cf5ba0c212208f69f9ecd47fbd7e" + integrity sha512-q1mwB6RUebIahbA3eriRs8DbG2Ij81Ynb9k8hMqTPkmbd8/S6Z0d6hVvfPmnyvX9Ej13IcmEYIbymuq/RBLghA== + dependencies: + "@google-cloud/common" "^0.32.0" + "@google-cloud/paginator" "^0.2.0" + "@google-cloud/promisify" "^0.4.0" + arrify "^1.0.0" + async "^2.0.1" + compressible "^2.0.12" + concat-stream "^2.0.0" + date-and-time "^0.6.3" + duplexify "^3.5.0" + extend "^3.0.0" + gcs-resumable-upload "^1.0.0" + hash-stream-validation "^0.2.1" + mime "^2.2.0" + mime-types "^2.0.8" + onetime "^5.1.0" + pumpify "^1.5.1" + snakeize "^0.1.0" + stream-events "^1.0.1" + teeny-request "^3.11.3" + through2 "^3.0.0" + xdg-basedir "^3.0.0" + "@google-cloud/storage@^5.6.0": version "5.6.0" resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.6.0.tgz#bc6925c7970c375212a4da21c123298fc9665dec" @@ -2824,39 +2970,11 @@ is-promise "4.0.0" tslib "~2.0.1" -"@hapi/address@^4.0.1": - version "4.1.0" - resolved "https://registry.npmjs.org/@hapi/address/-/address-4.1.0.tgz#d60c5c0d930e77456fdcde2598e77302e2955e1d" - integrity sha512-SkszZf13HVgGmChdHo/PxchnSaCJ6cetVqLzyciudzZRT0jcOouIF/Q93mgjw8cce+D+4F4C1Z/WrfFN+O3VHQ== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@hapi/formula@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@hapi/formula/-/formula-2.0.0.tgz#edade0619ed58c8e4f164f233cda70211e787128" - integrity sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A== - "@hapi/hoek@^9.0.0": version "9.0.4" resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz#e80ad4e8e8d2adc6c77d985f698447e8628b6010" integrity sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw== -"@hapi/joi@^17.1.1": - version "17.1.1" - resolved "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.1.tgz#9cc8d7e2c2213d1e46708c6260184b447c661350" - integrity sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg== - dependencies: - "@hapi/address" "^4.0.1" - "@hapi/formula" "^2.0.0" - "@hapi/hoek" "^9.0.0" - "@hapi/pinpoint" "^2.0.0" - "@hapi/topo" "^5.0.0" - -"@hapi/pinpoint@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.0.tgz#805b40d4dbec04fc116a73089494e00f073de8df" - integrity sha512-vzXR5MY7n4XeIvLpfl3HtE3coZYO4raKXW766R6DZw/6aLqR26iuZ109K7a0NtF2Db0jxqh7xz2AxkUwpUFybw== - "@hapi/topo@^5.0.0": version "5.0.0" resolved "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz#c19af8577fa393a06e9c77b60995af959be721e7" @@ -4106,12 +4224,12 @@ universal-user-agent "^5.0.0" "@octokit/graphql@^4.5.8": - version "4.5.8" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.8.tgz#d42373633c3015d0eafce64a8ce196be167fdd9b" - integrity sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA== + version "4.6.0" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.0.tgz#f9abca55f82183964a33439d5264674c701c3327" + integrity sha512-CJ6n7izLFXLvPZaWzCQDjU/RP+vHiZmWdOunaCS87v+2jxMsW9FB5ktfIxybRBxZjxuJGRnxk7xJecWTVxFUYQ== dependencies: "@octokit/request" "^5.3.0" - "@octokit/types" "^6.0.0" + "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" "@octokit/openapi-types@^2.2.0": @@ -4393,25 +4511,24 @@ react-router "6.0.0-beta.0" react-use "^15.3.6" -"@roadiehq/backstage-plugin-travis-ci@^0.2.8": - version "0.2.8" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.2.8.tgz#c730531519bf3e3dba35522d5e6c822176ddb9c8" - integrity sha512-8R8waHoviT5chHGybAMVVRHHmeytn+8Po7sU08WCYIt4Vkw2/gwu36fd90y+lW4x6T0s3ZiaJrsQ3KlA7LmRnQ== +"@roadiehq/backstage-plugin-travis-ci@^0.4.5": + version "0.4.5" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.4.5.tgz#c80eb972951badd110173f5521aae65b8a5cfc53" + integrity sha512-+y5kFtVaPjXgf6naGVU2E3C33nvoXuaBbfYEmCeByg2t0b5jHMHzsTv2tV5/8zeAcus/5+yIkRCq+F1Tcbwutw== dependencies: - "@backstage/catalog-model" "^0.2.0" - "@backstage/core" "^0.3.0" - "@backstage/core-api" "^0.2.1" - "@backstage/plugin-catalog" "^0.2.1" + "@backstage/catalog-model" "^0.7.0" + "@backstage/core" "^0.6.0" + "@backstage/plugin-catalog" "^0.3.1" "@backstage/theme" "^0.2.1" "@material-ui/core" "^4.9.1" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" - date-fns "^2.15.0" + cross-fetch "^3.0.6" + date-fns "^2.16.1" history "^5.0.0" - moment "^2.27.0" + moment "^2.29.1" react "^16.13.1" react-dom "^16.13.1" - react-lazylog "^4.5.2" react-router "6.0.0-beta.0" react-router-dom "6.0.0-beta.0" react-use "^15.3.3" @@ -4481,6 +4598,23 @@ dependencies: any-observable "^0.3.0" +"@sideway/address@^4.1.0": + version "4.1.1" + resolved "https://registry.npmjs.org/@sideway/address/-/address-4.1.1.tgz#9e321e74310963fdf8eebfbee09c7bd69972de4d" + integrity sha512-+I5aaQr3m0OAmMr7RQ3fR9zx55sejEYR2BFJaxL+zT3VM2611X0SHvPWIbAUBZVTn/YzYKbV8gJ2oT/QELknfQ== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -4643,7 +4777,7 @@ global "^4.3.2" regenerator-runtime "^0.13.7" -"@storybook/addons@6.1.20", "@storybook/addons@^6.1.11": +"@storybook/addons@6.1.20": version "6.1.20" resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.1.20.tgz#da01dabd6692919b719fcb30519d53ea80887097" integrity sha512-kIhXYgF+ARNpYxO3qhz8yThDvKpaq+HDst8odPU9sCNEI66PSH6hrILhTmnffNnqdtY3LnKkU9rGVfZn+3TOTA== @@ -4658,6 +4792,21 @@ global "^4.3.2" regenerator-runtime "^0.13.7" +"@storybook/addons@^6.1.11": + version "6.1.21" + resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.1.21.tgz#94bb66fc51d1dfee80d0fe84f5b83c10045651b5" + integrity sha512-xo5TGu9EZVCqgh3D1veVnfuGzyKDWWsvOMo18phVqRxj21G3/+hScVyfIYwNTv7Ys5/Ahp9JxJUMXL3V3ny+tw== + dependencies: + "@storybook/api" "6.1.21" + "@storybook/channels" "6.1.21" + "@storybook/client-logger" "6.1.21" + "@storybook/core-events" "6.1.21" + "@storybook/router" "6.1.21" + "@storybook/theming" "6.1.21" + core-js "^3.0.1" + global "^4.3.2" + regenerator-runtime "^0.13.7" + "@storybook/api@6.1.11": version "6.1.11" resolved "https://registry.npmjs.org/@storybook/api/-/api-6.1.11.tgz#1e0b798203df823ac21184386258cf8b5f17f440" @@ -4758,6 +4907,31 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/api@6.1.21": + version "6.1.21" + resolved "https://registry.npmjs.org/@storybook/api/-/api-6.1.21.tgz#be753ca8d3602efe4a11783c81c689463bee0825" + integrity sha512-QjZk70VSXMw/wPPoWdMp5Bl9VmkfmGhIz8PALrFLLEZHjzptpfZE2qkGEEJHG0NAksFUv6NxGki2/632dzR7Ug== + dependencies: + "@reach/router" "^1.3.3" + "@storybook/channels" "6.1.21" + "@storybook/client-logger" "6.1.21" + "@storybook/core-events" "6.1.21" + "@storybook/csf" "0.0.1" + "@storybook/router" "6.1.21" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.1.21" + "@types/reach__router" "^1.3.7" + core-js "^3.0.1" + fast-deep-equal "^3.1.1" + global "^4.3.2" + lodash "^4.17.15" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + store2 "^2.7.1" + telejson "^5.0.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/channel-postmessage@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.1.15.tgz#80ea2346d18496f9710dd7f87fd2a9eca46ef36f" @@ -4820,6 +4994,15 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/channels@6.1.21": + version "6.1.21" + resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.1.21.tgz#adbfae5f4767234c5b17d9578be983584dddead4" + integrity sha512-7WoizMjyHqCyvcWncLexSg9FLPIErWAZL4NvluEthwsHSO2sDybn9mh1pzsFHdYMuTP6ml06Zt9ayWMtIveHDg== + dependencies: + core-js "^3.0.1" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/client-api@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.1.15.tgz#8f8ead111459b94621571bdb2276f8a0aace17b1" @@ -4900,6 +5083,14 @@ core-js "^3.0.1" global "^4.3.2" +"@storybook/client-logger@6.1.21": + version "6.1.21" + resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.1.21.tgz#fe7d9e645ddb4eb9dc18fdacea24b4baf11bc6c9" + integrity sha512-QJV+gnVM2fQ4M7lSkRLCXkOw/RU+aEtUefo9TAnXxPHK3UGG+DyvLmha6fHGaz9GAcFxyWtgqCyVOhMe03Q35g== + dependencies: + core-js "^3.0.1" + global "^4.3.2" + "@storybook/components@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/components/-/components-6.1.15.tgz#b4a2af23ee6b9cba4c255191eae3d3463e29bfb7" @@ -5007,6 +5198,13 @@ dependencies: core-js "^3.0.1" +"@storybook/core-events@6.1.21": + version "6.1.21" + resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.1.21.tgz#11f537f78f8c73ba5e627b57b282a279793a3511" + integrity sha512-KWqnh1C7M1pT//WfQb3AD60yTR8jL48AfaeLGto2gO9VK7VVgj/EGsrXZP/GTL90ygyExbbBI5gkr7EBTu/HYw== + dependencies: + core-js "^3.0.1" + "@storybook/core@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/core/-/core-6.1.15.tgz#7ff8c314d3857497bf2e26c69a1fa93ef37301aa" @@ -5207,6 +5405,18 @@ memoizerific "^1.11.3" qs "^6.6.0" +"@storybook/router@6.1.21": + version "6.1.21" + resolved "https://registry.npmjs.org/@storybook/router/-/router-6.1.21.tgz#0a822fa9cc67589a082f7a10fff15c8413f17706" + integrity sha512-m75WvUhoCBWDVekICAdbkidji/w5hCjHo+M8L13UghpwXWEnyr4/QqvkOb/PcSC8aZzxeMqSCpRQ1o6LWULneg== + dependencies: + "@reach/router" "^1.3.3" + "@types/reach__router" "^1.3.7" + core-js "^3.0.1" + global "^4.3.2" + memoizerific "^1.11.3" + qs "^6.6.0" + "@storybook/semver@^7.3.2": version "7.3.2" resolved "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz#f3b9c44a1c9a0b933c04e66d0048fcf2fa10dac0" @@ -5304,6 +5514,24 @@ resolve-from "^5.0.0" ts-dedent "^2.0.0" +"@storybook/theming@6.1.21": + version "6.1.21" + resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.1.21.tgz#b8e612e5a39b77f7e63a5f9ea322ed62adb0d5b0" + integrity sha512-yq7+/mpdljRdSRJYw/In/9tnDGXIUDe//mhyMftFfrB2mq6zi1yAZpowCerWhiDE2ipGkrfzIYx/Sn7bcaXgqg== + dependencies: + "@emotion/core" "^10.1.1" + "@emotion/is-prop-valid" "^0.8.6" + "@emotion/styled" "^10.0.23" + "@storybook/client-logger" "6.1.21" + core-js "^3.0.1" + deep-object-diff "^1.1.0" + emotion-theming "^10.0.19" + global "^4.3.2" + memoizerific "^1.11.3" + polished "^3.4.4" + resolve-from "^5.0.0" + ts-dedent "^2.0.0" + "@storybook/ui@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/ui/-/ui-6.1.15.tgz#a0f6c49fcf81cf172cd2de4c8dba2be1296891f6" @@ -5838,10 +6066,10 @@ resolved "https://registry.npmjs.org/@types/dagre/-/dagre-0.7.44.tgz#8f4b796b118ca29c132da7068fbc0d0351ee5851" integrity sha512-N6HD+79w77ZVAaVO7JJDW5yJ9LAxM62FpgNGO9xEde+KVYjDRyhIMzfiErXpr1g0JPon9kwlBzoBK6s4fOww9Q== -"@types/diff@^4.0.2": - version "4.0.2" - resolved "https://registry.npmjs.org/@types/diff/-/diff-4.0.2.tgz#2e9bb89f9acc3ab0108f0f3dc4dbdcf2fff8a99c" - integrity sha512-mIenTfsIe586/yzsyfql69KRnA75S8SVXQbTLpDejRrjH0QSJcpu3AUOi/Vjnt9IOsXKxPhJfGpQUNMueIU1fQ== +"@types/diff@^5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@types/diff/-/diff-5.0.0.tgz#eb71e94feae62548282c4889308a3dfb57e36020" + integrity sha512-jrm2K65CokCCX4NmowtA+MfXyuprZC13jbRuwprs6/04z/EcFg/MCwYdsHn+zgV4CQBiATiI7AEq7y1sZCtWKA== "@types/dockerode@^3.2.1": version "3.2.1" @@ -6398,6 +6626,13 @@ dependencies: "@types/express" "*" +"@types/pkgcloud@^1.7.4": + version "1.7.4" + resolved "https://registry.npmjs.org/@types/pkgcloud/-/pkgcloud-1.7.4.tgz#ccce313cd584623787581d2d4384f6f911c06b64" + integrity sha512-syikaJpSx59aAxGd0i/FX5VgFLlO8OSxZLUVhwOhJqx+CRsdgaWB9TqIm5kSqJxLoyIird63QLt96Px63rHnOw== + dependencies: + "@types/node" "*" + "@types/pluralize@^0.0.29": version "0.0.29" resolved "https://registry.npmjs.org/@types/pluralize/-/pluralize-0.0.29.tgz#6ffa33ed1fc8813c469b859681d09707eb40d03c" @@ -6578,7 +6813,7 @@ resolved "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.28.tgz#6bda7db8653fa62643f5ee69e9f69c11a392e3a6" integrity sha1-a9p9uGU/piZD9e5p6facEaOS46Y= -"@types/request@^2.47.1": +"@types/request@^2.47.1", "@types/request@^2.48.1": version "2.48.5" resolved "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz#019b8536b402069f6d11bee1b2c03e7f232937a0" integrity sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ== @@ -6797,9 +7032,9 @@ integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ== "@types/webpack-dev-server@*", "@types/webpack-dev-server@^3.11.0": - version "3.11.0" - resolved "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#bcc3b85e7dc6ac2db25330610513f2228c2fcfb2" - integrity sha512-3+86AgSzl18n5P1iUP9/lz3G3GMztCp+wxdDvVuNhx1sr1jE79GpYfKHL8k+Vht3N74K2n98CuAEw4YPJCYtDA== + version "3.11.1" + resolved "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz#f8f4dac1da226d530bd15a1d5dc34b23ba766ccb" + integrity sha512-rIb+LtUkKnh7+oIJm3WiMJONd71Q0lZuqGLcSqhZ5qjN9gV/CNmZe7Bai+brnBPZ/KVYOsr+4bFLiNZwjBicLw== dependencies: "@types/connect-history-api-fallback" "*" "@types/express" "*" @@ -7202,6 +7437,13 @@ abbrev@1: resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-2.0.3.tgz#b174827a732efadff81227ed4b8d1cc569baf20a" + integrity sha512-EPSq5wr2aFyAZ1PejJB32IX9Qd4Nwus+adnp7STYFM5/23nLPBazqZ1oor6ZqbH+4otaaGXTlC8RN5hq3C8w9Q== + dependencies: + event-target-shim "^5.0.0" + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -7290,6 +7532,13 @@ agent-base@6: dependencies: debug "4" +agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + agentkeepalive@^4.1.3: version "4.1.4" resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" @@ -7869,16 +8118,7 @@ array.prototype.flat@^1.2.1, array.prototype.flat@^1.2.3: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -array.prototype.flatmap@^1.2.1, array.prototype.flatmap@^1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.3.tgz#1c13f84a178566042dd63de4414440db9222e443" - integrity sha512-OOEk+lkePcg+ODXIpvuU9PAryCikCJyo7GlDG1upleEpQRx6mzL9puEBkozQ5iAx20KV0l3DbyQwqciJtqe5Pg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" - -array.prototype.flatmap@^1.2.4: +array.prototype.flatmap@^1.2.1, array.prototype.flatmap@^1.2.3, array.prototype.flatmap@^1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" integrity sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q== @@ -7898,7 +8138,7 @@ array.prototype.map@^1.0.1: es-array-method-boxes-properly "^1.0.0" is-string "^1.0.4" -arrify@^1.0.1: +arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= @@ -8001,7 +8241,7 @@ async@0.9.x: resolved "https://registry.npmjs.org/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= -async@^2.6.1, async@^2.6.2: +async@^2.0.1, async@^2.6.1, async@^2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== @@ -8053,6 +8293,21 @@ autoprefixer@^9.7.2: postcss "^7.0.32" postcss-value-parser "^4.1.0" +aws-sdk@^2.382.0: + version "2.849.0" + resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.849.0.tgz#7fc9863e04f9e97c1f81f204fca84dfb2e4175dd" + integrity sha512-CzpK+0TcycRUzYKOdL5AnFcRmLDty+POWR+ZTeqCIY/rho3mM6jBTSseFreZTFiNxj8lIBBrMTsZ45KZ7Xm2Dg== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.3.2" + xml2js "0.4.19" + aws-sdk@^2.840.0: version "2.840.0" resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.840.0.tgz#f5529c9bd3bf0be7f8e855a23ff9c12b1705418f" @@ -8083,13 +8338,6 @@ aws4@^1.8.0: resolved "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== -axios@^0.19.2: - version "0.19.2" - resolved "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" - integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== - dependencies: - follow-redirects "1.5.10" - axios@^0.20.0: version "0.20.0" resolved "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd" @@ -8532,7 +8780,7 @@ base64-arraybuffer@^0.2.0: resolved "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45" integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ== -base64-js@^1.0.2, base64-js@^1.2.0, base64-js@^1.3.0, base64-js@^1.3.1: +base64-js@^1.0.2, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -9290,7 +9538,7 @@ chainsaw@~0.1.0: dependencies: traverse ">=0.3.0 <0.4" -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -9729,10 +9977,10 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== +color-string@^1.5.2, color-string@^1.5.4: + version "1.5.4" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6" + integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -9746,12 +9994,12 @@ color@3.0.x: color-string "^1.5.2" color@^3.0.0, color@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" - integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== + version "3.1.3" + resolved "https://registry.npmjs.org/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e" + integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ== dependencies: color-convert "^1.9.1" - color-string "^1.5.2" + color-string "^1.5.4" colorette@1.2.1, colorette@^1.2.1: version "1.2.1" @@ -9806,6 +10054,16 @@ command-exists@^1.2.9: resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== +commander@0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" + integrity sha1-+mihT2qUXVTbvlDYzbMyDp47GgY= + +commander@2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" + integrity sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM= + commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@~2.20.3: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -9963,6 +10221,18 @@ config-chain@^1.1.12: ini "^1.3.4" proto-list "~1.2.1" +configstore@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/configstore/-/configstore-4.0.0.tgz#5933311e95d3687efb592c528b922d9262d227e7" + integrity sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ== + dependencies: + dot-prop "^4.1.0" + graceful-fs "^4.1.2" + make-dir "^1.0.0" + unique-string "^1.0.0" + write-file-atomic "^2.0.0" + xdg-basedir "^3.0.0" + configstore@^5.0.0, configstore@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -10400,6 +10670,11 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +crypto-random-string@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" + integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= + crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -10964,15 +11239,20 @@ date-and-time@^0.14.0: resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-0.14.1.tgz#969634697b78956fb66b8be6fb0f39fbd631f2f6" integrity sha512-M4RggEH5OF2ZuCOxgOU67R6Z9ohjKbxGvAQz48vj53wLmL0bAgumkBvycR32f30pK+Og9pIR+RFDyChbaE4oLA== +date-and-time@^0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-0.6.3.tgz#2daee52df67c28bd93bce862756ac86b68cf4237" + integrity sha512-lcWy3AXDRJOD7MplwZMmNSRM//kZtJaLz4n6D1P5z9wEmZGBKhJRBIr1Xs9KNQJmdXPblvgffynYji4iylUTcA== + date-fns@^1.27.2: version "1.30.1" resolved "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== -date-fns@^2.0.0-alpha.27, date-fns@^2.0.1, date-fns@^2.15.0: - version "2.15.0" - resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.15.0.tgz#424de6b3778e4e69d3ff27046ec136af58ae5d5f" - integrity sha512-ZCPzAMJZn3rNUvvQIMlXhDr4A+Ar07eLeGsGREoWU19a3Pqf5oYa+ccd+B3F6XVtQY6HANMFdOQ8A+ipFnvJdQ== +date-fns@^2.0.0-alpha.27, date-fns@^2.0.1, date-fns@^2.16.1: + version "2.18.0" + resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.18.0.tgz#08e50aee300ad0d2c5e054e3f0d10d8f9cdfe09e" + integrity sha512-NYyAg4wRmGVU4miKq5ivRACOODdZRY3q5WLmOJSq8djyzftYphU7dTHLcEtLqEvfqMKQ0jVv91P4BAwIjsXIcw== dateformat@^3.0.0: version "3.0.3" @@ -10994,6 +11274,13 @@ debounce@^1.2.0: resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg== +debug@2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + integrity sha1-+HBX6ZWxofauaklgZkE3vFbwOdo= + dependencies: + ms "0.7.1" + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -11008,20 +11295,6 @@ debug@4, debug@4.3.1, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, de dependencies: ms "2.1.2" -debug@4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - -debug@=3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -11349,6 +11622,11 @@ diff3@0.0.3: resolved "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz#d4e5c3a4cdf4e5fe1211ab42e693fcb4321580fc" integrity sha1-1OXDpM305f4SEatC5pP8tDIVgPw= +diff@1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" + integrity sha1-fyjS657nsVqX79ic5j3P2qPMur8= + diff@^4.0.1, diff@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -11549,10 +11827,10 @@ domhandler@^4.0.0: dependencies: domelementtype "^2.1.0" -dompurify@^2.0.12, dompurify@^2.1.1, dompurify@^2.2.3: - version "2.2.3" - resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.2.3.tgz#ec653ba521b39f397c2ca045769438d593ea8a9f" - integrity sha512-8Hv7Q0FuwD9rWoB6qI2eZsfKbGXfoUVuGHHrE15vgk4ReOKwOkSgbqb2OMFtc0d5besOEkoLkcyuV10zQ2X5gw== +dompurify@^2.0.12, dompurify@^2.1.1, dompurify@^2.2.6: + version "2.2.6" + resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.2.6.tgz#54945dc5c0b45ce5ae228705777e8e59d7b2edc4" + integrity sha512-7b7ZArhhH0SP6W2R9cqK6RjaU82FZ2UPM7RO8qN1b1wyvC/NY1FNWcX1Pu00fFOAnzEORtwXe4bPaClg6pUybQ== domutils@1.5.1: version "1.5.1" @@ -11596,6 +11874,13 @@ dot-case@^3.0.3: no-case "^3.0.3" tslib "^1.10.0" +dot-prop@^4.1.0: + version "4.2.1" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4" + integrity sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ== + dependencies: + is-obj "^1.0.0" + dot-prop@^5.1.0: version "5.3.0" resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -11685,7 +11970,7 @@ duplexer@^0.1.1, duplexer@~0.1.1: resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= -duplexify@^3.4.2, duplexify@^3.6.0: +duplexify@^3.4.2, duplexify@^3.5.0, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== @@ -11918,6 +12203,11 @@ error-stack-parser@^2.0.6: dependencies: stackframe "^1.1.1" +errs@^0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/errs/-/errs-0.3.2.tgz#798099b2dbd37ca2bc749e538a7c1307d0b50499" + integrity sha1-eYCZstvTfKK8dJ5TinwTB9C1BJk= + es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4: version "1.17.4" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" @@ -11997,7 +12287,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: version "0.10.53" resolved "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== @@ -12020,6 +12310,18 @@ es6-iterator@^2.0.3, es6-iterator@~2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + es6-shim@^0.35.5: version "0.35.5" resolved "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.5.tgz#46f59dc0a84a1c5029e8ff1166ca0a902077a9ab" @@ -12033,7 +12335,7 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" -es6-weak-map@^2.0.2: +es6-weak-map@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== @@ -12068,6 +12370,11 @@ escape-html@^1.0.3, escape-html@~1.0.3: resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= +escape-string-regexp@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" + integrity sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE= + escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" @@ -12215,9 +12522,9 @@ eslint-plugin-react-hooks@^4.0.0: integrity sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ== eslint-plugin-react@^7.12.4: - version "7.21.5" - resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz#50b21a412b9574bfe05b21db176e8b7b3b15bff3" - integrity sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g== + version "7.22.0" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.22.0.tgz#3d1c542d1d3169c45421c1215d9470e341707269" + integrity sha512-p30tuX3VS+NWv9nQot9xIGAHBXR0+xJVaZriEsHoJrASGCJZDJ8JLNM0YqKqI0AKm6Uxaa1VUHoNEibxRCMQHA== dependencies: array-includes "^3.1.1" array.prototype.flatmap "^1.2.3" @@ -12408,6 +12715,11 @@ event-target-shim@^5.0.0: resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +eventemitter2@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452" + integrity sha1-YZegldX7a1folC9v1+qtY6CclFI= + eventemitter2@^6.4.2: version "6.4.3" resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.3.tgz#35c563619b13f3681e7eb05cbdaf50f56ba58820" @@ -12682,7 +12994,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@3.0.2, extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: +extend@3.0.2, extend@^3.0.0, extend@^3.0.1, extend@^3.0.2, extend@~3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -12745,7 +13057,7 @@ extsprintf@^1.2.0: resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@2.0.1: +fast-deep-equal@2.0.1, fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= @@ -12784,6 +13096,13 @@ fast-json-parse@^1.0.3: resolved "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== +fast-json-patch@^2.1.0: + version "2.2.1" + resolved "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz#18150d36c9ab65c7209e7d4eb113f4f8eaabe6d9" + integrity sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-patch@^3.0.0-1: version "3.0.0-1" resolved "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.0.0-1.tgz#4c68f2e7acfbab6d29d1719c44be51899c93dabb" @@ -12884,10 +13203,10 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -fecha@^2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd" - integrity sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg== +fecha@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" + integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== fetch-readablestream@^0.2.0: version "0.2.0" @@ -12958,6 +13277,13 @@ file-uri-to-path@1.0.0: resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +filed-mimefix@^0.1.3: + version "0.1.3" + resolved "https://registry.npmjs.org/filed-mimefix/-/filed-mimefix-0.1.3.tgz#0b0b67d075a63fc74f26fdf39c7f9d4314967bb5" + integrity sha1-Cwtn0HWmP8dPJv3znH+dQxSWe7U= + dependencies: + mime "^1.4.0" + filefy@0.1.10: version "0.1.10" resolved "https://registry.npmjs.org/filefy/-/filefy-0.1.10.tgz#174677c8e2fa5bc39a3af0ed6fb492f16b8fbf42" @@ -13134,13 +13460,6 @@ fn-name@~3.0.0: resolved "https://registry.npmjs.org/fn-name/-/fn-name-3.0.0.tgz#0596707f635929634d791f452309ab41558e3c5c" integrity sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA== -follow-redirects@1.5.10: - version "1.5.10" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" - integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== - dependencies: - debug "=3.1.0" - follow-redirects@^1.0.0, follow-redirects@^1.10.0: version "1.13.0" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" @@ -13428,6 +13747,16 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +gaxios@^1.0.2, gaxios@^1.0.4, gaxios@^1.2.1, gaxios@^1.5.0: + version "1.8.4" + resolved "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz#e08c34fe93c0a9b67a52b7b9e7a64e6435f9a339" + integrity sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw== + dependencies: + abort-controller "^3.0.0" + extend "^3.0.2" + https-proxy-agent "^2.2.1" + node-fetch "^2.3.0" + gaxios@^3.0.0: version "3.2.0" resolved "https://registry.npmjs.org/gaxios/-/gaxios-3.2.0.tgz#11b6f0e8fb08d94a10d4d58b044ad3bec6dd486a" @@ -13450,6 +13779,14 @@ gaxios@^4.0.0: is-stream "^2.0.0" node-fetch "^2.3.0" +gcp-metadata@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz#5212440229fa099fc2f7c2a5cdcb95575e9b2ca6" + integrity sha512-Q6HrgfrCQeEircnNP3rCcEgiDv7eF9+1B+1MMgpE190+/+0mjQR8PxeOaRgxZWmdDAF9EIryHB9g1moPiw1SbQ== + dependencies: + gaxios "^1.0.2" + json-bigint "^0.3.0" + gcp-metadata@^4.2.0: version "4.2.1" resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz#31849fbcf9025ef34c2297c32a89a1e7e9f2cd62" @@ -13458,6 +13795,18 @@ gcp-metadata@^4.2.0: gaxios "^4.0.0" json-bigint "^1.0.0" +gcs-resumable-upload@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-1.1.0.tgz#2b06f5876dcf60f18a309343f79ed951aff01399" + integrity sha512-uBz7uHqp44xjSDzG3kLbOYZDjxxR/UAGbB47A0cC907W6yd2LkcyFDTHg+bjivkHMwiJlKv4guVWcjPCk2zScg== + dependencies: + abort-controller "^2.0.2" + configstore "^4.0.0" + gaxios "^1.5.0" + google-auth-library "^3.0.0" + pumpify "^1.5.1" + stream-events "^1.0.4" + gcs-resumable-upload@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.1.tgz#67c766a0555d6a352f9651b7603337207167d0de" @@ -13697,6 +14046,14 @@ glob-to-regexp@^0.3.0: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= +glob@3.2.11: + version "3.2.11" + resolved "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + integrity sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0= + dependencies: + inherits "2" + minimatch "0.3" + glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -13885,6 +14242,21 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" +google-auth-library@^3.0.0, google-auth-library@^3.1.1: + version "3.1.2" + resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz#ff2f88cd5cd2118a57bd3d5ad3c093c8837fc350" + integrity sha512-cDQMzTotwyWMrg5jRO7q0A4TL/3GWBgO7I7q5xGKNiiFf9SmGY/OJ1YsLMgI2MVHHsEGyrqYnbnmV1AE+Z6DnQ== + dependencies: + base64-js "^1.3.0" + fast-text-encoding "^1.0.0" + gaxios "^1.2.1" + gcp-metadata "^1.0.0" + gtoken "^2.3.2" + https-proxy-agent "^2.2.1" + jws "^3.1.5" + lru-cache "^5.0.0" + semver "^5.5.0" + google-auth-library@^6.0.0, google-auth-library@^6.1.1: version "6.1.3" resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.3.tgz#39d868140b70d0c4b32c6f6d8f4ccc1400d84dca" @@ -13900,6 +14272,14 @@ google-auth-library@^6.0.0, google-auth-library@^6.1.1: jws "^4.0.0" lru-cache "^6.0.0" +google-p12-pem@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz#b77fb833a2eb9f7f3c689e2e54f095276f777605" + integrity sha512-SwLAUJqUfTB2iS+wFfSS/G9p7bt4eWcc2LyfvmUXe7cWp6p3mpxDo6LLI29MXdU6wvPcQ/up298X7GMC5ylAlA== + dependencies: + node-forge "^0.8.0" + pify "^4.0.0" + google-p12-pem@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz#673ac3a75d3903a87f05878f3c75e06fc151669e" @@ -14118,11 +14498,27 @@ graphql@15.5.0, graphql@^15.3.0: resolved "https://registry.npmjs.org/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" integrity sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA== +growl@1.9.2: + version "1.9.2" + resolved "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= +gtoken@^2.3.2: + version "2.3.3" + resolved "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz#8a7fe155c5ce0c4b71c886cfb282a9060d94a641" + integrity sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw== + dependencies: + gaxios "^1.0.4" + google-p12-pem "^1.0.0" + jws "^3.1.5" + mime "^2.2.0" + pify "^4.0.0" + gtoken@^5.0.4: version "5.1.0" resolved "https://registry.npmjs.org/gtoken/-/gtoken-5.1.0.tgz#4ba8d2fc9a8459098f76e7e8fd7beaa39fda9fe4" @@ -14276,7 +14672,7 @@ hash-base@^3.0.0: inherits "^2.0.1" safe-buffer "^5.0.1" -hash-stream-validation@^0.2.2: +hash-stream-validation@^0.2.1, hash-stream-validation@^0.2.2: version "0.2.4" resolved "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz#ee68b41bf822f7f44db1142ec28ba9ee7ccb7512" integrity sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ== @@ -14641,6 +15037,14 @@ https-browserify@^1.0.0: resolved "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +https-proxy-agent@^2.2.1: + version "2.2.4" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + https-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" @@ -14735,7 +15139,7 @@ ieee754@1.1.13: resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: +ieee754@^1.1.4, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -15004,7 +15408,7 @@ interpret@^2.0.0, interpret@^2.2.0: resolved "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== -invariant@^2.0.0, invariant@^2.2.3, invariant@^2.2.4: +invariant@^2.0.0, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -15346,7 +15750,7 @@ is-number@^7.0.0: resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-obj@^1.0.1: +is-obj@^1.0.0, is-obj@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= @@ -15431,7 +15835,7 @@ is-promise@4.0.0: resolved "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== -is-promise@^2.1, is-promise@^2.1.0: +is-promise@^2.1.0, is-promise@^2.2.2: version "2.2.2" resolved "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== @@ -15484,6 +15888,11 @@ is-ssh@^1.3.0: dependencies: protocols "^1.1.0" +is-stream-ended@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" + integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -15704,6 +16113,14 @@ iterate-value@^1.0.0: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" +jade@0.26.3: + version "0.26.3" + resolved "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" + integrity sha1-jxDXl32NefL2/4YqgbBRPMslaGw= + dependencies: + commander "0.6.1" + mkdirp "0.3.0" + jake@^10.6.1: version "10.8.2" resolved "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b" @@ -16189,6 +16606,17 @@ jmespath@0.15.0: resolved "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= +joi@^17.3.0: + version "17.4.0" + resolved "https://registry.npmjs.org/joi/-/joi-17.4.0.tgz#b5c2277c8519e016316e49ababd41a1908d9ef20" + integrity sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.0" + "@sideway/formula" "^3.0.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" @@ -16213,7 +16641,7 @@ js-cookie@^2.2.1: resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== -js-file-download@^0.4.1: +js-file-download@^0.4.12: version "0.4.12" resolved "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.12.tgz#10c70ef362559a5b23cdbdc3bd6f399c3d91d821" integrity sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg== @@ -16327,6 +16755,13 @@ jsesc@~0.5.0: resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= +json-bigint@^0.3.0: + version "0.3.1" + resolved "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.1.tgz#0c1729d679f580d550899d6a2226c228564afe60" + integrity sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ== + dependencies: + bignumber.js "^9.0.0" + json-bigint@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" @@ -16658,7 +17093,7 @@ jwa@^2.0.0: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jws@^3.2.2: +jws@^3.1.5, jws@^3.2.2: version "3.2.2" resolved "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== @@ -16928,6 +17363,14 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.0" +liboneandone@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/liboneandone/-/liboneandone-1.2.0.tgz#8d2c91c72a6323a96aed8410de05e06d6584b602" + integrity sha512-EB6Ak9qw+U4HAOnKqPtatxQ9pLclvtsBsggrvOuD4zclJ5xOeEASojsLKEC3O8KJ1Q4obE2JHhOeDuqWXvkoUQ== + dependencies: + mocha "^2.5.3" + request "^2.74.0" + liftoff@3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" @@ -17110,7 +17553,7 @@ loader-utils@1.2.3: emojis-list "^2.0.0" json5 "^1.0.1" -loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: +loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== @@ -17303,10 +17746,10 @@ lodash@4.17.15: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.0.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.20, lodash@~4.17.4: - version "4.17.20" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lodash@^4.0.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.20, lodash@~4.17.4: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^1.0.2: version "1.0.2" @@ -17349,13 +17792,13 @@ log-update@^4.0.0: wrap-ansi "^6.2.0" logform@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz#957155ebeb67a13164069825ce67ddb5bb2dd360" - integrity sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ== + version "2.2.0" + resolved "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2" + integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg== dependencies: colors "^1.2.1" fast-safe-stringify "^2.0.4" - fecha "^2.3.3" + fecha "^4.2.0" ms "^2.1.1" triple-beam "^1.3.0" @@ -17429,6 +17872,11 @@ lowlight@^1.17.0: fault "^1.0.0" highlight.js "~10.4.0" +lru-cache@2: + version "2.7.3" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI= + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -17451,7 +17899,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-queue@0.1: +lru-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= @@ -17485,6 +17933,13 @@ magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.4" +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -17742,19 +18197,19 @@ memoize-one@^5.1.1: resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA== -memoizee@^0.4.12: - version "0.4.14" - resolved "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57" - integrity sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg== +memoizee@^0.4.15: + version "0.4.15" + resolved "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" + integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== dependencies: - d "1" - es5-ext "^0.10.45" - es6-weak-map "^2.0.2" + d "^1.0.1" + es5-ext "^0.10.53" + es6-weak-map "^2.0.3" event-emitter "^0.3.5" - is-promise "^2.1" - lru-queue "0.1" - next-tick "1" - timers-ext "^0.1.5" + is-promise "^2.2.2" + lru-queue "^0.1.0" + next-tick "^1.1.0" + timers-ext "^0.1.7" memoizerific@^1.11.3: version "1.11.3" @@ -17976,7 +18431,7 @@ mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.17, m dependencies: mime-db "1.44.0" -mime@1.6.0, mime@^1.4.1: +mime@1.6.0, mime@^1.4.0, mime@^1.4.1: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -17991,6 +18446,11 @@ mime@^2.3.1, mime@^2.4.4: resolved "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== +mime@^2.4.1: + version "2.5.2" + resolved "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" + integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -18048,6 +18508,14 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= +minimatch@0.3: + version "0.3.0" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + integrity sha1-J12O2qxPG7MyZHIInnlJyDlGmd0= + dependencies: + lru-cache "2" + sigmund "~1.0.0" + "minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -18064,6 +18532,11 @@ minimist-options@4.1.0, minimist-options@^4.0.2: is-plain-obj "^1.1.0" kind-of "^6.0.3" +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: version "1.2.5" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -18223,6 +18696,18 @@ mkdirp-infer-owner@^2.0.0: infer-owner "^1.0.4" mkdirp "^1.0.3" +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -18235,6 +18720,22 @@ mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: dependencies: minimist "^1.2.5" +mocha@^2.5.3: + version "2.5.3" + resolved "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" + integrity sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg= + dependencies: + commander "2.3.0" + debug "2.2.0" + diff "1.4.0" + escape-string-regexp "1.0.2" + glob "3.2.11" + growl "1.9.2" + jade "0.26.3" + mkdirp "0.5.1" + supports-color "1.2.0" + to-iso-string "0.0.2" + mock-fs@^4.13.0: version "4.13.0" resolved "https://registry.npmjs.org/mock-fs/-/mock-fs-4.13.0.tgz#31c02263673ec3789f90eb7b6963676aa407a598" @@ -18245,7 +18746,7 @@ modify-values@^1.0.0: resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment@^2.19.3, moment@^2.25.3, moment@^2.27.0: +moment@^2.19.3, moment@^2.25.3, moment@^2.27.0, moment@^2.29.1: version "2.29.1" resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== @@ -18273,6 +18774,11 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" +ms@0.7.1: + version "0.7.1" + resolved "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg= + ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -18487,7 +18993,7 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: resolved "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== -next-tick@1: +next-tick@1, next-tick@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== @@ -18554,6 +19060,11 @@ node-forge@^0.10.0: resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== +node-forge@^0.8.0: + version "0.8.5" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz#57906f07614dc72762c84cef442f427c0e1b86ee" + integrity sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q== + node-gyp-build@~3.7.0: version "3.7.0" resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d" @@ -20081,7 +20592,7 @@ pify@^3.0.0: resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= -pify@^4.0.1: +pify@^4.0.0, pify@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== @@ -20145,6 +20656,28 @@ pkg-up@3.1.0, pkg-up@^3.1.0: dependencies: find-up "^3.0.0" +pkgcloud@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/pkgcloud/-/pkgcloud-2.2.0.tgz#2b4e644b6da53c76c0a30bcc0c3861971011a714" + integrity sha512-ZbbGqJA8gMwR0peq57aNbjzgLbDj52oi59QJEShZmGUl3ckFBZ92j0h/C2L0tJeCb2VE12tnTwmftBgQ0f3gNw== + dependencies: + "@google-cloud/storage" "^2.4.3" + async "^2.6.1" + aws-sdk "^2.382.0" + errs "^0.3.2" + eventemitter2 "^5.0.1" + fast-json-patch "^2.1.0" + filed-mimefix "^0.1.3" + ip "^1.1.5" + liboneandone "^1.2.0" + lodash "^4.17.10" + mime "^2.4.1" + qs "^6.5.2" + request "^2.88.0" + through2 "^3.0.1" + url-join "^4.0.0" + xml2js "^0.4.19" + pkginfo@0.2.x: version "0.2.3" resolved "https://registry.npmjs.org/pkginfo/-/pkginfo-0.2.3.tgz#7239c42a5ef6c30b8f328439d9b9ff71042490f8" @@ -20957,7 +21490,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@^1.3.3: +pumpify@^1.3.3, pumpify@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== @@ -21007,7 +21540,7 @@ qs@6.7.0: resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== -qs@^6.5.1, qs@^6.6.0, qs@^6.7.0, qs@^6.9.1, qs@^6.9.4: +qs@^6.5.1, qs@^6.5.2, qs@^6.6.0, qs@^6.7.0, qs@^6.9.1, qs@^6.9.4: version "6.9.6" resolved "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== @@ -21130,9 +21663,9 @@ raw-loader@^4.0.1: schema-utils "^2.6.5" rc-progress@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/rc-progress/-/rc-progress-3.1.1.tgz#f7ba5eb33cc438862513ab5a71739feaf6b3ef4f" - integrity sha512-1ns3pW7ll9bHfdXtlVLF+vngdvlxiCDtiqwXnZFEdurst11JTiPxVdeqnCNbhWx5hP4kCKkAPqG1N0FVfTSUGA== + version "3.1.3" + resolved "https://registry.npmjs.org/rc-progress/-/rc-progress-3.1.3.tgz#d77d8fd26d9d948d72c2a28b64b71a6e86df2426" + integrity sha512-Jl4fzbBExHYMoC6HBPzel0a9VmhcSXx24LVt/mdhDM90MuzoMCJjXZAlhA0V0CJi+SKjMhfBoIQ6Lla1nD4QNw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" @@ -21184,18 +21717,18 @@ react-color@^2.17.0: reactcss "^1.2.0" tinycolor2 "^1.4.1" -react-copy-to-clipboard@5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz#8eae107bb400be73132ed3b6a7b4fb156090208e" - integrity sha512-ELKq31/E3zjFs5rDWNCfFL4NvNFQvGRoJdAKReD/rUPA+xxiLPQmZBZBvy2vgH7V0GE9isIQpT9WXbwIVErYdA== +react-copy-to-clipboard@5.0.3: + version "5.0.3" + resolved "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.3.tgz#2a0623b1115a1d8c84144e9434d3342b5af41ab4" + integrity sha512-9S3j+m+UxDZOM0Qb8mhnT/rMR0NGSrj9A/073yz2DSxPMYhmYFBMYIdI2X4o8AjOjyFsSNxDRnCX6s/gRxpriw== dependencies: copy-to-clipboard "^3" prop-types "^15.5.8" -react-debounce-input@^3.2.0: - version "3.2.2" - resolved "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.2.2.tgz#d2cc99c1ce47fae89037965f5699edc1b0317197" - integrity sha512-RIBu68Cq/gImKz/2h1cE042REDqyqj3D+7SJ3lnnIpJX0ht9D9PfH7KAnL+SgDz6hvKa9pZS2CnAxlkrLmnQlg== +react-debounce-input@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.2.3.tgz#9e8c69771a621c81e8fe36b45ade49a95059cd87" + integrity sha512-7Bfjm9sxrtgB+IsSrdXoo4CVqKg7CbWC68dNhr8q7ZmY6C0AqtR524//SenHQWT+eeSG9DmSLWNWCUFSyaaWSQ== dependencies: lodash.debounce "^4" prop-types "^15.7.2" @@ -21319,10 +21852,10 @@ react-helmet@6.1.0: react-fast-compare "^3.1.1" react-side-effect "^2.1.0" -react-hook-form@^6.6.0: - version "6.6.0" - resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-6.6.0.tgz#bda76fc50a916096afe18119f6f57f2b3911cdb7" - integrity sha512-5UKx2SBMaWuwy614H730Vv2QmoGdsdYpN96MnWXYzC8cHIUi9N2hYwtI86TEIcBHJ6FvEewh3onfA7P243RM1g== +react-hook-form@^6.15.4, react-hook-form@^6.6.0: + version "6.15.4" + resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-6.15.4.tgz#328003e1ccc096cd158899ffe7e3b33735a9b024" + integrity sha512-K+Sw33DtTMengs8OdqFJI3glzNl1wBzSefD/ksQw/hJf9CnOHQAU6qy82eOrh0IRNt2G53sjr7qnnw1JDjvx1w== react-hot-loader@^4.12.21: version "4.12.21" @@ -21345,10 +21878,12 @@ react-hotkeys@2.0.0: dependencies: prop-types "^15.6.1" -react-immutable-proptypes@2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz#023d6f39bb15c97c071e9e60d00d136eac5fa0b4" - integrity sha1-Aj1vObsVyXwHHp5g0A0TbqxfoLQ= +react-immutable-proptypes@2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.2.0.tgz#cce96d68cc3c18e89617cbf3092d08e35126af4a" + integrity sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ== + dependencies: + invariant "^2.2.2" react-immutable-pure-component@^1.1.1: version "1.2.3" @@ -21835,7 +22370,7 @@ read@1, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -22244,7 +22779,7 @@ request-promise-native@^1.0.5, request-promise-native@^1.0.8: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.85.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: +request@^2.74.0, request@^2.85.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -22410,7 +22945,7 @@ ret@~0.1.10: resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -retry-request@^4.1.1: +retry-request@^4.0.0, retry-request@^4.1.1: version "4.1.3" resolved "https://registry.npmjs.org/retry-request/-/retry-request-4.1.3.tgz#d5f74daf261372cff58d08b0a1979b4d7cab0fde" integrity sha512-QnRZUpuPNgX0+D1xVxul6DbJ9slvo4Rm6iV/dn63e048MvGbUZiKySVt6Tenp04JqmchxjiLltGerOJys7kJYQ== @@ -22602,6 +23137,13 @@ rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.2, rxjs@^6.5.3, rxjs@^6.5.5, rxjs@^6.6.0: dependencies: tslib "^1.9.0" +rxjs@^6.6.3: + version "6.6.6" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" + integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -23001,6 +23543,11 @@ side-channel@^1.0.2: es-abstract "^1.17.0-next.1" object-inspect "^1.7.0" +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= + signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -23328,6 +23875,13 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" +split-array-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/split-array-stream/-/split-array-stream-2.0.0.tgz#85a4f8bfe14421d7bca7f33a6d176d0c076a53b1" + integrity sha512-hmMswlVY91WvGMxs0k8MRgq8zb2mSen4FmDNc5AFiTWtrBpdZN6nwD6kROVe4vNL+ywrvbCKsWVCnEd4riELIg== + dependencies: + is-stream-ended "^0.1.4" + split-ca@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" @@ -23492,17 +24046,17 @@ stacktrace-js@^2.0.0: stacktrace-gps "^3.0.4" start-server-and-test@^1.10.11: - version "1.11.2" - resolved "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.11.2.tgz#9144b7b6f25197148f159f261ae80119afbb17d5" - integrity sha512-rk1zS5WQvdbc8slE5hPtzfji1dFSnBAfm+vSjToZNrBvozHJvuAG80xE5u8N4tQjg3Ej1Crjc19J++r28HGJgg== + version "1.12.0" + resolved "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.12.0.tgz#e836553c928a13026f79c740757d378b92bee8d6" + integrity sha512-y3M/PLUPkPBsgKoengMIMQeceT8uOnOc4bkdor/RSCK9Ih/j8z4WthSCrAboXLjgtJJWOporAiEQsnYox+THXg== dependencies: bluebird "3.7.2" check-more-types "2.24.0" - debug "4.1.1" + debug "4.3.1" execa "3.4.0" lazy-ass "1.6.0" ps-tree "1.2.0" - wait-on "5.1.0" + wait-on "5.2.1" start-server-webpack-plugin@^2.2.5: version "2.2.5" @@ -23938,6 +24492,11 @@ supertest@^4.0.2: methods "^1.1.2" superagent "^3.8.3" +supports-color@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" + integrity sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4= + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -24003,10 +24562,10 @@ svgo@^1.0.0, svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -swagger-client@^3.12.1: - version "3.12.1" - resolved "https://registry.npmjs.org/swagger-client/-/swagger-client-3.12.1.tgz#bbe4521c0f662e72ad84fa9dbe3eb42c9960d901" - integrity sha512-jxhf7yvMq1SIlaMBlL0S+2mk5eovdYNzVAn1Ewrpe1pkby2pm5PnnP8XiSImCuX7d+BxUfVFkOgCTeK38+fBlA== +swagger-client@^3.13.1: + version "3.13.1" + resolved "https://registry.npmjs.org/swagger-client/-/swagger-client-3.13.1.tgz#d2531ede4a498911aa084384e387e5008e7b6a70" + integrity sha512-Hmy4+wVVa3kveWzC7PIeUwiAY5qcYbm4XlC4uZ7e5kAePfB2cprXImiqrZHIzL+ndU0YTN7I+9w/ZayTisn3Jg== dependencies: "@babel/runtime-corejs3" "^7.11.2" btoa "^1.2.1" @@ -24024,30 +24583,30 @@ swagger-client@^3.12.1: url "~0.11.0" swagger-ui-react@^3.37.2: - version "3.38.0" - resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-3.38.0.tgz#eebe7f03ba18e2b12cf921319da868729aadd3df" - integrity sha512-jkTRzKEWhilsHexZw1t9JvrDAInaGK9o1rF9EOXYqlb6wXeFVn5kK11eP6RhTRbYW6gM+unY2y10KV3dNbV5Mw== + version "3.44.0" + resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-3.44.0.tgz#4b553d7af46b45efa3bf96a53a05c58ed3b6e34e" + integrity sha512-+OMrm/D/V51XUPdh97RCUFCnomH4wJzR50NdE2ho6ZM0M+EsNaYzPmNLoeEhBupNHbL20NO6buSiVA99B4DjRg== dependencies: - "@babel/runtime-corejs3" "^7.11.2" + "@babel/runtime-corejs3" "^7.13.6" "@braintree/sanitize-url" "^5.0.0" "@kyleshockey/object-assign-deep" "^0.4.2" "@kyleshockey/xml" "^1.0.2" - base64-js "^1.2.0" + base64-js "^1.5.1" classnames "^2.2.6" css.escape "1.5.1" deep-extend "0.6.0" - dompurify "^2.2.3" - ieee754 "^1.1.13" + dompurify "^2.2.6" + ieee754 "^1.2.1" immutable "^3.x.x" - js-file-download "^0.4.1" + js-file-download "^0.4.12" js-yaml "^3.13.1" - lodash "^4.17.19" - memoizee "^0.4.12" + lodash "^4.17.21" + memoizee "^0.4.15" prop-types "^15.7.2" randombytes "^2.1.0" - react-copy-to-clipboard "5.0.1" - react-debounce-input "^3.2.0" - react-immutable-proptypes "2.1.0" + react-copy-to-clipboard "5.0.3" + react-debounce-input "^3.2.3" + react-immutable-proptypes "2.2.0" react-immutable-pure-component "^1.1.1" react-inspector "^2.3.0" react-motion "^0.5.2" @@ -24059,8 +24618,8 @@ swagger-ui-react@^3.37.2: reselect "^4.0.0" serialize-error "^2.1.0" sha.js "^2.4.11" - swagger-client "^3.12.1" - url-parse "^1.4.7" + swagger-client "^3.13.1" + url-parse "^1.5.1" xml-but-prettier "^1.0.1" zenscroll "^4.0.2" @@ -24191,6 +24750,15 @@ tarn@^3.0.1: resolved "https://registry.npmjs.org/tarn/-/tarn-3.0.1.tgz#ebac2c6dbc6977d34d4526e0a7814200386a8aec" integrity sha512-6usSlV9KyHsspvwu2duKH+FMUhqJnAh6J5J/4MITl8s94iSUQTLkJggdiewKv4RyARQccnigV48Z+khiuVZDJw== +teeny-request@^3.11.3: + version "3.11.3" + resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz#335c629f7645e5d6599362df2f3230c4cbc23a55" + integrity sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw== + dependencies: + https-proxy-agent "^2.2.1" + node-fetch "^2.2.0" + uuid "^3.3.2" + teeny-request@^7.0.0: version "7.0.1" resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz#bdd41fdffea5f8fbc0d29392cb47bec4f66b2b4c" @@ -24357,6 +24925,14 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" +through2@^3.0.0, through2@^3.0.1: + version "3.0.2" + resolved "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" + integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== + dependencies: + inherits "^2.0.4" + readable-stream "2 || 3" + through2@^4.0.0: version "4.0.2" resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" @@ -24391,7 +24967,7 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" -timers-ext@^0.1.5: +timers-ext@^0.1.7: version "0.1.7" resolved "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== @@ -24472,6 +25048,11 @@ to-fast-properties@^2.0.0: resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +to-iso-string@0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" + integrity sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE= + to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" @@ -24666,16 +25247,16 @@ ts-jest@^26.4.3: semver "7.x" yargs-parser "20.x" -ts-loader@^7.0.4: - version "7.0.4" - resolved "https://registry.npmjs.org/ts-loader/-/ts-loader-7.0.4.tgz#5d9b95227de5afb91fdd9668f8920eb193cfe0cc" - integrity sha512-5du6OQHl+4ZjO4crEyoYUyWSrmmo7bAO+inkaILZ68mvahqrfoa4nn0DRmpQ4ruT4l+cuJCgF0xD7SBIyLeeow== +ts-loader@^8.0.17: + version "8.0.17" + resolved "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.17.tgz#98f2ccff9130074f4079fd89b946b4c637b1f2fc" + integrity sha512-OeVfSshx6ot/TCxRwpBHQ/4lRzfgyTkvi7ghDVrLXOHzTbSK413ROgu/xNqM72i3AFeAIJgQy78FwSMKmOW68w== dependencies: - chalk "^2.3.0" + chalk "^4.1.0" enhanced-resolve "^4.0.0" - loader-utils "^1.0.2" + loader-utils "^2.0.0" micromatch "^4.0.0" - semver "^6.0.0" + semver "^7.3.4" ts-log@^2.2.3: version "2.2.3" @@ -25032,6 +25613,13 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unique-string@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" + integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo= + dependencies: + crypto-random-string "^1.0.0" + unique-string@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -25202,6 +25790,11 @@ urix@^0.1.0: resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= +url-join@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== + url-loader@^4.0.0, url-loader@^4.1.0: version "4.1.1" resolved "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" @@ -25218,10 +25811,10 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-parse@^1.4.3, url-parse@^1.4.7: - version "1.4.7" - resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== +url-parse@^1.4.3, url-parse@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b" + integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" @@ -25505,16 +26098,16 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" -wait-on@5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/wait-on/-/wait-on-5.1.0.tgz#b697f21c6fea0908b9c7ad6ed56ace4736768b66" - integrity sha512-JM0kgaE+V0nCDvSl72iM05W8NDt2E2M56WC5mzR7M+T+k6xjt2yYpyom+xA8RasSunFGzbxIpAXbVzXqtweAnA== +wait-on@5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/wait-on/-/wait-on-5.2.1.tgz#05b66fcb4d7f5da01537f03e7cf96e8836422996" + integrity sha512-H2F986kNWMU9hKlI9l/ppO6tN8ZSJd35yBljMLa1/vjzWP++Qh6aXyt77/u7ySJFZQqBtQxnvm/xgG48AObXcw== dependencies: - "@hapi/joi" "^17.1.1" - axios "^0.19.2" - lodash "^4.17.19" + axios "^0.21.1" + joi "^17.3.0" + lodash "^4.17.20" minimist "^1.2.5" - rxjs "^6.5.5" + rxjs "^6.6.3" walker@^1.0.7, walker@~1.0.5: version "1.0.7" @@ -25955,7 +26548,7 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^2.4.2: +write-file-atomic@^2.0.0, write-file-atomic@^2.4.2: version "2.4.3" resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== @@ -26038,6 +26631,11 @@ xcase@^2.0.1: resolved "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9" integrity sha1-x/pyyqD0QNt4/VZzQyA4rJhEULk= +xdg-basedir@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" + integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= + xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"