diff --git a/packages/app/package.json b/packages/app/package.json index 341953a204..e5dbbaa5ff 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -19,6 +19,7 @@ "@backstage/plugin-explore": "^0.3.2", "@backstage/plugin-gcp-projects": "^0.2.5", "@backstage/plugin-github-actions": "^0.4.2", + "@backstage/plugin-github-deployments": "^0.1.1", "@backstage/plugin-gitops-profiles": "^0.2.6", "@backstage/plugin-graphiql": "^0.2.9", "@backstage/plugin-jenkins": "^0.4.1", diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index 13b0daf7e3..d7ce9025ba 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -45,3 +45,4 @@ export { plugin as Org } from '@backstage/plugin-org'; export { plugin as Kafka } from '@backstage/plugin-kafka'; export { todoPlugin } from '@backstage/plugin-todo'; export { badgesPlugin } from '@backstage/plugin-badges'; +export { plugin as githubDeploymentsPlugin } from '@backstage/plugin-github-deployments'; diff --git a/plugins/github-deployments/.eslintrc.js b/plugins/github-deployments/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/github-deployments/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/github-deployments/README.md b/plugins/github-deployments/README.md new file mode 100644 index 0000000000..2c4a4cb49a --- /dev/null +++ b/plugins/github-deployments/README.md @@ -0,0 +1,75 @@ +# Github Deployments Plugin + +The Github Deployments Plugin displays recent deployments from Github. + +![github-deployments-card](./docs/github-deployments-card.png) + +## Getting Started + +1. Install the Github Deployments Plugin + +```bash +# packages/app + +yarn add @backstage/plugin-github-deployments +``` + +2. Add proxy and auth token for Github + +```yaml +# app-config.yaml + +proxy: + ... + '/github/api': + target: https://api.github.com + changeOrigin: true + secure: true + headers: + Authorization: + # Content: 'token OAUTH-TOKEN' + $env: GITHUB_OAUTH_TOKEN +``` + +3. Add the plugin to the app + +```typescript +// packages/app/src/plugins.ts + +export { plugin as GithubDeployments } from '@backstage/plugin-github-deployments'; +``` + +4. Add the ... to the EntityPage: + +```typescript +// packages/app/src/components/catalog/EntityPage.tsx + +import { EntityGithubDeploymentsCard } from '@backstage/plugin-github-deployments'; + +const OverviewContent = ({ entity }: { entity: Entity }) => ( + + // ... + + + + // ... + +); +``` + +5. Add the github.com/project-slug annotation to your catalog-info.yaml file: + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage + description: | + Backstage is an open-source developer portal that puts the developer experience first. + annotations: + github.com/project-slug: YOUR_PROJECT_SLUG +spec: + type: library + owner: CNCF + lifecycle: experimental +``` diff --git a/plugins/github-deployments/dev/index.tsx b/plugins/github-deployments/dev/index.tsx new file mode 100644 index 0000000000..dbb737c3c8 --- /dev/null +++ b/plugins/github-deployments/dev/index.tsx @@ -0,0 +1,19 @@ +/* + * 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 { createDevApp } from '@backstage/dev-utils'; +import { githubDeploymentsPlugin } from '../src/plugin'; + +createDevApp().registerPlugin(githubDeploymentsPlugin).render(); diff --git a/plugins/github-deployments/docs/github-deployments-card.png b/plugins/github-deployments/docs/github-deployments-card.png new file mode 100644 index 0000000000..f56301c0fb Binary files /dev/null and b/plugins/github-deployments/docs/github-deployments-card.png differ diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json new file mode 100644 index 0000000000..541e019122 --- /dev/null +++ b/plugins/github-deployments/package.json @@ -0,0 +1,51 @@ +{ + "name": "@backstage/plugin-github-deployments", + "version": "0.1.1", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "build": "backstage-cli plugin:build", + "start": "backstage-cli plugin:serve", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "diff": "backstage-cli plugin:diff", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/catalog-model": "^0.7.4", + "@backstage/core": "^0.7.1", + "@backstage/theme": "^0.2.4", + "@material-ui/core": "^4.11.0", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", + "@octokit/graphql": "^4.6.1", + "moment": "^2.29.1", + "nock": "^13.0.11", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "react-use": "^15.3.3" + }, + "devDependencies": { + "@backstage/cli": "^0.6.4", + "@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", + "@testing-library/user-event": "^12.0.7", + "@types/jest": "^26.0.7", + "@types/node": "^14.14.32", + "cross-fetch": "^3.0.6", + "msw": "^0.21.2" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/github-deployments/src/api/index.ts b/plugins/github-deployments/src/api/index.ts new file mode 100644 index 0000000000..15ba3e542c --- /dev/null +++ b/plugins/github-deployments/src/api/index.ts @@ -0,0 +1,89 @@ +/* + * 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 { createApiRef, DiscoveryApi } from '@backstage/core'; +import { graphql } from '@octokit/graphql'; + +export type GithubDeployment = { + environment: string; + state: string; + updatedAt: string; + commit: { + abbreviatedOid: string; + commitUrl: string; + }; +}; + +export interface GithubDeploymentsApi { + listDeployments(options: { + owner: string; + repo: string; + last: number; + }): Promise; +} + +export const githubDeploymentsApiRef = createApiRef({ + id: 'plugin.github-deployments.service', + description: 'Used by the Github Deployments plugin to make requests', +}); + +export type Options = { + discoveryApi: DiscoveryApi; + proxyPath?: string; +}; + +const deploymentsQuery = ` +query lastDeployments($owner: String!, $repo: String!, $last: Int) { + repository(owner: $owner, name: $repo) { + deployments(last: $last) { + nodes { + state + environment + updatedAt + commit { + abbreviatedOid + commitUrl + } + } + } + } +} +`; + +export class GithubDeploymentsApiClient implements GithubDeploymentsApi { + private readonly discoveryApi: DiscoveryApi; + + constructor(options: Options) { + this.discoveryApi = options.discoveryApi; + } + + private async getProxyUrl() { + return await this.discoveryApi.getBaseUrl('proxy'); + } + + async listDeployments(options: { + owner: string; + repo: string; + last: number; + }): Promise { + const proxyUrl = await this.getProxyUrl(); + const graphQlWithBaseURL = graphql.defaults({ + baseUrl: `${proxyUrl}/github/api`, + }); + + const response: any = await graphQlWithBaseURL(deploymentsQuery, options); + return response.repository?.deployments?.nodes?.reverse() || []; + } +} diff --git a/plugins/github-deployments/src/components/GithubDeploymentsCard.test.tsx b/plugins/github-deployments/src/components/GithubDeploymentsCard.test.tsx new file mode 100644 index 0000000000..2dc1b57ad2 --- /dev/null +++ b/plugins/github-deployments/src/components/GithubDeploymentsCard.test.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 React from 'react'; +import { + ApiProvider, + ApiRegistry, + errorApiRef, + UrlPatternDiscovery, + configApiRef, + ConfigReader, +} from '@backstage/core'; + +import { render } from '@testing-library/react'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { GithubDeploymentsApiClient, githubDeploymentsApiRef } from '../api'; +import { githubDeploymentsPlugin } from '../plugin'; +import { GithubDeploymentsCard } from './GithubDeploymentsCard'; + +import { entityStub, responseStub } from '../mocks/mocks'; + +const discoveryApi = UrlPatternDiscovery.compile('http://exampleapi.com'); +const errorApiMock = { post: jest.fn(), error$: jest.fn() }; + +const apis = ApiRegistry.from([ + [configApiRef, new ConfigReader({})], + [errorApiRef, errorApiMock], + [githubDeploymentsApiRef, new GithubDeploymentsApiClient({ discoveryApi })], +]); + +describe('github-deployments', () => { + const worker = setupServer(); + beforeAll(() => worker.listen()); + afterAll(() => worker.close()); + afterEach(() => worker.resetHandlers()); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('export-plugin', () => { + it('should export plugin', () => { + expect(githubDeploymentsPlugin).toBeDefined(); + }); + }); + + describe('GithubDeploymentsCard', () => { + it('should display fetched data', async () => { + worker.use(rest.post('*', (_, res, ctx) => res(ctx.json(responseStub)))); + + const rendered = render( + + + , + ); + + expect(await rendered.findByText('active')).toBeInTheDocument(); + expect(await rendered.findByText('prd')).toBeInTheDocument(); + expect(await rendered.findByText('12345')).toHaveAttribute( + 'href', + 'https://exampleapi.com/123456789', + ); + + expect(await rendered.findByText('pending')).toBeInTheDocument(); + expect(await rendered.findByText('lab')).toBeInTheDocument(); + expect(await rendered.findByText('54321')).toHaveAttribute( + 'href', + 'https://exampleapi.com/543212345', + ); + }); + }); +}); diff --git a/plugins/github-deployments/src/components/GithubDeploymentsCard.tsx b/plugins/github-deployments/src/components/GithubDeploymentsCard.tsx new file mode 100644 index 0000000000..5f6e072bfc --- /dev/null +++ b/plugins/github-deployments/src/components/GithubDeploymentsCard.tsx @@ -0,0 +1,75 @@ +/* + * 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 React from 'react'; +import { LinearProgress } from '@material-ui/core'; +import { Entity } from '@backstage/catalog-model'; +import { InfoCard, MissingAnnotationEmptyState, useApi } from '@backstage/core'; +import { useAsync } from 'react-use'; +import { githubDeploymentsApiRef } from '../api'; +import GithubDeploymentsTable from './GithubDeploymentsTable/GithubDeploymentsTable'; + +export const GITHUB_PROJECT_SLUG_ANNOTATION = 'github.com/project-slug'; + +export const isGithubDeploymentsAvailable = (entity: Entity) => + Boolean(entity?.metadata.annotations?.[GITHUB_PROJECT_SLUG_ANNOTATION]); + +const GithubDeploymentsComponent = ({ + entity, + last, +}: { + entity: Entity; + last: number; +}) => { + const api = useApi(githubDeploymentsApiRef); + const annotation = + entity.metadata.annotations?.[GITHUB_PROJECT_SLUG_ANNOTATION] ?? ''; + const [owner, repo] = annotation.split('/'); + + const { loading, value, error } = useAsync( + async () => await api.listDeployments({ owner, repo, last }), + ); + + if (loading) { + return ( + + + + ); + } + if (error) { + return ( + + Error occurred while fetching data. + + ); + } + + return ; +}; + +export const GithubDeploymentsCard = ({ + entity, + last, +}: { + entity: Entity; + last?: number; +}) => { + return !isGithubDeploymentsAvailable(entity) ? ( + + ) : ( + + ); +}; diff --git a/plugins/github-deployments/src/components/GithubDeploymentsTable/GithubDeploymentsTable.tsx b/plugins/github-deployments/src/components/GithubDeploymentsTable/GithubDeploymentsTable.tsx new file mode 100644 index 0000000000..1d57fc6ea6 --- /dev/null +++ b/plugins/github-deployments/src/components/GithubDeploymentsTable/GithubDeploymentsTable.tsx @@ -0,0 +1,90 @@ +/* + * 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 React from 'react'; +import { Table, TableColumn } from '@backstage/core'; +import { GithubDeployment } from '../../api'; +import moment from 'moment'; +import { Box, Typography, Link } from '@material-ui/core'; + +const lastUpdated = (start: string): string => moment(start).fromNow(); + +const State = ({ value }: { value: string }) => { + const colorMap: Record = { + PENDING: 'orange', + IN_PROGRESS: 'orange', + ACTIVE: 'green', + }; + + return ( + + + {value} + + ); +}; + +const columns: TableColumn[] = [ + { + title: 'Environment', + field: 'environment', + highlight: true, + }, + { + title: 'Status', + field: 'environment', + render: (row: any): React.ReactNode => , + }, + { + title: 'Commit', + render: (row: any): React.ReactNode => ( + + {row.commit.abbreviatedOid} + + ), + }, + { + title: 'Last Updated', + render: (row: any): React.ReactNode => lastUpdated(row.updatedAt), + }, +]; + +type GithubDeploymentsTableProps = { + deployments: GithubDeployment[]; +}; + +const GithubDeploymentsTable = ({ + deployments, +}: GithubDeploymentsTableProps) => { + return ( + + ); +}; + +export default GithubDeploymentsTable; diff --git a/plugins/github-deployments/src/index.ts b/plugins/github-deployments/src/index.ts new file mode 100644 index 0000000000..e9860050d6 --- /dev/null +++ b/plugins/github-deployments/src/index.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ +export { + githubDeploymentsPlugin as plugin, + EntityGithubDeploymentsCard, +} from './plugin'; diff --git a/plugins/github-deployments/src/mocks/mocks.ts b/plugins/github-deployments/src/mocks/mocks.ts new file mode 100644 index 0000000000..35c87a1f6b --- /dev/null +++ b/plugins/github-deployments/src/mocks/mocks.ts @@ -0,0 +1,67 @@ +/* + * 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. + */ +export const entityStub = { + metadata: { + namespace: 'default', + annotations: { + 'github.com/project-slug': 'org/repo', + }, + name: 'sample-service', + description: 'Sample service', + uid: 'g0h33dd9-56h7-835b-b63v-7x5da3j64851', + generation: 1, + }, + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + spec: { + type: 'service', + lifecycle: 'experimental', + }, + relations: [], +}; + +export const responseStub = { + data: { + repository: { + deployments: { + nodes: [ + { + state: 'active', + environment: 'prd', + updatedAt: '2021-03-25T12:08:45Z', + commit: { + commitUrl: 'https://exampleapi.com/123456789', + abbreviatedOid: '12345', + }, + }, + { + state: 'pending', + environment: 'lab', + updatedAt: '2021-03-25T12:08:47Z', + commit: { + commitUrl: 'https://exampleapi.com/543212345', + abbreviatedOid: '54321', + }, + }, + ], + }, + }, + }, +}; + +export const noDataResponseStub = { + data: {}, +}; diff --git a/plugins/github-deployments/src/plugin.test.ts b/plugins/github-deployments/src/plugin.test.ts new file mode 100644 index 0000000000..cac5d58c63 --- /dev/null +++ b/plugins/github-deployments/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * 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 { githubDeploymentsPlugin } from './plugin'; + +describe('graphiql', () => { + it('should export plugin', () => { + expect(githubDeploymentsPlugin).toBeDefined(); + }); +}); diff --git a/plugins/github-deployments/src/plugin.ts b/plugins/github-deployments/src/plugin.ts new file mode 100644 index 0000000000..2985688785 --- /dev/null +++ b/plugins/github-deployments/src/plugin.ts @@ -0,0 +1,45 @@ +/* + * 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 { + createApiFactory, + createComponentExtension, + createPlugin, + discoveryApiRef, +} from '@backstage/core'; +import { githubDeploymentsApiRef, GithubDeploymentsApiClient } from './api'; + +export const githubDeploymentsPlugin = createPlugin({ + id: 'github-deployments', + apis: [ + createApiFactory({ + api: githubDeploymentsApiRef, + deps: { discoveryApi: discoveryApiRef }, + factory: ({ discoveryApi }) => + new GithubDeploymentsApiClient({ discoveryApi }), + }), + ], +}); + +export const EntityGithubDeploymentsCard = githubDeploymentsPlugin.provide( + createComponentExtension({ + component: { + lazy: () => + import('./components/GithubDeploymentsCard').then( + m => m.GithubDeploymentsCard, + ), + }, + }), +); diff --git a/plugins/github-deployments/src/setupTests.ts b/plugins/github-deployments/src/setupTests.ts new file mode 100644 index 0000000000..0cec5b395d --- /dev/null +++ b/plugins/github-deployments/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * 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 '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/yarn.lock b/yarn.lock index a417d89adb..9c608c113d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4290,6 +4290,15 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" +"@octokit/graphql@^4.6.1": + version "4.6.1" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.1.tgz#f975486a46c94b7dbe58a0ca751935edc7e32cc9" + integrity sha512-2lYlvf4YTDgZCTXTW4+OX+9WTLFtEUc6hGm4qM1nlZjzxj+arizM4aHWzBVBCxY9glh7GIs0WEuiSgbVzv8cmA== + dependencies: + "@octokit/request" "^5.3.0" + "@octokit/types" "^6.0.3" + universal-user-agent "^6.0.0" + "@octokit/openapi-types@^2.2.0": version "2.2.0" resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-2.2.0.tgz#123e0438a0bc718ccdac3b5a2e69b3dd00daa85b" @@ -17940,6 +17949,11 @@ lodash.once@^4.0.0, lodash.once@^4.1.1: resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= +lodash.set@^4.3.2: + version "4.3.2" + resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" + integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -19278,6 +19292,16 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +nock@^13.0.11: + version "13.0.11" + resolved "https://registry.npmjs.org/nock/-/nock-13.0.11.tgz#ba733252e720897ca50033205c39db0c7470f331" + integrity sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash.set "^4.3.2" + propagate "^2.0.0" + node-abi@^2.7.0: version "2.19.3" resolved "https://registry.npmjs.org/node-abi/-/node-abi-2.19.3.tgz#252f5dcab12dad1b5503b2d27eddd4733930282d" @@ -21648,6 +21672,11 @@ prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, object-assign "^4.1.1" react-is "^16.8.1" +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + property-expr@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.2.tgz#fff2a43919135553a3bc2fdd94bdb841965b2330"