From 49de7aba6626702da46d9b7301ffcb0ec23a87bb Mon Sep 17 00:00:00 2001 From: Wan Chiu Date: Sat, 17 Jul 2021 04:56:50 +1000 Subject: [PATCH] feat(react): configure Cypress + MirageJS + GraphQL mock for functional testing plus a couple of example tests (#2597) --- datahub-web-react/README.md | 14 + datahub-web-react/cypress.json | 4 + datahub-web-react/cypress/.eslintrc.js | 12 + .../cypress/fixtures/example.json | 5 + .../cypress/helper/authHelper.ts | 11 + .../cypress/integration/Login.spec.ts | 40 + .../cypress/integration/Search.spec.ts | 36 + datahub-web-react/cypress/plugins/index.js | 47 + datahub-web-react/cypress/support/index.js | 20 + datahub-web-react/cypress/tsconfig.json | 10 + datahub-web-react/package.json | 24 +- datahub-web-react/src/App.tsx | 27 +- .../app/search/AllEntitiesSearchResults.tsx | 4 +- .../src/app/shared/ManageAccount.tsx | 2 +- .../src/graphql-mock/createServer.ts | 12 + .../fixtures/browseChart/index.ts | 13 + .../fixtures/browseDashboard/index.ts | 13 + .../fixtures/browseDataFlow/index.ts | 13 + .../fixtures/browseDataset/index.ts | 13 + .../graphql-mock/fixtures/browsePathHelper.ts | 159 +++ .../fixtures/entity/chartEntity.ts | 41 + .../fixtures/entity/dashboardEntity.ts | 87 ++ .../fixtures/entity/dataFlowEntity.ts | 43 + .../fixtures/entity/dataJobEntity.ts | 106 ++ .../fixtures/entity/datasetEntity.ts | 86 ++ .../fixtures/entity/userEntity.ts | 38 + .../src/graphql-mock/fixtures/index.ts | 5 + .../searchResult/chartSearchResult.ts | 68 ++ .../searchResult/dashboardSearchResult.ts | 57 ++ .../searchResult/dataFlowSearchResult.ts | 72 ++ .../fixtures/searchResult/dataGenerator.ts | 16 + .../searchResult/dataJobSearchResult.ts | 32 + .../searchResult/datasetSearchResult.ts | 280 ++++++ .../fixtures/searchResult/index.ts | 6 + .../fixtures/searchResult/userSearchResult.ts | 54 + .../src/graphql-mock/fixtures/tag.ts | 49 + .../src/graphql-mock/fixtures/user.ts | 21 + datahub-web-react/src/graphql-mock/helper.ts | 19 + .../src/graphql-mock/mutationHelper.ts | 105 ++ .../getAutoCompleteAllResultsResolver.ts | 152 +++ .../getAutoCompleteResultsResolver.ts | 94 ++ .../resolver/getBrowsePathsResolver.ts | 44 + .../resolver/getBrowseResultsResolver.ts | 27 + .../graphql-mock/resolver/getChartResolver.ts | 39 + .../resolver/getDashboardResolver.ts | 36 + .../resolver/getDataFlowResolver.ts | 24 + .../resolver/getDataJobResolver.ts | 32 + .../resolver/getDatasetResolver.ts | 43 + .../resolver/getSearchResultsResolver.ts | 114 +++ .../graphql-mock/resolver/getTagResolver.ts | 24 + .../src/graphql-mock/resolver/index.ts | 44 + .../resolver/isAnalyticsEnabledResolver.ts | 5 + .../resolver/updateChartResolver.ts | 46 + .../resolver/updateDashboardResolver.ts | 45 + .../resolver/updateDataFlowResolver.ts | 33 + .../resolver/updateDataJobResolver.ts | 26 + .../resolver/updateDatasetResolver.ts | 40 + .../resolver/updateTagResolver.ts | 18 + datahub-web-react/src/graphql-mock/schema.ts | 14 + datahub-web-react/src/graphql-mock/server.ts | 83 ++ datahub-web-react/src/graphql-mock/types.ts | 36 + datahub-web-react/src/index.tsx | 1 + datahub-web-react/src/setupProxy.js | 24 + datahub-web-react/yarn.lock | 923 +++++++++++++++++- 64 files changed, 3560 insertions(+), 71 deletions(-) create mode 100644 datahub-web-react/cypress.json create mode 100644 datahub-web-react/cypress/.eslintrc.js create mode 100644 datahub-web-react/cypress/fixtures/example.json create mode 100644 datahub-web-react/cypress/helper/authHelper.ts create mode 100644 datahub-web-react/cypress/integration/Login.spec.ts create mode 100644 datahub-web-react/cypress/integration/Search.spec.ts create mode 100644 datahub-web-react/cypress/plugins/index.js create mode 100644 datahub-web-react/cypress/support/index.js create mode 100644 datahub-web-react/cypress/tsconfig.json create mode 100644 datahub-web-react/src/graphql-mock/createServer.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/browseChart/index.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/browseDashboard/index.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/browseDataFlow/index.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/browseDataset/index.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/browsePathHelper.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/entity/chartEntity.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/entity/dashboardEntity.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/entity/dataFlowEntity.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/entity/dataJobEntity.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/entity/datasetEntity.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/entity/userEntity.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/index.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/searchResult/chartSearchResult.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/searchResult/dashboardSearchResult.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/searchResult/dataFlowSearchResult.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/searchResult/dataGenerator.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/searchResult/dataJobSearchResult.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/searchResult/datasetSearchResult.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/searchResult/index.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/searchResult/userSearchResult.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/tag.ts create mode 100644 datahub-web-react/src/graphql-mock/fixtures/user.ts create mode 100644 datahub-web-react/src/graphql-mock/helper.ts create mode 100644 datahub-web-react/src/graphql-mock/mutationHelper.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getAutoCompleteAllResultsResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getAutoCompleteResultsResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getBrowsePathsResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getBrowseResultsResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getChartResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getDashboardResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getDataFlowResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getDataJobResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getDatasetResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getSearchResultsResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/getTagResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/index.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/isAnalyticsEnabledResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/updateChartResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/updateDashboardResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/updateDataFlowResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/updateDataJobResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/updateDatasetResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/resolver/updateTagResolver.ts create mode 100644 datahub-web-react/src/graphql-mock/schema.ts create mode 100644 datahub-web-react/src/graphql-mock/server.ts create mode 100644 datahub-web-react/src/graphql-mock/types.ts create mode 100644 datahub-web-react/src/setupProxy.js diff --git a/datahub-web-react/README.md b/datahub-web-react/README.md index 73e9127173..c8b56841f7 100644 --- a/datahub-web-react/README.md +++ b/datahub-web-react/README.md @@ -47,6 +47,20 @@ can run the following in this directory: which will start a forwarding server at `localhost:3000`. Note that to fetch real data, `datahub-frontend` server will also need to be deployed, still at `http://localhost:9002`, to service GraphQL API requests. +Optionally you could also start the app with the mock server without running the docker containers by executing `yarn start:mock`. See [here](src/graphql-mock/fixtures/searchResult/userSearchResult.ts#L6) for available login users. + +### Functional testing + +Automated functional testing is powered by Cypress and MirageJS. When running the web server with Cypress the port is set to 3010 so that the usual web server running on port 3000 used for development can be started without interruptions. + +#### During development + +`yarn test:e2e` + +#### CI + +`yarn test:e2e:ci` + ### Theming #### Selecting a theme diff --git a/datahub-web-react/cypress.json b/datahub-web-react/cypress.json new file mode 100644 index 0000000000..c3d02c5954 --- /dev/null +++ b/datahub-web-react/cypress.json @@ -0,0 +1,4 @@ +{ + "baseUrl": "http://localhost:3010", + "video": false +} diff --git a/datahub-web-react/cypress/.eslintrc.js b/datahub-web-react/cypress/.eslintrc.js new file mode 100644 index 0000000000..6cb6d237f5 --- /dev/null +++ b/datahub-web-react/cypress/.eslintrc.js @@ -0,0 +1,12 @@ +module.exports = { + extends: '../.eslintrc.js', + parserOptions: { + project: 'cypress/tsconfig.json', + }, + globals: { + Cypress: true, + }, + rules: { + 'jest/expect-expect': 0, + }, +}; diff --git a/datahub-web-react/cypress/fixtures/example.json b/datahub-web-react/cypress/fixtures/example.json new file mode 100644 index 0000000000..02e4254378 --- /dev/null +++ b/datahub-web-react/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} diff --git a/datahub-web-react/cypress/helper/authHelper.ts b/datahub-web-react/cypress/helper/authHelper.ts new file mode 100644 index 0000000000..4cf7a2a76b --- /dev/null +++ b/datahub-web-react/cypress/helper/authHelper.ts @@ -0,0 +1,11 @@ +export const login = (username) => { + cy.visit('/'); + cy.get('input#username').type(username); + cy.get('input#password').type(username); + cy.contains('Log in').click(); +}; + +export const logout = (username) => { + cy.get(`a[href="/user/urn:li:corpuser:${username}"]`).children('.anticon.anticon-caret-down').trigger('mouseover'); + cy.get('li#user-profile-menu-logout').click(); +}; diff --git a/datahub-web-react/cypress/integration/Login.spec.ts b/datahub-web-react/cypress/integration/Login.spec.ts new file mode 100644 index 0000000000..43e87634de --- /dev/null +++ b/datahub-web-react/cypress/integration/Login.spec.ts @@ -0,0 +1,40 @@ +import { createLoginUsers } from '../../src/graphql-mock/fixtures/user'; +import { makeServer } from '../../src/graphql-mock/server'; +import { login, logout } from '../helper/authHelper'; + +describe('Login', () => { + let server; + + beforeEach(() => { + server = makeServer('test'); + createLoginUsers(server); + }); + + afterEach(() => { + server.shutdown(); + }); + + describe('given the login page is loaded', () => { + describe('when logging in with incorrect credentials', () => { + it('then the login should fail and the toast notification should be briefly displayed', () => { + login('kafkaa'); + + cy.contains('Failed to log in!').should('be.visible'); + }); + }); + + describe('when logging in with correct credentials', () => { + it('then the home page should be displayed', () => { + login('kafka'); + + cy.contains('Welcome back,').should('be.visible'); + cy.contains('Datasets').should('be.visible'); + cy.contains('Dashboard').should('be.visible'); + cy.contains('Chart').should('be.visible'); + cy.contains('Pipelines').should('be.visible'); + + logout('kafka'); + }); + }); + }); +}); diff --git a/datahub-web-react/cypress/integration/Search.spec.ts b/datahub-web-react/cypress/integration/Search.spec.ts new file mode 100644 index 0000000000..8585a124e7 --- /dev/null +++ b/datahub-web-react/cypress/integration/Search.spec.ts @@ -0,0 +1,36 @@ +import { createLoginUsers } from '../../src/graphql-mock/fixtures/user'; +import { makeServer } from '../../src/graphql-mock/server'; +import { login, logout } from '../helper/authHelper'; + +describe('Search', () => { + let server; + + beforeEach(() => { + server = makeServer('test'); + createLoginUsers(server); + }); + + afterEach(() => { + server.shutdown(); + }); + + describe('given the home page is loaded', () => { + describe('when the user enters a keyword in the search field and results found and the first item is selected from the search result dropdown', () => { + it('then the search result page should be displayed with the Task tab be selected and the selected item be displayed', () => { + login('kafka'); + + cy.get('input[placeholder="Search Datasets, People, & more..."]').type('load'); + + cy.get('div.rc-virtual-list-holder-inner') + .children('div.ant-select-item.ant-select-item-option.ant-select-item-option-grouped') + .contains('load_all_') + .click(); + + cy.get('.ant-tabs-tab.ant-tabs-tab-active').contains('Task').should('be.visible'); + cy.contains('load_all_').should('be.visible'); + + logout('kafka'); + }); + }); + }); +}); diff --git a/datahub-web-react/cypress/plugins/index.js b/datahub-web-react/cypress/plugins/index.js new file mode 100644 index 0000000000..de2161b56d --- /dev/null +++ b/datahub-web-react/cypress/plugins/index.js @@ -0,0 +1,47 @@ +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +const findWebpack = require('find-webpack'); +const wp = require('@cypress/webpack-preprocessor'); + +/** + * @type {Cypress.PluginConfig} + */ +module.exports = (on, config) => { + // find the Webpack config used by react-scripts + const webpackOptions = findWebpack.getWebpackOptions(); + + if (!webpackOptions) { + throw new Error('Could not find Webpack in this project 😢'); + } + + const cleanOptions = { + reactScripts: true, + }; + + findWebpack.cleanForCypress(cleanOptions, webpackOptions); + + const options = { + webpackOptions, + watchOptions: {}, + }; + + on('file:preprocessor', wp(options)); + + // add other tasks to be registered here + + // IMPORTANT to return the config object + // with the any changed environment variables + return config; +}; diff --git a/datahub-web-react/cypress/support/index.js b/datahub-web-react/cypress/support/index.js new file mode 100644 index 0000000000..725a6cb98a --- /dev/null +++ b/datahub-web-react/cypress/support/index.js @@ -0,0 +1,20 @@ +/* eslint-disable no-param-reassign */ + +Cypress.on('window:before:load', (win) => { + win.handleFromCypress = (request) => { + return fetch(request.url, { + method: request.method, + headers: request.requestHeaders, + body: request.requestBody, + }) + .then((res) => { + const content = res.headers.get('content-type').includes('application/json') ? res.json() : res.text(); + return new Promise((resolve) => { + content.then((body) => resolve([res.status, res.headers, body])); + }); + }) + .catch((error) => { + console.log('Cypress request proxy error', { error }); + }); + }; +}); diff --git a/datahub-web-react/cypress/tsconfig.json b/datahub-web-react/cypress/tsconfig.json new file mode 100644 index 0000000000..95042c164f --- /dev/null +++ b/datahub-web-react/cypress/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "target": "es5", + "lib": ["es5", "dom"], + "types": ["cypress"], + "noEmit": false + }, + "include": ["../node_modules/cypress", "**/*.ts", "support/index.js"] +} diff --git a/datahub-web-react/package.json b/datahub-web-react/package.json index f28b450d3a..faf4697c3b 100644 --- a/datahub-web-react/package.json +++ b/datahub-web-react/package.json @@ -12,7 +12,9 @@ "@ant-design/icons": "^4.3.0", "@apollo/client": "^3.3.19", "@craco/craco": "^6.1.1", + "@cypress/webpack-preprocessor": "5.8.0", "@data-ui/xy-chart": "^0.0.84", + "@miragejs/graphql": "^0.1.11", "@react-hook/window-size": "^3.0.7", "@testing-library/jest-dom": "^5.11.6", "@testing-library/react": "^11.2.2", @@ -43,14 +45,20 @@ "apollo-link-error": "^1.1.13", "apollo-link-http": "^1.5.17", "craco-antd": "^1.19.0", + "cypress": "7.3.0", "d3-scale": "^3.3.0", "d3-time-format": "^3.0.0", "diff": "^5.0.0", "dotenv": "^8.2.0", - "graphql": "^15.4.0", + "faker": "5.5.3", + "find-webpack": "2.2.1", + "graphql": "^15.5.0", + "graphql-tag": "2.10.3", + "graphql.macro": "^1.4.2", "history": "^5.0.0", "js-cookie": "^2.2.1", "lodash.debounce": "^4.0.8", + "miragejs": "^0.1.41", "query-string": "^6.13.8", "rc-table": "^7.13.1", "react": "^17.0.0", @@ -60,6 +68,7 @@ "react-router-dom": "^5.1.6", "react-scripts": "4.0.3", "sinon": "^11.1.1", + "start-server-and-test": "1.12.2", "styled-components": "^5.2.1", "typescript": "^4.1.3", "uuid": "^8.3.2", @@ -67,10 +76,17 @@ "web-vitals": "^0.2.4" }, "scripts": { - "start": "yarn run generate && BROWSER=none craco start", + "start": "yarn run generate && BROWSER=none REACT_APP_MOCK=false craco start", + "start:mock": "yarn run generate && BROWSER=none REACT_APP_MOCK=true craco start", + "start:e2e": "REACT_APP_MOCK=cy BROWSER=none PORT=3010 craco start", "ec2-dev": "yarn run generate && CI=true;export CI;BROWSER=none craco start", - "build": "yarn run generate && CI=false craco build && rm -rf dist/ && cp -r build/ dist/ && rm -r build/", + "build": "yarn run generate && CI=false REACT_APP_MOCK=false craco build && rm -rf dist/ && cp -r build/ dist/ && rm -r build/", "test": "craco test", + "cy:run:ci": "cypress run", + "pretest:e2e:ci": "yarn generate", + "test:e2e:ci": "start-server-and-test start:e2e 3010 cy:run:ci", + "cy:open": "cypress open", + "test:e2e": "start-server-and-test start:e2e 3010 cy:open", "eject": "react-scripts eject", "generate": "graphql-codegen --config codegen.yml", "lint": "eslint . --ext .ts,.tsx --quiet", @@ -112,9 +128,9 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^3.4.0", "eslint-plugin-react": "^7.23.2", + "http-proxy-middleware": "2.0.0", "prettier": "^2.3.0" }, - "proxy": "http://localhost:9002", "resolutions": { "@ant-design/colors": "5.0.0" } diff --git a/datahub-web-react/src/App.tsx b/datahub-web-react/src/App.tsx index 9b8e5e4139..c2f19d7906 100644 --- a/datahub-web-react/src/App.tsx +++ b/datahub-web-react/src/App.tsx @@ -3,12 +3,10 @@ import Cookies from 'js-cookie'; import { BrowserRouter as Router } from 'react-router-dom'; import { ApolloClient, ApolloProvider, createHttpLink, InMemoryCache, ServerError } from '@apollo/client'; import { onError } from '@apollo/client/link/error'; -import { MockedProvider } from '@apollo/client/testing'; import { ThemeProvider } from 'styled-components'; import './App.less'; import { Routes } from './app/Routes'; -import { mocks } from './Mocks'; import EntityRegistry from './app/entity/EntityRegistry'; import { DashboardEntity } from './app/entity/dashboard/DashboardEntity'; import { ChartEntity } from './app/entity/chart/ChartEntity'; @@ -26,11 +24,8 @@ import { isLoggedInVar } from './app/auth/checkAuthStatus'; import { GlobalCfg } from './conf'; import { GlossaryTermEntity } from './app/entity/glossaryTerm/GlossaryTermEntity'; -// Enable to use the Apollo MockProvider instead of a real HTTP client -const MOCK_MODE = false; - /* - Construct Apollo Client + Construct Apollo Client */ const httpLink = createHttpLink({ uri: '/api/v2/graphql' }); @@ -112,23 +107,9 @@ const App: React.VFC = () => { - {/* Temporary: For local testing during development. */} - {MOCK_MODE ? ( - - - - ) : ( - - - - )} + + + diff --git a/datahub-web-react/src/app/search/AllEntitiesSearchResults.tsx b/datahub-web-react/src/app/search/AllEntitiesSearchResults.tsx index 55245b57e8..d8013bc038 100644 --- a/datahub-web-react/src/app/search/AllEntitiesSearchResults.tsx +++ b/datahub-web-react/src/app/search/AllEntitiesSearchResults.tsx @@ -64,7 +64,9 @@ export const AllEntitiesSearchResults = ({ query }: Props) => { {Object.keys(allSearchResultsByType).map((type: any) => { const searchResults = allSearchResultsByType[type].data?.search?.searchResults; if (searchResults && searchResults.length > 0) { - return ; + return ( + + ); } return null; })} diff --git a/datahub-web-react/src/app/shared/ManageAccount.tsx b/datahub-web-react/src/app/shared/ManageAccount.tsx index e8238e1792..d8ef084b3c 100644 --- a/datahub-web-react/src/app/shared/ManageAccount.tsx +++ b/datahub-web-react/src/app/shared/ManageAccount.tsx @@ -65,7 +65,7 @@ export const ManageAccount = ({ urn: _urn, pictureLink: _pictureLink, name }: Pr ); })} - + Log out diff --git a/datahub-web-react/src/graphql-mock/createServer.ts b/datahub-web-react/src/graphql-mock/createServer.ts new file mode 100644 index 0000000000..260da614e7 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/createServer.ts @@ -0,0 +1,12 @@ +/* eslint-disable global-require */ +/* eslint-disable @typescript-eslint/no-var-requires */ + +if (process.env.REACT_APP_MOCK === 'true' || process.env.REACT_APP_MOCK === 'cy') { + if (process.env.REACT_APP_MOCK === 'cy') { + require('./server').makeServerForCypress(); + } else { + require('./server').makeServer(); + } +} + +export {}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/browseChart/index.ts b/datahub-web-react/src/graphql-mock/fixtures/browseChart/index.ts new file mode 100644 index 0000000000..c567e36734 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/browseChart/index.ts @@ -0,0 +1,13 @@ +import { EntityType } from '../../../types.generated'; +import { BrowsePathResolver } from '../browsePathHelper'; +import { chartBrowsePaths, filterChartByPath } from '../searchResult/chartSearchResult'; + +const browsePathResolver = new BrowsePathResolver({ + entityType: EntityType.Chart, + paths: chartBrowsePaths, + filterEntityHandler: filterChartByPath, +}); + +export default { + ...browsePathResolver.getBrowse(), +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/browseDashboard/index.ts b/datahub-web-react/src/graphql-mock/fixtures/browseDashboard/index.ts new file mode 100644 index 0000000000..165c896542 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/browseDashboard/index.ts @@ -0,0 +1,13 @@ +import { EntityType } from '../../../types.generated'; +import { BrowsePathResolver } from '../browsePathHelper'; +import { dashboardBrowsePaths, filterDashboardByPath } from '../searchResult/dashboardSearchResult'; + +const browsePathResolver = new BrowsePathResolver({ + entityType: EntityType.Dashboard, + paths: dashboardBrowsePaths, + filterEntityHandler: filterDashboardByPath, +}); + +export default { + ...browsePathResolver.getBrowse(), +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/browseDataFlow/index.ts b/datahub-web-react/src/graphql-mock/fixtures/browseDataFlow/index.ts new file mode 100644 index 0000000000..c3f05e6935 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/browseDataFlow/index.ts @@ -0,0 +1,13 @@ +import { EntityType } from '../../../types.generated'; +import { BrowsePathResolver } from '../browsePathHelper'; +import { dataFlowBrowsePaths, filterDataFlowByPath } from '../searchResult/dataFlowSearchResult'; + +const browsePathResolver = new BrowsePathResolver({ + entityType: EntityType.DataFlow, + paths: dataFlowBrowsePaths, + filterEntityHandler: filterDataFlowByPath, +}); + +export default { + ...browsePathResolver.getBrowse(), +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/browseDataset/index.ts b/datahub-web-react/src/graphql-mock/fixtures/browseDataset/index.ts new file mode 100644 index 0000000000..2a9f593917 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/browseDataset/index.ts @@ -0,0 +1,13 @@ +import { EntityType } from '../../../types.generated'; +import { BrowsePathResolver } from '../browsePathHelper'; +import { datasetBrowsePaths, filterDatasetByPath } from '../searchResult/datasetSearchResult'; + +const browsePathResolver = new BrowsePathResolver({ + entityType: EntityType.Dataset, + paths: datasetBrowsePaths, + filterEntityHandler: filterDatasetByPath, +}); + +export default { + ...browsePathResolver.getBrowse(), +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/browsePathHelper.ts b/datahub-web-react/src/graphql-mock/fixtures/browsePathHelper.ts new file mode 100644 index 0000000000..4864ea8a35 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/browsePathHelper.ts @@ -0,0 +1,159 @@ +import { BrowseInput, BrowseResultGroup, BrowseResults, Entity, EntityType, SearchResult } from '../../types.generated'; +import { toLowerCaseEntityType, toTitleCase } from '../helper'; +import { EntityBrowseFn, EntityBrowsePath, GetBrowseResults, StringNumber } from '../types'; + +type ToFlatPathsArg = { + flatPaths: StringNumber[][]; + paths: EntityBrowsePath[]; + parentPaths: string[]; +}; + +export const toFlatPaths = ({ flatPaths, paths, parentPaths }: ToFlatPathsArg) => { + paths.forEach(({ name, paths: childPaths, count = 0 }) => { + if (childPaths.length) { + parentPaths.push(name); + toFlatPaths({ flatPaths, parentPaths, paths: childPaths }); + } else { + flatPaths.push([...parentPaths, name, count]); + } + }); + parentPaths.pop(); +}; + +type FilterEntityByPathArg = { + term: string; + searchResults: SearchResult[]; +}; + +export const filterEntityByPath = ({ term, searchResults }: FilterEntityByPathArg): Entity[] => { + return searchResults + .filter((r) => { + const regex = new RegExp(term); + return regex.test(r.entity.urn); + }) + .map((r) => r.entity); +}; + +export class BrowsePathResolver { + private readonly browse: Record; + + private readonly paths: EntityBrowsePath[]; + + private readonly filterEntityHandler: (path: string[]) => Entity[]; + + private readonly baseBrowseResult: GetBrowseResults = { + data: { + browse: { + entities: [], + start: 0, + count: 0, + total: 0, + metadata: { + path: [], + groups: [], + totalNumEntities: 0, + __typename: 'BrowseResultMetadata', + }, + __typename: 'BrowseResults', + }, + }, + }; + + constructor({ + entityType, + paths, + filterEntityHandler, + }: { + entityType: EntityType; + paths: EntityBrowsePath[]; + filterEntityHandler(path: string[]): Entity[]; + }) { + this.browse = {}; + this.paths = paths; + this.filterEntityHandler = filterEntityHandler; + const browsePathKey = `${toLowerCaseEntityType(entityType)}Browse`; + const groups = this.paths.map(({ name, paths: rootPaths, count = 0 }) => ({ + name, + count: rootPaths.length ? rootPaths.reduce(this.sumTotalEntityByPaths, 0) : count, + __typename: 'BrowseResultGroup', + })); + this.initBrowsePathResolver({ browsePathKey, groups }); + this.initBrowsePathResolverForPaths({ prefixPathKey: browsePathKey, paths }); + } + + public getBrowse() { + return this.browse; + } + + private initBrowsePathResolver({ browsePathKey, groups }: { browsePathKey: string; groups: BrowseResultGroup[] }) { + if (!this.browse.hasOwnProperty(browsePathKey)) { + const dataBrowse: BrowseResults = JSON.parse(JSON.stringify(this.baseBrowseResult.data.browse)); + + Object.assign(this.browse, { + [browsePathKey]: ({ start, count, path }: BrowseInput): GetBrowseResults => { + const startValue = start as number; + const countValue = count as number; + const paths = path as string[]; + const entities = groups.length ? [] : this.filterEntityHandler(paths); + const chunkEntities = entities.slice(startValue, startValue + countValue); + + return { + data: { + browse: { + ...dataBrowse, + entities: chunkEntities, + start: startValue, + count: chunkEntities.length, + total: entities.length, + metadata: { + ...dataBrowse.metadata, + path: paths, + groups, + totalNumEntities: groups.reduce(this.sumTotalEntityByGroups, 0), + }, + }, + }, + }; + }, + }); + } + } + + private initBrowsePathResolverForPaths({ + prefixPathKey, + paths, + }: { + prefixPathKey: string; + paths: EntityBrowsePath[]; + }) { + paths.forEach(({ name, paths: childPaths }) => { + const browsePathKey = `${prefixPathKey}${toTitleCase(name)}`; + + if (childPaths.length) { + const groups = childPaths.map( + ({ name: childName, paths: child2Paths, count = 0 }) => ({ + name: childName, + count: child2Paths.length ? child2Paths.reduce(this.sumTotalEntityByPaths, 0) : count, + __typename: 'BrowseResultGroup', + }), + ); + + this.initBrowsePathResolver({ browsePathKey, groups }); + this.initBrowsePathResolverForPaths({ prefixPathKey: browsePathKey, paths: childPaths }); + } else { + this.initBrowsePathResolver({ browsePathKey, groups: [] }); + } + }); + } + + private sumTotalEntityByGroups = (out: number, { count = 0 }: BrowseResultGroup): number => { + return out + count; + }; + + private sumTotalEntityByPaths = (out: number, { paths, count = 0 }: EntityBrowsePath): number => { + if (paths.length) { + return paths.reduce(this.sumTotalEntityByPaths, out); + } + return out + count; + }; +} diff --git a/datahub-web-react/src/graphql-mock/fixtures/entity/chartEntity.ts b/datahub-web-react/src/graphql-mock/fixtures/entity/chartEntity.ts new file mode 100644 index 0000000000..1a559eb73f --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/entity/chartEntity.ts @@ -0,0 +1,41 @@ +import * as faker from 'faker'; +import { Chart, ChartType, EntityType, OwnershipType } from '../../../types.generated'; +import { findUserByUsername } from '../searchResult/userSearchResult'; + +export const chartEntity = (tool): Chart => { + const name = `${faker.company.bsNoun()}_${faker.company.bsNoun()}_${faker.company.bsNoun()}`; + const description = `${faker.commerce.productDescription()}`; + const datahubUser = findUserByUsername('datahub'); + + return { + urn: `urn:li:chart:(${tool},${name})`, + type: EntityType.Chart, + tool, + chartId: '2', + info: { + name, + description, + externalUrl: + 'https://superset.demo.datahubproject.io/superset/explore/?form_data=%7B%22slice_id%22%3A%202%7D', + type: ChartType.Pie, + access: null, + lastModified: { time: 1619137330, __typename: 'AuditStamp' }, + created: { time: 1619137330, __typename: 'AuditStamp' }, + __typename: 'ChartInfo', + }, + editableProperties: null, + ownership: { + owners: [ + { + owner: datahubUser, + type: OwnershipType.Stakeholder, + __typename: 'Owner', + }, + ], + lastModified: { time: 1619717962718, __typename: 'AuditStamp' }, + __typename: 'Ownership', + }, + globalTags: null, + __typename: 'Chart', + }; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/entity/dashboardEntity.ts b/datahub-web-react/src/graphql-mock/fixtures/entity/dashboardEntity.ts new file mode 100644 index 0000000000..a0f5fc9a85 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/entity/dashboardEntity.ts @@ -0,0 +1,87 @@ +import * as faker from 'faker'; +import { generateTag } from '../tag'; +import { Dashboard, EntityType, Ownership, OwnershipType } from '../../../types.generated'; +import { findUserByUsername } from '../searchResult/userSearchResult'; + +export const dashboardEntity = (tool): Dashboard => { + const name = `${faker.company.bsNoun()}`; + const description = `${faker.commerce.productDescription()}`; + const datahubUser = findUserByUsername('datahub'); + const kafkaUser = findUserByUsername('kafka'); + const lookerUser = findUserByUsername('looker'); + const datahubOwnership: Ownership = { + owners: [ + { + owner: datahubUser, + type: OwnershipType.Stakeholder, + __typename: 'Owner', + }, + ], + lastModified: { time: 1619993818664, __typename: 'AuditStamp' }, + __typename: 'Ownership', + }; + const kafkaOwnership: Ownership = { + owners: [ + { + owner: kafkaUser, + type: OwnershipType.Stakeholder, + __typename: 'Owner', + }, + ], + lastModified: { time: 1619993818664, __typename: 'AuditStamp' }, + __typename: 'Ownership', + }; + + return { + urn: `urn:li:dashboard:(${tool},${name})`, + type: EntityType.Dashboard, + tool, + dashboardId: '3', + editableProperties: null, + info: { + name, + description, + externalUrl: null, + access: null, + charts: [], + lastModified: { time: 1619160920, __typename: 'AuditStamp' }, + created: { time: 1619160920, __typename: 'AuditStamp' }, + __typename: 'DashboardInfo', + }, + ownership: { + owners: [ + { + owner: datahubUser, + type: OwnershipType.Stakeholder, + __typename: 'Owner', + }, + { + owner: kafkaUser, + type: OwnershipType.Developer, + __typename: 'Owner', + }, + { + owner: lookerUser, + type: OwnershipType.Developer, + __typename: 'Owner', + }, + ], + lastModified: { time: 1619993818664, __typename: 'AuditStamp' }, + __typename: 'Ownership', + }, + globalTags: { + tags: [ + { + tag: generateTag(datahubOwnership), + __typename: 'TagAssociation', + }, + { + tag: generateTag(kafkaOwnership), + __typename: 'TagAssociation', + }, + ], + __typename: 'GlobalTags', + }, + __typename: 'Dashboard', + }; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/entity/dataFlowEntity.ts b/datahub-web-react/src/graphql-mock/fixtures/entity/dataFlowEntity.ts new file mode 100644 index 0000000000..0869e97d0d --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/entity/dataFlowEntity.ts @@ -0,0 +1,43 @@ +import * as faker from 'faker'; +import { DataFlow, EntityType, OwnershipType } from '../../../types.generated'; +import { findUserByUsername } from '../searchResult/userSearchResult'; + +export type DataFlowEntityArg = { + orchestrator: string; + cluster: string; +}; + +export const dataFlowEntity = ({ orchestrator, cluster }: DataFlowEntityArg): DataFlow => { + const flowId = `${faker.company.bsNoun()}_${faker.company.bsNoun()}`; + const description = `${faker.commerce.productDescription()}`; + const datahubUser = findUserByUsername('datahub'); + + return { + urn: `urn:li:dataFlow:(${orchestrator},${flowId},${cluster})`, + type: EntityType.DataFlow, + orchestrator, + flowId, + cluster, + info: { + name: flowId, + description, + project: null, + __typename: 'DataFlowInfo', + }, + editableProperties: null, + ownership: { + owners: [ + { + owner: datahubUser, + type: OwnershipType.Stakeholder, + __typename: 'Owner', + }, + ], + lastModified: { time: 1620224528712, __typename: 'AuditStamp' }, + __typename: 'Ownership', + }, + globalTags: { tags: [], __typename: 'GlobalTags' }, + dataJobs: null, + __typename: 'DataFlow', + }; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/entity/dataJobEntity.ts b/datahub-web-react/src/graphql-mock/fixtures/entity/dataJobEntity.ts new file mode 100644 index 0000000000..aa6aa2c542 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/entity/dataJobEntity.ts @@ -0,0 +1,106 @@ +import * as faker from 'faker'; +import { DataJob, EntityType, OwnershipType } from '../../../types.generated'; +import { findUserByUsername } from '../searchResult/userSearchResult'; + +export const dataJobEntity = (): DataJob => { + const orchestrator = 'airflow'; + const flowId = `datajob_${faker.company.bsNoun()}_${faker.company.bsNoun()}`; + const cluster = 'prod'; + const dataFlowURN = `(urn:li:dataFlow:(${orchestrator},${flowId},${cluster})`; + const description = faker.commerce.productDescription(); + const jobId = `load_all_${faker.company.bsNoun()}_${faker.company.bsNoun()}`; + const kafkaUser = findUserByUsername('kafka'); + + return { + urn: `urn:li:dataJob:${dataFlowURN},${jobId})`, + type: EntityType.DataJob, + dataFlow: { + urn: dataFlowURN, + type: EntityType.DataFlow, + orchestrator, + flowId, + cluster, + info: { + name: flowId, + description, + project: null, + externalUrl: 'https://airflow.demo.datahubproject.io/tree?dag_id=datahub_analytics_refresh', + customProperties: [ + { key: 'end_date', value: 'None', __typename: 'StringMapEntry' }, + { key: 'orientation', value: "'LR'", __typename: 'StringMapEntry' }, + { key: 'max_active_runs', value: '16', __typename: 'StringMapEntry' }, + { key: 'is_paused_upon_creation', value: 'None', __typename: 'StringMapEntry' }, + { key: 'timezone', value: "'UTC'", __typename: 'StringMapEntry' }, + { key: 'params', value: '{}', __typename: 'StringMapEntry' }, + { + key: 'fileloc', + value: "'/opt/airflow/dags/repo/airflow/dags/datahub_analytics_refresh.py'", + __typename: 'StringMapEntry', + }, + { + key: 'default_args', + value: "{: {'owner': 'harshal', 'depends_on_past': False, 'email': ['harshal@acryl.io'], 'email_on_failure': False, 'execution_timeout': {: 300.0, : }}, : }", + __typename: 'StringMapEntry', + }, + { key: 'tags', value: 'None', __typename: 'StringMapEntry' }, + { key: '_access_control', value: 'None', __typename: 'StringMapEntry' }, + { key: 'doc_md', value: 'None', __typename: 'StringMapEntry' }, + { key: 'dagrun_timeout', value: 'None', __typename: 'StringMapEntry' }, + { + key: '_dag_id', + value: "'datahub_analytics_refresh'", + __typename: 'StringMapEntry', + }, + { key: 'catchup', value: 'False', __typename: 'StringMapEntry' }, + { + key: 'schedule_interval', + value: "{: 86400.0, : }", + __typename: 'StringMapEntry', + }, + { key: '_default_view', value: 'None', __typename: 'StringMapEntry' }, + { + key: '_description', + value: "'Refresh snowflake tables for analytics purposes'", + __typename: 'StringMapEntry', + }, + { key: '_concurrency', value: '16', __typename: 'StringMapEntry' }, + { + key: 'tasks', + value: "[{'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': []}, 'ui_color': '#f0ede4', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'run_data_task', '_inlets': {'auto': False, 'task_ids': [], 'datasets': []}, 'template_fields': ['bash_command', 'env'], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['split_s3_task'], '_task_type': 'BashOperator', '_task_module': 'airflow.operators.bash_operator', 'bash_command': \"echo 'This is where we might run the backup job'\"}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.all_entities', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.user_basic_info', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.user_extra_info', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_ownerships', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_properties', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_schemas', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_schema_extras', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_tags', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_lineages', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_statuses', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.tag_ownerships', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.tag_properties', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataflow_ownerships', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataflow_info', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataflow_tags', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.datajob_ownerships', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.datajob_info', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.datajob_lineages', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.datajob_tags', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.chart_info', env='PROD')\", \"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dashboard_info', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'split_s3_task', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahub-demo-backup.demo.aspects', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['load_all_entities_to_snowflake', 'load_tag_ownerships_to_snowflake', 'load_dataset_tags_to_snowflake', 'load_dataset_properties_to_snowflake', 'load_dataset_schema_extras_to_snowflake', 'load_dataset_schemas_to_snowflake', 'load_datajob_lineages_to_snowflake', 'load_dataflow_ownerships_to_snowflake', 'load_dashboard_info_to_snowflake', 'load_datajob_tags_to_snowflake', 'load_dataset_statuses_to_snowflake', 'load_chart_info_to_snowflake', 'load_user_basic_info_to_snowflake', 'load_user_extra_info_to_snowflake', 'load_datajob_info_to_snowflake', 'load_dataset_lineages_to_snowflake', 'load_dataset_ownerships_to_snowflake', 'load_datajob_ownerships_to_snowflake', 'load_tag_properties_to_snowflake', 'load_dataflow_info_to_snowflake', 'load_dataflow_tags_to_snowflake'], '_task_type': 'S3FileToDirectoryTransform', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': []}, 'ui_color': '#e8f7e4', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'wait_for_load_finish', '_inlets': {'auto': False, 'task_ids': [], 'datasets': []}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['update_generated_latest_dataset_owners', 'update_generated_latest_dataset_info', 'update_generated_dataset_platforms'], '_task_type': 'DummyOperator', '_task_module': 'airflow.operators.dummy_operator'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.all_entities', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_all_entities_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.all_entities', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.user_basic_info', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_user_basic_info_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.user_basic_info', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.user_extra_info', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_user_extra_info_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.user_extra_info', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_ownerships', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dataset_ownerships_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_ownerships', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_properties', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dataset_properties_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_properties', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_schemas', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dataset_schemas_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_schemas', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_schema_extras', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dataset_schema_extras_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_schema_extras', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_tags', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dataset_tags_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_tags', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_lineages', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dataset_lineages_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_lineages', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_statuses', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dataset_statuses_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataset_statuses', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.tag_ownerships', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_tag_ownerships_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.tag_ownerships', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.tag_properties', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_tag_properties_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.tag_properties', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dataflow_ownerships', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dataflow_ownerships_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataflow_ownerships', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dataflow_info', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dataflow_info_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataflow_info', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dataflow_tags', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dataflow_tags_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dataflow_tags', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.datajob_ownerships', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_datajob_ownerships_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.datajob_ownerships', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.datajob_info', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_datajob_info_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.datajob_info', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.datajob_lineages', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_datajob_lineages_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.datajob_lineages', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.datajob_tags', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_datajob_tags_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.datajob_tags', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.chart_info', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_chart_info_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.chart_info', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.dashboard_info', env='PROD')\"]}, 'ui_color': '#fff', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'load_dashboard_info_to_snowflake', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='s3', name='datahubproject-demo-pipelines.entity_aspect_splits.dashboard_info', env='PROD')\"]}, 'template_fields': [], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['wait_for_load_finish'], '_task_type': 'LoadS3IntoSnowflakeOperator', '_task_module': 'unusual_prefix_436311363ce00ecbd709f747db697044ba2913d4_datahub_analytics_refresh'}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.generated_dataset_platforms', env='PROD')\"]}, 'ui_color': '#ededed', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'update_generated_dataset_platforms', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.all_entities', env='PROD')\"]}, 'template_fields': ['sql'], 'email': ['harshal@acryl.io'], '_downstream_task_ids': ['update_generated_latest_dataset_owners', 'update_generated_latest_dataset_info'], '_task_type': 'SnowflakeOperator', '_task_module': 'airflow.providers.snowflake.operators.snowflake', 'sql': \"\\nCREATE OR REPLACE TABLE generated_dataset_platforms AS (\\n SELECT urn, split(split(urn, ',')[0], ':')[6]::string as platform\\n FROM all_entities\\n WHERE all_entities.entity = 'dataset'\\n);\\n \"}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.generated_latest_dataset_owners', env='PROD')\"]}, 'ui_color': '#ededed', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'update_generated_latest_dataset_owners', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.all_entities', env='PROD')\", \"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_ownerships', env='PROD')\", \"Dataset(platform='snowflake', name='demo_pipeline.public.generated_dataset_platforms', env='PROD')\"]}, 'template_fields': ['sql'], 'email': ['harshal@acryl.io'], '_downstream_task_ids': [], '_task_type': 'SnowflakeOperator', '_task_module': 'airflow.providers.snowflake.operators.snowflake', 'sql': \"\\nCREATE OR REPLACE TABLE generated_latest_dataset_owners AS (\\n WITH latest_dataset_owners AS (SELECT * FROM dataset_ownerships WHERE version = 0)\\n SELECT all_entities.urn, generated_dataset_platforms.platform, metadata:owners as owners, ARRAY_SIZE(COALESCE(metadata:owners, array_construct())) as owner_count\\n FROM all_entities\\n LEFT JOIN latest_dataset_owners ON all_entities.urn = latest_dataset_owners.urn\\n LEFT JOIN generated_dataset_platforms ON all_entities.urn = generated_dataset_platforms.urn\\n WHERE all_entities.entity = 'dataset'\\n);\\n \"}, {'execution_timeout': 300.0, 'ui_fgcolor': '#000', '_outlets': {'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.generated_latest_dataset_info', env='PROD')\"]}, 'ui_color': '#ededed', 'email_on_failure': False, 'owner': 'harshal', 'task_id': 'update_generated_latest_dataset_info', '_inlets': {'auto': False, 'task_ids': [], 'datasets': [\"Dataset(platform='snowflake', name='demo_pipeline.public.all_entities', env='PROD')\", \"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_properties', env='PROD')\", \"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_tags', env='PROD')\", \"Dataset(platform='snowflake', name='demo_pipeline.public.dataset_lineages', env='PROD')\", \"Dataset(platform='snowflake', name='demo_pipeline.public.generated_dataset_platforms', env='PROD')\"]}, 'template_fields': ['sql'], 'email': ['harshal@acryl.io'], '_downstream_task_ids': [], '_task_type': 'SnowflakeOperator', '_task_module': 'airflow.providers.snowflake.operators.snowflake', 'sql': \"\\nCREATE OR REPLACE TABLE generated_latest_dataset_info AS (\\n WITH\\n latest_dataset_info AS (SELECT * FROM dataset_properties WHERE version = 0),\\n latest_dataset_tags AS (SELECT * FROM dataset_tags WHERE version = 0),\\n latest_dataset_lineages AS (SELECT * FROM dataset_lineages WHERE version = 0)\\n SELECT\\n all_entities.urn,\\n generated_dataset_platforms.platform,\\n latest_dataset_info.metadata:description::string as description,\\n COALESCE(IS_NULL_VALUE(latest_dataset_info.metadata:description), TRUE) as is_missing_docs,\\n latest_dataset_info.metadata:customProperties as properties,\\n ARRAY_CAT(COALESCE(latest_dataset_tags.metadata:tags, array_construct()), COALESCE(latest_dataset_info.metadata:tags, array_construct())) as tags,\\n latest_dataset_lineages.metadata:upstreams as upstreams,\\n COALESCE(ARRAY_SIZE(latest_dataset_lineages.metadata:upstreams), 0) as upstream_count,\\n COALESCE(ARRAY_SIZE(latest_dataset_lineages.metadata:upstreams), 0) > 0 as has_upstreams\\n FROM all_entities\\n LEFT JOIN latest_dataset_info ON all_entities.urn = latest_dataset_info.urn\\n LEFT JOIN latest_dataset_tags ON all_entities.urn = latest_dataset_tags.urn\\n LEFT JOIN latest_dataset_lineages ON all_entities.urn = latest_dataset_lineages.urn\\n LEFT JOIN generated_dataset_platforms ON all_entities.urn = generated_dataset_platforms.urn\\n WHERE all_entities.entity = 'dataset'\\n);\\n \"}]", + __typename: 'StringMapEntry', + }, + { key: 'start_date', value: '1619913600.0', __typename: 'StringMapEntry' }, + ], + __typename: 'DataFlowInfo', + }, + editableProperties: null, + ownership: { + owners: [], + lastModified: { time: 1620224528712, __typename: 'AuditStamp' }, + __typename: 'Ownership', + }, + __typename: 'DataFlow', + }, + jobId, + ownership: { + owners: [ + { + owner: kafkaUser, + type: OwnershipType.Developer, + __typename: 'Owner', + }, + ], + lastModified: { time: 1620079975489, __typename: 'AuditStamp' }, + __typename: 'Ownership', + }, + inputOutput: { + inputDatasets: [], + outputDatasets: [], + __typename: 'DataJobInputOutput', + }, + editableProperties: null, + info: { name: jobId, description: null, __typename: 'DataJobInfo' }, + globalTags: { tags: [], __typename: 'GlobalTags' }, + __typename: 'DataJob', + }; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/entity/datasetEntity.ts b/datahub-web-react/src/graphql-mock/fixtures/entity/datasetEntity.ts new file mode 100644 index 0000000000..f9b9b75381 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/entity/datasetEntity.ts @@ -0,0 +1,86 @@ +import * as faker from 'faker'; +import { DataPlatform, Dataset, EntityType, FabricType, OwnershipType, PlatformType } from '../../../types.generated'; +import kafkaLogo from '../../../images/kafkalogo.png'; +import s3Logo from '../../../images/s3.png'; +import snowflakeLogo from '../../../images/snowflakelogo.png'; +import bigqueryLogo from '../../../images/bigquerylogo.png'; +import { findUserByUsername } from '../searchResult/userSearchResult'; + +const platformLogo = { + kafka: kafkaLogo, + s3: s3Logo, + snowflake: snowflakeLogo, + bigquery: bigqueryLogo, +}; + +const generatePlatform = ({ platform, urn }): DataPlatform => { + return { + urn, + type: EntityType.Dataset, + name: platform, + info: { + type: PlatformType.Others, + datasetNameDelimiter: '', + logoUrl: platformLogo[platform], + __typename: 'DataPlatformInfo', + }, + __typename: 'DataPlatform', + }; +}; + +export type DatasetEntityArg = { + platform: string; + origin: FabricType; + path: string; +}; + +export const datasetEntity = ({ + platform, + origin, + path, +}: DatasetEntityArg): Dataset & { previousSchemaMetadata: any } => { + const name = `${path}.${faker.company.bsNoun()}_${faker.company.bsNoun()}`; + const description = `${faker.commerce.productDescription()}`; + const datahubUser = findUserByUsername('datahub'); + const platformURN = `urn:li:dataPlatform:${platform}`; + + return { + urn: `urn:li:dataset:(${platformURN},${name},${origin.toUpperCase()})`, + type: EntityType.Dataset, + name, + origin, + description, + uri: null, + platform: generatePlatform({ platform, urn: platformURN }), + platformNativeType: null, + tags: [], + properties: null, + editableProperties: null, + editableSchemaMetadata: null, + deprecation: null, + ownership: { + owners: [ + { + owner: datahubUser, + type: OwnershipType.Dataowner, + __typename: 'Owner', + }, + ], + lastModified: { + time: 1616107219521, + __typename: 'AuditStamp', + }, + __typename: 'Ownership', + }, + globalTags: { + tags: [], + __typename: 'GlobalTags', + }, + institutionalMemory: null, + usageStats: null, + glossaryTerms: null, + schemaMetadata: null, + previousSchemaMetadata: null, + __typename: 'Dataset', + }; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/entity/userEntity.ts b/datahub-web-react/src/graphql-mock/fixtures/entity/userEntity.ts new file mode 100644 index 0000000000..ec09ed2de7 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/entity/userEntity.ts @@ -0,0 +1,38 @@ +import * as faker from 'faker'; +import { CorpUser, EntityType } from '../../../types.generated'; + +export type UserEntityArg = { + username: string; +}; + +export const userEntity = (option?: UserEntityArg): CorpUser => { + const username = option?.username || `${faker.internet.userName()}`; + const title = `${faker.name.title()}`; + const firstName = `${faker.name.firstName()}`; + const lastName = `${faker.name.lastName()}`; + const email = `${faker.internet.email()}`; + + return { + urn: `urn:li:corpuser:${username}`, + type: EntityType.CorpUser, + username, + info: { + active: true, + displayName: `${firstName} ${lastName}`, + title, + email, + firstName, + lastName, + fullName: `${firstName} ${lastName}`, + __typename: 'CorpUserInfo', + }, + editableInfo: { + aboutMe: `about ${username}`, + teams: [faker.company.companyName(), faker.company.companyName()], + skills: [faker.company.catchPhrase(), faker.company.catchPhrase()], + pictureLink: null, + __typename: 'CorpUserEditableInfo', + }, + __typename: 'CorpUser', + }; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/index.ts b/datahub-web-react/src/graphql-mock/fixtures/index.ts new file mode 100644 index 0000000000..d776447fe9 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/index.ts @@ -0,0 +1,5 @@ +export * from './searchResult'; +export { default as datasetBrowseResult } from './browseDataset'; +export { default as dashboardBrowseResult } from './browseDashboard'; +export { default as chartBrowseResult } from './browseChart'; +export { default as dataflowBrowseResult } from './browseDataFlow'; diff --git a/datahub-web-react/src/graphql-mock/fixtures/searchResult/chartSearchResult.ts b/datahub-web-react/src/graphql-mock/fixtures/searchResult/chartSearchResult.ts new file mode 100644 index 0000000000..684bb2c398 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/searchResult/chartSearchResult.ts @@ -0,0 +1,68 @@ +import { Chart, SearchResult, SearchResults } from '../../../types.generated'; +import { EntityBrowsePath } from '../../types'; +import { filterEntityByPath } from '../browsePathHelper'; +import { chartEntity } from '../entity/chartEntity'; +import { generateData } from './dataGenerator'; + +const searchResult = (tool: string) => (): SearchResult => { + return { + entity: chartEntity(tool), + matchedFields: [], + __typename: 'SearchResult', + }; +}; + +export const chartBrowsePaths: EntityBrowsePath[] = [{ name: 'superset', paths: [], count: 6 }]; + +const generateSearchResults = (): SearchResult[] => { + return chartBrowsePaths.flatMap(({ name, count = 0 }) => { + return generateData({ generator: searchResult(name), count }); + }); +}; + +const searchResults = generateSearchResults(); + +export const chartSearchResult: SearchResults = { + start: 0, + count: 0, + total: 0, + searchResults, + facets: [ + { field: 'access', aggregations: [], __typename: 'FacetMetadata' }, + { + field: 'type', + aggregations: [ + { value: 'TABLE', count: 1, __typename: 'AggregationMetadata' }, + { value: 'BAR', count: 3, __typename: 'AggregationMetadata' }, + { value: 'PIE', count: 1, __typename: 'AggregationMetadata' }, + { value: 'LINE', count: 1, __typename: 'AggregationMetadata' }, + ], + __typename: 'FacetMetadata', + }, + { + field: 'tool', + aggregations: [{ value: 'superset', count: 6, __typename: 'AggregationMetadata' }], + __typename: 'FacetMetadata', + }, + { field: 'queryType', aggregations: [], __typename: 'FacetMetadata' }, + ], + __typename: 'SearchResults', +}; + +export const filterChartByTool = (tool: string): Chart[] => { + return searchResults + .filter((r) => { + return (r.entity as Chart).tool === tool; + }) + .map((r) => r.entity as Chart); +}; + +export const findChartByURN = (urn: string): Chart => { + return searchResults.find((r) => { + return (r.entity as Chart).urn === urn; + })?.entity as Chart; +}; + +export const filterChartByPath = (path: string[]): Chart[] => { + return filterEntityByPath({ term: path.slice(-2).join('.'), searchResults }) as Chart[]; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/searchResult/dashboardSearchResult.ts b/datahub-web-react/src/graphql-mock/fixtures/searchResult/dashboardSearchResult.ts new file mode 100644 index 0000000000..2d01104aaa --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/searchResult/dashboardSearchResult.ts @@ -0,0 +1,57 @@ +import { Dashboard, SearchResult, SearchResults } from '../../../types.generated'; +import { EntityBrowsePath } from '../../types'; +import { filterEntityByPath } from '../browsePathHelper'; +import { dashboardEntity } from '../entity/dashboardEntity'; +import { generateData } from './dataGenerator'; + +const searchResult = (tool: string) => (): SearchResult => { + return { + entity: dashboardEntity(tool), + matchedFields: [], + __typename: 'SearchResult', + }; +}; + +export const dashboardBrowsePaths: EntityBrowsePath[] = [{ name: 'superset', paths: [], count: 3 }]; + +const generateSearchResults = (): SearchResult[] => { + return dashboardBrowsePaths.flatMap(({ name, count = 0 }) => { + return generateData({ generator: searchResult(name), count }); + }); +}; + +const searchResults = generateSearchResults(); + +export const dashboardSearchResult: SearchResults = { + start: 0, + count: 0, + total: 0, + searchResults, + facets: [ + { + field: 'tool', + aggregations: [{ value: 'superset', count: 1, __typename: 'AggregationMetadata' }], + __typename: 'FacetMetadata', + }, + { field: 'access', aggregations: [], __typename: 'FacetMetadata' }, + ], + __typename: 'SearchResults', +}; + +export const filterDashboardByTool = (tool: string): Dashboard[] => { + return searchResults + .filter((r) => { + return (r.entity as Dashboard).tool === tool; + }) + .map((r) => r.entity as Dashboard); +}; + +export const findDashboardByURN = (urn: string): Dashboard => { + return searchResults.find((r) => { + return (r.entity as Dashboard).urn === urn; + })?.entity as Dashboard; +}; + +export const filterDashboardByPath = (path: string[]): Dashboard[] => { + return filterEntityByPath({ term: path.slice(-2).join('.'), searchResults }) as Dashboard[]; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/searchResult/dataFlowSearchResult.ts b/datahub-web-react/src/graphql-mock/fixtures/searchResult/dataFlowSearchResult.ts new file mode 100644 index 0000000000..20a836df42 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/searchResult/dataFlowSearchResult.ts @@ -0,0 +1,72 @@ +import { DataFlow, SearchResult, SearchResults } from '../../../types.generated'; +import { EntityBrowsePath } from '../../types'; +import { filterEntityByPath } from '../browsePathHelper'; +import { dataFlowEntity, DataFlowEntityArg } from '../entity/dataFlowEntity'; +import { generateData } from './dataGenerator'; + +type SearchResultArg = DataFlowEntityArg; + +const searchResult = + ({ orchestrator, cluster }: SearchResultArg) => + (): SearchResult => { + return { + entity: dataFlowEntity({ orchestrator, cluster }), + matchedFields: [], + __typename: 'SearchResult', + }; + }; + +export const dataFlowBrowsePaths: EntityBrowsePath[] = [ + { + name: 'airflow', + paths: [ + { + name: 'prod', + paths: [], + count: 4, + }, + { + name: 'dev', + paths: [], + count: 1, + }, + ], + }, +]; + +const generateSearchResults = (): SearchResult[] => { + return dataFlowBrowsePaths.flatMap(({ name: orchestrator, paths }) => { + return paths.flatMap(({ name: cluster, count = 0 }) => { + return generateData({ generator: searchResult({ orchestrator, cluster }), count }); + }); + }); +}; + +const searchResults = generateSearchResults(); + +export const dataFlowSearchResult: SearchResults = { + start: 0, + count: 0, + total: 0, + searchResults, + facets: [], + __typename: 'SearchResults', +}; + +export const filterDataFlowByOrchestrator = (orchestrator: string): DataFlow[] => { + return searchResults + .filter((r) => { + return (r.entity as DataFlow).orchestrator === orchestrator; + }) + .map((r) => r.entity as DataFlow); +}; + +export const findDataFlowByURN = (urn: string): DataFlow => { + return searchResults.find((r) => { + return (r.entity as DataFlow).urn === urn; + })?.entity as DataFlow; +}; + +export const filterDataFlowByPath = (path: string[]): DataFlow[] => { + return filterEntityByPath({ term: path.slice(-2).join(',[\\s\\S]+,'), searchResults }) as DataFlow[]; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/searchResult/dataGenerator.ts b/datahub-web-react/src/graphql-mock/fixtures/searchResult/dataGenerator.ts new file mode 100644 index 0000000000..d952281791 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/searchResult/dataGenerator.ts @@ -0,0 +1,16 @@ +import { AnyRecord } from '../../types'; + +type GenerateDataArg = { + generator(): T; + count: number; +}; + +export const times = (count: number) => { + return Array.from(new Array(count)); +}; + +export const generateData = ({ generator, count }: GenerateDataArg): T[] => { + return times(count).map(() => { + return generator(); + }); +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/searchResult/dataJobSearchResult.ts b/datahub-web-react/src/graphql-mock/fixtures/searchResult/dataJobSearchResult.ts new file mode 100644 index 0000000000..dbe9b84e1c --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/searchResult/dataJobSearchResult.ts @@ -0,0 +1,32 @@ +import { DataJob, SearchResult, SearchResults } from '../../../types.generated'; +import { dataJobEntity } from '../entity/dataJobEntity'; +import { generateData } from './dataGenerator'; + +const searchResult = (): SearchResult => { + return { + entity: dataJobEntity(), + matchedFields: [], + __typename: 'SearchResult', + }; +}; + +const generateSearchResults = (): SearchResult[] => { + return generateData({ generator: searchResult, count: 2 }); +}; + +const searchResults = generateSearchResults(); + +export const dataJobSearchResult: SearchResults = { + start: 0, + count: 0, + total: 0, + searchResults, + facets: [], + __typename: 'SearchResults', +}; + +export const findDataJobByURN = (urn: string): DataJob => { + return searchResults.find((r) => { + return (r.entity as DataJob).urn === urn; + })?.entity as DataJob; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/searchResult/datasetSearchResult.ts b/datahub-web-react/src/graphql-mock/fixtures/searchResult/datasetSearchResult.ts new file mode 100644 index 0000000000..a6320d75be --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/searchResult/datasetSearchResult.ts @@ -0,0 +1,280 @@ +import { generateData } from './dataGenerator'; +import { datasetEntity, DatasetEntityArg } from '../entity/datasetEntity'; +import { Dataset, FabricType, SearchResult, SearchResults } from '../../../types.generated'; +import { EntityBrowsePath, StringNumber } from '../../types'; +import { filterEntityByPath, toFlatPaths } from '../browsePathHelper'; + +type SearchResultArg = DatasetEntityArg; + +const searchResult = + ({ platform, origin, path }: SearchResultArg) => + (): SearchResult => { + return { + entity: datasetEntity({ platform, origin, path }), + matchedFields: [], + __typename: 'SearchResult', + }; + }; + +export const datasetBrowsePaths: EntityBrowsePath[] = [ + { + name: FabricType.Prod.toLowerCase(), + paths: [ + { + name: 'kafka', + paths: [ + { + name: 'australia', + paths: [ + { + name: 'queensland', + paths: [ + { + name: 'brisbane', + paths: [ + { + name: 'queensland-brisbane-sunnybank-vaccine-test', + paths: [ + { + name: 'topic-queensland-brisbane-sunnybank-vaccine-test-jan', + paths: [], + count: 2, + }, + { + name: 'topic-queensland-brisbane-sunnybank-vaccine-test-feb', + paths: [], + count: 1, + }, + ], + }, + { + name: 'topic-queensland-brisbane-carindale-vaccine-test-jan', + paths: [], + count: 2, + }, + ], + }, + { + name: 'topic-queensland-sunshine-coast-vaccine-test-feb', + paths: [], + count: 1, + }, + ], + }, + { + name: 'victoria', + paths: [ + { + name: 'topic-victoria-vaccine-test-mar', + paths: [], + count: 2, + }, + ], + }, + ], + }, + { + name: 'topic-papua-new-guinea-digital-transformation', + paths: [], + count: 3, + }, + ], + }, + { + name: 's3', + paths: [ + { + name: 'datahub-demo-backup', + paths: [ + { + name: 'demo', + paths: [], + count: 3, + }, + ], + }, + { + name: 'datahubproject-demo-pipelines', + paths: [ + { + name: 'entity_aspect_splits', + paths: [], + count: 21, + }, + ], + }, + ], + }, + { + name: 'snowflake', + paths: [ + { + name: 'mydb', + paths: [ + { + name: 'schema', + paths: [], + count: 3, + }, + ], + }, + { + name: 'demo_pipeline', + paths: [ + { + name: 'public', + paths: [], + count: 2, + }, + ], + }, + ], + }, + { + name: 'bigquery', + paths: [ + { + name: 'bigquery-pubic-data', + paths: [ + { + name: 'covid19-ecdc', + paths: [], + count: 1, + }, + { + name: 'covid19-open-data', + paths: [], + count: 24, + }, + ], + }, + ], + }, + ], + }, + { + name: FabricType.Dev.toLowerCase(), + paths: [ + { + name: 'kafka', + paths: [ + { + name: 'topic-mysql-customer-schema', + paths: [], + count: 1, + }, + { + name: 'rnd', + paths: [ + { + name: 'topic-mysql-marketing-ml-schema', + paths: [], + count: 3, + }, + { + name: 'topic-mysql-sales-ml-schema', + paths: [], + count: 2, + }, + ], + }, + ], + }, + ], + }, +]; + +const generateSearchResults = (): SearchResult[] => { + return datasetBrowsePaths.flatMap(({ name: origin, paths }) => { + return paths.flatMap(({ name: platform, paths: childPaths }) => { + const flatPaths: StringNumber[][] = []; + const parentPaths: string[] = []; + + toFlatPaths({ flatPaths, paths: childPaths, parentPaths }); + + return flatPaths.flatMap((fp) => { + const count = fp.pop() as number; + const path = fp.join('.'); + return generateData({ + generator: searchResult({ platform, origin: origin.toUpperCase() as FabricType, path }), + count, + }); + }); + }); + }); +}; + +const searchResults = generateSearchResults(); + +export const datasetSearchResult: SearchResults = { + start: 0, + count: 0, + total: 0, + searchResults, + facets: [ + { + field: 'platform', + aggregations: [ + { + value: 's3', + count: 22, + __typename: 'AggregationMetadata', + }, + { + value: 'snowflake', + count: 69, + __typename: 'AggregationMetadata', + }, + { + value: 'bigquery', + count: 104, + __typename: 'AggregationMetadata', + }, + { + value: 'kafka', + count: 7, + __typename: 'AggregationMetadata', + }, + ], + __typename: 'FacetMetadata', + }, + { + field: 'origin', + aggregations: [ + { + value: 'prod', + count: 202, + __typename: 'AggregationMetadata', + }, + ], + __typename: 'FacetMetadata', + }, + ], + __typename: 'SearchResults', +}; + +export const filterDatasetByPlatform = (platform: string): Dataset[] => { + return searchResults + .filter((r) => { + return (r.entity as Dataset).platform.name === platform; + }) + .map((r) => r.entity as Dataset); +}; + +export const filterDatasetByName = (name: string): Dataset[] => { + return searchResults + .filter((r) => { + return (r.entity as Dataset).name.indexOf(name) >= 0; + }) + .map((r) => r.entity as Dataset); +}; + +export const findDatasetByURN = (urn: string): Dataset => { + return searchResults.find((r) => { + return (r.entity as Dataset).urn === urn; + })?.entity as Dataset; +}; + +export const filterDatasetByPath = (path: string[]): Dataset[] => { + return filterEntityByPath({ term: path.slice(-2).join('.'), searchResults }) as Dataset[]; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/searchResult/index.ts b/datahub-web-react/src/graphql-mock/fixtures/searchResult/index.ts new file mode 100644 index 0000000000..481dc4cfc4 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/searchResult/index.ts @@ -0,0 +1,6 @@ +export { datasetSearchResult } from './datasetSearchResult'; +export { dashboardSearchResult } from './dashboardSearchResult'; +export { dataFlowSearchResult } from './dataFlowSearchResult'; +export { dataJobSearchResult } from './dataJobSearchResult'; +export { chartSearchResult } from './chartSearchResult'; +export { userSearchResult } from './userSearchResult'; diff --git a/datahub-web-react/src/graphql-mock/fixtures/searchResult/userSearchResult.ts b/datahub-web-react/src/graphql-mock/fixtures/searchResult/userSearchResult.ts new file mode 100644 index 0000000000..68680e79c6 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/searchResult/userSearchResult.ts @@ -0,0 +1,54 @@ +/* eslint-disable prefer-object-spread */ +import { CorpUser, SearchResult, SearchResults } from '../../../types.generated'; +import { userEntity, UserEntityArg } from '../entity/userEntity'; +import { generateData } from './dataGenerator'; + +// login with one of these usernames +const usernames = ['kafka', 'looker', 'datahub']; + +type SearchResultArg = UserEntityArg; + +const searchResult = (option?: SearchResultArg) => (): SearchResult => { + return { + entity: userEntity(option), + matchedFields: [], + __typename: 'SearchResult', + }; +}; + +const generateSearchResults = (): SearchResult[] => { + const loginUsers = usernames.map((username) => { + return searchResult({ username })(); + }); + + return [...loginUsers, ...generateData({ generator: searchResult(), count: 25 })]; +}; + +const searchResults = generateSearchResults(); + +export const userSearchResult: SearchResults = { + start: 0, + count: 0, + total: 0, + searchResults, + facets: [], + __typename: 'SearchResults', +}; + +export const findUserByUsername = (username: string): CorpUser => { + const result = searchResults.find((r) => { + return (r.entity as CorpUser).username === username; + }); + + return Object.assign({}, result?.entity as CorpUser); +}; + +export const getUsers = (): CorpUser[] => { + return searchResults.map((r) => Object.assign({}, r.entity as CorpUser)); +}; + +export const findUserByURN = (urn: string | null): CorpUser => { + return searchResults.find((r) => { + return (r.entity as CorpUser).urn === urn; + })?.entity as CorpUser; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/tag.ts b/datahub-web-react/src/graphql-mock/fixtures/tag.ts new file mode 100644 index 0000000000..89dfe574b5 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/tag.ts @@ -0,0 +1,49 @@ +import * as faker from 'faker'; +import { EntityType, Ownership, OwnershipType, Tag, TagUpdate } from '../../types.generated'; +import { getActor } from '../helper'; +import { findUserByURN } from './searchResult/userSearchResult'; + +export const tagDb: Tag[] = []; + +export const generateTag = (ownership?: Ownership): Tag => { + const name = `${faker.company.bsNoun()}`; + const description = `${faker.commerce.productDescription()}`; + const tag: Tag = { + urn: `urn:li:tag:${name}`, + name, + description, + type: EntityType.Tag, + ownership, + __typename: 'Tag', + }; + + tagDb.push(tag); + + return tag; +}; + +export const createTag = ({ name, urn, description }: TagUpdate): Tag => { + const user = findUserByURN(getActor()); + const tag: Tag = { + urn, + name, + description, + type: EntityType.Tag, + ownership: { + owners: [ + { + owner: user, + type: OwnershipType.Dataowner, + __typename: 'Owner', + }, + ], + lastModified: { time: Date.now(), __typename: 'AuditStamp' }, + __typename: 'Ownership', + }, + __typename: 'Tag', + }; + + tagDb.push(tag); + + return tag; +}; diff --git a/datahub-web-react/src/graphql-mock/fixtures/user.ts b/datahub-web-react/src/graphql-mock/fixtures/user.ts new file mode 100644 index 0000000000..d931876260 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/fixtures/user.ts @@ -0,0 +1,21 @@ +import { getUsers } from './searchResult/userSearchResult'; + +const createCorpUserSchema = ({ server, user }) => { + const { info, editableInfo } = user; + + // eslint-disable-next-line no-param-reassign + delete user.info; + // eslint-disable-next-line no-param-reassign + delete user.editableInfo; + + const userSchema = server.create('CorpUser', user); + userSchema.createInfo(info); + userSchema.createEditableInfo(editableInfo); +}; + +export const createLoginUsers = (server) => { + const users = getUsers(); + users.forEach((user) => { + createCorpUserSchema({ server, user }); + }); +}; diff --git a/datahub-web-react/src/graphql-mock/helper.ts b/datahub-web-react/src/graphql-mock/helper.ts new file mode 100644 index 0000000000..d6dbb24afd --- /dev/null +++ b/datahub-web-react/src/graphql-mock/helper.ts @@ -0,0 +1,19 @@ +import { EntityType } from '../types.generated'; + +/** + * Common helpers + */ + +export const getActor = (): string | null => { + const cookie = new URLSearchParams(document.cookie.replaceAll('; ', '&')); + return cookie.get('actor'); +}; + +export const toLowerCaseEntityType = (type: EntityType): string => { + return type.toLowerCase().replace(/[_]/g, ''); +}; + +export const toTitleCase = (str: string): string => { + // eslint-disable-next-line no-useless-escape + return `${str.charAt(0).toUpperCase()}${str.substr(1)}`.replace(/[\-_]/g, ''); +}; diff --git a/datahub-web-react/src/graphql-mock/mutationHelper.ts b/datahub-web-react/src/graphql-mock/mutationHelper.ts new file mode 100644 index 0000000000..f73b75265b --- /dev/null +++ b/datahub-web-react/src/graphql-mock/mutationHelper.ts @@ -0,0 +1,105 @@ +import { + Chart, + Dashboard, + DataFlow, + DataJob, + Dataset, + Entity, + EntityType, + GlobalTags, + GlobalTagsUpdate, + InstitutionalMemory, + InstitutionalMemoryMetadata, + InstitutionalMemoryUpdate, + Owner, + OwnerUpdate, + TagAssociation, +} from '../types.generated'; +import { findUserByURN } from './fixtures/searchResult/userSearchResult'; +import { tagDb } from './fixtures/tag'; +import { getActor } from './helper'; + +type UpdateEntityOwnersArg = { + entity?: Entity; + owners?: OwnerUpdate[]; +}; + +export const updateEntityOwners = ({ entity, owners }: UpdateEntityOwnersArg) => { + const updateOwners = owners + ?.map((o) => { + const user = findUserByURN(o.owner); + return user + ? { + owner: user, + type: o.type, + __typename: 'Owner', + } + : null; + }) + .filter(Boolean) as Owner[]; + + const dataEntity = entity as Dataset | Chart | Dashboard | DataFlow | DataJob; + if (dataEntity?.ownership?.owners) { + // eslint-disable-next-line no-param-reassign + dataEntity.ownership.owners = updateOwners; + } +}; + +type UpdateEntityTagArg = { + entity?: Entity; + globalTags: GlobalTagsUpdate; +}; + +export const updateEntityTag = ({ entity, globalTags }: UpdateEntityTagArg) => { + const tagAssociations = globalTags.tags + ?.map((t) => { + const tag = tagDb.find((ti) => { + return ti.urn === t.tag.urn; + }); + + return tag + ? { + tag, + __typename: 'TagAssociation', + } + : null; + }) + .filter(Boolean) as TagAssociation[]; + const baseTags: TagAssociation[] = []; + const baseGlobalTags: GlobalTags = { + __typename: 'GlobalTags', + tags: baseTags, + }; + const dataEntity = entity as Dataset | Chart | Dashboard | DataFlow | DataJob; + + dataEntity.globalTags = dataEntity.globalTags || baseGlobalTags; + if (dataEntity.globalTags.tags) { + dataEntity.globalTags.tags = tagAssociations; + } +}; + +type UpdateEntityLinkArg = { + entity: Dataset; + institutionalMemory: InstitutionalMemoryUpdate; +}; + +export const updateEntityLink = ({ entity, institutionalMemory }: UpdateEntityLinkArg) => { + const dataEntity = entity; + const baseElements: InstitutionalMemoryMetadata[] = []; + const baseInstitutionalMemory: InstitutionalMemory = { + elements: baseElements, + __typename: 'InstitutionalMemory', + }; + + dataEntity.institutionalMemory = dataEntity.institutionalMemory || baseInstitutionalMemory; + dataEntity.institutionalMemory.elements = institutionalMemory.elements.map((e) => { + const link: InstitutionalMemoryMetadata = { + __typename: 'InstitutionalMemoryMetadata', + url: e.url, + description: e.description as string, + author: { urn: e.author, username: '', type: EntityType.CorpUser }, + created: { time: Date.now(), actor: getActor(), __typename: 'AuditStamp' }, + }; + return link; + }); +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getAutoCompleteAllResultsResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getAutoCompleteAllResultsResolver.ts new file mode 100644 index 0000000000..c6286ae408 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getAutoCompleteAllResultsResolver.ts @@ -0,0 +1,152 @@ +import { + AutoCompleteAllResults, + AutoCompleteInput, + AutoCompleteResultForEntity, + Chart, + CorpUser, + Dashboard, + DataFlow, + DataJob, + Dataset, + EntityType, + Maybe, +} from '../../types.generated'; +import * as fixtures from '../fixtures'; +import { tagDb } from '../fixtures/tag'; + +type GetAutoCompleteAllResults = { + data: { + autoCompleteForAll: AutoCompleteAllResults; + }; +}; + +const findSuggestions = ({ query, type }: AutoCompleteInput): AutoCompleteResultForEntity[] => { + const q = query.toLowerCase().trim(); + + if (type === EntityType.Tag) { + const results = q + ? tagDb.filter((t) => { + return t.name.indexOf(q) >= 0; + }) + : []; + return [ + { + type: EntityType.Tag, + suggestions: results.map((r) => { + return r.name; + }), + __typename: 'AutoCompleteResultForEntity', + }, + ]; + } + + const filterSearchResults = (): AutoCompleteResultForEntity[] => { + const datasetResults = fixtures.datasetSearchResult.searchResults.filter((r) => { + return ((r.entity as Dataset).name?.toLowerCase()?.indexOf(q) ?? -1) >= 0; + }); + const datasetQueryResults: Maybe = datasetResults.length + ? { + type: EntityType.Dataset, + suggestions: datasetResults.map((r) => { + return (r.entity as Dataset).name; + }), + __typename: 'AutoCompleteResultForEntity', + } + : null; + + const dashboardResults = fixtures.dashboardSearchResult.searchResults.filter((r) => { + return ((r.entity as Dashboard).info?.name?.toLowerCase()?.indexOf(q) ?? -1) >= 0; + }); + const dashboardQueryResults: Maybe = dashboardResults.length + ? { + type: EntityType.Dashboard, + suggestions: dashboardResults.map((r) => { + return (r.entity as Dashboard).info?.name || ''; + }), + __typename: 'AutoCompleteResultForEntity', + } + : null; + + const chartResults = fixtures.chartSearchResult.searchResults.filter((r) => { + return ((r.entity as Chart).info?.name?.toLowerCase()?.indexOf(q) ?? -1) >= 0; + }); + const chartQueryResults: Maybe = chartResults.length + ? { + type: EntityType.Chart, + suggestions: chartResults.map((r) => { + return (r.entity as Chart).info?.name || ''; + }), + __typename: 'AutoCompleteResultForEntity', + } + : null; + + const dataFlowResults = fixtures.dataFlowSearchResult.searchResults.filter((r) => { + return ((r.entity as DataFlow).info?.name?.toLowerCase()?.indexOf(q) ?? -1) >= 0; + }); + const dataFlowQueryResults: Maybe = dataFlowResults.length + ? { + type: EntityType.DataFlow, + suggestions: dataFlowResults.map((r) => { + return (r.entity as DataFlow).info?.name || ''; + }), + __typename: 'AutoCompleteResultForEntity', + } + : null; + + const dataJobResults = fixtures.dataJobSearchResult.searchResults.filter((r) => { + return ((r.entity as DataJob).info?.name?.toLowerCase()?.indexOf(q) ?? -1) >= 0; + }); + const dataJobQueryResults: Maybe = dataJobResults.length + ? { + type: EntityType.DataJob, + suggestions: dataJobResults.map((r) => { + return (r.entity as DataJob).info?.name || ''; + }), + __typename: 'AutoCompleteResultForEntity', + } + : null; + + const userResults = fixtures.userSearchResult.searchResults.filter((r) => { + return ((r.entity as CorpUser).info?.fullName?.toLowerCase()?.indexOf(q) ?? -1) >= 0; + }); + const userQueryResults: Maybe = userResults.length + ? { + type: EntityType.CorpUser, + suggestions: userResults.map((r) => { + return (r.entity as CorpUser).info?.fullName || ''; + }), + __typename: 'AutoCompleteResultForEntity', + } + : null; + + return [ + datasetQueryResults, + dashboardQueryResults, + chartQueryResults, + dataFlowQueryResults, + dataJobQueryResults, + userQueryResults, + ].filter(Boolean) as AutoCompleteResultForEntity[]; + }; + + return q ? filterSearchResults() : []; +}; + +export const getAutoCompleteAllResultsResolver = { + getAutoCompleteAllResults({ variables: { input } }): GetAutoCompleteAllResults { + const { query }: AutoCompleteInput = input; + const suggestions = findSuggestions(input); + + return { + data: { + autoCompleteForAll: { + query, + suggestions: suggestions.filter((s, i) => { + return suggestions.indexOf(s) === i; + }), + __typename: 'AutoCompleteAllResults', + }, + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getAutoCompleteResultsResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getAutoCompleteResultsResolver.ts new file mode 100644 index 0000000000..4f952c2ffb --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getAutoCompleteResultsResolver.ts @@ -0,0 +1,94 @@ +import { + AutoCompleteInput, + AutoCompleteResults, + Chart, + CorpUser, + Dashboard, + DataFlow, + DataJob, + Dataset, + EntityType, +} from '../../types.generated'; +import * as fixtures from '../fixtures'; +import { tagDb } from '../fixtures/tag'; + +type GetAutoCompleteResults = { + data: { + autoComplete: AutoCompleteResults; + }; +}; + +const findSuggestions = ({ query, type, field }: AutoCompleteInput): string[] => { + const q = query.toLowerCase().trim(); + + if (type === EntityType.Tag) { + const results = q + ? tagDb.filter((t) => { + return t.name.indexOf(q) >= 0; + }) + : []; + return results.map((r) => { + return r.name; + }); + } + + const filterSearchResults = () => { + if (field === 'ldap') { + return fixtures.userSearchResult.searchResults.filter((r) => { + return ((r.entity as CorpUser).username?.indexOf(q) ?? -1) >= 0; + }); + } + return [ + ...fixtures.datasetSearchResult.searchResults.filter((r) => { + return ((r.entity as Dataset).name?.indexOf(q) ?? -1) >= 0; + }), + ...fixtures.dashboardSearchResult.searchResults.filter((r) => { + return ((r.entity as Dashboard).info?.name?.indexOf(q) ?? -1) >= 0; + }), + ...fixtures.chartSearchResult.searchResults.filter((r) => { + return ((r.entity as Chart).info?.name?.indexOf(q) ?? -1) >= 0; + }), + ...fixtures.dataFlowSearchResult.searchResults.filter((r) => { + return ((r.entity as DataFlow).info?.name?.indexOf(q) ?? -1) >= 0; + }), + ...fixtures.dataJobSearchResult.searchResults.filter((r) => { + return ((r.entity as DataJob).info?.name?.indexOf(q) ?? -1) >= 0; + }), + ...fixtures.userSearchResult.searchResults.filter((r) => { + return ((r.entity as CorpUser).info?.fullName?.toLowerCase()?.indexOf(q) ?? -1) >= 0; + }), + ]; + }; + + const results = q ? filterSearchResults() : []; + + return results + .map((r) => { + return field === 'ldap' + ? (r.entity as CorpUser)?.username + : (r.entity as Dataset)?.name || + (r.entity as Dashboard | Chart | DataFlow | DataJob)?.info?.name || + (r.entity as CorpUser)?.info?.fullName || + ''; + }) + .filter(Boolean); +}; + +export const getAutoCompleteResultsResolver = { + getAutoCompleteResults({ variables: { input } }): GetAutoCompleteResults { + const { query }: AutoCompleteInput = input; + const suggestions = findSuggestions(input); + + return { + data: { + autoComplete: { + query, + suggestions: suggestions.filter((s, i) => { + return suggestions.indexOf(s) === i; + }), + __typename: 'AutoCompleteResults', + }, + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getBrowsePathsResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getBrowsePathsResolver.ts new file mode 100644 index 0000000000..2e438b93a7 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getBrowsePathsResolver.ts @@ -0,0 +1,44 @@ +import { BrowsePath, BrowsePathsInput, EntityType } from '../../types.generated'; + +const paths = { + [EntityType.Dataset](urn) { + const result = urn.replace('urn:li:dataset:(urn:li:dataPlatform:', '').replace(')', '').split(','); + return [result[result.length - 1].toLowerCase(), result[0], ...result[1].split('.')]; + }, + [EntityType.Dashboard](urn) { + return urn.replace('urn:li:dashboard:(', '').replace(')', '').split(','); + }, + [EntityType.Chart](urn) { + return urn.replace('urn:li:chart:(', '').replace(')', '').split(','); + }, + [EntityType.DataFlow](urn) { + const result = urn.replace('urn:li:dataFlow:(', '').replace(')', '').split(','); + return [result[0], result[result.length - 1].toLowerCase(), result[1]]; + }, + [EntityType.DataJob]() { + return []; + }, +}; + +type GetBrowsePaths = { + data: { + browsePaths: BrowsePath[]; + }; +}; + +export const getBrowsePathsResolver = { + getBrowsePaths({ variables: { input } }): GetBrowsePaths { + const { urn, type }: BrowsePathsInput = input; + + return { + data: { + browsePaths: [ + { + path: paths[type](urn), + __typename: 'BrowsePath', + }, + ], + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getBrowseResultsResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getBrowseResultsResolver.ts new file mode 100644 index 0000000000..c62c65dffa --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getBrowseResultsResolver.ts @@ -0,0 +1,27 @@ +import * as fixtures from '../fixtures'; +import { BrowseInput } from '../../types.generated'; +import { EntityBrowseFn, GetBrowseResults } from '../types'; +import { toLowerCaseEntityType, toTitleCase } from '../helper'; + +const toPathTitle = (paths: string[]): string => { + return paths?.map((p) => toTitleCase(p)).join(''); +}; + +export const getBrowseResultsResolver = { + getBrowseResults({ variables: { input } }): GetBrowseResults { + const { type, path = [], start = 0, count = 0 }: BrowseInput = input; + const startValue = start as number; + const countValue = count as number; + const paths = path as string[]; + const entityType = toLowerCaseEntityType(type); + const pathTitle = toPathTitle(paths); + + const result: GetBrowseResults | EntityBrowseFn = + fixtures[`${entityType}BrowseResult`][`${entityType}Browse${pathTitle}`]; + + if (typeof result === 'function') { + return result({ start: startValue, count: countValue, path: paths }); + } + return result; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getChartResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getChartResolver.ts new file mode 100644 index 0000000000..2be8be4469 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getChartResolver.ts @@ -0,0 +1,39 @@ +import { Chart } from '../../types.generated'; +import { findChartByURN } from '../fixtures/searchResult/chartSearchResult'; + +type GetChart = { + data: { + chart: Chart; + }; +}; + +export const getChartResolver = { + getChart({ variables: { urn } }): GetChart { + const chart = findChartByURN(urn) as Chart; + return { + data: { + chart: Object.assign(chart, { + info: { + ...chart.info, + inputs: [], + customProperties: [], + lastRefreshed: null, + created: { + time: 1619160920, + __typename: 'AuditStamp', + }, + }, + query: null, + downstreamLineage: { + entities: [], + __typename: 'DownstreamEntityRelationships', + }, + upstreamLineage: { + entities: [], + __typename: 'UpstreamEntityRelationships', + }, + }), + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getDashboardResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getDashboardResolver.ts new file mode 100644 index 0000000000..1cd571ede1 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getDashboardResolver.ts @@ -0,0 +1,36 @@ +import { Dashboard } from '../../types.generated'; +import { findDashboardByURN } from '../fixtures/searchResult/dashboardSearchResult'; + +type GetDashboard = { + data: { dashboard: Dashboard }; +}; + +export const getDashboardResolver = { + getDashboard({ variables: { urn } }): GetDashboard { + const dashboard = findDashboardByURN(urn) as Dashboard; + return { + data: { + dashboard: Object.assign(dashboard, { + info: { + ...dashboard.info, + charts: [], + customProperties: [], + lastRefreshed: null, + created: { + time: 1619160920, + __typename: 'AuditStamp', + }, + }, + downstreamLineage: { + entities: [], + __typename: 'DownstreamEntityRelationships', + }, + upstreamLineage: { + entities: [], + __typename: 'UpstreamEntityRelationships', + }, + }), + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getDataFlowResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getDataFlowResolver.ts new file mode 100644 index 0000000000..c677c48ef9 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getDataFlowResolver.ts @@ -0,0 +1,24 @@ +import { DataFlow } from '../../types.generated'; +import { findDataFlowByURN } from '../fixtures/searchResult/dataFlowSearchResult'; + +type GetDataFlow = { + data: { dataFlow: DataFlow }; +}; + +export const getDataFlowResolver = { + getDataFlow({ variables: { urn } }): GetDataFlow { + const dataFlow = findDataFlowByURN(urn) as DataFlow; + return { + data: { + dataFlow: Object.assign(dataFlow, { + info: { + ...dataFlow.info, + externalUrl: 'https://airflow.demo.datahubproject.io/tree?dag_id=datahub_analytics_refresh', + inputs: [], + customProperties: [], + }, + }), + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getDataJobResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getDataJobResolver.ts new file mode 100644 index 0000000000..584abcaf20 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getDataJobResolver.ts @@ -0,0 +1,32 @@ +import { DataJob } from '../../types.generated'; +import { findDataJobByURN } from '../fixtures/searchResult/dataJobSearchResult'; + +type GetJobFlow = { + data: { dataJob: DataJob }; +}; + +export const getDataJobResolver = { + getDataJob({ variables: { urn } }): GetJobFlow { + const dataJob = findDataJobByURN(urn) as DataJob; + return { + data: { + dataJob: Object.assign(dataJob, { + info: { + ...dataJob.info, + externalUrl: 'https://airflow.demo.datahubproject.io/tree?dag_id=datahub_analytics_refresh', + inputs: [], + customProperties: [], + }, + downstreamLineage: { + entities: [], + __typename: 'DownstreamEntityRelationships', + }, + upstreamLineage: { + entities: [], + __typename: 'UpstreamEntityRelationships', + }, + }), + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getDatasetResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getDatasetResolver.ts new file mode 100644 index 0000000000..ffdf3920c9 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getDatasetResolver.ts @@ -0,0 +1,43 @@ +import { Dataset, InstitutionalMemory, InstitutionalMemoryMetadata } from '../../types.generated'; +import { findDatasetByURN } from '../fixtures/searchResult/datasetSearchResult'; + +type GetDataset = { + data: { dataset: Dataset }; +}; + +export const getDatasetResolver = { + getDataset({ variables: { urn } }): GetDataset { + const dataset = findDatasetByURN(urn); + + if (!dataset.institutionalMemory) { + const baseElements: InstitutionalMemoryMetadata[] = []; + const baseInstitutionalMemory: InstitutionalMemory = { + elements: baseElements, + __typename: 'InstitutionalMemory', + }; + dataset.institutionalMemory = baseInstitutionalMemory; + } + + return { + data: { + dataset: Object.assign(dataset, { + schema: null, + editableProperties: null, + editableSchemaMetadata: null, + deprecation: null, + downstreamLineage: { + entities: [], + __typename: 'DownstreamEntityRelationships', + }, + upstreamLineage: { + entities: [], + __typename: 'UpstreamEntityRelationships', + }, + glossaryTerms: null, + institutionalMemory: null, + usageStats: null, + }), + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getSearchResultsResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getSearchResultsResolver.ts new file mode 100644 index 0000000000..7634cb4c9a --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getSearchResultsResolver.ts @@ -0,0 +1,114 @@ +import * as fixtures from '../fixtures'; +import { + Chart, + CorpUser, + Dashboard, + DataFlow, + DataJob, + Dataset, + EntityType, + SearchInput, + SearchResult, + SearchResults, +} from '../../types.generated'; + +const entitySearchResults = { + [EntityType.Dataset]: fixtures.datasetSearchResult, + [EntityType.Dashboard]: fixtures.dashboardSearchResult, + [EntityType.Chart]: fixtures.chartSearchResult, + [EntityType.CorpUser]: fixtures.userSearchResult, + [EntityType.DataFlow]: fixtures.dataFlowSearchResult, + [EntityType.DataJob]: fixtures.dataJobSearchResult, +}; + +type FindByQueryArg = { + query: string; + searchResults: SearchResult[]; + start: number; + count: number; +}; + +type QueryResult = { + results: SearchResult[]; + total: number; +}; + +const findByQuery = ({ query, searchResults, start, count }: FindByQueryArg): QueryResult => { + if (query === '*') { + const results = searchResults.slice(start, start + count); + + return { results, total: results.length }; + } + + if (query.indexOf('owners:') >= 0) { + const [, ownerQuery] = query.split(':'); + const results = searchResults.filter((r) => { + return (r.entity as Dataset | Dashboard | Chart | DataFlow | DataJob).ownership?.owners?.filter((o) => { + return (o.owner as CorpUser).username === ownerQuery; + }).length; + }); + + return { results, total: results.length }; + } + + if (query.indexOf('tags:') >= 0) { + const [, tagQuery] = query.split(':'); + const results = searchResults.filter((r) => { + return (r.entity as Dataset | Dashboard | Chart | DataFlow | DataJob).globalTags?.tags?.filter((t) => { + return t.tag.name === tagQuery; + }).length; + }); + + return { results, total: results.length }; + } + + const results = [ + ...searchResults.filter((r) => { + return ((r.entity as Dataset).name?.indexOf(query) ?? -1) >= 0; + }), + ...searchResults.filter((r) => { + return ((r.entity as Dashboard | Chart | DataFlow | DataJob).info?.name?.indexOf(query) ?? -1) >= 0; + }), + ...searchResults.filter((r) => { + return ((r.entity as CorpUser).info?.fullName?.toLowerCase()?.indexOf(query) ?? -1) >= 0; + }), + ]; + + return { results, total: results.length }; +}; + +type GetSearchResults = { + data: { + search: SearchResults; + }; +} | null; + +export const getSearchResultsResolver = { + getSearchResults({ variables: { input } }): GetSearchResults { + const { type, start = 0, count = 10, query }: SearchInput = input; + const startValue = start as number; + const countValue = count as number; + const entitySearchResult: SearchResults = entitySearchResults[type]; + const { results, total } = findByQuery({ + query: query.toLowerCase(), + searchResults: entitySearchResult.searchResults, + start: startValue, + count: countValue, + }); + + return entitySearchResult + ? { + data: { + search: { + start: startValue, + count: results.length, + total, + searchResults: results, + facets: entitySearchResult.facets, + __typename: entitySearchResult.__typename, + }, + }, + } + : null; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/getTagResolver.ts b/datahub-web-react/src/graphql-mock/resolver/getTagResolver.ts new file mode 100644 index 0000000000..2c72292ae6 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/getTagResolver.ts @@ -0,0 +1,24 @@ +import { Tag } from '../../types.generated'; +import { tagDb } from '../fixtures/tag'; + +type GetTag = { + data: { tag: Tag | undefined }; +}; + +export const getTagResolver = { + getTag({ variables: { urn } }): GetTag { + const tag = tagDb.find((t) => { + return t.urn === urn; + }); + + if (tag && !tag?.ownership) { + tag.ownership = null; + } + + return { + data: { + tag, + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/index.ts b/datahub-web-react/src/graphql-mock/resolver/index.ts new file mode 100644 index 0000000000..519629d9ea --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/index.ts @@ -0,0 +1,44 @@ +import { getBrowseResultsResolver } from './getBrowseResultsResolver'; +import { getSearchResultsResolver } from './getSearchResultsResolver'; +import { getAutoCompleteAllResultsResolver } from './getAutoCompleteAllResultsResolver'; +import { getAutoCompleteResultsResolver } from './getAutoCompleteResultsResolver'; +import { getBrowsePathsResolver } from './getBrowsePathsResolver'; +import { getDatasetResolver } from './getDatasetResolver'; +import { getDashboardResolver } from './getDashboardResolver'; +import { getChartResolver } from './getChartResolver'; +import { getDataFlowResolver } from './getDataFlowResolver'; +import { getDataJobResolver } from './getDataJobResolver'; +import { getTagResolver } from './getTagResolver'; +import { isAnalyticsEnabledResolver } from './isAnalyticsEnabledResolver'; +import { updateDatasetResolver } from './updateDatasetResolver'; +import { updateDashboardResolver } from './updateDashboardResolver'; +import { updateChartResolver } from './updateChartResolver'; +import { updateDataFlowResolver } from './updateDataFlowResolver'; +import { updateDataJobResolver } from './updateDataJobResolver'; +import { updateTagResolver } from './updateTagResolver'; + +const resolver = { + ...getSearchResultsResolver, + ...getBrowseResultsResolver, + ...getAutoCompleteAllResultsResolver, + ...getAutoCompleteResultsResolver, + ...getBrowsePathsResolver, + ...getDatasetResolver, + ...getDashboardResolver, + ...getChartResolver, + ...getDataFlowResolver, + ...getDataJobResolver, + ...getTagResolver, + ...isAnalyticsEnabledResolver, + ...updateDatasetResolver, + ...updateDashboardResolver, + ...updateChartResolver, + ...updateDataFlowResolver, + ...updateDataJobResolver, + ...updateTagResolver, +}; + +export const resolveRequest = (schema, request) => { + const { operationName, variables } = JSON.parse(request.requestBody); + return resolver[operationName] && resolver[operationName]({ schema, variables }); +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/isAnalyticsEnabledResolver.ts b/datahub-web-react/src/graphql-mock/resolver/isAnalyticsEnabledResolver.ts new file mode 100644 index 0000000000..cb229a64a1 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/isAnalyticsEnabledResolver.ts @@ -0,0 +1,5 @@ +export const isAnalyticsEnabledResolver = { + isAnalyticsEnabled(): boolean { + return false; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/updateChartResolver.ts b/datahub-web-react/src/graphql-mock/resolver/updateChartResolver.ts new file mode 100644 index 0000000000..1ae5795cce --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/updateChartResolver.ts @@ -0,0 +1,46 @@ +import { Chart, ChartUpdateInput } from '../../types.generated'; +import { findChartByURN } from '../fixtures/searchResult/chartSearchResult'; +import { updateEntityOwners, updateEntityTag } from '../mutationHelper'; + +type UpdateChart = { + data: { updateChart: Chart }; +}; + +export const updateChartResolver = { + updateChart({ variables: { input } }): UpdateChart { + const { globalTags, urn, ownership }: ChartUpdateInput = input; + const chart = findChartByURN(urn); + + if (ownership) { + updateEntityOwners({ entity: chart, owners: ownership?.owners }); + } else if (globalTags) { + updateEntityTag({ entity: chart, globalTags }); + } + + return { + data: { + updateChart: Object.assign(chart, { + info: { + ...chart.info, + inputs: [], + customProperties: [], + lastRefreshed: null, + created: { + time: 1619160920, + __typename: 'AuditStamp', + }, + }, + query: null, + downstreamLineage: { + entities: [], + __typename: 'DownstreamEntityRelationships', + }, + upstreamLineage: { + entities: [], + __typename: 'UpstreamEntityRelationships', + }, + }), + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/updateDashboardResolver.ts b/datahub-web-react/src/graphql-mock/resolver/updateDashboardResolver.ts new file mode 100644 index 0000000000..4be6e16ccf --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/updateDashboardResolver.ts @@ -0,0 +1,45 @@ +import { Dashboard, DashboardUpdateInput } from '../../types.generated'; +import { findDashboardByURN } from '../fixtures/searchResult/dashboardSearchResult'; +import { updateEntityOwners, updateEntityTag } from '../mutationHelper'; + +type UpdateDashboard = { + data: { updateDashboard: Dashboard }; +}; + +export const updateDashboardResolver = { + updateDashboard({ variables: { input } }): UpdateDashboard { + const { urn, ownership, globalTags }: DashboardUpdateInput = input; + const dashboard = findDashboardByURN(urn); + + if (ownership) { + updateEntityOwners({ entity: dashboard, owners: ownership?.owners }); + } else if (globalTags) { + updateEntityTag({ entity: dashboard, globalTags }); + } + + return { + data: { + updateDashboard: Object.assign(dashboard, { + info: { + ...dashboard.info, + charts: [], + customProperties: [], + lastRefreshed: null, + created: { + time: 1619160920, + __typename: 'AuditStamp', + }, + }, + downstreamLineage: { + entities: [], + __typename: 'DownstreamEntityRelationships', + }, + upstreamLineage: { + entities: [], + __typename: 'UpstreamEntityRelationships', + }, + }), + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/updateDataFlowResolver.ts b/datahub-web-react/src/graphql-mock/resolver/updateDataFlowResolver.ts new file mode 100644 index 0000000000..5c99ea8f72 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/updateDataFlowResolver.ts @@ -0,0 +1,33 @@ +import { DataFlow, DataFlowUpdateInput } from '../../types.generated'; +import { findDataFlowByURN } from '../fixtures/searchResult/dataFlowSearchResult'; +import { updateEntityOwners, updateEntityTag } from '../mutationHelper'; + +type UpdateDataFlow = { + data: { updateDataFlow: DataFlow }; +}; + +export const updateDataFlowResolver = { + updateDataFlow({ variables: { input } }): UpdateDataFlow { + const { globalTags, urn, ownership }: DataFlowUpdateInput = input; + const dataFlow = findDataFlowByURN(urn); + + if (ownership) { + updateEntityOwners({ entity: dataFlow, owners: ownership?.owners }); + } else if (globalTags) { + updateEntityTag({ entity: dataFlow, globalTags }); + } + + return { + data: { + updateDataFlow: Object.assign(dataFlow, { + info: { + ...dataFlow.info, + externalUrl: 'https://airflow.demo.datahubproject.io/tree?dag_id=datahub_analytics_refresh', + inputs: [], + customProperties: [], + }, + }), + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/updateDataJobResolver.ts b/datahub-web-react/src/graphql-mock/resolver/updateDataJobResolver.ts new file mode 100644 index 0000000000..e45dae4a2d --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/updateDataJobResolver.ts @@ -0,0 +1,26 @@ +import { DataJob, DataJobUpdateInput } from '../../types.generated'; +import { findDataJobByURN } from '../fixtures/searchResult/dataJobSearchResult'; +import { updateEntityOwners, updateEntityTag } from '../mutationHelper'; + +type UpdateDataJob = { + data: { updateDataJob: DataJob }; +}; + +export const updateDataJobResolver = { + updateDataJob({ variables: { input } }): UpdateDataJob { + const { urn, ownership, globalTags }: DataJobUpdateInput = input; + const dataJob = findDataJobByURN(urn); + + if (ownership) { + updateEntityOwners({ entity: dataJob, owners: ownership?.owners }); + } else if (globalTags) { + updateEntityTag({ entity: dataJob, globalTags }); + } + + return { + data: { + updateDataJob: dataJob, + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/updateDatasetResolver.ts b/datahub-web-react/src/graphql-mock/resolver/updateDatasetResolver.ts new file mode 100644 index 0000000000..30b3f6c422 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/updateDatasetResolver.ts @@ -0,0 +1,40 @@ +import { Dataset, DatasetUpdateInput } from '../../types.generated'; +import { findDatasetByURN } from '../fixtures/searchResult/datasetSearchResult'; +import { updateEntityLink, updateEntityOwners, updateEntityTag } from '../mutationHelper'; + +type UpdateDataset = { + data: { updateDataset: Dataset }; +}; + +export const updateDatasetResolver = { + updateDataset({ variables: { input } }): UpdateDataset { + const { urn, ownership, globalTags, institutionalMemory }: DatasetUpdateInput = input; + const dataset = findDatasetByURN(urn); + + if (ownership) { + updateEntityOwners({ entity: dataset, owners: ownership.owners }); + } else if (globalTags) { + updateEntityTag({ entity: dataset, globalTags }); + } else if (institutionalMemory) { + updateEntityLink({ entity: dataset, institutionalMemory }); + } + + return { + data: { + updateDataset: Object.assign(dataset, { + schema: null, + editableSchemaMetadata: null, + deprecation: null, + downstreamLineage: { + entities: [], + __typename: 'DownstreamEntityRelationships', + }, + upstreamLineage: { + entities: [], + __typename: 'UpstreamEntityRelationships', + }, + }), + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/resolver/updateTagResolver.ts b/datahub-web-react/src/graphql-mock/resolver/updateTagResolver.ts new file mode 100644 index 0000000000..0d9ad3d6a8 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/resolver/updateTagResolver.ts @@ -0,0 +1,18 @@ +import { Tag } from '../../types.generated'; +import { createTag } from '../fixtures/tag'; + +type UpdateTag = { + data: { updateTag: Tag | undefined }; +}; + +export const updateTagResolver = { + updateTag({ variables: { input } }): UpdateTag { + const tag: Tag = createTag(input); + + return { + data: { + updateTag: tag, + }, + }; + }, +}; diff --git a/datahub-web-react/src/graphql-mock/schema.ts b/datahub-web-react/src/graphql-mock/schema.ts new file mode 100644 index 0000000000..b95511e0a0 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/schema.ts @@ -0,0 +1,14 @@ +import { loader } from 'graphql.macro'; + +import gql from 'graphql-tag'; +import { buildASTSchema } from 'graphql'; + +const gmsSchema = loader('../../../datahub-graphql-core/src/main/resources/gms.graphql'); +const feSchema = loader('../../../datahub-frontend/conf/datahub-frontend.graphql'); + +const graphQLSchemaAST = gql` + ${gmsSchema} + ${feSchema} +`; + +export const graphQLSchema = buildASTSchema(graphQLSchemaAST); diff --git a/datahub-web-react/src/graphql-mock/server.ts b/datahub-web-react/src/graphql-mock/server.ts new file mode 100644 index 0000000000..a4896e7290 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/server.ts @@ -0,0 +1,83 @@ +/* eslint-disable no-restricted-syntax */ +import { Model, Response, createServer, belongsTo } from 'miragejs'; +import { createGraphQLHandler } from '@miragejs/graphql'; + +import { graphQLSchema } from './schema'; +import { GlobalCfg } from '../conf'; + +import { resolveRequest } from './resolver'; +import { createLoginUsers } from './fixtures/user'; +import { findUserByURN } from './fixtures/searchResult/userSearchResult'; + +export function makeServer(environment = 'development') { + return createServer({ + environment, + models: { + CorpUser: Model.extend({ + info: belongsTo('CorpUserInfo'), + editableInfo: belongsTo('CorpUserEditableInfo'), + }), + }, + + seeds(server) { + createLoginUsers(server); + + console.log(server.db.dump()); + }, + + routes() { + const graphQLHandler = createGraphQLHandler(graphQLSchema, this.schema); + + this.post('/api/v2/graphql', (schema, request) => { + return resolveRequest(schema, request) ?? graphQLHandler(schema, request); + }); + + this.get('/authenticate', () => new Response(200)); + + this.post('/logIn', (_schema, request) => { + const payload = JSON.parse(request.requestBody); + const cookieExpiration = new Date(Date.now() + 24 * 3600 * 1000); + const urn = `urn:li:corpuser:${payload.username}`; + const user = findUserByURN(urn); + + if (!user) { + return new Response(404); + } + + document.cookie = `${ + GlobalCfg.CLIENT_AUTH_COOKIE + }=${urn}; domain=localhost; path=/; expires=${cookieExpiration.toUTCString()};`; + + return new Response(200); + }); + + this.post('/track', () => new Response(200)); + }, + }); +} + +export function makeServerForCypress() { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (window.Cypress) { + const otherDomains = []; + const methods = ['head', 'get', 'put', 'patch', 'post', 'delete']; + + createServer({ + environment: 'test', + + routes() { + for (const domain of ['/*', ...otherDomains]) { + for (const method of methods) { + this[method](`${domain}`, async (_schema, request) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const [status, headers, body] = await window.handleFromCypress(request); + return new Response(status, headers, body); + }); + } + } + }, + }); + } +} diff --git a/datahub-web-react/src/graphql-mock/types.ts b/datahub-web-react/src/graphql-mock/types.ts new file mode 100644 index 0000000000..f04ebfbf41 --- /dev/null +++ b/datahub-web-react/src/graphql-mock/types.ts @@ -0,0 +1,36 @@ +import { BrowseResults } from '../types.generated'; + +export type DataSchema = { + id?: string | undefined; + attrs: AnyRecord; + modelName: string; + save(): void; + update(key: K, value: AnyRecord[K]): void; + update(changes: Partial): void; + destroy(): void; + reload(): void; +}; + +export type AnyRecord = Record; + +export type EntityBrowsePath = { + name: string; + paths: EntityBrowsePath[]; + count?: number; +}; + +export type StringNumber = string | number; + +export type GetBrowseResults = { + data: { + browse: BrowseResults; + }; +}; + +type EntityBrowseFnArg = { + start: number; + count: number; + path: string[]; +}; + +export type EntityBrowseFn = (arg: EntityBrowseFnArg) => GetBrowseResults; diff --git a/datahub-web-react/src/index.tsx b/datahub-web-react/src/index.tsx index ae32061f44..2eb918a450 100644 --- a/datahub-web-react/src/index.tsx +++ b/datahub-web-react/src/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; +import './graphql-mock/createServer'; import App from './App'; import reportWebVitals from './reportWebVitals'; diff --git a/datahub-web-react/src/setupProxy.js b/datahub-web-react/src/setupProxy.js new file mode 100644 index 0000000000..0a4adfab12 --- /dev/null +++ b/datahub-web-react/src/setupProxy.js @@ -0,0 +1,24 @@ +if (process.env.REACT_APP_MOCK === 'true' || process.env.REACT_APP_MOCK === 'cy') { + // no proxy needed, MirageJS will intercept all http requests + module.exports = function () {}; +} else { + // create a proxy to the graphql server running in docker container + const { createProxyMiddleware } = require('http-proxy-middleware'); + + module.exports = function (app) { + app.use( + '/logIn', + createProxyMiddleware({ + target: 'http://localhost:9002', + changeOrigin: true, + }), + ); + app.use( + '/api/v2/graphql', + createProxyMiddleware({ + target: 'http://localhost:9002', + changeOrigin: true, + }), + ); + }; +} diff --git a/datahub-web-react/yarn.lock b/datahub-web-react/yarn.lock index f07a4334b4..ad086cfaa6 100644 --- a/datahub-web-react/yarn.lock +++ b/datahub-web-react/yarn.lock @@ -138,6 +138,13 @@ dependencies: "@babel/highlight" "^7.12.13" +"@babel/code-frame@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" + integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== + dependencies: + "@babel/highlight" "^7.14.5" + "@babel/compat-data@^7.12.1", "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.14.0": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919" @@ -195,6 +202,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" + integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== + dependencies: + "@babel/types" "^7.14.5" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" @@ -270,6 +286,15 @@ "@babel/template" "^7.12.13" "@babel/types" "^7.14.2" +"@babel/helper-function-name@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" + integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== + dependencies: + "@babel/helper-get-function-arity" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/types" "^7.14.5" + "@babel/helper-get-function-arity@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" @@ -277,6 +302,13 @@ dependencies: "@babel/types" "^7.12.13" +"@babel/helper-get-function-arity@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" + integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== + dependencies: + "@babel/types" "^7.14.5" + "@babel/helper-hoist-variables@^7.13.0": version "7.13.16" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz#1b1651249e94b51f8f0d33439843e33e39775b30" @@ -285,6 +317,13 @@ "@babel/traverse" "^7.13.15" "@babel/types" "^7.13.16" +"@babel/helper-hoist-variables@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" + integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== + dependencies: + "@babel/types" "^7.14.5" + "@babel/helper-member-expression-to-functions@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" @@ -365,11 +404,23 @@ dependencies: "@babel/types" "^7.12.13" +"@babel/helper-split-export-declaration@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" + integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== + dependencies: + "@babel/types" "^7.14.5" + "@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.0": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== +"@babel/helper-validator-identifier@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" + integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== + "@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.12.17": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" @@ -403,6 +454,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@7.12.16": version "7.12.16" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.16.tgz#cc31257419d2c3189d394081635703f549fc1ed4" @@ -413,6 +473,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.3.tgz#9b530eecb071fd0c93519df25c5ff9f14759f298" integrity sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ== +"@babel/parser@^7.1.6", "@babel/parser@^7.14.5", "@babel/parser@^7.14.7": + version "7.14.7" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" + integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a" @@ -1309,6 +1374,15 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" +"@babel/template@^7.14.5", "@babel/template@^7.4.4": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" + integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/parser" "^7.14.5" + "@babel/types" "^7.14.5" + "@babel/traverse@7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.13.tgz#689f0e4b4c08587ad26622832632735fb8c4e0c0" @@ -1338,6 +1412,21 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.1.6": + version "7.14.7" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753" + integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.5" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-hoist-variables" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/parser" "^7.14.7" + "@babel/types" "^7.14.5" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.13.tgz#8be1aa8f2c876da11a9cf650c0ecf656913ad611" @@ -1355,6 +1444,14 @@ "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" +"@babel/types@^7.1.6", "@babel/types@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" + integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1393,6 +1490,59 @@ resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f" integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ== +"@cypress/listr-verbose-renderer@^0.4.1": + version "0.4.1" + resolved "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" + integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo= + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +"@cypress/request@^2.88.5": + version "2.88.5" + resolved "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz#8d7ecd17b53a849cfd5ab06d5abe7d84976375d7" + integrity sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +"@cypress/webpack-preprocessor@5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@cypress/webpack-preprocessor/-/webpack-preprocessor-5.8.0.tgz#ae89775c0833d6d3c00fe9349ded270043cc6843" + integrity sha512-8R0EIWTPsOQQx1yGuQf6QFkaPxH3WtLbmIezeC4nWs8ITBTRASTTIhiXtbrntVphqFQX0Hm0eCiYEEwzBgVbog== + dependencies: + bluebird "^3.7.1" + debug "4.3.2" + lodash "^4.17.20" + +"@cypress/xvfb@^1.2.4": + version "1.2.4" + resolved "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + "@data-ui/shared@^0.0.84": version "0.0.84" resolved "https://registry.yarnpkg.com/@data-ui/shared/-/shared-0.0.84.tgz#42bd025d677f9be2beada3e1a84a53d33ac0eb10" @@ -1891,6 +2041,11 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== +"@hapi/hoek@^9.0.0": + version "9.2.0" + resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" + integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug== + "@hapi/joi@^15.1.0": version "15.1.1" resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7" @@ -1908,6 +2063,13 @@ dependencies: "@hapi/hoek" "^8.3.0" +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@iarna/toml@^2.2.5": version "2.2.5" resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" @@ -2114,6 +2276,19 @@ resolved "https://registry.yarnpkg.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz#9ceecc94b49fbaa15666e38ae8587f64acce007d" integrity sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA== +"@miragejs/graphql@^0.1.11": + version "0.1.12" + resolved "https://registry.npmjs.org/@miragejs/graphql/-/graphql-0.1.12.tgz#60679c4ad807fc4a001bc88aba396ba3fa5a958b" + integrity sha512-9FI7+ZWeIl7oqL//0hp72o1mXzcL4qphGUuqFaptX6Yrd/UPd1fNbmbgCGnFVZGCsNuEhNX0xgn0/YDdncvJDg== + dependencies: + graphql "^15.0.0" + miragejs "^0.1.0" + +"@miragejs/pretender-node-polyfill@^0.1.0": + version "0.1.2" + resolved "https://registry.npmjs.org/@miragejs/pretender-node-polyfill/-/pretender-node-polyfill-0.1.2.tgz#d26b6b7483fb70cd62189d05c95d2f67153e43f2" + integrity sha512-M/BexG/p05C5lFfMunxo/QcgIJnMT2vDVCd00wNqK2ImZONIlEETZwWJu1QtLxtmYlSHlCFl3JNzp0tLe7OJ5g== + "@nodelib/fs.scandir@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" @@ -2238,6 +2413,23 @@ component-type "^1.2.1" join-component "^1.1.0" +"@sideway/address@^4.1.0": + version "4.1.2" + resolved "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1" + integrity sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA== + 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.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -2607,6 +2799,13 @@ dependencies: "@types/node" "*" +"@types/http-proxy@^1.17.5": + version "1.17.7" + resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz#30ea85cc2c868368352a37f0d0d3581e24834c6f" + integrity sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w== + dependencies: + "@types/node" "*" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -2693,6 +2892,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.13.tgz#e743bae112bd779ac9650f907197dd2caa7f0364" integrity sha512-1x8W5OpxPq+T85OUsHRP6BqXeosKmeXRtjoF39STcdf/UWLqUsoehstZKOi0CunhVqHG17AyZgpj20eRVooK6A== +"@types/node@^14.14.31": + version "14.17.5" + resolved "https://registry.npmjs.org/@types/node/-/node-14.17.5.tgz#b59daf6a7ffa461b5648456ca59050ba8e40ed54" + integrity sha512-bjqH2cX/O33jXT/UmReo2pM7DIJREPMnarixbQ57DOOzzFaI6D2+IcwaJQaJpv0M1E9TIhPCYVxrkcityLjlqA== + "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -2775,6 +2979,16 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== +"@types/sinonjs__fake-timers@^6.0.2": + version "6.0.3" + resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.3.tgz#79df6f358ae8f79e628fe35a63608a0ea8e7cf08" + integrity sha512-E1dU4fzC9wN2QK2Cr1MLCfyHM8BoNnRFvuf45LYMPNDA+WqbNzC45S4UzPxvp1fFJ1rvSGU0bPvdd35VLmXG8g== + +"@types/sizzle@^2.3.2": + version "2.3.3" + resolved "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" + integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -3918,6 +4132,11 @@ aproba@^1.1.1: resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +arch@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -4107,6 +4326,11 @@ async@^2.6.2: dependencies: lodash "^4.17.14" +async@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -4207,6 +4431,15 @@ babel-jest@^26.6.0, babel-jest@^26.6.3: graceful-fs "^4.2.4" slash "^3.0.0" +babel-literal-to-ast@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/babel-literal-to-ast/-/babel-literal-to-ast-2.1.0.tgz#c8b12f9c36a8cee13572d65aabf6cff8adb1e8b3" + integrity sha512-CxfpQ0ysQ0bZOhlaPgcWjl79Em16Rhqc6++UAFn0A3duiXmuyhhj8yyl9PYbj0I0CyjrHovdDbp2QEKT7uIMxw== + dependencies: + "@babel/parser" "^7.1.6" + "@babel/traverse" "^7.1.6" + "@babel/types" "^7.1.6" + babel-loader@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" @@ -4275,9 +4508,9 @@ babel-plugin-lodash@^3.3.2: lodash "^4.17.10" require-package-name "^2.0.1" -babel-plugin-macros@2.8.0: +babel-plugin-macros@2.8.0, babel-plugin-macros@^2.5.0: version "2.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" + resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== dependencies: "@babel/runtime" "^7.7.2" @@ -4526,9 +4759,14 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bluebird@^3.5.5: +blob-util@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" + integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== + +bluebird@3.7.2, bluebird@^3.5.5, bluebird@^3.7.1, bluebird@^3.7.2: version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== bluebird@~2.2.2: @@ -4709,6 +4947,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" @@ -4838,6 +5081,11 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cachedir@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" + integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -5052,6 +5300,11 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +check-more-types@2.24.0, check-more-types@^2.24.0: + version "2.24.0" + resolved "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= + check-types@^11.1.1: version "11.1.2" resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f" @@ -5111,6 +5364,11 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.1.1: + version "3.2.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" + integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -5159,6 +5417,13 @@ cli-color@~0.2.3: es5-ext "~0.9.2" memoizee "~0.2.5" +cli-cursor@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= + dependencies: + restore-cursor "^1.0.1" + cli-cursor@^2.0.0, cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -5173,6 +5438,16 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-table3@~0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee" + integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ== + dependencies: + object-assign "^4.1.0" + string-width "^4.2.0" + optionalDependencies: + colors "^1.1.2" + cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -5311,6 +5586,11 @@ colorette@^1.2.1, colorette@^1.2.2: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== +colors@^1.1.2: + version "1.4.0" + resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -5333,6 +5613,11 @@ commander@^4.1.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + common-tags@1.8.0, common-tags@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" @@ -5390,9 +5675,9 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.5.0: +concat-stream@^1.5.0, concat-stream@^1.6.2: version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: buffer-from "^1.0.0" @@ -5628,7 +5913,7 @@ cross-fetch@3.1.4, cross-fetch@^3.0.4, cross-fetch@^3.0.6: dependencies: node-fetch "2.6.1" -cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2: +cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -5919,6 +6204,51 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +cypress@7.3.0: + version "7.3.0" + resolved "https://registry.npmjs.org/cypress/-/cypress-7.3.0.tgz#17345b8d18681c120f033e7d8fd0f0271e9d0d51" + integrity sha512-aseRCH1tRVCrM6oEfja6fR/bo5l6e4SkHRRSATh27UeN4f/ANC8U7tGIulmrISJVy9xuOkOdbYKbUb2MNM+nrw== + dependencies: + "@cypress/listr-verbose-renderer" "^0.4.1" + "@cypress/request" "^2.88.5" + "@cypress/xvfb" "^1.2.4" + "@types/node" "^14.14.31" + "@types/sinonjs__fake-timers" "^6.0.2" + "@types/sizzle" "^2.3.2" + arch "^2.2.0" + blob-util "^2.0.2" + bluebird "^3.7.2" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-table3 "~0.6.0" + commander "^5.1.0" + common-tags "^1.8.0" + dayjs "^1.10.4" + debug "4.3.2" + eventemitter2 "^6.4.3" + execa "4.1.0" + executable "^4.1.1" + extract-zip "^1.7.0" + fs-extra "^9.1.0" + getos "^3.2.1" + is-ci "^3.0.0" + is-installed-globally "~0.4.0" + lazy-ass "^1.6.0" + listr "^0.14.3" + lodash "^4.17.21" + log-symbols "^4.0.0" + minimist "^1.2.5" + ospath "^1.2.2" + pretty-bytes "^5.6.0" + ramda "~0.27.1" + request-progress "^3.0.0" + supports-color "^8.1.1" + tmp "~0.2.1" + untildify "^4.0.0" + url "^0.11.0" + yauzl "^2.10.0" + d3-array@2, d3-array@^2.3.0: version "2.12.1" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" @@ -6085,6 +6415,11 @@ date-fns@^2.15.0: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.21.3.tgz#8f5f6889d7a96bbcc1f0ea50239b397a83357f9b" integrity sha512-HeYdzCaFflc1i4tGbj7JKMjM4cKGYoyxwcIIkHzNgCkX8xXDNJDZXgDDVchIWpN4eQc3lH37WarduXFZJOtxfw== +dayjs@^1.10.4: + version "1.10.6" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63" + integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw== + debounce@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" @@ -6097,16 +6432,30 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@4, debug@4.3.1, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" -debug@^3.1.1, debug@^3.2.6, debug@^3.2.7: +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@4.3.2: + version "4.3.2" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + +debug@^3.1.0, debug@^3.1.1, debug@^3.2.6, debug@^3.2.7: version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" @@ -6448,7 +6797,7 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= -duplexer@^0.1.1: +duplexer@^0.1.1, duplexer@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== @@ -6999,11 +7348,29 @@ event-emitter@~0.2.2: dependencies: es5-ext "~0.9.2" +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +eventemitter2@^6.4.3: + version "6.4.4" + resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.4.tgz#aa96e8275c4dbeb017a5d0e03780c65612a1202b" + integrity sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw== + eventemitter3@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" @@ -7039,6 +7406,36 @@ exec-sh@^0.3.2: resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== +execa@4.1.0, execa@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +execa@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" + integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -7052,20 +7449,17 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== +executable@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" + pify "^2.2.0" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= exit@^0.1.2: version "0.1.2" @@ -7188,6 +7582,16 @@ extract-files@9.0.0, extract-files@^9.0.0: resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== +extract-zip@^1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" + integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== + dependencies: + concat-stream "^1.6.2" + debug "^2.6.9" + mkdirp "^0.5.4" + yauzl "^2.10.0" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -7198,6 +7602,16 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= +fake-xml-http-request@^2.1.1: + version "2.1.2" + resolved "https://registry.npmjs.org/fake-xml-http-request/-/fake-xml-http-request-2.1.2.tgz#f1786720cae50bbb46273035a0173414f3e85e74" + integrity sha512-HaFMBi7r+oEC9iJNpc3bvcW7Z7iLmM26hPDmlb0mFwyANSsOQAtJxbdWsXITKOzZUyMYK0zYCv3h5yDj9TsiXg== + +faker@5.5.3: + version "5.5.3" + resolved "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" + integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -7269,6 +7683,13 @@ fbjs@^3.0.0: setimmediate "^1.0.5" ua-parser-js "^0.7.18" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" @@ -7396,6 +7817,23 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +find-webpack@2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/find-webpack/-/find-webpack-2.2.1.tgz#96e7b701a2d37c3500cae30d4dc59e14923ba460" + integrity sha512-OdDtn2AzQvu3l9U1TS5ALc7uTVcLK/yv3fhjo+Pz7yuv4hG3ANKnbkKnPIPZ5ofd9mpYe6wRf5g5H4X9Lx48vQ== + dependencies: + debug "4.1.1" + find-yarn-workspace-root "1.2.1" + mocked-env "1.3.2" + +find-yarn-workspace-root@1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz#40eb8e6e7c2502ddfaa2577c176f221422f860db" + integrity sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q== + dependencies: + fs-extra "^4.0.3" + micromatch "^3.1.4" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -7502,6 +7940,20 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +from@~0: + version "0.1.7" + resolved "https://registry.npmjs.org/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= + +fs-extra@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -7520,9 +7972,9 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.1: +fs-extra@^9.0.1, fs-extra@^9.1.0: version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: at-least-node "^1.0.0" @@ -7618,11 +8070,23 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +getos@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" + integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== + dependencies: + async "^3.2.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -7657,6 +8121,13 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + dependencies: + ini "2.0.0" + global-modules@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" @@ -7782,6 +8253,18 @@ graphql-request@^3.3.0: extract-files "^9.0.0" form-data "^3.0.0" +graphql-tag@2.10.3: + version "2.10.3" + resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.3.tgz#ea1baba5eb8fc6339e4c4cf049dabe522b0edf03" + integrity sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA== + +graphql-tag@^2.10.1: + version "2.12.5" + resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.5.tgz#5cff974a67b417747d05c8d9f5f3cb4495d0db8f" + integrity sha512-5xNhP4063d16Pz3HBtKprutsPrmHZi5IdUGOWRxA2B6VF7BIRGOHZ5WQvDmJXZuPcBg7rYwaFxvQYjqkSdR3TQ== + dependencies: + tslib "^2.1.0" + graphql-tag@^2.11.0, graphql-tag@^2.12.0: version "2.12.4" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.4.tgz#d34066688a4f09e72d6f4663c74211e9b4b7c4bf" @@ -7794,11 +8277,26 @@ graphql-ws@^4.4.1: resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-4.5.1.tgz#d9dc6e047c6d4ddb928ccbfb3ca3022580a89925" integrity sha512-GE7vCMKe2D7fc0ugkM1V8QMneHcbV9c3BpPBzdlW/Uzkqv0F/zZq9DDHxLzg55ZhE5OSLL+n/gyqAMPgH59hcw== -graphql@*, graphql@^15.4.0: +graphql.macro@^1.4.2: + version "1.4.2" + resolved "https://registry.npmjs.org/graphql.macro/-/graphql.macro-1.4.2.tgz#183c347828cb0319a6fe02690b2023b30b34cbc5" + integrity sha512-vcIaStPgS65gp5i1M3DSBimNVkyus0Z7k4VObWAyZS319tKlpX/TEIJSWTgOZU5k8dn4RRzGoS/elQhX2E6yBw== + dependencies: + "@babel/template" "^7.4.4" + babel-literal-to-ast "^2.1.0" + babel-plugin-macros "^2.5.0" + graphql-tag "^2.10.1" + +graphql@*: version "15.5.0" resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" integrity sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA== +graphql@^15.0.0, graphql@^15.5.0: + version "15.5.1" + resolved "https://registry.npmjs.org/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad" + integrity sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw== + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -8228,7 +8726,18 @@ http-proxy-middleware@0.19.1: lodash "^4.17.11" micromatch "^3.1.10" -http-proxy@^1.17.0: +http-proxy-middleware@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.0.tgz#20d1ac3409199c83e5d0383ba6436b04e7acb9fe" + integrity sha512-S+RN5njuyvYV760aiVKnyuTXqUMcSIvYOsHA891DOVQyrdZOwaXtBHpt9FUVPEDAsOvsPArZp6VXQLs44yvkow== + dependencies: + "@types/http-proxy" "^1.17.5" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.17.0, http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== @@ -8264,6 +8773,11 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -8398,6 +8912,11 @@ infer-owner@^1.0.3, infer-owner@^1.0.4: resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== +inflected@^2.0.4: + version "2.1.0" + resolved "https://registry.npmjs.org/inflected/-/inflected-2.1.0.tgz#2816ac17a570bbbc8303ca05bca8bf9b3f959687" + integrity sha512-hAEKNxvHf2Iq3H60oMBHkB4wl5jn3TPF3+fXek/sRwAB5gP9xWs4r7aweSF95f99HFoz69pnZTcu8f0SIHV18w== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -8421,6 +8940,11 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +ini@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + ini@^1.3.5, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" @@ -8597,6 +9121,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-ci@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" + integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== + dependencies: + ci-info "^3.1.1" + is-color-stop@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" @@ -8726,6 +9257,14 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +is-installed-globally@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + is-lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-2.0.2.tgz#1c0884d3012c841556243483aa5d522f47396d2a" @@ -8796,6 +9335,11 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -8806,6 +9350,11 @@ is-plain-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -9457,6 +10006,17 @@ jest@26.6.0: import-local "^3.0.2" jest-cli "^26.6.0" +joi@^17.3.0: + version "17.4.1" + resolved "https://registry.npmjs.org/joi/-/joi-17.4.1.tgz#15d2f23c8cbe4d1baded2dd190c58f8dbe11cca0" + integrity sha512-gDPOwQ5sr+BUxXuPDGrC1pSNcVR/yGGcTI0aCnjYxZEa3za60K/iCQ+OFIkEHWZGVCUcUlXlFKvMmrlmxrG6UQ== + 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" + join-component@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" @@ -9766,6 +10326,11 @@ latest-version@5.1.0: dependencies: package-json "^6.3.0" +lazy-ass@1.6.0, lazy-ass@^1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + less-loader@^6.1.0: version "6.2.0" resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-6.2.0.tgz#8b26f621c155b342eefc24f5bd6e9dc40c42a719" @@ -9939,31 +10504,86 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= +lodash.compact@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/lodash.compact/-/lodash.compact-3.0.1.tgz#540ce3837745975807471e16b4a2ba21e7256ca5" + integrity sha1-VAzjg3dFl1gHRx4WtKK6IeclbKU= + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= +lodash.find@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1" + integrity sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E= + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + +lodash.forin@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.forin/-/lodash.forin-4.4.0.tgz#5d3f20ae564011fbe88381f7d98949c9c9519731" + integrity sha1-XT8grlZAEfvog4H32YlJyclRlzE= + lodash.get@^4, lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= +lodash.has@^4.5.2: + version "4.5.2" + resolved "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862" + integrity sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI= + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= +lodash.invokemap@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.invokemap/-/lodash.invokemap-4.6.0.tgz#1748cda5d8b0ef8369c4eb3ec54c21feba1f2d62" + integrity sha1-F0jNpdiw74NpxOs+xUwh/rofLWI= + lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= +lodash.isempty@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" + integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + +lodash.isfunction@^3.0.9: + version "3.0.9" + resolved "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== + lodash.isinteger@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" @@ -9984,6 +10604,21 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= +lodash.lowerfirst@^4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/lodash.lowerfirst/-/lodash.lowerfirst-4.3.1.tgz#de3c7b12e02c6524a0059c2f6cb7c5c52655a13d" + integrity sha1-3jx7EuAsZSSgBZwvbLfFxSZVoT0= + +lodash.map@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= + +lodash.mapvalues@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" + integrity sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw= + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -9994,11 +10629,21 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.once@^4.0.0: +lodash.once@^4.0.0, lodash.once@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= +lodash.pick@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40= + lodash.template@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" @@ -10024,6 +10669,16 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= +lodash.uniqby@^4.7.0: + version "4.7.0" + resolved "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" + integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI= + +lodash.values@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" + integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c= + lodash@4.17.21, "lodash@>=3.5 <5", lodash@^4.17.10, lodash@^4.17.11, 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.7.0, lodash@~4.17.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -10152,6 +10807,11 @@ map-cache@^0.2.0, map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -10575,6 +11235,38 @@ minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" +miragejs@^0.1.0, miragejs@^0.1.41: + version "0.1.41" + resolved "https://registry.npmjs.org/miragejs/-/miragejs-0.1.41.tgz#1b06a2d2d9de65624f5bb1cee7ebb4a208f554d0" + integrity sha512-ur8x7sBskgey64vdzKGVCVC3hgKXWl2Cg5lZbxd6OmKrhr9LCCP/Bv7qh4wsQxIMHZnENxybFATXnrQ+rzSOWQ== + dependencies: + "@miragejs/pretender-node-polyfill" "^0.1.0" + inflected "^2.0.4" + lodash.assign "^4.2.0" + lodash.camelcase "^4.3.0" + lodash.clonedeep "^4.5.0" + lodash.compact "^3.0.1" + lodash.find "^4.6.0" + lodash.flatten "^4.4.0" + lodash.forin "^4.4.0" + lodash.get "^4.4.2" + lodash.has "^4.5.2" + lodash.invokemap "^4.6.0" + lodash.isempty "^4.4.0" + lodash.isequal "^4.5.0" + lodash.isfunction "^3.0.9" + lodash.isinteger "^4.0.4" + lodash.isplainobject "^4.0.6" + lodash.lowerfirst "^4.3.1" + lodash.map "^4.6.0" + lodash.mapvalues "^4.6.0" + lodash.pick "^4.4.0" + lodash.snakecase "^4.1.1" + lodash.uniq "^4.5.0" + lodash.uniqby "^4.7.0" + lodash.values "^4.3.0" + pretender "^3.4.3" + mississippi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" @@ -10599,9 +11291,9 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" @@ -10611,6 +11303,16 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mocked-env@1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/mocked-env/-/mocked-env-1.3.2.tgz#548eb2fde141d083de70dc6b231cd9f3210d8731" + integrity sha512-jwm3ziowCjpbLNhUNYwn2G0tawV/ZGRuWeEGt6PItrkQT74Nk3pDldL2pmwm9sQZw6a/x+ZBGeBVYq54acTauQ== + dependencies: + check-more-types "2.24.0" + debug "4.1.1" + lazy-ass "1.6.0" + ramda "0.26.1" + moment@^2.24.0, moment@^2.25.3: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" @@ -10874,7 +11576,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -11032,6 +11734,11 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= + onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -11039,7 +11746,7 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -11118,6 +11825,11 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -11405,6 +12117,13 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= + dependencies: + through "~2.3" + pbkdf2@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" @@ -11416,6 +12135,11 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -11426,9 +12150,9 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -pify@^2.0.0: +pify@^2.0.0, pify@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^3.0.0: @@ -12203,6 +12927,14 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +pretender@^3.4.3: + version "3.4.3" + resolved "https://registry.npmjs.org/pretender/-/pretender-3.4.3.tgz#a3b4160516007075d29127262f3a0063d19896e9" + integrity sha512-AlbkBly9R8KR+R0sTCJ/ToOeEoUMtt52QVCetui5zoSmeLOU3S8oobFsyPLm1O2txR6t58qDNysqPnA1vVi8Hg== + dependencies: + fake-xml-http-request "^2.1.1" + route-recognizer "^0.3.3" + prettier-linter-helpers@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" @@ -12215,9 +12947,9 @@ prettier@^2.3.0: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== -pretty-bytes@^5.3.0: +pretty-bytes@^5.3.0, pretty-bytes@^5.6.0: version "5.6.0" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== pretty-error@^2.1.1: @@ -12324,6 +13056,13 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -12456,6 +13195,16 @@ raf@^3.4.1: dependencies: performance-now "^2.1.0" +ramda@0.26.1: + version "0.26.1" + resolved "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" + integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== + +ramda@~0.27.1: + version "0.27.1" + resolved "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" + integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -13377,6 +14126,13 @@ replaceall@^0.1.6: resolved "https://registry.yarnpkg.com/replaceall/-/replaceall-0.1.6.tgz#81d81ac7aeb72d7f5c4942adf2697a3220688d8e" integrity sha1-gdgax663LX9cSUKt8ml6MiBojY4= +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= + dependencies: + throttleit "^1.0.0" + request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" @@ -13529,6 +14285,14 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -13640,6 +14404,11 @@ rollup@^1.31.1: "@types/node" "*" acorn "^7.1.0" +route-recognizer@^0.3.3: + version "0.3.4" + resolved "https://registry.npmjs.org/route-recognizer/-/route-recognizer-0.3.4.tgz#39ab1ffbce1c59e6d2bdca416f0932611e4f3ca3" + integrity sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g== + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -13664,7 +14433,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.3.3, rxjs@^6.6.0: +rxjs@^6.3.3, rxjs@^6.6.0, rxjs@^6.6.3: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -13978,7 +14747,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== @@ -14225,6 +14994,13 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split@0.3: + version "0.3.3" + resolved "https://registry.npmjs.org/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= + dependencies: + through "2" + sponge-case@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c" @@ -14283,6 +15059,19 @@ stackframe@^1.1.1: resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== +start-server-and-test@1.12.2: + version "1.12.2" + resolved "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.12.2.tgz#13afe6f22d7347e0fd47a739cdd085786fced14b" + integrity sha512-rjJF8N/8XVukEYR44Ehm8LAZIDjWCQKXX54W8UQ8pXz3yDKPCdqTqJy7VYnCAknPw65cmLfPxz8M2+K/zCAvVQ== + dependencies: + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.3.1" + execa "5.0.0" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "5.3.0" + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -14304,6 +15093,13 @@ stream-browserify@^2.0.1: inherits "~2.0.1" readable-stream "^2.0.2" +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= + dependencies: + duplexer "~0.1.1" + stream-each@^1.1.0: version "1.2.3" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" @@ -14608,6 +15404,13 @@ supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: dependencies: has-flag "^4.0.0" +supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-hyperlinks@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" @@ -14788,6 +15591,11 @@ throat@^5.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= + through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -14796,9 +15604,9 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -through@^2.3.6, through@~2.3.4: +through@2, through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.4: version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= thunky@^1.0.2: @@ -14847,6 +15655,13 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@~0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" @@ -15296,6 +16111,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + upath@^1.1.1, upath@^1.1.2, upath@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -15530,6 +16350,17 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +wait-on@5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/wait-on/-/wait-on-5.3.0.tgz#584e17d4b3fe7b46ac2b9f8e5e102c005c2776c7" + integrity sha512-DwrHrnTK+/0QFaB9a8Ol5Lna3k7WvUR4jzSKmz0YaPBpuN2sACyiPVKVfj6ejnjcajAcvn3wlbTyMIn9AZouOg== + dependencies: + axios "^0.21.1" + joi "^17.3.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^6.6.3" + walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" @@ -16129,6 +16960,14 @@ yargs@^16.1.1: y18n "^5.0.5" yargs-parser "^20.2.2" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"