diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..81ed7f265d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,33 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{js,py}] +charset = utf-8 + +# 4 space indentation +[*.py] +indent_style = space +indent_size = 4 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab + +# Indentation override for all JS under lib directory +[lib/**.js] +indent_style = space +indent_size = 2 + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/Makefile b/Makefile index bf2b038992..8dc880a971 100644 --- a/Makefile +++ b/Makefile @@ -115,11 +115,9 @@ down-arm: @docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.arm.yaml down -v clear-standalone-data: - @cd $(STANDALONE_DIRECTORY) - @docker run --rm -v "data:/pwd" busybox \ + @docker run --rm -v "$(PWD)/$(STANDALONE_DIRECTORY)/data:/pwd" busybox \ sh -c "cd /pwd && rm -rf alertmanager/* clickhouse/* signoz/*" clear-swarm-data: - @cd $(SWARM_DIRECTORY) - @docker run --rm -v "data:/pwd" busybox \ + @docker run --rm -v "$(PWD)/$(SWARM_DIRECTORY)/data:/pwd" busybox \ sh -c "cd /pwd && rm -rf alertmanager/* clickhouse/* signoz/*" diff --git a/deploy/docker-swarm/clickhouse-setup/clickhouse-config.xml b/deploy/docker-swarm/clickhouse-setup/clickhouse-config.xml index 23898ef5e7..7a5f40d299 100644 --- a/deploy/docker-swarm/clickhouse-setup/clickhouse-config.xml +++ b/deploy/docker-swarm/clickhouse-setup/clickhouse-config.xml @@ -1,11 +1,8 @@ - trace - /var/log/clickhouse-server/clickhouse-server.log - /var/log/clickhouse-server/clickhouse-server.err.log - 1000M - 10 + information + 1 8123 @@ -45,6 +42,34 @@ + + + + - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/deploy/docker/clickhouse-setup/docker-compose.arm.yaml b/deploy/docker/clickhouse-setup/docker-compose.arm.yaml index 0540fa2868..9b4f86bc05 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.arm.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.arm.yaml @@ -3,10 +3,17 @@ version: "2.4" services: clickhouse: image: altinity/clickhouse-server:21.12.3.32.altinitydev.arm + # ports: + # - "9000:9000" + # - "8123:8123" volumes: - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml - ./data/clickhouse/:/var/lib/clickhouse/ restart: on-failure + logging: + options: + max-size: 50m + max-file: "3" healthcheck: # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"] @@ -15,17 +22,19 @@ services: retries: 3 alertmanager: - image: signoz/alertmanager:0.6.0 + image: signoz/alertmanager:0.6.1 volumes: - ./data/alertmanager:/data depends_on: - - query-service + query-service: + condition: service_healthy + restart: on-failure command: - --queryService.url=http://query-service:8080 - --storage.path=/data query-service: - image: signoz/query-service:0.7.4 + image: signoz/query-service:0.7.5 container_name: query-service command: ["-config=/root/config/prometheus.yml"] volumes: @@ -40,14 +49,21 @@ services: - DEPLOYMENT_TYPE=docker-standalone-arm restart: on-failure + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/version"] + interval: 30s + timeout: 5s + retries: 3 depends_on: clickhouse: condition: service_healthy frontend: - image: signoz/frontend:0.7.4 + image: signoz/frontend:0.7.5 container_name: frontend + restart: on-failure depends_on: + - alertmanager - query-service ports: - "3301:3301" diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index b9c96c7bdb..1d7fa921e7 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -3,10 +3,17 @@ version: "2.4" services: clickhouse: image: yandex/clickhouse-server:21.12.3.32 + # ports: + # - "9000:9000" + # - "8123:8123" volumes: - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml - ./data/clickhouse/:/var/lib/clickhouse/ restart: on-failure + logging: + options: + max-size: 50m + max-file: "3" healthcheck: # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"] @@ -15,11 +22,13 @@ services: retries: 3 alertmanager: - image: signoz/alertmanager:0.6.0 + image: signoz/alertmanager:0.6.1 volumes: - ./data/alertmanager:/data depends_on: - - query-service + query-service: + condition: service_healthy + restart: on-failure command: - --queryService.url=http://query-service:8080 - --storage.path=/data @@ -27,7 +36,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:0.7.4 + image: signoz/query-service:0.7.5 container_name: query-service command: ["-config=/root/config/prometheus.yml"] volumes: @@ -41,14 +50,21 @@ services: - TELEMETRY_ENABLED=true - DEPLOYMENT_TYPE=docker-standalone-amd restart: on-failure + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/version"] + interval: 30s + timeout: 5s + retries: 3 depends_on: clickhouse: condition: service_healthy frontend: - image: signoz/frontend:0.7.4 + image: signoz/frontend:0.7.5 container_name: frontend + restart: on-failure depends_on: + - alertmanager - query-service ports: - "3301:3301" diff --git a/deploy/install.sh b/deploy/install.sh index 569394bee5..abca7f0878 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -247,7 +247,7 @@ bye() { # Prints a friendly good bye message and exits the script. echo "or reach us for support in #help channel in our Slack Community https://signoz.io/slack" echo "++++++++++++++++++++++++++++++++++++++++" - if [[ email == "" ]]; then + if [[ $email == "" ]]; then echo -e "\n📨 Please share your email to receive support with the installation" read -rp 'Email: ' email diff --git a/frontend/.eslintignore b/frontend/.eslintignore index b7dab5e9cb..dd87e2d73f 100644 --- a/frontend/.eslintignore +++ b/frontend/.eslintignore @@ -1,2 +1,2 @@ node_modules -build \ No newline at end of file +build diff --git a/frontend/babel.config.js b/frontend/babel.config.js new file mode 100644 index 0000000000..b3df57dfb0 --- /dev/null +++ b/frontend/babel.config.js @@ -0,0 +1,6 @@ +module.exports = { + presets: [ + ['@babel/preset-env', { targets: { node: 'current' } }], + '@babel/preset-typescript', + ], +}; diff --git a/frontend/jest.config.ts b/frontend/jest.config.ts index a949a7611b..5b5ebcdbc7 100644 --- a/frontend/jest.config.ts +++ b/frontend/jest.config.ts @@ -9,12 +9,19 @@ const config: Config.InitialOptions = { moduleNameMapper: { '\\.(css|less)$': '/__mocks__/cssMock.ts', }, - notify: true, - notifyMode: 'always', - testMatch: ['/src/**/?(*.)(test).(ts|js)?(x)'], - transform: { - '\\.(js|jsx|ts|tsx)?$': 'babel-jest', + globals: { + extensionsToTreatAsEsm: ['.ts'], + 'ts-jest': { + useESM: true, + }, }, + testMatch: ['/src/**/?(*.)(test).(ts|js)?(x)'], + preset: 'ts-jest/presets/js-with-ts-esm', + transform: { + '^.+\\.(ts|tsx)?$': 'ts-jest', + '^.+\\.(js|jsx)$': 'babel-jest', + }, + transformIgnorePatterns: ['node_modules/(?!(lodash-es)/)'], setupFilesAfterEnv: ['jest.setup.ts'], testPathIgnorePatterns: ['/node_modules/', '/public/'], moduleDirectories: ['node_modules', 'src'], diff --git a/frontend/package.json b/frontend/package.json index 28666512fb..ed63737caf 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -57,7 +57,7 @@ "i18next": "^21.6.12", "i18next-browser-languagedetector": "^6.1.3", "i18next-http-backend": "^1.3.2", - "jest": "26.6.0", + "jest": "^27.5.1", "less": "^4.1.2", "less-loader": "^10.2.0", "lodash-es": "^4.17.21", @@ -68,6 +68,7 @@ "react-graph-vis": "^1.0.5", "react-grid-layout": "^1.2.5", "react-i18next": "^11.16.1", + "react-query": "^3.34.19", "react-redux": "^7.2.2", "react-router-dom": "^5.2.0", "react-use": "^17.3.2", @@ -107,6 +108,7 @@ "@babel/preset-typescript": "^7.12.17", "@jest/globals": "^27.5.1", "@testing-library/cypress": "^8.0.0", + "@testing-library/react-hooks": "^7.0.2", "@types/color": "^3.0.3", "@types/compression-webpack-plugin": "^9.0.0", "@types/copy-webpack-plugin": "^8.0.1", @@ -155,6 +157,7 @@ "portfinder-sync": "^0.0.2", "prettier": "2.2.1", "react-hot-loader": "^4.13.0", + "ts-jest": "^27.1.4", "ts-node": "^10.2.1", "typescript-plugin-css-modules": "^3.4.0", "webpack-bundle-analyzer": "^4.5.0", diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index e63d6d083e..23330080e6 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -1,3 +1,27 @@ { - "monitor_signup": "Monitor your applications. Find what is causing issues." + "monitor_signup": "Monitor your applications. Find what is causing issues.", + "version": "Version", + "latest_version": "Latest version", + "current_version": "Current version", + "release_notes": "Release Notes", + "read_how_to_upgrade": "Read instructions on how to upgrade", + "latest_version_signoz": "You are running the latest version of SigNoz.", + "stale_version": "You are on an older version and may be loosing on the latest features we have shipped. We recommend to upgrade to the latest version", + "oops_something_went_wrong_version": "Oops.. facing issues with fetching updated version information", + "n_a": "N/A", + "routes": { + "general": "General", + "alert_channels": "Alert Channels" + }, + "settings": { + "total_retention_period": "Total Retention Period", + "move_to_s3": "Move to S3\n(should be lower than total retention period)", + "retention_success_message": "Congrats. The retention periods for {{name}} has been updated successfully.", + "retention_error_message": "There was an issue in changing the retention period for {{name}}. Please try again or reach out to support@signoz.io", + "retention_failed_message": "There was an issue in changing the retention period. Please try again or reach out to support@signoz.io", + "retention_comparison_error": "Total retention period for {{name}} can’t be lower or equal to the period after which data is moved to s3.", + "retention_null_value_error": "Retention Period for {{name}} is not set yet. Please set by choosing below", + "retention_confirmation": "Are you sure you want to change the retention period?", + "retention_confirmation_description": "This will change the amount of storage needed for saving metrics & traces." + } } diff --git a/frontend/src/AppRoutes/pageComponents.ts b/frontend/src/AppRoutes/pageComponents.ts index eb7430eb3c..25d4dc5886 100644 --- a/frontend/src/AppRoutes/pageComponents.ts +++ b/frontend/src/AppRoutes/pageComponents.ts @@ -85,3 +85,7 @@ export const EditAlertChannelsAlerts = Loadable( export const AllAlertChannels = Loadable( () => import(/* webpackChunkName: "All Channels" */ 'pages/AllAlertChannels'), ); + +export const StatusPage = Loadable( + () => import(/* webpackChunkName: "All Status" */ 'pages/Status'), +); diff --git a/frontend/src/AppRoutes/routes.ts b/frontend/src/AppRoutes/routes.ts index 474f71f510..10f5a1997a 100644 --- a/frontend/src/AppRoutes/routes.ts +++ b/frontend/src/AppRoutes/routes.ts @@ -17,6 +17,7 @@ import { ServicesTablePage, SettingsPage, SignupPage, + StatusPage, TraceDetail, TraceFilter, UsageExplorerPage, @@ -113,6 +114,11 @@ const routes: AppRoutes[] = [ exact: true, component: AllAlertChannels, }, + { + path: ROUTES.VERSION, + exact: true, + component: StatusPage, + }, ]; interface AppRoutes { diff --git a/frontend/src/api/disks/getDisks.ts b/frontend/src/api/disks/getDisks.ts new file mode 100644 index 0000000000..9dced1b0fd --- /dev/null +++ b/frontend/src/api/disks/getDisks.ts @@ -0,0 +1,24 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps } from 'types/api/disks/getDisks'; + +const getDisks = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get(`/disks`); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getDisks; diff --git a/frontend/src/api/settings/setRetention.ts b/frontend/src/api/settings/setRetention.ts index dcf0a0f2c3..62aa3559e5 100644 --- a/frontend/src/api/settings/setRetention.ts +++ b/frontend/src/api/settings/setRetention.ts @@ -9,7 +9,11 @@ const setRetention = async ( ): Promise | ErrorResponse> => { try { const response = await axios.post( - `/settings/ttl?duration=${props.duration}&type=${props.type}`, + `/settings/ttl?duration=${props.totalDuration}&type=${props.type}${ + props.coldStorage + ? `&coldStorage=${props.coldStorage};toColdDuration=${props.toColdDuration}` + : '' + }`, ); return { diff --git a/frontend/src/api/trace/getTagValue.ts b/frontend/src/api/trace/getTagValue.ts new file mode 100644 index 0000000000..25156d32ef --- /dev/null +++ b/frontend/src/api/trace/getTagValue.ts @@ -0,0 +1,28 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/trace/getTagValue'; + +const getTagValue = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/getTagValues`, { + start: props.start.toString(), + end: props.end.toString(), + tagKey: props.tagKey, + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getTagValue; diff --git a/frontend/src/api/user/getLatestVersion.ts b/frontend/src/api/user/getLatestVersion.ts new file mode 100644 index 0000000000..28a72f78be --- /dev/null +++ b/frontend/src/api/user/getLatestVersion.ts @@ -0,0 +1,25 @@ +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import axios, { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps } from 'types/api/user/getLatestVersion'; + +const getLatestVersion = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get( + `https://api.github.com/repos/signoz/signoz/releases/latest`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getLatestVersion; diff --git a/frontend/src/components/Graph/Plugin/EmptyGraph.ts b/frontend/src/components/Graph/Plugin/EmptyGraph.ts new file mode 100644 index 0000000000..0f0577bb80 --- /dev/null +++ b/frontend/src/components/Graph/Plugin/EmptyGraph.ts @@ -0,0 +1,17 @@ +import { grey } from '@ant-design/colors'; +import { Chart } from 'chart.js'; + +export const emptyGraph = { + id: 'emptyChart', + afterDraw(chart: Chart): void { + const { height, width, ctx } = chart; + chart.clear(); + ctx.save(); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.font = '1.5rem sans-serif'; + ctx.fillStyle = `${grey.primary}`; + ctx.fillText('No data to display', width / 2, height / 2); + ctx.restore(); + }, +}; diff --git a/frontend/src/components/Graph/hasData.ts b/frontend/src/components/Graph/hasData.ts new file mode 100644 index 0000000000..acaa4fac1e --- /dev/null +++ b/frontend/src/components/Graph/hasData.ts @@ -0,0 +1,19 @@ +/* eslint-disable no-restricted-syntax */ +import { ChartData } from 'chart.js'; + +export const hasData = (data: ChartData): boolean => { + const { datasets = [] } = data; + let hasData = false; + try { + for (const dataset of datasets) { + if (dataset.data.length > 0 && dataset.data.some((item) => item !== 0)) { + hasData = true; + break; + } + } + } catch (error) { + console.error(error); + } + + return hasData; +}; diff --git a/frontend/src/components/Graph/index.tsx b/frontend/src/components/Graph/index.tsx index a8f668235d..e51ff21616 100644 --- a/frontend/src/components/Graph/index.tsx +++ b/frontend/src/components/Graph/index.tsx @@ -27,7 +27,9 @@ import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; import AppReducer from 'types/reducer/app'; +import { hasData } from './hasData'; import { legend } from './Plugin'; +import { emptyGraph } from './Plugin/EmptyGraph'; import { LegendsContainer } from './styles'; import { useXAxisTimeUnit } from './xAxisConfig'; import { getYAxisFormattedValue } from './yAxisConfig'; @@ -128,6 +130,7 @@ function Graph({ grid: { display: true, color: getGridColor(), + drawTicks: true, }, adapters: { date: chartjsAdapter, @@ -180,12 +183,18 @@ function Graph({ } }, }; - + const chartHasData = hasData(data); + const chartPlugins = []; + if (chartHasData) { + chartPlugins.push(legend(name, data.datasets.length > 3)); + } else { + chartPlugins.push(emptyGraph); + } lineChartRef.current = new Chart(chartRef.current, { type, data, options, - plugins: [legend(name, data.datasets.length > 3)], + plugins: chartPlugins, }); } }, [ diff --git a/frontend/src/components/NotFound/NotFound.test.tsx b/frontend/src/components/NotFound/NotFound.test.tsx index c8aab78ef7..3f4be8c009 100644 --- a/frontend/src/components/NotFound/NotFound.test.tsx +++ b/frontend/src/components/NotFound/NotFound.test.tsx @@ -1,3 +1,7 @@ +/** + * @jest-environment jsdom + */ + import { expect } from '@jest/globals'; import { render } from '@testing-library/react'; import React from 'react'; diff --git a/frontend/src/components/NotFound/__snapshots__/NotFound.test.tsx.snap b/frontend/src/components/NotFound/__snapshots__/NotFound.test.tsx.snap index e65af86a8a..0e9ce92e30 100644 --- a/frontend/src/components/NotFound/__snapshots__/NotFound.test.tsx.snap +++ b/frontend/src/components/NotFound/__snapshots__/NotFound.test.tsx.snap @@ -3,7 +3,7 @@ exports[`Not Found page test should render Not Found page without errors 1`] = `

Ah, seems like we reached a dead end!

Page Not Found

diff --git a/frontend/src/constants/routes.ts b/frontend/src/constants/routes.ts index acd95003ea..e62cec59f2 100644 --- a/frontend/src/constants/routes.ts +++ b/frontend/src/constants/routes.ts @@ -17,6 +17,7 @@ const ROUTES = { ALL_CHANNELS: '/settings/channels', CHANNELS_NEW: '/setting/channels/new', CHANNELS_EDIT: '/setting/channels/edit/:id', + VERSION: '/status', }; export default ROUTES; diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 84f7d237e2..1fa47522c5 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -1,11 +1,24 @@ +import { notification } from 'antd'; +import getLatestVersion from 'api/user/getLatestVersion'; +import getVersion from 'api/user/getVersion'; import ROUTES from 'constants/routes'; import TopNav from 'container/Header'; import SideNav from 'container/SideNav'; +import useFetch from 'hooks/useFetch'; import history from 'lib/history'; -import React, { ReactNode, useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; +import React, { ReactNode, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; +import { Dispatch } from 'redux'; import { AppState } from 'store/reducers'; +import AppActions from 'types/actions'; +import { + UPDATE_CURRENT_ERROR, + UPDATE_CURRENT_VERSION, + UPDATE_LATEST_VERSION, + UPDATE_LATEST_VERSION_ERROR, +} from 'types/actions/app'; import AppReducer from 'types/reducer/app'; import { Content, Layout } from './styles'; @@ -13,11 +26,24 @@ import { Content, Layout } from './styles'; function AppLayout(props: AppLayoutProps): JSX.Element { const { isLoggedIn } = useSelector((state) => state.app); const { pathname } = useLocation(); + const { t } = useTranslation(); const [isSignUpPage, setIsSignUpPage] = useState(ROUTES.SIGN_UP === pathname); + const { payload: versionPayload, loading, error: getVersionError } = useFetch( + getVersion, + ); + + const { + payload: latestVersionPayload, + loading: latestLoading, + error: latestError, + } = useFetch(getLatestVersion); + const { children } = props; + const dispatch = useDispatch>(); + useEffect(() => { if (!isLoggedIn) { setIsSignUpPage(true); @@ -27,6 +53,72 @@ function AppLayout(props: AppLayoutProps): JSX.Element { } }, [isLoggedIn, isSignUpPage]); + const latestCurrentCounter = useRef(0); + const latestVersionCounter = useRef(0); + + useEffect(() => { + if (isLoggedIn && pathname === ROUTES.SIGN_UP) { + history.push(ROUTES.APPLICATION); + } + + if (!latestLoading && latestError && latestCurrentCounter.current === 0) { + latestCurrentCounter.current = 1; + + dispatch({ + type: UPDATE_LATEST_VERSION_ERROR, + payload: { + isError: true, + }, + }); + notification.error({ + message: t('oops_something_went_wrong_version'), + }); + } + + if (!loading && getVersionError && latestVersionCounter.current === 0) { + latestVersionCounter.current = 1; + + dispatch({ + type: UPDATE_CURRENT_ERROR, + payload: { + isError: true, + }, + }); + notification.error({ + message: t('oops_something_went_wrong_version'), + }); + } + + if (!latestLoading && versionPayload) { + dispatch({ + type: UPDATE_CURRENT_VERSION, + payload: { + currentVersion: versionPayload.version, + }, + }); + } + + if (!loading && latestVersionPayload) { + dispatch({ + type: UPDATE_LATEST_VERSION, + payload: { + latestVersion: latestVersionPayload.name, + }, + }); + } + }, [ + dispatch, + loading, + latestLoading, + versionPayload, + latestVersionPayload, + isLoggedIn, + pathname, + getVersionError, + latestError, + t, + ]); + return ( {!isSignUpPage && } diff --git a/frontend/src/container/CreateAlertChannels/index.tsx b/frontend/src/container/CreateAlertChannels/index.tsx index f999627154..02cd7b274a 100644 --- a/frontend/src/container/CreateAlertChannels/index.tsx +++ b/frontend/src/container/CreateAlertChannels/index.tsx @@ -18,12 +18,14 @@ function CreateAlertChannels({ preType = 'slack', }: CreateAlertChannelsProps): JSX.Element { const [formInstance] = Form.useForm(); + const [selectedConfig, setSelectedConfig] = useState< Partial >({ - text: ` {{ range .Alerts -}} - *Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} + text: `{{ range .Alerts -}} + *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} + *Summary:* {{ .Annotations.summary }} *Description:* {{ .Annotations.description }} *Details:* diff --git a/frontend/src/container/GeneralSettings/Retention.tsx b/frontend/src/container/GeneralSettings/Retention.tsx index b2f5cb5a09..54fb89a394 100644 --- a/frontend/src/container/GeneralSettings/Retention.tsx +++ b/frontend/src/container/GeneralSettings/Retention.tsx @@ -1,108 +1,118 @@ -import { DownOutlined } from '@ant-design/icons'; -import { Button, Menu } from 'antd'; -import React from 'react'; +import { Col, Row, Select } from 'antd'; +import { find } from 'lodash-es'; +import React, { useEffect, useRef, useState } from 'react'; -import { SettingPeroid } from '.'; import { - Dropdown, Input, RetentionContainer, - TextContainer, - Typography, + RetentionFieldInputContainer, + RetentionFieldLabel, } from './styles'; +import { + convertHoursValueToRelevantUnit, + SettingPeriod, + TimeUnits, +} from './utils'; + +const { Option } = Select; function Retention({ retentionValue, - setRentionValue, - selectedRetentionPeroid, - setSelectedRetentionPeroid, + setRetentionValue, text, -}: RetentionProps): JSX.Element { - const options: Option[] = [ - { - key: 'hr', - value: 'Hrs', - }, - { - key: 'day', - value: 'Days', - }, - { - key: 'month', - value: 'Months', - }, - ]; - - const onClickHandler = ( - e: { key: string }, - func: React.Dispatch>, - ): void => { - const selected = e.key as SettingPeroid; - func(selected); - }; - - const menu = ( - onClickHandler(e, setSelectedRetentionPeroid)}> - {options.map((option) => ( - {option.value} - ))} - + hide, +}: RetentionProps): JSX.Element | null { + const { + value: initialValue, + timeUnitValue: initialTimeUnitValue, + } = convertHoursValueToRelevantUnit(Number(retentionValue)); + const [selectedTimeUnit, setSelectTimeUnit] = useState(initialTimeUnitValue); + const [selectedValue, setSelectedValue] = useState( + initialValue, ); + const interacted = useRef(false); + useEffect(() => { + if (!interacted.current) setSelectedValue(initialValue); + }, [initialValue]); - const currentSelectedOption = (option: SettingPeroid): string | undefined => { - return options.find((e) => e.key === option)?.value; + useEffect(() => { + if (!interacted.current) setSelectTimeUnit(initialTimeUnitValue); + }, [initialTimeUnitValue]); + + const menuItems = TimeUnits.map((option) => ( + + )); + + const currentSelectedOption = (option: SettingPeriod): void => { + const selectedValue = find(TimeUnits, (e) => e.value === option)?.value; + if (selectedValue) setSelectTimeUnit(selectedValue); }; + useEffect(() => { + const inverseMultiplier = find( + TimeUnits, + (timeUnit) => timeUnit.value === selectedTimeUnit, + )?.multiplier; + if (!selectedValue) setRetentionValue(null); + if (selectedValue && inverseMultiplier) { + setRetentionValue(selectedValue * (1 / inverseMultiplier)); + } + }, [selectedTimeUnit, selectedValue, setRetentionValue]); + const onChangeHandler = ( e: React.ChangeEvent, - func: React.Dispatch>, + func: React.Dispatch>, ): void => { + interacted.current = true; const { value } = e.target; const integerValue = parseInt(value, 10); if (value.length > 0 && integerValue.toString() === value) { - const parsedValue = Math.abs(integerValue).toString(); + const parsedValue = Math.abs(integerValue); func(parsedValue); } if (value.length === 0) { - func(''); + func(null); } }; - + if (hide) { + return null; + } return ( - - {text} - - - onChangeHandler(e, setRentionValue)} - /> - - - - + + + {text} + + + + = 0 ? selectedValue : ''} + onChange={(e): void => onChangeHandler(e, setSelectedValue)} + style={{ width: 75 }} + /> + + + + ); } -interface Option { - key: SettingPeroid; - value: string; -} - interface RetentionProps { - retentionValue: string; + retentionValue: number | null; text: string; - setRentionValue: React.Dispatch>; - selectedRetentionPeroid: SettingPeroid; - setSelectedRetentionPeroid: React.Dispatch< - React.SetStateAction - >; + setRetentionValue: React.Dispatch>; + hide: boolean; } export default Retention; diff --git a/frontend/src/container/GeneralSettings/index.tsx b/frontend/src/container/GeneralSettings/index.tsx index e741e81886..6a76282192 100644 --- a/frontend/src/container/GeneralSettings/index.tsx +++ b/frontend/src/container/GeneralSettings/index.tsx @@ -1,43 +1,73 @@ -import { Button, Modal, notification, Typography } from 'antd'; -import getRetentionperoidApi from 'api/settings/getRetention'; +/* eslint-disable sonarjs/cognitive-complexity */ +import { Button, Col, Modal, notification, Row, Typography } from 'antd'; +import getDisks from 'api/disks/getDisks'; +import getRetentionPeriodApi from 'api/settings/getRetention'; import setRetentionApi from 'api/settings/setRetention'; import Spinner from 'components/Spinner'; import TextToolTip from 'components/TextToolTip'; import useFetch from 'hooks/useFetch'; -import convertIntoHr from 'lib/convertIntoHr'; -import getSettingsPeroid from 'lib/getSettingsPeroid'; -import React, { useCallback, useEffect, useState } from 'react'; +import { find } from 'lodash-es'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { IDiskType } from 'types/api/disks/getDisks'; import { PayloadProps } from 'types/api/settings/getRetention'; import Retention from './Retention'; -import { - ButtonContainer, - Container, - ErrorText, - ErrorTextContainer, - ToolTipContainer, -} from './styles'; +import { ButtonContainer, ErrorText, ErrorTextContainer } from './styles'; function GeneralSettings(): JSX.Element { - const [ - selectedMetricsPeroid, - setSelectedMetricsPeroid, - ] = useState('month'); + const { t } = useTranslation(); const [notifications, Element] = notification.useNotification(); - - const [retentionPeroidMetrics, setRetentionPeroidMetrics] = useState( - '', - ); const [modal, setModal] = useState(false); const [postApiLoading, setPostApiLoading] = useState(false); - const [selectedTracePeroid, setSelectedTracePeroid] = useState( - 'hr', - ); + const [availableDisks, setAvailableDisks] = useState(null); - const [retentionPeroidTrace, setRetentionPeroidTrace] = useState(''); - const [isDefaultMetrics, setIsDefaultMetrics] = useState(false); - const [isDefaultTrace, setIsDefaultTrace] = useState(false); + useEffect(() => { + getDisks().then((response) => setAvailableDisks(response.payload)); + }, []); + + const { payload, loading, error, errorMessage } = useFetch< + PayloadProps, + undefined + >(getRetentionPeriodApi, undefined); + + const [currentTTLValues, setCurrentTTLValues] = useState(payload); + + useEffect(() => { + setCurrentTTLValues(payload); + }, [payload]); + + const [metricsTotalRetentionPeriod, setMetricsTotalRetentionPeriod] = useState< + number | null + >(null); + const [metricsS3RetentionPeriod, setMetricsS3RetentionPeriod] = useState< + number | null + >(null); + const [tracesTotalRetentionPeriod, setTracesTotalRetentionPeriod] = useState< + number | null + >(null); + const [tracesS3RetentionPeriod, setTracesS3RetentionPeriod] = useState< + number | null + >(null); + + useEffect(() => { + if (currentTTLValues) { + setMetricsTotalRetentionPeriod(currentTTLValues.metrics_ttl_duration_hrs); + setMetricsS3RetentionPeriod( + currentTTLValues.metrics_move_ttl_duration_hrs + ? currentTTLValues.metrics_move_ttl_duration_hrs + : null, + ); + setTracesTotalRetentionPeriod(currentTTLValues.traces_ttl_duration_hrs); + setTracesS3RetentionPeriod( + currentTTLValues.traces_move_ttl_duration_hrs + ? currentTTLValues.traces_move_ttl_duration_hrs + : null, + ); + } + console.log({ changed: currentTTLValues }); + }, [currentTTLValues]); const onModalToggleHandler = (): void => { setModal((modal) => !modal); @@ -47,182 +77,236 @@ function GeneralSettings(): JSX.Element { onModalToggleHandler(); }, []); - const { payload, loading, error, errorMessage } = useFetch< - PayloadProps, - undefined - >(getRetentionperoidApi, undefined); + const s3Enabled = useMemo( + () => !!find(availableDisks, (disks: IDiskType) => disks?.type === 's3'), + [availableDisks], + ); - const checkMetricTraceDefault = (trace: number, metric: number): void => { - if (metric === -1) { - setIsDefaultMetrics(true); - } else { - setIsDefaultMetrics(false); + const renderConfig = [ + { + name: 'Metrics', + retentionFields: [ + { + name: t('settings.total_retention_period'), + value: metricsTotalRetentionPeriod, + setValue: setMetricsTotalRetentionPeriod, + }, + { + name: t('settings.move_to_s3'), + value: metricsS3RetentionPeriod, + setValue: setMetricsS3RetentionPeriod, + hide: !s3Enabled, + }, + ], + }, + { + name: 'Traces', + retentionFields: [ + { + name: t('settings.total_retention_period'), + value: tracesTotalRetentionPeriod, + setValue: setTracesTotalRetentionPeriod, + }, + { + name: t('settings.move_to_s3'), + value: tracesS3RetentionPeriod, + setValue: setTracesS3RetentionPeriod, + hide: !s3Enabled, + }, + ], + }, + ].map((category): JSX.Element | null => { + if ( + Array.isArray(category.retentionFields) && + category.retentionFields.length > 0 + ) { + return ( + + {category.name} + + {category.retentionFields.map((retentionField) => ( + + ))} + + ); } - - if (trace === -1) { - setIsDefaultTrace(true); - } else { - setIsDefaultTrace(false); - } - }; - - useEffect(() => { - if (!loading && payload !== undefined) { - const { - metrics_ttl_duration_hrs: metricTllDuration, - traces_ttl_duration_hrs: traceTllDuration, - } = payload; - - checkMetricTraceDefault(traceTllDuration, metricTllDuration); - - const traceValue = getSettingsPeroid(traceTllDuration); - const metricsValue = getSettingsPeroid(metricTllDuration); - - setRetentionPeroidTrace(traceValue.value.toString()); - setSelectedTracePeroid(traceValue.peroid); - - setRetentionPeroidMetrics(metricsValue.value.toString()); - setSelectedMetricsPeroid(metricsValue.peroid); - } - }, [setSelectedMetricsPeroid, loading, payload]); + return null; + }); const onOkHandler = async (): Promise => { try { setPostApiLoading(true); - const retentionTraceValue = - retentionPeroidTrace === '0' && (payload?.traces_ttl_duration_hrs || 0) < 0 - ? payload?.traces_ttl_duration_hrs || 0 - : parseInt(retentionPeroidTrace, 10); - - const retentionMetricsValue = - retentionPeroidMetrics === '0' && - (payload?.metrics_ttl_duration_hrs || 0) < 0 - ? payload?.metrics_ttl_duration_hrs || 0 - : parseInt(retentionPeroidMetrics, 10); - - const [tracesResponse, metricsResponse] = await Promise.all([ - setRetentionApi({ - duration: `${convertIntoHr(retentionTraceValue, selectedTracePeroid)}h`, - type: 'traces', - }), - setRetentionApi({ - duration: `${convertIntoHr( - retentionMetricsValue, - selectedMetricsPeroid, - )}h`, - type: 'metrics', - }), - ]); + const apiCalls = []; if ( - tracesResponse.statusCode === 200 && - metricsResponse.statusCode === 200 + !( + currentTTLValues?.metrics_move_ttl_duration_hrs === + metricsS3RetentionPeriod && + currentTTLValues.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod + ) ) { - notifications.success({ - message: 'Success!', - placement: 'topRight', - description: 'Congrats. The retention periods were updated correctly.', - }); - - checkMetricTraceDefault(retentionTraceValue, retentionMetricsValue); - - onModalToggleHandler(); + apiCalls.push(() => + setRetentionApi({ + type: 'metrics', + totalDuration: `${metricsTotalRetentionPeriod || -1}h`, + coldStorage: s3Enabled ? 's3' : null, + toColdDuration: `${metricsS3RetentionPeriod || -1}h`, + }), + ); } else { - notifications.error({ - message: 'Error', - description: - 'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io', - placement: 'topRight', - }); + apiCalls.push(() => Promise.resolve(null)); } + + if ( + !( + currentTTLValues?.traces_move_ttl_duration_hrs === + tracesS3RetentionPeriod && + currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod + ) + ) { + apiCalls.push(() => + setRetentionApi({ + type: 'traces', + totalDuration: `${tracesTotalRetentionPeriod || -1}h`, + coldStorage: s3Enabled ? 's3' : null, + toColdDuration: `${tracesS3RetentionPeriod || -1}h`, + }), + ); + } else { + apiCalls.push(() => Promise.resolve(null)); + } + const apiCallSequence = ['metrics', 'traces']; + const apiResponses = await Promise.all(apiCalls.map((api) => api())); + + apiResponses.forEach((apiResponse, idx) => { + const name = apiCallSequence[idx]; + if (apiResponse) { + if (apiResponse.statusCode === 200) { + notifications.success({ + message: 'Success!', + placement: 'topRight', + + description: t('settings.retention_success_message', { name }), + }); + } else { + notifications.error({ + message: 'Error', + description: t('settings.retention_error_message', { name }), + placement: 'topRight', + }); + } + } + }); + onModalToggleHandler(); setPostApiLoading(false); } catch (error) { notifications.error({ message: 'Error', - description: - 'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io', + description: t('settings.retention_failed_message'), placement: 'topRight', }); } + // Updates the currentTTL Values in order to avoid pushing the same values. + setCurrentTTLValues({ + metrics_ttl_duration_hrs: metricsTotalRetentionPeriod || -1, + metrics_move_ttl_duration_hrs: metricsS3RetentionPeriod || -1, + traces_ttl_duration_hrs: tracesTotalRetentionPeriod || -1, + traces_move_ttl_duration_hrs: tracesS3RetentionPeriod || -1, + }); + + setModal(false); }; + const [isDisabled, errorText] = useMemo((): [boolean, string] => { + // Various methods to return dynamic error message text. + const messages = { + compareError: (name: string | number): string => + t('settings.retention_comparison_error', { name }), + nullValueError: (name: string | number): string => + t('settings.retention_null_value_error', { name }), + }; + + // Defaults to button not disabled and empty error message text. + let isDisabled = false; + let errorText = ''; + + if (s3Enabled) { + if ( + (metricsTotalRetentionPeriod || metricsS3RetentionPeriod) && + Number(metricsTotalRetentionPeriod) <= Number(metricsS3RetentionPeriod) + ) { + isDisabled = true; + errorText = messages.compareError('metrics'); + } else if ( + (tracesTotalRetentionPeriod || tracesS3RetentionPeriod) && + Number(tracesTotalRetentionPeriod) <= Number(tracesS3RetentionPeriod) + ) { + isDisabled = true; + errorText = messages.compareError('traces'); + } + } + + if (!metricsTotalRetentionPeriod || !tracesTotalRetentionPeriod) { + isDisabled = true; + if (!metricsTotalRetentionPeriod && !tracesTotalRetentionPeriod) { + errorText = messages.nullValueError('metrics and traces'); + } else if (!metricsTotalRetentionPeriod) { + errorText = messages.nullValueError('metrics'); + } else if (!tracesTotalRetentionPeriod) { + errorText = messages.nullValueError('traces'); + } + } + if ( + currentTTLValues?.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod && + currentTTLValues.metrics_move_ttl_duration_hrs === + metricsS3RetentionPeriod && + currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod && + currentTTLValues.traces_move_ttl_duration_hrs === tracesS3RetentionPeriod + ) { + isDisabled = true; + } + return [isDisabled, errorText]; + }, [ + currentTTLValues, + metricsS3RetentionPeriod, + metricsTotalRetentionPeriod, + s3Enabled, + t, + tracesS3RetentionPeriod, + tracesTotalRetentionPeriod, + ]); + if (error) { return {errorMessage}; } - if (loading || payload === undefined) { + if (loading || currentTTLValues === undefined) { return ; } - const getErrorText = (): string => { - const getValue = (value: string): string => - `Retention Peroid for ${value} is not set yet. Please set by choosing below`; - - if (!isDefaultMetrics && !isDefaultTrace) { - return ''; - } - - if (isDefaultMetrics && !isDefaultTrace) { - return `${getValue('Metrics')}`; - } - - if (!isDefaultMetrics && isDefaultTrace) { - return `${getValue('Trace')}`; - } - - return `${getValue('Trace , Metrics')}`; - }; - - const isDisabledHandler = (): boolean => { - return !!(retentionPeroidTrace === '' || retentionPeroidMetrics === ''); - }; - - const errorText = getErrorText(); - return ( - + {Element} + + + {errorText && {errorText}} + - {errorText ? ( - - {errorText} - - - - ) : ( - - - - )} - - - - + {renderConfig} - - This will change the amount of storage needed for saving metrics & traces. - + {t('settings.retention_confirmation_description')} - - + ); } -export type SettingPeroid = 'hr' | 'day' | 'month'; +export type SettingPeriod = 'hr' | 'day' | 'month'; export default GeneralSettings; diff --git a/frontend/src/container/GeneralSettings/styles.ts b/frontend/src/container/GeneralSettings/styles.ts index 5233ce7e50..6dc7b9fe71 100644 --- a/frontend/src/container/GeneralSettings/styles.ts +++ b/frontend/src/container/GeneralSettings/styles.ts @@ -1,16 +1,13 @@ import { + Col, Dropdown as DropDownComponent, Input as InputComponent, Typography as TypographyComponent, } from 'antd'; import styled from 'styled-components'; -export const RetentionContainer = styled.div` - width: 50%; - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; +export const RetentionContainer = styled(Col)` + margin: 0.75rem 0; `; export const Input = styled(InputComponent)` @@ -37,13 +34,6 @@ export const ButtonContainer = styled.div` } `; -export const Container = styled.div` - &&& { - display: flex; - flex-direction: column; - } -`; - export const Dropdown = styled(DropDownComponent)` &&& { display: flex; @@ -66,6 +56,7 @@ export const ErrorTextContainer = styled.div` margin-bottom: 2rem; display: flex; align-items: center; + gap: 1rem; > article { margin-right: 1rem; @@ -90,3 +81,12 @@ export const ErrorText = styled(TypographyComponent)` font-style: italic; } `; + +export const RetentionFieldLabel = styled(TypographyComponent)` + vertical-align: middle; + white-space: pre-wrap; +`; + +export const RetentionFieldInputContainer = styled.div` + display: inline-flex; +`; diff --git a/frontend/src/container/GeneralSettings/utils.ts b/frontend/src/container/GeneralSettings/utils.ts new file mode 100644 index 0000000000..30efd6786a --- /dev/null +++ b/frontend/src/container/GeneralSettings/utils.ts @@ -0,0 +1,42 @@ +export type SettingPeriod = 'hr' | 'day' | 'month'; + +export interface ITimeUnit { + value: SettingPeriod; + key: string; + multiplier: number; +} +export const TimeUnits: ITimeUnit[] = [ + { + value: 'hr', + key: 'Hours', + multiplier: 1, + }, + { + value: 'day', + key: 'Days', + multiplier: 1 / 24, + }, + { + value: 'month', + key: 'Months', + multiplier: 1 / (24 * 30), + }, +]; + +export const convertHoursValueToRelevantUnit = ( + value: number, +): { value: number; timeUnitValue: SettingPeriod } => { + if (value) + for (let idx = TimeUnits.length - 1; idx >= 0; idx -= 1) { + const timeUnit = TimeUnits[idx]; + const convertedValue = timeUnit.multiplier * value; + + if ( + convertedValue >= 1 && + convertedValue === parseInt(`${convertedValue}`, 10) + ) { + return { value: convertedValue, timeUnitValue: timeUnit.value }; + } + } + return { value, timeUnitValue: TimeUnits[0].value }; +}; diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/EmptyGraph.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/EmptyGraph.tsx deleted file mode 100644 index c284d83b7c..0000000000 --- a/frontend/src/container/GridGraphLayout/Graph/FullView/EmptyGraph.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import Graph, { GraphOnClickHandler } from 'components/Graph'; -import { timePreferance } from 'container/NewWidget/RightContainer/timeItems'; -import GetMaxMinTime from 'lib/getMaxMinTime'; -import { colors } from 'lib/getRandomColor'; -import getStartAndEndTime from 'lib/getStartAndEndTime'; -import getTimeString from 'lib/getTimeString'; -import React, { useCallback } from 'react'; -import { useSelector } from 'react-redux'; -import { AppState } from 'store/reducers'; -import { Widgets } from 'types/api/dashboard/getAll'; -import { GlobalReducer } from 'types/reducer/globalTime'; - -function EmptyGraph({ - selectedTime, - widget, - onClickHandler, -}: EmptyGraphProps): JSX.Element { - const { minTime, maxTime, loading } = useSelector( - (state) => state.globalTime, - ); - - const maxMinTime = GetMaxMinTime({ - graphType: widget.panelTypes, - maxTime, - minTime, - }); - - const { end, start } = getStartAndEndTime({ - type: selectedTime.enum, - maxTime: maxMinTime.maxTime, - minTime: maxMinTime.minTime, - }); - - const dateFunction = useCallback(() => { - if (!loading) { - const dates: Date[] = []; - - const startString = getTimeString(start); - const endString = getTimeString(end); - - const parsedStart = parseInt(startString, 10); - const parsedEnd = parseInt(endString, 10); - - let startDate = parsedStart; - const endDate = parsedEnd; - - while (endDate >= startDate) { - const newDate = new Date(startDate); - - startDate += 20000; - - dates.push(newDate); - } - return dates; - } - return []; - }, [start, end, loading]); - - const date = dateFunction(); - - return ( - - ); -} - -interface EmptyGraphProps { - selectedTime: timePreferance; - widget: Widgets; - onClickHandler: GraphOnClickHandler | undefined; -} - -export default EmptyGraph; diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx index 0f0ce7b1fb..ba8a66a82d 100644 --- a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx +++ b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx @@ -23,14 +23,12 @@ import { AppState } from 'store/reducers'; import { Widgets } from 'types/api/dashboard/getAll'; import { GlobalReducer } from 'types/reducer/globalTime'; -import EmptyGraph from './EmptyGraph'; import { NotFoundContainer, TimeContainer } from './styles'; function FullView({ widget, fullViewOptions = true, onClickHandler, - noDataGraph = false, name, yAxisUnit, }: FullViewProps): JSX.Element { @@ -166,38 +164,6 @@ function FullView({ ); } - if (state.loading === false && state.payload.datasets.length === 0) { - return ( - <> - {fullViewOptions && ( - - - - - )} - - {noDataGraph ? ( - - ) : ( - - No Data - - )} - - ); - } - return ( <> {fullViewOptions && ( @@ -243,7 +209,6 @@ interface FullViewProps { widget: Widgets; fullViewOptions?: boolean; onClickHandler?: GraphOnClickHandler; - noDataGraph?: boolean; name: string; yAxisUnit?: string; } @@ -251,7 +216,6 @@ interface FullViewProps { FullView.defaultProps = { fullViewOptions: undefined, onClickHandler: undefined, - noDataGraph: undefined, yAxisUnit: undefined, }; diff --git a/frontend/src/container/Header/Breadcrumbs/index.tsx b/frontend/src/container/Header/Breadcrumbs/index.tsx index f1c295e1ff..d88384b75c 100644 --- a/frontend/src/container/Header/Breadcrumbs/index.tsx +++ b/frontend/src/container/Header/Breadcrumbs/index.tsx @@ -11,6 +11,7 @@ const breadcrumbNameMap = { [ROUTES.INSTRUMENTATION]: 'Add instrumentation', [ROUTES.SETTINGS]: 'Settings', [ROUTES.DASHBOARD]: 'Dashboard', + [ROUTES.VERSION]: 'Status', }; function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element { diff --git a/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx b/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx index f0b2d183df..fd9ef16001 100644 --- a/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx +++ b/frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx @@ -1,4 +1,6 @@ -import React, { useCallback } from 'react'; +import { ExclamationCircleOutlined } from '@ant-design/icons'; +import { Modal } from 'antd'; +import React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; @@ -8,14 +10,29 @@ import AppActions from 'types/actions'; import { Data } from '../index'; import { TableLinkText } from './styles'; -function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element { - const onClickHandler = useCallback(() => { - deleteDashboard({ - uuid: id, - }); - }, [id, deleteDashboard]); +const { confirm } = Modal; - return Delete; +function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element { + const openConfirmationDialog = (): void => { + confirm({ + title: 'Do you really want to delete this dashboard?', + icon: , + onOk() { + deleteDashboard({ + uuid: id, + }); + }, + okText: 'Delete', + okButtonProps: { danger: true }, + centered: true, + }); + }; + + return ( + + Delete + + ); } interface DispatchProps { diff --git a/frontend/src/container/ListOfDashboard/index.tsx b/frontend/src/container/ListOfDashboard/index.tsx index 8828d748f2..492da5c846 100644 --- a/frontend/src/container/ListOfDashboard/index.tsx +++ b/frontend/src/container/ListOfDashboard/index.tsx @@ -157,7 +157,7 @@ function ListOfAllDashboard(): JSX.Element { diff --git a/frontend/src/container/MetricsApplication/Tabs/Application.tsx b/frontend/src/container/MetricsApplication/Tabs/Application.tsx index 774d8d7118..8b8ae4f38f 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Application.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Application.tsx @@ -36,7 +36,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element { history.replace( `${ ROUTES.TRACE - }?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"status":["ok","error"]}&filterToFetchData=["duration","status","serviceName"]&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&isSelectedFilterSkipped=true`, + }?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=[]&&isFilterExclude={"serviceName":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`, ); }; @@ -88,7 +88,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element { history.replace( `${ ROUTES.TRACE - }?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&userSelectedFilter={"status":["error"],"serviceName":["${servicename}"]}&isSelectedFilterSkipped=true`, + }?${urlParams.toString()}?selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=[]&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`, ); }; @@ -179,7 +179,6 @@ function Application({ getWidget }: DashboardProps): JSX.Element { { onClickhandler(event, element, chart, data, 'Request'); @@ -214,7 +213,6 @@ function Application({ getWidget }: DashboardProps): JSX.Element { { onClickhandler(ChartEvent, activeElements, chart, data, 'Error'); diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx index 5b918338e4..20206c21d7 100644 --- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx @@ -17,7 +17,6 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element { External Call duration(by Address) diff --git a/frontend/src/container/SideNav/index.tsx b/frontend/src/container/SideNav/index.tsx index 4902986dbd..44bb2201e8 100644 --- a/frontend/src/container/SideNav/index.tsx +++ b/frontend/src/container/SideNav/index.tsx @@ -1,3 +1,4 @@ +import { CheckCircleTwoTone, WarningOutlined } from '@ant-design/icons'; import { Menu, Typography } from 'antd'; import getLocalStorageKey from 'api/browser/localstorage/get'; import { IS_SIDEBAR_COLLAPSED } from 'constants/app'; @@ -5,6 +6,7 @@ import ROUTES from 'constants/routes'; import history from 'lib/history'; import setTheme, { AppMode } from 'lib/theme/setTheme'; import React, { useCallback, useLayoutEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { connect, useDispatch, useSelector } from 'react-redux'; import { NavLink, useLocation } from 'react-router-dom'; import { bindActionCreators } from 'redux'; @@ -19,11 +21,13 @@ import menus from './menuItems'; import Slack from './Slack'; import { Logo, + RedDot, Sider, SlackButton, SlackMenuItemContainer, ThemeSwitcherWrapper, ToggleButton, + VersionContainer, } from './styles'; function SideNav({ toggleDarkMode }: Props): JSX.Element { @@ -31,9 +35,15 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element { const [collapsed, setCollapsed] = useState( getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true', ); - const { isDarkMode } = useSelector((state) => state.app); + const { + isDarkMode, + currentVersion, + latestVersion, + isCurrentVersionError, + } = useSelector((state) => state.app); const { pathname } = useLocation(); + const { t } = useTranslation(''); const toggleTheme = useCallback(() => { const preMode: AppMode = isDarkMode ? 'lightMode' : 'darkMode'; @@ -77,6 +87,38 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element { window.open('https://signoz.io/slack', '_blank'); }; + const onClickVersionHandler = (): void => { + history.push(ROUTES.VERSION); + }; + + const isNotCurrentVersion = currentVersion !== latestVersion; + + const sidebar = [ + { + onClick: onClickSlackHandler, + icon: , + text: Support, + }, + { + onClick: onClickVersionHandler, + icon: isNotCurrentVersion ? ( + + ) : ( + + ), + text: ( + + {!isCurrentVersionError ? ( + {currentVersion} + ) : ( + {t('n_a')} + )} + {isNotCurrentVersion && } + + ), + }, + ]; + return ( @@ -87,7 +129,7 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element { /> - + {name} ))} - - }> - Support - - + {sidebar.map((props, index) => ( + + + {props.text} + + + ))} ); diff --git a/frontend/src/container/SideNav/styles.ts b/frontend/src/container/SideNav/styles.ts index 0066d2affd..975aa65ba9 100644 --- a/frontend/src/container/SideNav/styles.ts +++ b/frontend/src/container/SideNav/styles.ts @@ -19,6 +19,7 @@ export const Logo = styled.img` interface LogoProps { collapsed: boolean; + index: number; } export const Sider = styled(SiderComponent)` @@ -50,9 +51,10 @@ export const SlackButton = styled(Typography)` export const SlackMenuItemContainer = styled.div` position: fixed; - bottom: 48px; + bottom: ${({ index }): string => `${index * 48 + (index + 16)}px`}; background: #262626; width: ${({ collapsed }): string => (!collapsed ? '200px' : '80px')}; + transition: inherit; &&& { li { @@ -60,11 +62,14 @@ export const SlackMenuItemContainer = styled.div` collapsed && css` padding-left: 24px; + padding-top: 6px; `} } svg { margin-left: ${({ collapsed }): string => (collapsed ? '0' : '24px')}; + width: 28px; + height: 28px; ${({ collapsed }): StyledCSS => collapsed && @@ -73,5 +78,24 @@ export const SlackMenuItemContainer = styled.div` margin: 0 auto; `} } + .ant-menu-title-content { + margin: 0; + } + } +`; + +export const RedDot = styled.div` + width: 12px; + height: 12px; + background: #d32029; + border-radius: 50%; + + margin-left: 1rem; + margin-top: 0.5rem; +`; + +export const VersionContainer = styled.div` + &&& { + display: flex; } `; diff --git a/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx b/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx index e177f1b4b3..1c0c5fae75 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx @@ -93,11 +93,15 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element { if (response.statusCode === 200) { const updatedFilter = getFilter(response.payload); - updatedFilter.forEach((value, key) => { - if (key !== 'duration' && name !== key) { - preUserSelectedMap.set(key, Object.keys(value)); - } - }); + // updatedFilter.forEach((value, key) => { + // if (key !== 'duration' && name !== key) { + // preUserSelectedMap.set(key, Object.keys(value)); + // } + + // if (key === 'duration') { + // newSelectedMap.set('duration', [value.maxDuration, value.minDuration]); + // } + // }); updatedFilter.set(name, { [`${keyValue}`]: '-1', @@ -115,6 +119,7 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element { selectedFilter: newSelectedMap, userSelected: preUserSelectedMap, isFilterExclude: preIsFilterExclude, + order: spansAggregate.order, }, }); @@ -125,9 +130,9 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element { filterToFetchData, spansAggregate.currentPage, selectedTags, - updatedFilter, preIsFilterExclude, preUserSelectedMap, + spansAggregate.order, ); } else { setIsLoading(false); diff --git a/frontend/src/container/Trace/Filters/Panel/PanelBody/CommonCheckBox/index.tsx b/frontend/src/container/Trace/Filters/Panel/PanelBody/CommonCheckBox/index.tsx index 161bb6f8ac..0681e7e5d5 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelBody/CommonCheckBox/index.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelBody/CommonCheckBox/index.tsx @@ -18,16 +18,26 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element { return ( <> - {statusObj.map((e) => ( - - ))} + {statusObj + .sort((a, b) => { + const countA = +status[a]; + const countB = +status[b]; + + if (countA === countB) { + return a.length - b.length; + } + return countA - countB; + }) + .map((e) => ( + + ))} ); } diff --git a/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx b/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx index ef61e3a6a5..e6d44f3b61 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx @@ -5,7 +5,7 @@ import getFilters from 'api/trace/getFilters'; import dayjs from 'dayjs'; import durationPlugin from 'dayjs/plugin/duration'; import useDebouncedFn from 'hooks/useDebouncedFunction'; -import React, { useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Dispatch } from 'redux'; import { getFilter, updateURL } from 'store/actions/trace/util'; @@ -20,11 +20,13 @@ import { Container, InputContainer, Text } from './styles'; dayjs.extend(durationPlugin); const getMs = (value: string): string => { - return dayjs - .duration({ - milliseconds: parseInt(value, 10) / 1000000, - }) - .format('SSS'); + return parseFloat( + dayjs + .duration({ + milliseconds: parseInt(value, 10) / 1000000, + }) + .format('SSS'), + ).toFixed(2); }; function Duration(): JSX.Element { @@ -43,9 +45,10 @@ function Duration(): JSX.Element { (state) => state.globalTime, ); - const getDuration = (): - | { maxDuration: string; minDuration: string } - | Record => { + const preLocalMaxDuration = useRef(); + const preLocalMinDuration = useRef(); + + const getDuration = useMemo(() => { const selectedDuration = selectedFilter.get('duration'); if (selectedDuration) { @@ -56,17 +59,29 @@ function Duration(): JSX.Element { } return filter.get('duration') || {}; - }; + }, [selectedFilter, filter]); - const duration = getDuration(); + const [preMax, setPreMax] = useState(''); + const [preMin, setPreMin] = useState(''); - const maxDuration = duration.maxDuration || '0'; - const minDuration = duration.minDuration || '0'; + useEffect(() => { + const duration = getDuration || {}; - const [localMax, setLocalMax] = useState(maxDuration); - const [localMin, setLocalMin] = useState(minDuration); + const maxDuration = duration.maxDuration || '0'; + const minDuration = duration.minDuration || '0'; - const defaultValue = [parseFloat(minDuration), parseFloat(maxDuration)]; + if (preLocalMaxDuration.current === undefined) { + preLocalMaxDuration.current = parseFloat(maxDuration); + } + if (preLocalMinDuration.current === undefined) { + preLocalMinDuration.current = parseFloat(minDuration); + } + + setPreMax(maxDuration); + setPreMin(minDuration); + }, [getDuration]); + + const defaultValue = [parseFloat(preMin), parseFloat(preMax)]; const updatedUrl = async (min: number, max: number): Promise => { const preSelectedFilter = new Map(selectedFilter); @@ -74,7 +89,6 @@ function Duration(): JSX.Element { preSelectedFilter.set('duration', [String(max), String(min)]); - console.log('on the update Url'); const response = await getFilters({ end: String(globalTime.maxTime), getFilters: filterToFetchData, @@ -87,8 +101,9 @@ function Duration(): JSX.Element { const preFilter = getFilter(response.payload); preFilter.forEach((value, key) => { - if (key !== 'duration') { - preUserSelected.set(key, Object.keys(value)); + const values = Object.keys(value); + if (key !== 'duration' && values.length) { + preUserSelected.set(key, values); } }); @@ -102,6 +117,7 @@ function Duration(): JSX.Element { selectedTags, userSelected: preUserSelected, isFilterExclude, + order: spansAggregate.order, }, }); @@ -110,9 +126,9 @@ function Duration(): JSX.Element { filterToFetchData, spansAggregate.currentPage, selectedTags, - preFilter, isFilterExclude, userSelectedFilter, + spansAggregate.order, ); } }; @@ -120,13 +136,12 @@ function Duration(): JSX.Element { const onRangeSliderHandler = (number: [number, number]): void => { const [min, max] = number; - setLocalMin(min.toString()); - setLocalMax(max.toString()); + setPreMin(min.toString()); + setPreMax(max.toString()); }; const debouncedFunction = useDebouncedFn( (min, max) => { - console.log('debounce function'); updatedUrl(min as number, max as number); }, 500, @@ -137,11 +152,9 @@ function Duration(): JSX.Element { event, ) => { const { value } = event.target; - const min = parseFloat(localMin); + const min = parseFloat(preMin); const max = parseFloat(value) * 1000000; - console.log('on change in max'); - onRangeSliderHandler([min, max]); debouncedFunction(min, max); }; @@ -151,9 +164,8 @@ function Duration(): JSX.Element { ) => { const { value } = event.target; const min = parseFloat(value) * 1000000; - const max = parseFloat(localMax); + const max = parseFloat(preMax); onRangeSliderHandler([min, max]); - console.log('on change in min'); debouncedFunction(min, max); }; @@ -170,7 +182,7 @@ function Duration(): JSX.Element { @@ -179,27 +191,27 @@ function Duration(): JSX.Element { { if (value === undefined) { return
; } - return
{`${getMs(value.toString())}ms`}
; + return
{`${getMs(value?.toString())}ms`}
; }} onChange={([min, max]): void => { onRangeSliderHandler([min, max]); }} onAfterChange={onRangeHandler} - value={[parseFloat(localMin), parseFloat(localMax)]} + value={[parseFloat(preMin), parseFloat(preMax)]} />
diff --git a/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx b/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx index 50686d1b78..9d2b9b1da1 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx @@ -21,7 +21,7 @@ import { ButtonContainer, Container, IconContainer, - TextCotainer, + TextContainer, } from './styles'; const { Text } = Typography; @@ -64,16 +64,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { const getprepdatedSelectedFilter = new Map(selectedFilter); const getPreUserSelected = new Map(userSelectedFilter); - if (!isDefaultOpen) { - updatedFilterData = [PanelName]; - } else { - // removing the selected filter - updatedFilterData = [ - ...filterToFetchData.filter((name) => name !== PanelName), - ]; - getprepdatedSelectedFilter.delete(PanelName); - getPreUserSelected.delete(PanelName); - } + updatedFilterData = [PanelName]; const response = await getFilters({ end: String(global.maxTime), @@ -86,33 +77,14 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { if (response.statusCode === 200) { const updatedFilter = getFilter(response.payload); - // is closed - if (!isDefaultOpen) { - // getprepdatedSelectedFilter.set( - // props.name, - // Object.keys(updatedFilter.get(props.name) || {}), - // ); - + if (!getPreUserSelected.has(PanelName)) { getPreUserSelected.set( PanelName, - Object.keys(updatedFilter.get(PanelName) || {}), + Object.keys(updatedFilter.get(PanelName) || []), ); - - updatedFilterData = [...filterToFetchData, PanelName]; } - // now append the non prop.name trace filter enum over the list - // selectedFilter.forEach((value, key) => { - // if (key !== props.name) { - // getprepdatedSelectedFilter.set(key, value); - // } - // }); - - getPreUserSelected.forEach((value, key) => { - if (key !== PanelName) { - getPreUserSelected.set(key, value); - } - }); + updatedFilterData = [...filterToFetchData, PanelName]; filter.forEach((value, key) => { if (key !== PanelName) { updatedFilter.set(key, value); @@ -129,6 +101,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { selectedTags, userSelected: getPreUserSelected, isFilterExclude, + order: spansAggregate.order, }, }); @@ -137,9 +110,9 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { updatedFilterData, spansAggregate.currentPage, selectedTags, - updatedFilter, isFilterExclude, getPreUserSelected, + spansAggregate.order, ); } else { notification.error({ @@ -155,6 +128,43 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { } }; + /** + * @description this function removes the selected filter + */ + const onCloseHandler = (): void => { + const preSelectedFilter = new Map(selectedFilter); + // removing the filter from filter to fetch the data + const preFilterToFetchTheData = [ + ...filterToFetchData.filter((name) => name !== PanelName), + ]; + + // preSelectedFilter.delete(PanelName); + + dispatch({ + type: UPDATE_ALL_FILTERS, + payload: { + current: spansAggregate.currentPage, + filter, + filterToFetchData: preFilterToFetchTheData, + selectedFilter: preSelectedFilter, + selectedTags, + userSelected: userSelectedFilter, + isFilterExclude, + order: spansAggregate.order, + }, + }); + + updateURL( + preSelectedFilter, + preFilterToFetchTheData, + spansAggregate.currentPage, + selectedTags, + isFilterExclude, + userSelectedFilter, + spansAggregate.order, + ); + }; + const onClearAllHandler = async (): Promise => { try { setIsLoading(true); @@ -177,18 +187,19 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { }); if (response.statusCode === 200 && response.payload) { - const getUpatedFilter = getFilter(response.payload); + const getUpdatedFilter = getFilter(response.payload); dispatch({ type: UPDATE_ALL_FILTERS, payload: { current: spansAggregate.currentPage, - filter: getUpatedFilter, + filter: getUpdatedFilter, filterToFetchData, selectedFilter: updatedFilter, selectedTags, userSelected: preUserSelected, isFilterExclude: postIsFilterExclude, + order: spansAggregate.order, }, }); @@ -197,9 +208,9 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { filterToFetchData, spansAggregate.currentPage, selectedTags, - getUpatedFilter, postIsFilterExclude, preUserSelected, + spansAggregate.order, ); } else { notification.error({ @@ -280,7 +291,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { aria-disabled={filterLoading || isLoading} aria-expanded={IsPanelOpen} > - + {!IsPanelOpen ? : } @@ -288,7 +299,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { {AllPanelHeading.find((e) => e.key === PanelName)?.displayValue || ''} - + {PanelName !== 'duration' && ( diff --git a/frontend/src/container/Trace/Filters/Panel/PanelHeading/styles.ts b/frontend/src/container/Trace/Filters/Panel/PanelHeading/styles.ts index d2c66cfb23..812be65ac7 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelHeading/styles.ts +++ b/frontend/src/container/Trace/Filters/Panel/PanelHeading/styles.ts @@ -30,7 +30,7 @@ export const IconContainer = styled.div` } `; -export const TextCotainer = styled.div` +export const TextContainer = styled.div` &&& { display: flex; cursor: pointer; diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx new file mode 100644 index 0000000000..d58675ffad --- /dev/null +++ b/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx @@ -0,0 +1,70 @@ +import { Select } from 'antd'; +import { DefaultOptionType } from 'antd/lib/select'; +import getTagValue from 'api/trace/getTagValue'; +import useFetch from 'hooks/useFetch'; +import React from 'react'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { PayloadProps, Props } from 'types/api/trace/getTagValue'; +import { GlobalReducer } from 'types/reducer/globalTime'; +import { TraceReducer } from 'types/reducer/trace'; + +import { Value } from '.'; +import { SelectComponent } from './styles'; + +function TagValue(props: TagValueProps): JSX.Element { + const { tag, setLocalSelectedTags, index, tagKey } = props; + const { + Key: selectedKey, + Operator: selectedOperator, + Values: selectedValues, + } = tag; + + const globalReducer = useSelector( + (state) => state.globalTime, + ); + + const valueSuggestion = useFetch(getTagValue, { + end: globalReducer.maxTime, + start: globalReducer.minTime, + tagKey, + }); + + return ( + { + if (typeof value === 'string') { + setLocalSelectedTags((tags) => [ + ...tags.slice(0, index), + { + Key: selectedKey, + Operator: selectedOperator, + Values: [...selectedValues, value], + }, + ...tags.slice(index + 1, tags.length), + ]); + } + }} + loading={valueSuggestion.loading || false} + > + {valueSuggestion.payload && + valueSuggestion.payload.map((suggestion) => ( + + {suggestion.tagValues} + + ))} + + ); +} + +interface TagValueProps { + index: number; + tag: FlatArray; + setLocalSelectedTags: React.Dispatch< + React.SetStateAction + >; + tagKey: string; +} + +export default TagValue; diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx index e6ad17956f..ad9b0e7972 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx +++ b/frontend/src/container/Trace/Search/AllTags/Tag/index.tsx @@ -5,13 +5,9 @@ import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; import { TraceReducer } from 'types/reducer/trace'; -import { - Container, - IconContainer, - SelectComponent, - ValueSelect, -} from './styles'; +import { Container, IconContainer, SelectComponent } from './styles'; import TagsKey from './TagKey'; +import TagValue from './TagValue'; const { Option } = Select; @@ -68,7 +64,6 @@ function SingleTags(props: AllTagsProps): JSX.Element { tag={tag} setLocalSelectedTags={setLocalSelectedTags} /> - e.key === selectedOperator)?.value || ''} @@ -80,21 +75,16 @@ function SingleTags(props: AllTagsProps): JSX.Element { ))} - { - setLocalSelectedTags((tags) => [ - ...tags.slice(0, index), - { - Key: selectedKey, - Operator: selectedOperator, - Values: value as string[], - }, - ...tags.slice(index + 1, tags.length), - ]); - }} - mode="tags" - /> + {selectedKey[0] ? ( + + ) : ( + + )} onDeleteTagHandler(index)}> @@ -112,4 +102,10 @@ interface AllTagsProps { >; } +export interface Value { + key: string; + label: string; + value: string; +} + export default SingleTags; diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/styles.ts b/frontend/src/container/Trace/Search/AllTags/Tag/styles.ts index 8da702197d..347bc287f2 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/styles.ts +++ b/frontend/src/container/Trace/Search/AllTags/Tag/styles.ts @@ -15,12 +15,6 @@ export const SelectComponent = styled(Select)` } `; -export const ValueSelect = styled(Select)` - &&& { - width: 100%; - } -`; - export const Container = styled.div` &&& { display: flex; diff --git a/frontend/src/container/Trace/Search/AllTags/index.tsx b/frontend/src/container/Trace/Search/AllTags/index.tsx index 91e2d08cb0..fc132eafe8 100644 --- a/frontend/src/container/Trace/Search/AllTags/index.tsx +++ b/frontend/src/container/Trace/Search/AllTags/index.tsx @@ -5,7 +5,7 @@ import { connect, useSelector } from 'react-redux'; import { bindActionCreators } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; import { UpdateTagIsError } from 'store/actions/trace/updateIsTagsError'; -import { UpdateTagVisiblity } from 'store/actions/trace/updateTagPanelVisiblity'; +import { UpdateTagVisibility } from 'store/actions/trace/updateTagPanelVisiblity'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; import { TraceReducer } from 'types/reducer/trace'; @@ -27,7 +27,7 @@ const { Paragraph } = Typography; function AllTags({ updateTagIsError, onChangeHandler, - updateTagVisiblity, + updateTagVisibility, updateFilters, }: AllTagsProps): JSX.Element { const traces = useSelector((state) => state.traces); @@ -63,7 +63,7 @@ function AllTags({ onChangeHandler(parsedQuery.payload); updateFilters(localSelectedTags); updateTagIsError(false); - updateTagVisiblity(false); + updateTagVisibility(false); } }; @@ -75,7 +75,7 @@ function AllTags({ return ( - Unrecognised query format. Please reset your query by clicking `X` in the + Unrecognized query format. Please reset your query by clicking `X` in the search bar above. @@ -116,14 +116,16 @@ function AllTags({ - - + + + +
); @@ -131,14 +133,14 @@ function AllTags({ interface DispatchProps { updateTagIsError: (value: boolean) => void; - updateTagVisiblity: (value: boolean) => void; + updateTagVisibility: (value: boolean) => void; } const mapDispatchToProps = ( dispatch: ThunkDispatch, ): DispatchProps => ({ updateTagIsError: bindActionCreators(UpdateTagIsError, dispatch), - updateTagVisiblity: bindActionCreators(UpdateTagVisiblity, dispatch), + updateTagVisibility: bindActionCreators(UpdateTagVisibility, dispatch), }); interface AllTagsProps extends DispatchProps { diff --git a/frontend/src/container/Trace/Search/AllTags/styles.ts b/frontend/src/container/Trace/Search/AllTags/styles.ts index c43b32c5ea..3ce74315e1 100644 --- a/frontend/src/container/Trace/Search/AllTags/styles.ts +++ b/frontend/src/container/Trace/Search/AllTags/styles.ts @@ -6,7 +6,7 @@ export const Container = styled(Card)` min-height: 20vh; width: 100%; z-index: 2; - position: absolute; + position: absolute !important; .ant-card-body { padding-bottom: 0; @@ -35,20 +35,16 @@ export const Wrapper = styled.div` } `; -export const ButtonContainer = styled.div` +export const ButtonContainer = styled(Card)` display: flex; justify-content: flex-end; align-items: center; - background-color: #303030; - padding-top: 11px; - padding-bottom: 11px; - padding-right: 38.01px; - margin-top: 1rem; + padding-top: 11px !important; + padding-bottom: 11px !important; + padding-right: 38.01px !important; - > button:nth-child(1) { - margin-right: 1rem; - } + margin-top: 1rem !important; `; export const CurrentTagsContainer = styled.div` diff --git a/frontend/src/container/Trace/Search/index.tsx b/frontend/src/container/Trace/Search/index.tsx index cb8fdd453d..54bb51fa5a 100644 --- a/frontend/src/container/Trace/Search/index.tsx +++ b/frontend/src/container/Trace/Search/index.tsx @@ -1,12 +1,11 @@ import { CaretRightFilled } from '@ant-design/icons'; -import { Space } from 'antd'; import useClickOutside from 'hooks/useClickOutside'; import React, { useEffect, useRef, useState } from 'react'; import { connect, useDispatch, useSelector } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; import { UpdateTagIsError } from 'store/actions/trace/updateIsTagsError'; -import { UpdateTagVisiblity } from 'store/actions/trace/updateTagPanelVisiblity'; +import { UpdateTagVisibility } from 'store/actions/trace/updateTagPanelVisiblity'; import { updateURL } from 'store/actions/trace/util'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; @@ -18,7 +17,7 @@ import { Container, SearchComponent } from './styles'; import { parseQueryToTags, parseTagsToQuery } from './util'; function Search({ - updateTagVisiblity, + updateTagVisibility, updateTagIsError, }: SearchProps): JSX.Element { const traces = useSelector((state) => state.traces); @@ -66,7 +65,7 @@ function Search({ !(e.ariaSelected === 'true') && traces.isTagModalOpen ) { - updateTagVisiblity(false); + updateTagVisibility(false); } }); @@ -75,7 +74,7 @@ function Search({ }; const setIsTagsModalHandler = (value: boolean): void => { - updateTagVisiblity(value); + updateTagVisibility(value); }; const onFocusHandler: React.FocusEventHandler = (e) => { @@ -96,6 +95,7 @@ function Search({ selectedFilter: traces.selectedFilter, userSelected: traces.userSelectedFilter, isFilterExclude: traces.isFilterExclude, + order: traces.spansAggregate.order, }, }); @@ -104,60 +104,58 @@ function Search({ traces.filterToFetchData, traces.spansAggregate.currentPage, selectedTags, - traces.filter, traces.isFilterExclude, traces.userSelectedFilter, + traces.spansAggregate.order, ); }; return ( - - - onChangeHandler(event.target.value)} - value={value} - allowClear - disabled={traces.filterLoading} - onFocus={onFocusHandler} - placeholder="Click to filter by tags" - type="search" - enterButton={} - onSearch={(string): void => { - if (string.length === 0) { - updateTagVisiblity(false); - updateFilters([]); - return; - } + + onChangeHandler(event.target.value)} + value={value} + allowClear + disabled={traces.filterLoading} + onFocus={onFocusHandler} + placeholder="Click to filter by tags" + type="search" + enterButton={} + onSearch={(string): void => { + if (string.length === 0) { + updateTagVisibility(false); + updateFilters([]); + return; + } - const { isError, payload } = parseQueryToTags(string); + const { isError, payload } = parseQueryToTags(string); - if (isError) { - updateTagIsError(true); - } else { - updateTagIsError(false); - updateTagVisiblity(false); - updateFilters(payload); - } - }} - /> + if (isError) { + updateTagIsError(true); + } else { + updateTagIsError(false); + updateTagVisibility(false); + updateFilters(payload); + } + }} + /> - {traces.isTagModalOpen && ( - - )} - - + {traces.isTagModalOpen && ( + + )} + ); } interface DispatchProps { - updateTagVisiblity: (value: boolean) => void; + updateTagVisibility: (value: boolean) => void; updateTagIsError: (value: boolean) => void; } const mapDispatchToProps = ( dispatch: ThunkDispatch, ): DispatchProps => ({ - updateTagVisiblity: bindActionCreators(UpdateTagVisiblity, dispatch), + updateTagVisibility: bindActionCreators(UpdateTagVisibility, dispatch), updateTagIsError: bindActionCreators(UpdateTagIsError, dispatch), }); diff --git a/frontend/src/container/Trace/Search/styles.ts b/frontend/src/container/Trace/Search/styles.ts index 9cbec7a213..5ade20596d 100644 --- a/frontend/src/container/Trace/Search/styles.ts +++ b/frontend/src/container/Trace/Search/styles.ts @@ -6,6 +6,7 @@ const { Search } = Input; export const Container = styled.div` display: flex; position: relative; + width: 100%; `; export const SearchComponent = styled(Search)` diff --git a/frontend/src/container/Trace/TraceTable/index.tsx b/frontend/src/container/Trace/TraceTable/index.tsx index 944afddb45..b68a180ad7 100644 --- a/frontend/src/container/Trace/TraceTable/index.tsx +++ b/frontend/src/container/Trace/TraceTable/index.tsx @@ -3,35 +3,36 @@ import Table, { ColumnsType } from 'antd/lib/table'; import ROUTES from 'constants/routes'; import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; +import history from 'lib/history'; import React from 'react'; -import { connect, useSelector } from 'react-redux'; -import { Link } from 'react-router-dom'; -import { bindActionCreators } from 'redux'; -import { ThunkDispatch } from 'redux-thunk'; -import { - GetSpansAggregate, - GetSpansAggregateProps, -} from 'store/actions/trace/getInitialSpansAggregate'; +import { useDispatch, useSelector } from 'react-redux'; +import { Dispatch } from 'redux'; +import { updateURL } from 'store/actions/trace/util'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; -import { GlobalReducer } from 'types/reducer/globalTime'; +import { + UPDATE_SPAN_ORDER, + UPDATE_SPANS_AGGREGATE_PAGE_NUMBER, +} from 'types/actions/trace'; import { TraceReducer } from 'types/reducer/trace'; dayjs.extend(duration); -function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { +function TraceTable(): JSX.Element { const { spansAggregate, selectedFilter, selectedTags, filterLoading, + userSelectedFilter, + filter, + isFilterExclude, + filterToFetchData, } = useSelector((state) => state.traces); - const globalTime = useSelector( - (state) => state.globalTime, - ); + const dispatch = useDispatch>(); - const { loading, total } = spansAggregate; + const { loading, total, order: spansAggregateOrder } = spansAggregate; type TableType = FlatArray; @@ -39,30 +40,17 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { return `${ROUTES.TRACE}/${record.traceID}?spanId=${record.spanID}`; }; - const getValue = (value: string, record: TableType): JSX.Element => { - return ( - - {value} - - ); + const getValue = (value: string): JSX.Element => { + return {value}; }; const getHttpMethodOrStatus = ( value: TableType['httpMethod'], - record: TableType, ): JSX.Element => { if (value.length === 0) { - return ( - - - - - ); + return -; } - return ( - - {value} - - ); + return {value}; }; const columns: ColumnsType = [ @@ -71,13 +59,9 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { dataIndex: 'timestamp', key: 'timestamp', sorter: true, - render: (value: TableType['timestamp'], record): JSX.Element => { + render: (value: TableType['timestamp']): JSX.Element => { const day = dayjs(value); - return ( - - {day.format('YYYY/MM/DD HH:mm:ss')} - - ); + return {day.format('YYYY/MM/DD HH:mm:ss')}; }, }, { @@ -96,14 +80,13 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { title: 'Duration', dataIndex: 'durationNano', key: 'durationNano', - render: (value: TableType['durationNano'], record): JSX.Element => ( - - - {`${dayjs - .duration({ milliseconds: value / 1000000 }) - .asMilliseconds()} ms`} - - + render: (value: TableType['durationNano']): JSX.Element => ( + + {`${dayjs + .duration({ milliseconds: value / 1000000 }) + .asMilliseconds() + .toFixed(2)} ms`} + ), }, { @@ -126,17 +109,33 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { sort, ) => { if (!Array.isArray(sort)) { - const { order = 'ascend' } = sort; + const { order = spansAggregateOrder } = sort; if (props.current && props.pageSize) { - getSpansAggregate({ - maxTime: globalTime.maxTime, - minTime: globalTime.minTime, - selectedFilter, - current: props.current, - pageSize: props.pageSize, - selectedTags, - order: order === 'ascend' ? 'ascending' : 'descending', + const spanOrder = order || spansAggregateOrder; + + dispatch({ + type: UPDATE_SPAN_ORDER, + payload: { + order: spanOrder, + }, }); + + dispatch({ + type: UPDATE_SPANS_AGGREGATE_PAGE_NUMBER, + payload: { + currentPage: props.current, + }, + }); + + updateURL( + selectedFilter, + filterToFetchData, + props.current, + selectedTags, + isFilterExclude, + userSelectedFilter, + spanOrder, + ); } } }; @@ -147,10 +146,17 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { dataSource={spansAggregate.data} loading={loading || filterLoading} columns={columns} - rowKey="timestamp" + rowKey={(record): string => `${record.traceID}-${record.spanID}`} style={{ cursor: 'pointer', }} + onRow={(record): React.HTMLAttributes => ({ + onClick: (event): void => { + event.preventDefault(); + event.stopPropagation(); + history.push(getLink(record)); + }, + })} pagination={{ current: spansAggregate.currentPage, pageSize: spansAggregate.pageSize, @@ -158,20 +164,9 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { position: ['bottomLeft'], total, }} + sortDirections={['ascend', 'descend']} /> ); } -interface DispatchProps { - getSpansAggregate: (props: GetSpansAggregateProps) => void; -} - -const mapDispatchToProps = ( - dispatch: ThunkDispatch, -): DispatchProps => ({ - getSpansAggregate: bindActionCreators(GetSpansAggregate, dispatch), -}); - -type TraceProps = DispatchProps; - -export default connect(null, mapDispatchToProps)(TraceTable); +export default TraceTable; diff --git a/frontend/src/container/TraceFlameGraph/__tests__/TraceFlameGraph.test.tsx b/frontend/src/container/TraceFlameGraph/__tests__/TraceFlameGraph.test.tsx index f97e41764c..e17784cbbd 100644 --- a/frontend/src/container/TraceFlameGraph/__tests__/TraceFlameGraph.test.tsx +++ b/frontend/src/container/TraceFlameGraph/__tests__/TraceFlameGraph.test.tsx @@ -1,42 +1,51 @@ +/** + * @jest-environment jsdom + */ + import { expect } from '@jest/globals'; import { render } from '@testing-library/react'; +import { renderHook } from '@testing-library/react-hooks'; import TraceFlameGraph from 'container/TraceFlameGraph'; import React, { useState } from 'react'; +import { Provider } from 'react-redux'; +import store from 'store'; test('loads and displays greeting', () => { - const onSpanHover = useState(''); + const { rerender } = renderHook(() => useState('')); const { asFragment } = render( - {}, - selectedSpanId: '', - traceMetaData: { - globalEnd: 0, - globalStart: 0, - levels: 0, - spread: 0, - totalSpans: 0, - }, - treeData: { - children: [], - id: '', - name: '', - serviceColour: '', - serviceName: '', - startTime: 0, - tags: [], - time: 0, - value: 0, - event: [], - hasError: false, - parent: undefined, - }, - }} - />, + + {}, + selectedSpanId: '', + traceMetaData: { + globalEnd: 0, + globalStart: 0, + levels: 0, + spread: 0, + totalSpans: 0, + }, + treeData: { + children: [], + id: '', + name: '', + serviceColour: '#a0e', + serviceName: '', + startTime: 0, + tags: [], + time: 100, + value: 100, + event: [], + hasError: false, + parent: undefined, + }, + }} + /> + , ); expect(asFragment()).toMatchSnapshot(); }); diff --git a/frontend/src/container/TraceFlameGraph/__tests__/__snapshots__/TraceFlameGraph.test.tsx.snap b/frontend/src/container/TraceFlameGraph/__tests__/__snapshots__/TraceFlameGraph.test.tsx.snap index ec5ff7b75b..78b91ec72d 100644 --- a/frontend/src/container/TraceFlameGraph/__tests__/__snapshots__/TraceFlameGraph.test.tsx.snap +++ b/frontend/src/container/TraceFlameGraph/__tests__/__snapshots__/TraceFlameGraph.test.tsx.snap @@ -1,3 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`loads and displays greeting 1`] = ``; +exports[`loads and displays greeting 1`] = ` + +
+
+
+ +`; diff --git a/frontend/src/container/Version/index.tsx b/frontend/src/container/Version/index.tsx new file mode 100644 index 0000000000..e36a0c6d18 --- /dev/null +++ b/frontend/src/container/Version/index.tsx @@ -0,0 +1,110 @@ +import { WarningFilled } from '@ant-design/icons'; +import { Button, Card, Form, Space, Typography } from 'antd'; +import React, { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import AppReducer from 'types/reducer/app'; + +import { InputComponent } from './styles'; + +const { Title } = Typography; + +function Version(): JSX.Element { + const [form] = Form.useForm(); + const { t } = useTranslation(); + + const onClickUpgradeHandler = useCallback((link: string) => { + window.open(link, '_blank'); + }, []); + + const { + currentVersion, + latestVersion, + isCurrentVersionError, + isLatestVersionError, + } = useSelector((state) => state.app); + + const isLatestVersion = currentVersion === latestVersion; + const isError = isCurrentVersionError || isLatestVersionError; + + return ( + + + {t('version')} + + +
+ + + + + + + + +
+ + {!isError && isLatestVersion && ( +
+ + ✅ + + {t('latest_version_signoz')} + + +
+ )} + + {!isError && !isLatestVersion && ( +
+ + + + + {t('stale_version')} + +
+ )} + + {!isError && !isLatestVersion && ( + + )} +
+ ); +} + +export default Version; diff --git a/frontend/src/container/Version/styles.ts b/frontend/src/container/Version/styles.ts new file mode 100644 index 0000000000..42c7bb1477 --- /dev/null +++ b/frontend/src/container/Version/styles.ts @@ -0,0 +1,8 @@ +import { Input } from 'antd'; +import styled from 'styled-components'; + +export const InputComponent = styled(Input)` + &&& { + max-width: 183px; + } +`; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 86d258b26b..0abe9a6453 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -4,6 +4,7 @@ import './ReactI18'; import AppRoutes from 'AppRoutes'; import React from 'react'; import ReactDOM from 'react-dom'; +import { QueryClient, QueryClientProvider } from 'react-query'; import { Provider } from 'react-redux'; import reportWebVitals from 'reportWebVitals'; import store from 'store'; @@ -12,12 +13,16 @@ if (process.env.NODE_ENV === 'development') { reportWebVitals(console.log); } +const queryClient = new QueryClient(); + ReactDOM.render( - - - - - , + + + + + + + , document.querySelector('#root'), ); diff --git a/frontend/src/lib/__tests__/getStep.test.ts b/frontend/src/lib/__tests__/getStep.test.ts index 399f0cfbc6..d3eaab422c 100644 --- a/frontend/src/lib/__tests__/getStep.test.ts +++ b/frontend/src/lib/__tests__/getStep.test.ts @@ -1,6 +1,6 @@ import { expect } from '@jest/globals'; import dayjs from 'dayjs'; -import getStep, { DefaultStepSize } from 'lib/getStep'; +import getStep, { DefaultStepSize, MaxDataPoints } from 'lib/getStep'; describe('lib/getStep', () => { test('should return default step when the given range is less than 1 day', () => { @@ -40,7 +40,8 @@ describe('lib/getStep', () => { const startUnix = start.valueOf(); const endUnix = end.valueOf(); - const expectedStepSize = end.diff(start, 'days') * DefaultStepSize; + const expectedStepSize = Math.floor(end.diff(start, 's') / MaxDataPoints); + expect( getStep({ start: startUnix / 1e3, diff --git a/frontend/src/lib/convertIntoHr.ts b/frontend/src/lib/convertIntoHr.ts index cd2d059e94..f7e408c2b1 100644 --- a/frontend/src/lib/convertIntoHr.ts +++ b/frontend/src/lib/convertIntoHr.ts @@ -1,6 +1,6 @@ -import { SettingPeroid } from 'container/GeneralSettings'; +import { SettingPeriod } from 'container/GeneralSettings'; -const converIntoHr = (value: number, peroid: SettingPeroid): number => { +const converIntoHr = (value: number, peroid: SettingPeriod): number => { if (peroid === 'day') { return value * 24; } diff --git a/frontend/src/lib/getSettingsPeroid.ts b/frontend/src/lib/getSettingsPeroid.ts index 72da4c3abb..0ea60ca0d5 100644 --- a/frontend/src/lib/getSettingsPeroid.ts +++ b/frontend/src/lib/getSettingsPeroid.ts @@ -1,4 +1,4 @@ -import { SettingPeroid } from 'container/GeneralSettings'; +import { SettingPeriod } from 'container/GeneralSettings'; const getSettingsPeroid = (hr: number): PayloadProps => { if (hr <= 0) { @@ -30,7 +30,7 @@ const getSettingsPeroid = (hr: number): PayloadProps => { interface PayloadProps { value: number; - peroid: SettingPeroid; + peroid: SettingPeriod; } export default getSettingsPeroid; diff --git a/frontend/src/lib/getStep.ts b/frontend/src/lib/getStep.ts index c7388bbe06..347f9dc332 100644 --- a/frontend/src/lib/getStep.ts +++ b/frontend/src/lib/getStep.ts @@ -30,6 +30,7 @@ const convertToMs = ( }; export const DefaultStepSize = 60; +export const MaxDataPoints = 200; /** * Returns relevant step size based on given start and end date. @@ -37,13 +38,9 @@ export const DefaultStepSize = 60; const getStep = ({ start, end, inputFormat = 'ms' }: GetStepInput): number => { const startDate = dayjs(convertToMs(Number(start), inputFormat)); const endDate = dayjs(convertToMs(Number(end), inputFormat)); - const diffDays = Math.abs(endDate.diff(startDate, 'days')); + const diffSec = Math.abs(endDate.diff(startDate, 's')); - if (diffDays > 1) { - return DefaultStepSize * diffDays; - } - - return DefaultStepSize; + return Math.max(Math.floor(diffSec / MaxDataPoints), DefaultStepSize); }; export default getStep; diff --git a/frontend/src/modules/Servicemap/ServiceMap.tsx b/frontend/src/modules/Servicemap/ServiceMap.tsx index 9d07892fa5..7408fe206d 100644 --- a/frontend/src/modules/Servicemap/ServiceMap.tsx +++ b/frontend/src/modules/Servicemap/ServiceMap.tsx @@ -1,12 +1,13 @@ /* eslint-disable */ //@ts-nocheck +import { Card } from 'antd'; import Spinner from 'components/Spinner'; import React, { useEffect, useRef } from 'react'; import { ForceGraph2D } from 'react-force-graph'; import { connect } from 'react-redux'; import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { getDetailedServiceMapItems, getServiceMapItems } from 'store/actions'; +import { getDetailedServiceMapItems, ServiceMapStore } from 'store/actions'; import { AppState } from 'store/reducers'; import styled from 'styled-components'; import { GlobalTime } from 'types/actions/globalTime'; @@ -31,9 +32,8 @@ const Container = styled.div` `; interface ServiceMapProps extends RouteComponentProps { - serviceMap: serviceMapStore; + serviceMap: ServiceMapStore; globalTime: GlobalTime; - getServiceMapItems: (time: GlobalTime) => void; getDetailedServiceMapItems: (time: GlobalTime) => void; } interface graphNode { @@ -53,29 +53,32 @@ export interface graphDataType { function ServiceMap(props: ServiceMapProps): JSX.Element { const fgRef = useRef(); - const { - getDetailedServiceMapItems, - getServiceMapItems, - globalTime, - serviceMap, - } = props; + const { getDetailedServiceMapItems, globalTime, serviceMap } = props; useEffect(() => { /* Call the apis only when the route is loaded. Check this issue: https://github.com/SigNoz/signoz/issues/110 */ - getServiceMapItems(globalTime); getDetailedServiceMapItems(globalTime); - }, [globalTime, getServiceMapItems, getDetailedServiceMapItems]); + }, [globalTime, getDetailedServiceMapItems]); useEffect(() => { fgRef.current && fgRef.current.d3Force('charge').strength(-400); }); - if (!serviceMap.items.length || !serviceMap.services.length) { + + if (serviceMap.loading) { return ; } + if (!serviceMap.loading && serviceMap.items.length === 0) { + return ( + + No Service Found + + ); + } + const zoomToService = (value: string): void => { fgRef && fgRef.current && @@ -149,7 +152,6 @@ const mapStateToProps = ( export default withRouter( connect(mapStateToProps, { - getServiceMapItems, getDetailedServiceMapItems, })(ServiceMap), ); diff --git a/frontend/src/modules/Usage/UsageExplorer.tsx b/frontend/src/modules/Usage/UsageExplorer.tsx index 502ae0294b..ec37b6ae33 100644 --- a/frontend/src/modules/Usage/UsageExplorer.tsx +++ b/frontend/src/modules/Usage/UsageExplorer.tsx @@ -84,7 +84,7 @@ function _UsageExplorer(props: UsageExplorerProps): JSX.Element { if (selectedTime && selectedInterval) { const maxTime = new Date().getTime() * 1000000; const minTime = maxTime - selectedTime.value * 24 * 3600000 * 1000000; - + getUsageData(minTime, maxTime, selectedInterval.value, selectedService); } }, [selectedTime, selectedInterval, selectedService, getUsageData]); diff --git a/frontend/src/pages/AlertChannelCreate/index.tsx b/frontend/src/pages/AlertChannelCreate/index.tsx index 59b2361cf8..ecc94247a4 100644 --- a/frontend/src/pages/AlertChannelCreate/index.tsx +++ b/frontend/src/pages/AlertChannelCreate/index.tsx @@ -5,29 +5,32 @@ import CreateAlertChannels from 'container/CreateAlertChannels'; import GeneralSettings from 'container/GeneralSettings'; import history from 'lib/history'; import React from 'react'; +import { useTranslation } from 'react-i18next'; function SettingsPage(): JSX.Element { const pathName = history.location.pathname; - + const { t } = useTranslation(); return ( { return ; }, - name: 'Alert Channels', + name: t('routes.alert_channels'), route: ROUTES.ALL_CHANNELS, }, ], activeKey: - pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels', + pathName === ROUTES.SETTINGS + ? t('routes.general') + : t('routes.alert_channels'), }} /> ); diff --git a/frontend/src/pages/AllAlertChannels/index.tsx b/frontend/src/pages/AllAlertChannels/index.tsx index 8e64751064..1ab97d0db1 100644 --- a/frontend/src/pages/AllAlertChannels/index.tsx +++ b/frontend/src/pages/AllAlertChannels/index.tsx @@ -4,27 +4,30 @@ import AlertChannels from 'container/AllAlertChannels'; import GeneralSettings from 'container/GeneralSettings'; import history from 'lib/history'; import React from 'react'; +import { useTranslation } from 'react-i18next'; function AllAlertChannels(): JSX.Element { const pathName = history.location.pathname; - + const { t } = useTranslation(); return ( ); diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index e661fb6193..208fcde7c1 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -14,7 +14,7 @@ function SettingsPage(): JSX.Element { routes: [ { Component: GeneralSettings, - name: 'General Settings', + name: 'General', route: ROUTES.SETTINGS, }, { @@ -23,8 +23,7 @@ function SettingsPage(): JSX.Element { route: ROUTES.ALL_CHANNELS, }, ], - activeKey: - pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General Settings', + activeKey: pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General', }} /> ); diff --git a/frontend/src/pages/Status/index.tsx b/frontend/src/pages/Status/index.tsx new file mode 100644 index 0000000000..f923b428b4 --- /dev/null +++ b/frontend/src/pages/Status/index.tsx @@ -0,0 +1,8 @@ +import Version from 'container/Version'; +import React from 'react'; + +function Status(): JSX.Element { + return ; +} + +export default Status; diff --git a/frontend/src/pages/Trace/index.tsx b/frontend/src/pages/Trace/index.tsx index acdc32aeb1..9764c83afe 100644 --- a/frontend/src/pages/Trace/index.tsx +++ b/frontend/src/pages/Trace/index.tsx @@ -5,6 +5,7 @@ import TraceGraph from 'container/Trace/Graph'; import Search from 'container/Trace/Search'; import TraceGraphFilter from 'container/Trace/TraceGraphFilter'; import TraceTable from 'container/Trace/TraceTable'; +import getStep from 'lib/getStep'; import history from 'lib/history'; import React, { useCallback, useEffect, useState } from 'react'; import { connect, useDispatch, useSelector } from 'react-redux'; @@ -63,7 +64,7 @@ function Trace({ current: spansAggregate.currentPage, pageSize: spansAggregate.pageSize, selectedTags, - order: 'ascending', + order: spansAggregate.order === 'ascend' ? 'ascending' : 'descending', }); }, [ selectedTags, @@ -73,6 +74,7 @@ function Trace({ getSpansAggregate, spansAggregate.currentPage, spansAggregate.pageSize, + spansAggregate.order, ]); useEffect(() => { @@ -83,7 +85,7 @@ function Trace({ selectedFilter, selectedTags, start: minTime, - step: 60, + step: getStep({ start: minTime, end: maxTime, inputFormat: 'ns' }), isFilterExclude, }); }, [ @@ -93,8 +95,8 @@ function Trace({ selectedTags, maxTime, minTime, - isFilterExclude, getSpans, + isFilterExclude, ]); useEffect(() => { diff --git a/frontend/src/pages/TraceDetail/index.tsx b/frontend/src/pages/TraceDetail/index.tsx index 8b57b8668b..d8f6634888 100644 --- a/frontend/src/pages/TraceDetail/index.tsx +++ b/frontend/src/pages/TraceDetail/index.tsx @@ -2,27 +2,30 @@ import { Typography } from 'antd'; import getTraceItem from 'api/trace/getTraceItem'; import Spinner from 'components/Spinner'; import TraceDetailContainer from 'container/TraceDetail'; -import useFetch from 'hooks/useFetch'; import React from 'react'; +import { useQuery } from 'react-query'; import { useParams } from 'react-router-dom'; import { Props as TraceDetailProps } from 'types/api/trace/getTraceItem'; function TraceDetail(): JSX.Element { const { id } = useParams(); + const { data: traceDetailResponse, error, isLoading, isError } = useQuery( + `getTraceItem/${id}`, + () => getTraceItem({ id }), + { + cacheTime: 3000, + }, + ); - const traceDetailResponse = useFetch(getTraceItem, { - id, - }); - - if (traceDetailResponse.error) { + if (traceDetailResponse?.error || error || isError) { return ( - {traceDetailResponse.errorMessage || 'Something went wrong'} + {traceDetailResponse?.error || 'Something went wrong'} ); } - if (traceDetailResponse.loading || traceDetailResponse.payload === undefined) { + if (isLoading || !(traceDetailResponse && traceDetailResponse.payload)) { return ; } diff --git a/frontend/src/store/actions/dashboard/getQueryResults.ts b/frontend/src/store/actions/dashboard/getQueryResults.ts index b87aca985f..e2ced192db 100644 --- a/frontend/src/store/actions/dashboard/getQueryResults.ts +++ b/frontend/src/store/actions/dashboard/getQueryResults.ts @@ -5,6 +5,7 @@ import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems import GetMaxMinTime from 'lib/getMaxMinTime'; import GetMinMax from 'lib/getMinMax'; import GetStartAndEndTime from 'lib/getStartAndEndTime'; +import getStep from 'lib/getStep'; import { Dispatch } from 'redux'; import store from 'store'; import AppActions from 'types/actions'; @@ -45,7 +46,7 @@ export const GetQueryResults = ( end, query: encodeURIComponent(query.query), start, - step: '60', + step: `${getStep({ start, end, inputFormat: 'ms' })}`, }); return { query: query.query, diff --git a/frontend/src/store/actions/serviceMap.ts b/frontend/src/store/actions/serviceMap.ts index be5a84a239..a6f079a7eb 100644 --- a/frontend/src/store/actions/serviceMap.ts +++ b/frontend/src/store/actions/serviceMap.ts @@ -7,6 +7,7 @@ import { ActionTypes } from './types'; export interface ServiceMapStore { items: ServicesMapItem[]; services: ServicesItem[]; + loading: boolean; } export interface ServicesItem { @@ -37,38 +38,39 @@ export interface ServicesAction { payload: ServicesItem[]; } -export const getServiceMapItems = (globalTime: GlobalTime) => { - return async (dispatch: Dispatch): Promise => { - dispatch({ - type: ActionTypes.getServiceMapItems, - payload: [], - }); - - const requestString = `/serviceMapDependencies?start=${globalTime.minTime}&end=${globalTime.maxTime}`; - - const response = await api.get(requestString); - - dispatch({ - type: ActionTypes.getServiceMapItems, - payload: response.data, - }); +export interface ServiceMapLoading { + type: ActionTypes.serviceMapLoading; + payload: { + loading: ServiceMapStore['loading']; }; -}; +} export const getDetailedServiceMapItems = (globalTime: GlobalTime) => { return async (dispatch: Dispatch): Promise => { - dispatch({ - type: ActionTypes.getServices, - payload: [], - }); - const requestString = `/services?start=${globalTime.minTime}&end=${globalTime.maxTime}`; - const response = await api.get(requestString); + const serviceMapDependencies = `/serviceMapDependencies?start=${globalTime.minTime}&end=${globalTime.maxTime}`; + + const [serviceMapDependenciesResponse, response] = await Promise.all([ + api.get(serviceMapDependencies), + api.get(requestString), + ]); dispatch({ type: ActionTypes.getServices, payload: response.data, }); + + dispatch({ + type: ActionTypes.getServiceMapItems, + payload: serviceMapDependenciesResponse.data, + }); + + dispatch({ + type: ActionTypes.serviceMapLoading, + payload: { + loading: false, + }, + }); }; }; diff --git a/frontend/src/store/actions/trace/getInitialFilter.ts b/frontend/src/store/actions/trace/getInitialFilter.ts index ab0a49f79e..9b189cf60c 100644 --- a/frontend/src/store/actions/trace/getInitialFilter.ts +++ b/frontend/src/store/actions/trace/getInitialFilter.ts @@ -18,6 +18,7 @@ import { parseIsSkippedSelection, parseQueryIntoCurrent, parseQueryIntoFilter, + parseQueryIntoOrder, parseQueryIntoSelectedTags, parseSelectedFilter, } from './util'; @@ -66,6 +67,11 @@ export const GetInitialTraceFilter = ( traces.spansAggregate.currentPage, ); + const parsedQueryOrder = parseQueryIntoOrder( + query, + traces.spansAggregate.order, + ); + const isSelectionSkipped = parseIsSkippedSelection(query); const parsedSelectedTags = parseQueryIntoSelectedTags( @@ -148,6 +154,7 @@ export const GetInitialTraceFilter = ( selectedTags: parsedSelectedTags.currentValue, userSelected: getUserSelected.currentValue, isFilterExclude: getIsFilterExcluded.currentValue, + order: parsedQueryOrder.currentValue, }, }); } else { diff --git a/frontend/src/store/actions/trace/getInitialSpansAggregate.ts b/frontend/src/store/actions/trace/getInitialSpansAggregate.ts index 11d061e079..e723a84939 100644 --- a/frontend/src/store/actions/trace/getInitialSpansAggregate.ts +++ b/frontend/src/store/actions/trace/getInitialSpansAggregate.ts @@ -3,11 +3,13 @@ import getSpansAggregate from 'api/trace/getSpansAggregate'; import { Dispatch, Store } from 'redux'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; -import { UPDATE_SPANS_AGGREEGATE } from 'types/actions/trace'; +import { UPDATE_SPANS_AGGREGATE } from 'types/actions/trace'; import { Props as GetSpanAggregateProps } from 'types/api/trace/getSpanAggregate'; import { GlobalReducer } from 'types/reducer/globalTime'; import { TraceReducer } from 'types/reducer/trace'; +import { updateURL } from './util'; + export const GetSpansAggregate = ( props: GetSpansAggregateProps, ): (( @@ -29,10 +31,12 @@ export const GetSpansAggregate = ( return; } + const order = props.order === 'ascending' ? 'ascend' : 'descend'; + try { // triggering loading dispatch({ - type: UPDATE_SPANS_AGGREEGATE, + type: UPDATE_SPANS_AGGREGATE, payload: { spansAggregate: { currentPage: props.current, @@ -41,6 +45,7 @@ export const GetSpansAggregate = ( error: false, total: spansAggregate.total, pageSize: props.pageSize, + order, }, }, }); @@ -53,12 +58,12 @@ export const GetSpansAggregate = ( offset: props.current * props.pageSize - props.pageSize, selectedTags: props.selectedTags, isFilterExclude: traces.isFilterExclude, - order: props.order, + order, }); if (response.statusCode === 200) { dispatch({ - type: UPDATE_SPANS_AGGREEGATE, + type: UPDATE_SPANS_AGGREGATE, payload: { spansAggregate: { currentPage: props.current, @@ -67,16 +72,27 @@ export const GetSpansAggregate = ( error: false, total: response.payload.totalSpans, pageSize: props.pageSize, + order, }, }, }); + + updateURL( + traces.selectedFilter, + traces.filterToFetchData, + props.current, + traces.selectedTags, + traces.isFilterExclude, + traces.userSelectedFilter, + order, + ); } else { notification.error({ message: response.error || 'Something went wrong', }); dispatch({ - type: UPDATE_SPANS_AGGREEGATE, + type: UPDATE_SPANS_AGGREGATE, payload: { spansAggregate: { currentPage: props.current, @@ -85,13 +101,14 @@ export const GetSpansAggregate = ( error: true, total: spansAggregate.total, pageSize: props.pageSize, + order, }, }, }); } } catch (error) { dispatch({ - type: UPDATE_SPANS_AGGREEGATE, + type: UPDATE_SPANS_AGGREGATE, payload: { spansAggregate: { currentPage: props.current, @@ -100,6 +117,7 @@ export const GetSpansAggregate = ( error: true, total: spansAggregate.total, pageSize: props.pageSize, + order, }, }, }); diff --git a/frontend/src/store/actions/trace/parseFilter/index.ts b/frontend/src/store/actions/trace/parseFilter/index.ts index fc7a14bc9f..f2489c2076 100644 --- a/frontend/src/store/actions/trace/parseFilter/index.ts +++ b/frontend/src/store/actions/trace/parseFilter/index.ts @@ -1,4 +1,3 @@ -export * from './current'; export * from './filter'; export * from './filterToFetchData'; export * from './isFilterExclude'; @@ -6,3 +5,5 @@ export * from './minMaxTime'; export * from './selectedFilter'; export * from './selectedTags'; export * from './skippedSelected'; +export * from './spanAggregateCurrentPage'; +export * from './spanAggregateOrder'; diff --git a/frontend/src/store/actions/trace/parseFilter/current.ts b/frontend/src/store/actions/trace/parseFilter/spanAggregateCurrentPage.ts similarity index 93% rename from frontend/src/store/actions/trace/parseFilter/current.ts rename to frontend/src/store/actions/trace/parseFilter/spanAggregateCurrentPage.ts index 4bdd0aa7f7..3135992558 100644 --- a/frontend/src/store/actions/trace/parseFilter/current.ts +++ b/frontend/src/store/actions/trace/parseFilter/spanAggregateCurrentPage.ts @@ -10,7 +10,7 @@ export const parseQueryIntoCurrent = ( let current = 1; - const selected = url.get('current'); + const selected = url.get('spanAggregateCurrentPage'); if (selected) { try { diff --git a/frontend/src/store/actions/trace/parseFilter/spanAggregateOrder.ts b/frontend/src/store/actions/trace/parseFilter/spanAggregateOrder.ts new file mode 100644 index 0000000000..1641ebe8f2 --- /dev/null +++ b/frontend/src/store/actions/trace/parseFilter/spanAggregateOrder.ts @@ -0,0 +1,39 @@ +import { TraceReducer } from 'types/reducer/trace'; + +import { ParsedUrl } from '../util'; + +export const parseQueryIntoOrder = ( + query: string, + stateCurrent: TraceReducer['spansAggregate']['order'], +): ParsedUrl => { + const url = new URLSearchParams(query); + + let current = 'ascend'; + + const selected = url.get('spanAggregateOrder'); + + if (selected) { + try { + const parsedValue = selected; + + if (parsedValue && typeof parsedValue === 'string') { + current = parsedValue; + } + } catch (error) { + console.log(error); + console.log('error while parsing json'); + } + } + + if (selected) { + return { + currentValue: current, + urlValue: current, + }; + } + + return { + currentValue: stateCurrent, + urlValue: current, + }; +}; diff --git a/frontend/src/store/actions/trace/selectTraceFilter.ts b/frontend/src/store/actions/trace/selectTraceFilter.ts index f23a361f0a..23cf27d648 100644 --- a/frontend/src/store/actions/trace/selectTraceFilter.ts +++ b/frontend/src/store/actions/trace/selectTraceFilter.ts @@ -44,9 +44,9 @@ export const SelectedTraceFilter = (props: { traces.filterToFetchData, traces.spansAggregate.currentPage, traces.selectedTags, - traces.filter, traces.isFilterExclude, traces.userSelectedFilter, + traces.spansAggregate.order, ); }; }; diff --git a/frontend/src/store/actions/trace/updateTagPanelVisiblity.ts b/frontend/src/store/actions/trace/updateTagPanelVisiblity.ts index 9fce20d0de..59fb070007 100644 --- a/frontend/src/store/actions/trace/updateTagPanelVisiblity.ts +++ b/frontend/src/store/actions/trace/updateTagPanelVisiblity.ts @@ -1,14 +1,14 @@ import { Dispatch } from 'redux'; import AppActions from 'types/actions'; -import { UPDATE_TAG_MODAL_VISIBLITY } from 'types/actions/trace'; +import { UPDATE_TAG_MODAL_VISIBILITY } from 'types/actions/trace'; import { TraceReducer } from 'types/reducer/trace'; -export const UpdateTagVisiblity = ( +export const UpdateTagVisibility = ( isTagModalOpen: TraceReducer['isTagModalOpen'], ): ((dispatch: Dispatch) => void) => { return (dispatch): void => { dispatch({ - type: UPDATE_TAG_MODAL_VISIBLITY, + type: UPDATE_TAG_MODAL_VISIBILITY, payload: { isTagModalOpen, }, diff --git a/frontend/src/store/actions/trace/util.ts b/frontend/src/store/actions/trace/util.ts index 63d91216f7..1693f2693a 100644 --- a/frontend/src/store/actions/trace/util.ts +++ b/frontend/src/store/actions/trace/util.ts @@ -18,11 +18,11 @@ export function isTraceFilterEnum( export const updateURL = ( selectedFilter: TraceReducer['selectedFilter'], filterToFetchData: TraceReducer['filterToFetchData'], - current: TraceReducer['spansAggregate']['total'], + spanAggregateCurrentPage: TraceReducer['spansAggregate']['currentPage'], selectedTags: TraceReducer['selectedTags'], - filter: TraceReducer['filter'], isFilterExclude: TraceReducer['isFilterExclude'], userSelectedFilter: TraceReducer['userSelectedFilter'], + spanAggregateOrder: TraceReducer['spansAggregate']['order'], ): void => { const search = new URLSearchParams(window.location.search); const preResult: { key: string; value: string }[] = []; @@ -30,11 +30,12 @@ export const updateURL = ( const keyToSkip = [ 'selected', 'filterToFetchData', - 'current', 'selectedTags', 'filter', 'isFilterExclude', 'userSelectedFilter', + 'spanAggregateCurrentPage', + 'spanAggregateOrder', ]; search.forEach((value, key) => { @@ -51,15 +52,15 @@ export const updateURL = ( Object.fromEntries(selectedFilter), )}&filterToFetchData=${JSON.stringify( filterToFetchData, - )}¤t=${current}&selectedTags=${JSON.stringify( + )}&spanAggregateCurrentPage=${spanAggregateCurrentPage}&selectedTags=${JSON.stringify( selectedTags, - )}&filter=${JSON.stringify(Object.fromEntries(filter))}&${preResult + )}&${preResult .map((e) => `${e.key}=${e.value}`) .join('&')}&isFilterExclude=${JSON.stringify( Object.fromEntries(isFilterExclude), )}&userSelectedFilter=${JSON.stringify( Object.fromEntries(userSelectedFilter), - )}`, + )}&spanAggregateCurrentPage=${spanAggregateCurrentPage}&spanAggregateOrder=${spanAggregateOrder}`, ); }; diff --git a/frontend/src/store/actions/types.ts b/frontend/src/store/actions/types.ts index c15ea00286..702997d49b 100644 --- a/frontend/src/store/actions/types.ts +++ b/frontend/src/store/actions/types.ts @@ -1,14 +1,22 @@ -import { ServiceMapItemAction, ServicesAction } from './serviceMap'; +import { + ServiceMapItemAction, + ServiceMapLoading, + ServicesAction, +} from './serviceMap'; import { GetUsageDataAction } from './usage'; export enum ActionTypes { - updateTraceFilters = 'UPDATE_TRACES_FILTER', updateTimeInterval = 'UPDATE_TIME_INTERVAL', getServiceMapItems = 'GET_SERVICE_MAP_ITEMS', getServices = 'GET_SERVICES', getUsageData = 'GET_USAGE_DATE', fetchTraces = 'FETCH_TRACES', fetchTraceItem = 'FETCH_TRACE_ITEM', + serviceMapLoading = 'UPDATE_SERVICE_MAP_LOADING', } -export type Action = GetUsageDataAction | ServicesAction | ServiceMapItemAction; +export type Action = + | GetUsageDataAction + | ServicesAction + | ServiceMapItemAction + | ServiceMapLoading; diff --git a/frontend/src/store/reducers/app.ts b/frontend/src/store/reducers/app.ts index 2f45bee614..a8feece5a4 100644 --- a/frontend/src/store/reducers/app.ts +++ b/frontend/src/store/reducers/app.ts @@ -7,6 +7,10 @@ import { LOGGED_IN, SIDEBAR_COLLAPSE, SWITCH_DARK_MODE, + UPDATE_CURRENT_ERROR, + UPDATE_CURRENT_VERSION, + UPDATE_LATEST_VERSION, + UPDATE_LATEST_VERSION_ERROR, } from 'types/actions/app'; import InitialValueTypes from 'types/reducer/app'; @@ -14,6 +18,10 @@ const InitialValue: InitialValueTypes = { isDarkMode: getTheme() === 'darkMode', isLoggedIn: getLocalStorageKey(IS_LOGGED_IN) === 'yes', isSideBarCollapsed: getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true', + currentVersion: '', + latestVersion: '', + isCurrentVersionError: false, + isLatestVersionError: false, }; const appReducer = ( @@ -42,6 +50,28 @@ const appReducer = ( }; } + case UPDATE_CURRENT_VERSION: { + return { + ...state, + currentVersion: action.payload.currentVersion, + }; + } + + case UPDATE_LATEST_VERSION: { + return { ...state, latestVersion: action.payload.latestVersion }; + } + + case UPDATE_CURRENT_ERROR: { + return { ...state, isCurrentVersionError: true }; + } + + case UPDATE_LATEST_VERSION_ERROR: { + return { + ...state, + isLatestVersionError: true, + }; + } + default: return state; } diff --git a/frontend/src/store/reducers/serviceMap.ts b/frontend/src/store/reducers/serviceMap.ts index ef7cd21496..18ec21a9ec 100644 --- a/frontend/src/store/reducers/serviceMap.ts +++ b/frontend/src/store/reducers/serviceMap.ts @@ -3,6 +3,7 @@ import { Action, ActionTypes, ServiceMapStore } from 'store/actions'; const initialState: ServiceMapStore = { items: [], services: [], + loading: true, }; export const ServiceMapReducer = ( @@ -20,6 +21,12 @@ export const ServiceMapReducer = ( ...state, services: action.payload, }; + case ActionTypes.serviceMapLoading: { + return { + ...state, + loading: action.payload.loading, + }; + } default: return state; } diff --git a/frontend/src/store/reducers/trace.ts b/frontend/src/store/reducers/trace.ts index 81b27b7e9e..010813c16e 100644 --- a/frontend/src/store/reducers/trace.ts +++ b/frontend/src/store/reducers/trace.ts @@ -9,8 +9,10 @@ import { UPDATE_SELECTED_FUNCTION, UPDATE_SELECTED_GROUP_BY, UPDATE_SELECTED_TAGS, - UPDATE_SPANS_AGGREEGATE, - UPDATE_TAG_MODAL_VISIBLITY, + UPDATE_SPAN_ORDER, + UPDATE_SPANS_AGGREGATE, + UPDATE_SPANS_AGGREGATE_PAGE_NUMBER, + UPDATE_TAG_MODAL_VISIBILITY, UPDATE_TRACE_FILTER, UPDATE_TRACE_FILTER_LOADING, UPDATE_TRACE_GRAPH_ERROR, @@ -37,6 +39,7 @@ const initialValue: TraceReducer = { error: false, total: 0, pageSize: 10, + order: 'ascend', }, selectedGroupBy: '', selectedFunction: 'count', @@ -71,6 +74,7 @@ const traceReducer = ( selectedTags, userSelected, isFilterExclude, + order, } = payload; return { @@ -84,6 +88,7 @@ const traceReducer = ( spansAggregate: { ...state.spansAggregate, currentPage: current, + order, }, }; } @@ -115,14 +120,14 @@ const traceReducer = ( }; } - case UPDATE_SPANS_AGGREEGATE: { + case UPDATE_SPANS_AGGREGATE: { return { ...state, spansAggregate: action.payload.spansAggregate, }; } - case UPDATE_TAG_MODAL_VISIBLITY: { + case UPDATE_TAG_MODAL_VISIBILITY: { return { ...state, isTagModalOpen: action.payload.isTagModalOpen, @@ -199,6 +204,26 @@ const traceReducer = ( }; } + case UPDATE_SPAN_ORDER: { + return { + ...state, + spansAggregate: { + ...state.spansAggregate, + order: action.payload.order, + }, + }; + } + + case UPDATE_SPANS_AGGREGATE_PAGE_NUMBER: { + return { + ...state, + spansAggregate: { + ...state.spansAggregate, + currentPage: action.payload.currentPage, + }, + }; + } + default: return state; } diff --git a/frontend/src/types/actions/app.ts b/frontend/src/types/actions/app.ts index f58b589a3d..b07cde36d4 100644 --- a/frontend/src/types/actions/app.ts +++ b/frontend/src/types/actions/app.ts @@ -1,7 +1,15 @@ +import AppReducer from 'types/reducer/app'; + export const SWITCH_DARK_MODE = 'SWITCH_DARK_MODE'; export const LOGGED_IN = 'LOGGED_IN'; export const SIDEBAR_COLLAPSE = 'SIDEBAR_COLLAPSE'; +export const UPDATE_CURRENT_VERSION = 'UPDATE_CURRENT_VERSION'; +export const UPDATE_LATEST_VERSION = 'UPDATE_LATEST_VERSION'; + +export const UPDATE_CURRENT_ERROR = 'UPDATE_CURRENT_ERROR'; +export const UPDATE_LATEST_VERSION_ERROR = 'UPDATE_LATEST_VERSION_ERROR'; + export interface SwitchDarkMode { type: typeof SWITCH_DARK_MODE; } @@ -15,4 +23,31 @@ export interface SideBarCollapse { payload: boolean; } -export type AppAction = SwitchDarkMode | LoggedInUser | SideBarCollapse; +export interface UpdateAppVersion { + type: typeof UPDATE_CURRENT_VERSION; + payload: { + currentVersion: AppReducer['currentVersion']; + }; +} + +export interface UpdateLatestVersion { + type: typeof UPDATE_LATEST_VERSION; + payload: { + latestVersion: AppReducer['latestVersion']; + }; +} + +export interface UpdateVersionError { + type: typeof UPDATE_CURRENT_ERROR | typeof UPDATE_LATEST_VERSION_ERROR; + payload: { + isError: boolean; + }; +} + +export type AppAction = + | SwitchDarkMode + | LoggedInUser + | SideBarCollapse + | UpdateAppVersion + | UpdateLatestVersion + | UpdateVersionError; diff --git a/frontend/src/types/actions/trace.ts b/frontend/src/types/actions/trace.ts index 9c22a55220..cfdb86bcbb 100644 --- a/frontend/src/types/actions/trace.ts +++ b/frontend/src/types/actions/trace.ts @@ -7,9 +7,9 @@ export const UPDATE_TRACE_FILTER_LOADING = 'UPDATE_TRACE_FILTER_LOADING'; export const SELECT_TRACE_FILTER = 'SELECT_TRACE_FILTER'; export const UPDATE_ALL_FILTERS = 'UPDATE_ALL_FILTERS'; export const UPDATE_SELECTED_TAGS = 'UPDATE_SELECTED_TAGS'; -export const UPDATE_TAG_MODAL_VISIBLITY = 'UPDATE_TAG_MODAL_VISIBLITY'; +export const UPDATE_TAG_MODAL_VISIBILITY = 'UPDATE_TAG_MODAL_VISIBILITY'; -export const UPDATE_SPANS_AGGREEGATE = 'UPDATE_SPANS_AGGREEGATE'; +export const UPDATE_SPANS_AGGREGATE = 'UPDATE_SPANS_AGGREGATE'; export const UPDATE_IS_TAG_ERROR = 'UPDATE_IS_TAG_ERROR'; @@ -25,6 +25,10 @@ export const UPDATE_FILTER_RESPONSE_SELECTED = 'UPDATE_FILTER_RESPONSE_SELECTED'; export const UPDATE_FILTER_EXCLUDE = 'UPDATE_FILTER_EXCLUDE'; +export const UPDATE_SPAN_ORDER = 'UPDATE_SPAN_ORDER'; +export const UPDATE_SPANS_AGGREGATE_PAGE_NUMBER = + 'UPDATE_SPANS_AGGREGATE_PAGE_NUMBER'; + export interface UpdateFilter { type: typeof UPDATE_TRACE_FILTER; payload: { @@ -33,14 +37,14 @@ export interface UpdateFilter { } export interface UpdateSpansAggregate { - type: typeof UPDATE_SPANS_AGGREEGATE; + type: typeof UPDATE_SPANS_AGGREGATE; payload: { spansAggregate: TraceReducer['spansAggregate']; }; } -export interface UpdateTagVisiblity { - type: typeof UPDATE_TAG_MODAL_VISIBLITY; +export interface UpdateTagVisibility { + type: typeof UPDATE_TAG_MODAL_VISIBILITY; payload: { isTagModalOpen: TraceReducer['isTagModalOpen']; }; @@ -70,6 +74,7 @@ export interface UpdateAllFilters { selectedTags: TraceReducer['selectedTags']; userSelected: TraceReducer['userSelectedFilter']; isFilterExclude: TraceReducer['isFilterExclude']; + order: TraceReducer['spansAggregate']['order']; }; } @@ -149,6 +154,20 @@ export interface UpdateSpans { }; } +export interface UpdateSpansAggregatePageNumber { + type: typeof UPDATE_SPANS_AGGREGATE_PAGE_NUMBER; + payload: { + currentPage: TraceReducer['spansAggregate']['currentPage']; + }; +} + +export interface UpdateSpanOrder { + type: typeof UPDATE_SPAN_ORDER; + payload: { + order: TraceReducer['spansAggregate']['order']; + }; +} + export type TraceActions = | UpdateFilter | GetTraceFilter @@ -156,7 +175,7 @@ export type TraceActions = | SelectTraceFilter | UpdateAllFilters | UpdateSelectedTags - | UpdateTagVisiblity + | UpdateTagVisibility | UpdateSpansAggregate | UpdateIsTagError | UpdateSelectedGroupBy @@ -166,4 +185,6 @@ export type TraceActions = | UpdateSpans | ResetTraceFilter | UpdateSelected - | UpdateFilterExclude; + | UpdateFilterExclude + | UpdateSpanOrder + | UpdateSpansAggregatePageNumber; diff --git a/frontend/src/types/api/disks/getDisks.ts b/frontend/src/types/api/disks/getDisks.ts new file mode 100644 index 0000000000..9e444df53d --- /dev/null +++ b/frontend/src/types/api/disks/getDisks.ts @@ -0,0 +1,5 @@ +export type PayloadProps = IDiskType[]; +export interface IDiskType { + name: string; + type: string; +} diff --git a/frontend/src/types/api/settings/getRetention.ts b/frontend/src/types/api/settings/getRetention.ts index e6fd1eae43..f8a2f8b1bc 100644 --- a/frontend/src/types/api/settings/getRetention.ts +++ b/frontend/src/types/api/settings/getRetention.ts @@ -1,4 +1,6 @@ export interface PayloadProps { metrics_ttl_duration_hrs: number; + metrics_move_ttl_duration_hrs?: number; traces_ttl_duration_hrs: number; + traces_move_ttl_duration_hrs?: number; } diff --git a/frontend/src/types/api/settings/setRetention.ts b/frontend/src/types/api/settings/setRetention.ts index 6e82f8ef2a..134e409f18 100644 --- a/frontend/src/types/api/settings/setRetention.ts +++ b/frontend/src/types/api/settings/setRetention.ts @@ -1,6 +1,8 @@ export interface Props { type: 'metrics' | 'traces'; - duration: string; + totalDuration: string; + coldStorage?: 's3' | null; + toColdDuration?: string; } export interface PayloadProps { diff --git a/frontend/src/types/api/trace/getSpanAggregate.ts b/frontend/src/types/api/trace/getSpanAggregate.ts index 3263621009..5f6d413a81 100644 --- a/frontend/src/types/api/trace/getSpanAggregate.ts +++ b/frontend/src/types/api/trace/getSpanAggregate.ts @@ -7,7 +7,7 @@ export interface Props { limit: number; offset: number; selectedTags: TraceReducer['selectedTags']; - order?: 'descending' | 'ascending'; + order?: TraceReducer['spansAggregate']['order']; isFilterExclude: TraceReducer['isFilterExclude']; } diff --git a/frontend/src/types/api/trace/getTagValue.ts b/frontend/src/types/api/trace/getTagValue.ts new file mode 100644 index 0000000000..e90975d1d5 --- /dev/null +++ b/frontend/src/types/api/trace/getTagValue.ts @@ -0,0 +1,13 @@ +import { GlobalReducer } from 'types/reducer/globalTime'; + +export interface Props { + start: GlobalReducer['minTime']; + end: GlobalReducer['maxTime']; + tagKey: string; +} + +interface Value { + tagValues: string; +} + +export type PayloadProps = Value[]; diff --git a/frontend/src/types/api/user/getLatestVersion.ts b/frontend/src/types/api/user/getLatestVersion.ts new file mode 100644 index 0000000000..043ccb1b45 --- /dev/null +++ b/frontend/src/types/api/user/getLatestVersion.ts @@ -0,0 +1,18 @@ +export interface PayloadProps { + body: string; + created_at: string; + draft: boolean; + html_url: string; + id: number; + mentions_count: number; + name: string; + node_id: number; + prerelease: boolean; + published_at: string; + tag_name: number; + tarball_url: string; + target_commitish: string; + upload_url: string; + url: string; + zipball_url: string; +} diff --git a/frontend/src/types/reducer/app.ts b/frontend/src/types/reducer/app.ts index 36fa886ec9..68af65d075 100644 --- a/frontend/src/types/reducer/app.ts +++ b/frontend/src/types/reducer/app.ts @@ -2,4 +2,8 @@ export default interface AppReducer { isDarkMode: boolean; isLoggedIn: boolean; isSideBarCollapsed: boolean; + currentVersion: string; + latestVersion: string; + isCurrentVersionError: boolean; + isLatestVersionError: boolean; } diff --git a/frontend/src/types/reducer/trace.ts b/frontend/src/types/reducer/trace.ts index a033bf2879..339fd63483 100644 --- a/frontend/src/types/reducer/trace.ts +++ b/frontend/src/types/reducer/trace.ts @@ -4,8 +4,10 @@ export interface TraceReducer { filter: Map>; filterToFetchData: TraceFilterEnum[]; filterLoading: boolean; + selectedFilter: Map; userSelectedFilter: Map; + isFilterExclude: Map; selectedTags: Tags[]; isTagModalOpen: boolean; @@ -18,6 +20,7 @@ export interface TraceReducer { error: boolean; total: number; pageSize: number; + order: string; }; selectedGroupBy: string; selectedFunction: string; diff --git a/frontend/src/utils/getSpanTreeMetadata.ts b/frontend/src/utils/getSpanTreeMetadata.ts index 3cce2667d2..d85480566d 100644 --- a/frontend/src/utils/getSpanTreeMetadata.ts +++ b/frontend/src/utils/getSpanTreeMetadata.ts @@ -25,8 +25,7 @@ export const getSpanTreeMetadata = ( globalEnd = Math.max(globalEnd, endTime); if (treeNode.hasError) { treeNode.serviceColour = errorColor; - } - treeNode.serviceColour = spanServiceColours[treeNode.serviceName]; + } else treeNode.serviceColour = spanServiceColours[treeNode.serviceName]; treeNode.children.forEach((childNode) => { traverse(childNode, level + 1); }); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 3ff85194f8..fcc3663f1b 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -22,5 +22,5 @@ "plugins": [{ "name": "typescript-plugin-css-modules" }] }, "exclude": ["node_modules"], - "include": ["./src"] + "include": ["./src", "./babel.config.js", "./jest.config.ts"] } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 13f9bd136c..f7654074e0 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -92,7 +92,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== -"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.5": +"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": version "7.17.8" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.8.tgz#3dac27c190ebc3a4381110d46c80e77efe172e1a" integrity sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ== @@ -113,7 +113,7 @@ json5 "^2.1.2" semver "^6.3.0" -"@babel/generator@^7.17.3", "@babel/generator@^7.17.7": +"@babel/generator@^7.17.3", "@babel/generator@^7.17.7", "@babel/generator@^7.7.2": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.7.tgz#8da2599beb4a86194a3b24df6c085931d9ee45ad" integrity sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w== @@ -631,7 +631,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.16.7": +"@babel/plugin-syntax-typescript@^7.16.7", "@babel/plugin-syntax-typescript@^7.7.2": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== @@ -1070,7 +1070,7 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.17.8" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2" integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA== @@ -1086,7 +1086,7 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.3", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": +"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.3", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": version "7.17.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== @@ -1281,62 +1281,52 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" - integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^26.6.2" - jest-util "^26.6.2" + jest-message-util "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" -"@jest/core@^26.6.0", "@jest/core@^26.6.3": - version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" - integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== dependencies: - "@jest/console" "^26.6.2" - "@jest/reporters" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" + emittery "^0.8.1" exit "^0.1.2" - graceful-fs "^4.2.4" - jest-changed-files "^26.6.2" - jest-config "^26.6.3" - jest-haste-map "^26.6.2" - jest-message-util "^26.6.2" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-resolve-dependencies "^26.6.3" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - jest-watcher "^26.6.2" - micromatch "^4.0.2" - p-each-series "^2.1.0" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" + micromatch "^4.0.4" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" - integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== - dependencies: - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - jest-mock "^26.6.2" - "@jest/environment@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" @@ -1347,18 +1337,6 @@ "@types/node" "*" jest-mock "^27.5.1" -"@jest/fake-timers@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" - integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== - dependencies: - "@jest/types" "^26.6.2" - "@sinonjs/fake-timers" "^6.0.1" - "@types/node" "*" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-util "^26.6.2" - "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" @@ -1371,15 +1349,6 @@ jest-mock "^27.5.1" jest-util "^27.5.1" -"@jest/globals@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" - integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== - dependencies: - "@jest/environment" "^26.6.2" - "@jest/types" "^26.6.2" - expect "^26.6.2" - "@jest/globals@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" @@ -1389,67 +1358,65 @@ "@jest/types" "^27.5.1" expect "^27.5.1" -"@jest/reporters@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" - integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.3" + istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - jest-haste-map "^26.6.2" - jest-resolve "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" + istanbul-reports "^3.1.3" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" - v8-to-istanbul "^7.0.0" - optionalDependencies: - node-notifier "^8.0.0" + v8-to-istanbul "^8.1.0" -"@jest/source-map@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" - integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== dependencies: callsites "^3.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" source-map "^0.6.0" -"@jest/test-result@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" - integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== dependencies: - "@jest/console" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^26.6.3": - version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" - integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== dependencies: - "@jest/test-result" "^26.6.2" - graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" "@jest/transform@^26.6.2": version "26.6.2" @@ -1472,6 +1439,27 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.5.1" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -1594,13 +1582,6 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@sinonjs/fake-timers@^8.0.1": version "8.1.0" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" @@ -1659,6 +1640,17 @@ lodash "^4.17.15" redent "^3.0.0" +"@testing-library/react-hooks@^7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz#3388d07f562d91e7f2431a4a21b5186062ecfee0" + integrity sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg== + dependencies: + "@babel/runtime" "^7.12.5" + "@types/react" ">=16.9.0" + "@types/react-dom" ">=16.9.0" + "@types/react-test-renderer" ">=16.9.0" + react-error-boundary "^3.1.0" + "@testing-library/react@^11.1.0": version "11.2.7" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.7.tgz#b29e2e95c6765c815786c0bc1d5aed9cb2bf7818" @@ -1714,7 +1706,7 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.7": version "7.1.19" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== @@ -2212,17 +2204,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.26.tgz#63d204d136c9916fb4dcd1b50f9740fe86884e47" integrity sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ== -"@types/normalize-package-data@^2.4.0": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" - integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== - "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/prettier@^2.0.0": +"@types/prettier@^2.1.5": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== @@ -2242,6 +2229,13 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/react-dom@>=16.9.0": + version "17.0.14" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.14.tgz#c8f917156b652ddf807711f5becbd2ab018dea9f" + integrity sha512-H03xwEP1oXmSfl3iobtmQ/2dHF5aBHr8aUMwyGZya6OW45G+xtdzmq6HkncefiBt5JU8DVyaWl/nWZbjZCnzAQ== + dependencies: + "@types/react" "*" + "@types/react-dom@^16.9.9": version "16.9.14" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.14.tgz#674b8f116645fe5266b40b525777fc6bb8eb3bcd" @@ -2283,6 +2277,13 @@ "@types/history" "^4.7.11" "@types/react" "*" +"@types/react-test-renderer@>=16.9.0": + version "17.0.1" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz#3120f7d1c157fba9df0118dae20cb0297ee0e06b" + integrity sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^17.0.0": version "17.0.41" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.41.tgz#6e179590d276394de1e357b3f89d05d7d3da8b85" @@ -2292,6 +2293,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@>=16.9.0": + version "17.0.43" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.43.tgz#4adc142887dd4a2601ce730bc56c3436fdb07a55" + integrity sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/react@^16": version "16.14.24" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.24.tgz#f2c5e9fa78f83f769884b83defcf7924b9eb5c82" @@ -3237,7 +3247,7 @@ babel-helper-to-multiple-sequence-expressions@^0.5.0: resolved "https://registry.yarnpkg.com/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz#a3f924e3561882d42fcf48907aa98f7979a4588d" integrity sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA== -babel-jest@^26.6.0, babel-jest@^26.6.3: +babel-jest@^26.6.0: version "26.6.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== @@ -3251,6 +3261,20 @@ babel-jest@^26.6.0, babel-jest@^26.6.3: graceful-fs "^4.2.4" slash "^3.0.0" +babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== + dependencies: + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + babel-loader@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" @@ -3269,7 +3293,7 @@ babel-plugin-dynamic-import-node@^2.3.3: dependencies: object.assign "^4.1.0" -babel-plugin-istanbul@^6.0.0: +babel-plugin-istanbul@^6.0.0, babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== @@ -3290,6 +3314,16 @@ babel-plugin-jest-hoist@^26.6.2: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + babel-plugin-macros@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" @@ -3520,6 +3554,14 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== + dependencies: + babel-plugin-jest-hoist "^27.5.1" + babel-preset-current-node-syntax "^1.0.0" + babel-preset-minify@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/babel-preset-minify/-/babel-preset-minify-0.5.1.tgz#25f5d0bce36ec818be80338d0e594106e21eaa9f" @@ -3606,6 +3648,11 @@ bcrypt-pbkdf@^1.0.0: resolved "https://registry.yarnpkg.com/bezier-js/-/bezier-js-6.1.0.tgz#162b7bdbabe866e3a796285a89d71085140755ec" integrity sha512-oc8fkHqG0R+dQuNiXVbPMB0cc8iDqkLAjbA2gq26QmV8tZqW9GGI7iNEX1ioRWlZperQS7v5BX03+9FLVWZbSw== +big-integer@^1.6.16: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" @@ -3695,6 +3742,20 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" +broadcast-channel@^3.4.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937" + integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg== + dependencies: + "@babel/runtime" "^7.7.2" + detect-node "^2.1.0" + js-sha3 "0.8.0" + microseconds "0.2.0" + nano-time "1.0.0" + oblivious-set "1.0.0" + rimraf "3.0.2" + unload "2.2.0" + browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" @@ -3711,6 +3772,13 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4 node-releases "^2.0.2" picocolors "^1.0.0" +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -3794,12 +3862,12 @@ camel-case@^4.1.1: pascal-case "^3.1.2" tslib "^2.0.3" -camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0: +camelcase@^6.0.0, camelcase@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -3932,10 +4000,10 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== -cjs-module-lexer@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" - integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== +cjs-module-lexer@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== class-utils@^0.3.5: version "0.3.6" @@ -3996,14 +4064,14 @@ cli-truncate@^3.1.0: slice-ansi "^5.0.0" string-width "^5.0.0" -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" + wrap-ansi "^7.0.0" clone-deep@^4.0.1: version "4.0.1" @@ -5219,11 +5287,6 @@ debug@~3.1.0: dependencies: ms "2.0.0" -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - decimal.js@^10.2.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" @@ -5241,6 +5304,11 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + deep-assign@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/deep-assign/-/deep-assign-2.0.0.tgz#ebe06b1f07f08dae597620e3dd1622f371a1c572" @@ -5350,7 +5418,7 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -detect-node@^2.0.4: +detect-node@^2.0.4, detect-node@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== @@ -5530,10 +5598,10 @@ emitter-component@^1.1.1: resolved "https://registry.yarnpkg.com/emitter-component/-/emitter-component-1.1.1.tgz#065e2dbed6959bf470679edabeaf7981d1003ab6" integrity sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY= -emittery@^0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" - integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== emoji-regex@^8.0.0: version "8.0.0" @@ -6009,7 +6077,7 @@ 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: +execa@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== @@ -6077,18 +6145,6 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" - integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== - dependencies: - "@jest/types" "^26.6.2" - ansi-styles "^4.0.0" - jest-get-type "^26.3.0" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-regex-util "^26.0.0" - expect@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" @@ -6211,7 +6267,7 @@ fast-glob@^3.2.5, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -6467,7 +6523,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2, fsevents@~2.3.2: +fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -6507,7 +6563,7 @@ geotiff@^1.0.8: threads "^1.7.0" xml-utils "^1.0.2" -get-caller-file@^2.0.1: +get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -6645,11 +6701,6 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== -growly@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" - integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= - gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -6756,11 +6807,6 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react- dependencies: react-is "^16.7.0" -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -7449,17 +7495,7 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - -istanbul-lib-instrument@^5.0.4: +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== @@ -7488,7 +7524,7 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.0.2: +istanbul-reports@^3.1.3: version "3.1.4" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== @@ -7501,59 +7537,89 @@ jerrypick@^1.0.4: resolved "https://registry.yarnpkg.com/jerrypick/-/jerrypick-1.0.5.tgz#808a2a53acd332b1d972a097c510c5d28fb8e3e2" integrity sha512-95dIyL2QXqevDDJ70rmiqKLsTi+riZftLQo708tSFR/O5pQOf6VscDhjnN8mkNQwqnQmGhw+6dfZ5d4bmH/yww== -jest-changed-files@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" - integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== dependencies: - "@jest/types" "^26.6.2" - execa "^4.0.0" - throat "^5.0.0" + "@jest/types" "^27.5.1" + execa "^5.0.0" + throat "^6.0.1" -jest-cli@^26.6.0: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" - integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== dependencies: - "@jest/core" "^26.6.3" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== + dependencies: + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" exit "^0.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" import-local "^3.0.2" - is-ci "^2.0.0" - jest-config "^26.6.3" - jest-util "^26.6.2" - jest-validate "^26.6.2" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" prompts "^2.0.1" - yargs "^15.4.1" + yargs "^16.2.0" -jest-config@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" - integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^26.6.3" - "@jest/types" "^26.6.2" - babel-jest "^26.6.3" + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" chalk "^4.0.0" + ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.1" - graceful-fs "^4.2.4" - jest-environment-jsdom "^26.6.2" - jest-environment-node "^26.6.2" - jest-get-type "^26.3.0" - jest-jasmine2 "^26.6.3" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - micromatch "^4.0.2" - pretty-format "^26.6.2" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^27.5.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" -jest-diff@^26.0.0, jest-diff@^26.6.2: +jest-diff@^26.0.0: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== @@ -7573,48 +7639,48 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-docblock@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" - integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== dependencies: detect-newline "^3.0.0" -jest-each@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" - integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.5.1" chalk "^4.0.0" - jest-get-type "^26.3.0" - jest-util "^26.6.2" - pretty-format "^26.6.2" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" -jest-environment-jsdom@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" - integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" - jsdom "^16.4.0" + jest-mock "^27.5.1" + jest-util "^27.5.1" + jsdom "^16.6.0" -jest-environment-node@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" - integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" + jest-mock "^27.5.1" + jest-util "^27.5.1" jest-get-type@^26.3.0: version "26.3.0" @@ -7647,47 +7713,56 @@ jest-haste-map@^26.6.2: optionalDependencies: fsevents "^2.1.2" -jest-jasmine2@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" - integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/types" "^27.5.1" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + micromatch "^4.0.4" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^26.6.2" + expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - pretty-format "^26.6.2" - throat "^5.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + throat "^6.0.1" -jest-leak-detector@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" - integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== dependencies: - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-matcher-utils@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" - integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== - dependencies: - chalk "^4.0.0" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: version "27.5.1" @@ -7699,21 +7774,6 @@ jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-message-util@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" - integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^26.6.2" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.2" - pretty-format "^26.6.2" - slash "^3.0.0" - stack-utils "^2.0.2" - jest-message-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" @@ -7729,14 +7789,6 @@ jest-message-util@^27.5.1: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" - integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== - dependencies: - "@jest/types" "^26.6.2" - "@types/node" "*" - jest-mock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" @@ -7755,87 +7807,90 @@ jest-regex-util@^26.0.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-resolve-dependencies@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" - integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== - dependencies: - "@jest/types" "^26.6.2" - jest-regex-util "^26.0.0" - jest-snapshot "^26.6.2" +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-resolve@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" - integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" + +jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== + dependencies: + "@jest/types" "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" jest-pnp-resolver "^1.2.2" - jest-util "^26.6.2" - read-pkg-up "^7.0.1" - resolve "^1.18.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + resolve "^1.20.0" + resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" - integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" - emittery "^0.7.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-config "^26.6.3" - jest-docblock "^26.0.0" - jest-haste-map "^26.6.2" - jest-leak-detector "^26.6.2" - jest-message-util "^26.6.2" - jest-resolve "^26.6.2" - jest-runtime "^26.6.3" - jest-util "^26.6.2" - jest-worker "^26.6.2" + emittery "^0.8.1" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" source-map-support "^0.5.6" - throat "^5.0.0" + throat "^6.0.1" -jest-runtime@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" - integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/globals" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/yargs" "^15.0.0" + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" - cjs-module-lexer "^0.6.0" + cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - exit "^0.1.2" + execa "^5.0.0" glob "^7.1.3" - graceful-fs "^4.2.4" - jest-config "^26.6.3" - jest-haste-map "^26.6.2" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^15.4.1" jest-serializer@^26.6.2: version "26.6.2" @@ -7845,26 +7900,40 @@ jest-serializer@^26.6.2: "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" - integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== dependencies: + "@types/node" "*" + graceful-fs "^4.2.9" + +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== + dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/types" "^26.6.2" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.0.0" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^26.6.2" - graceful-fs "^4.2.4" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - jest-haste-map "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-resolve "^26.6.2" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" natural-compare "^1.4.0" - pretty-format "^26.6.2" + pretty-format "^27.5.1" semver "^7.3.2" jest-util@^26.6.2: @@ -7879,7 +7948,7 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" -jest-util@^27.5.1: +jest-util@^27.0.0, jest-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== @@ -7891,29 +7960,29 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" - integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== dependencies: - "@jest/types" "^26.6.2" - camelcase "^6.0.0" + "@jest/types" "^27.5.1" + camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^26.3.0" + jest-get-type "^27.5.1" leven "^3.1.0" - pretty-format "^26.6.2" + pretty-format "^27.5.1" -jest-watcher@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" - integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== dependencies: - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^26.6.2" + jest-util "^27.5.1" string-length "^4.0.1" jest-worker@^26.6.2: @@ -7925,7 +7994,7 @@ jest-worker@^26.6.2: merge-stream "^2.0.0" supports-color "^7.0.0" -jest-worker@^27.0.2, jest-worker@^27.4.5: +jest-worker@^27.0.2, jest-worker@^27.4.5, jest-worker@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== @@ -7934,20 +8003,25 @@ jest-worker@^27.0.2, jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@26.6.0: - version "26.6.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.0.tgz#546b25a1d8c888569dbbe93cae131748086a4a25" - integrity sha512-jxTmrvuecVISvKFFhOkjsWRZV7sFqdSUAd1ajOKY+/QE/aLBVstsJ/dX8GczLzwiT6ZEwwmZqtCUHLHHQVzcfA== +jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== dependencies: - "@jest/core" "^26.6.0" + "@jest/core" "^27.5.1" import-local "^3.0.2" - jest-cli "^26.6.0" + jest-cli "^27.5.1" js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -7966,7 +8040,7 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^16.4.0: +jsdom@^16.6.0: version "16.7.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== @@ -8056,6 +8130,11 @@ json2mq@^0.2.0: dependencies: string-convert "^0.2.0" +json5@2.x, json5@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + json5@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -8068,11 +8147,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.2: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== - jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -8373,7 +8447,7 @@ lodash.isequal@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.memoize@^4.1.2: +lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= @@ -8472,7 +8546,7 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" -make-error@^1.1.1: +make-error@1.x, make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -8513,6 +8587,14 @@ marked@4.0.10: resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.10.tgz#423e295385cc0c3a70fa495e0df68b007b879423" integrity sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw== +match-sorter@^6.0.2: + version "6.3.1" + resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda" + integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw== + dependencies: + "@babel/runtime" "^7.12.5" + remove-accents "0.4.2" + mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" @@ -8582,6 +8664,11 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" +microseconds@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" + integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== + mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -8739,6 +8826,13 @@ nano-css@^5.3.1: stacktrace-js "^2.0.2" stylis "^4.0.6" +nano-time@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" + integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8= + dependencies: + big-integer "^1.6.16" + nanoid@^2.0.3: version "2.1.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" @@ -8866,33 +8960,11 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-notifier@^8.0.0: - version "8.0.2" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" - integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== - dependencies: - growly "^1.3.0" - is-wsl "^2.2.0" - semver "^7.3.2" - shellwords "^0.1.1" - uuid "^8.3.0" - which "^2.0.2" - node-releases@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== -normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -9042,6 +9114,11 @@ object.values@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" +oblivious-set@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566" + integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw== + observable-fns@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/observable-fns/-/observable-fns-0.6.1.tgz#636eae4fdd1132e88c0faf38d33658cc79d87e37" @@ -9147,11 +9224,6 @@ ospath@^1.2.2: resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= -p-each-series@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" - integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -9272,7 +9344,7 @@ parse-headers@^2.0.0, parse-headers@^2.0.2: resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9" integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA== -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -9422,7 +9494,7 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pirates@^4.0.1: +pirates@^4.0.1, pirates@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== @@ -10394,6 +10466,13 @@ react-draggable@^4.0.0, react-draggable@^4.0.3: clsx "^1.1.1" prop-types "^15.6.0" +react-error-boundary@^3.1.0: + version "3.1.4" + resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0" + integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA== + dependencies: + "@babel/runtime" "^7.12.5" + react-force-graph@^1.41.0: version "1.41.12" resolved "https://registry.yarnpkg.com/react-force-graph/-/react-force-graph-1.41.12.tgz#ceffca39f50f3cf0528b48df846ae667058dc98c" @@ -10483,6 +10562,15 @@ react-motion@^0.5.2: prop-types "^15.5.8" raf "^3.1.0" +react-query@^3.34.19: + version "3.34.19" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.34.19.tgz#0ff049b6e0d2ed148e9abfdd346625d0e88dc229" + integrity sha512-JO0Ymi58WKmvnhgg6bGIrYIeKb64KsKaPWo8JcGnmK2jJxAs2XmMBzlP75ZepSU7CHzcsWtIIyhMrLbX3pb/3w== + dependencies: + "@babel/runtime" "^7.5.5" + broadcast-channel "^3.4.1" + match-sorter "^6.0.2" + react-redux@^7.2.2: version "7.2.6" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa" @@ -10597,25 +10685,6 @@ react@17.0.2: loose-envify "^1.1.0" object-assign "^4.1.1" -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== - dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" - -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - readable-stream@^2.0.1: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" @@ -10746,6 +10815,11 @@ relateurl@^0.2.7: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= +remove-accents@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" + integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U= + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -10789,11 +10863,6 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -10843,7 +10912,12 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.9.0: +resolve.exports@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + +resolve@^1.10.1, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.9.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -10893,7 +10967,7 @@ rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== -rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -11056,28 +11130,28 @@ selfsigned@^2.0.0: dependencies: node-forge "^1.2.0" -"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - semver@7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: +semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" +semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + send@0.17.2: version "0.17.2" resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" @@ -11134,11 +11208,6 @@ serve-static@1.14.2: parseurl "~1.3.3" send "0.17.2" -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - set-harmonic-interval@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" @@ -11200,11 +11269,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shellwords@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" - integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -11409,32 +11473,6 @@ sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.11" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" - integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== - spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" @@ -11497,7 +11535,7 @@ stack-generator@^2.0.5: dependencies: stackframe "^1.1.1" -stack-utils@^2.0.2, stack-utils@^2.0.3: +stack-utils@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== @@ -11941,10 +11979,10 @@ three-render-objects@^1.26: resolved "https://registry.yarnpkg.com/three/-/three-0.138.3.tgz#7be5153d79dcbf9e9baad82e7faf8c29edda4ed0" integrity sha512-4t1cKC8gimNyJChJbaklg8W/qj3PpsLJUIFm5LIuAy/hVxxNm1ru2FGTSfbTSsuHmC/7ipsyuGKqrSAKLNtkzg== -throat@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" - integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== +throat@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== throttle-debounce@^3.0.1: version "3.0.1" @@ -12096,6 +12134,20 @@ ts-easing@^0.2.0: resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== +ts-jest@^27.1.4: + version "27.1.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" + integrity sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^27.0.0" + json5 "2.x" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "20.x" + ts-node@^10.2.1: version "10.7.0" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" @@ -12192,16 +12244,6 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -12294,6 +12336,14 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +unload@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" + integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA== + dependencies: + "@babel/runtime" "^7.6.2" + detect-node "^2.0.4" + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -12367,7 +12417,7 @@ uuid@^2.0.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" integrity sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho= -uuid@^8.3.0, uuid@^8.3.2: +uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -12382,23 +12432,15 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -v8-to-istanbul@^7.0.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" - integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== +v8-to-istanbul@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" source-map "^0.7.3" -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - value-equal@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" @@ -12700,11 +12742,6 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -12712,7 +12749,7 @@ which@^1.2.9: dependencies: isexe "^2.0.0" -which@^2.0.1, which@^2.0.2: +which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -12846,10 +12883,10 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^4.0.0: version "4.0.0" @@ -12861,30 +12898,23 @@ yaml@^1.10.0, yaml@^1.10.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" +yargs-parser@20.x, yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@^15.4.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" + y18n "^5.0.5" + yargs-parser "^20.2.2" yauzl@^2.10.0: version "2.10.0" diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 167aa6236a..de49aa0e49 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -13,6 +13,7 @@ import ( "net/http" "net/url" "os" + "regexp" "sort" "strconv" "strings" @@ -44,8 +45,8 @@ import ( "github.com/prometheus/prometheus/util/strutil" "go.signoz.io/query-service/constants" - "go.signoz.io/query-service/model" am "go.signoz.io/query-service/integrations/alertManager" + "go.signoz.io/query-service/model" "go.uber.org/zap" ) @@ -75,7 +76,7 @@ type ClickHouseReader struct { remoteStorage *remote.Storage ruleManager *rules.Manager promConfig *config.Config - alertManager am.Manager + alertManager am.Manager } // NewTraceReader returns a TraceReader for the database @@ -95,7 +96,7 @@ func NewReader(localDB *sqlx.DB) *ClickHouseReader { return &ClickHouseReader{ db: db, localDB: localDB, - alertManager: alertManager, + alertManager: alertManager, operationsTable: options.primary.OperationsTable, indexTable: options.primary.IndexTable, errorTable: options.primary.ErrorTable, @@ -850,7 +851,6 @@ func (r *ClickHouseReader) EditChannel(receiver *am.Receiver, id string) (*am.Re } - func (r *ClickHouseReader) CreateChannel(receiver *am.Receiver) (*am.Receiver, *model.ApiError) { tx, err := r.localDB.Begin() @@ -860,8 +860,8 @@ func (r *ClickHouseReader) CreateChannel(receiver *am.Receiver) (*am.Receiver, * channel_type := getChannelType(receiver) receiverString, _ := json.Marshal(receiver) - - // todo: check if the channel name already exists, raise an error if so + + // todo: check if the channel name already exists, raise an error if so { stmt, err := tx.Prepare(`INSERT INTO notification_channels (created_at, updated_at, name, type, data) VALUES($1,$2,$3,$4,$5);`) @@ -884,7 +884,7 @@ func (r *ClickHouseReader) CreateChannel(receiver *am.Receiver) (*am.Receiver, * tx.Rollback() return nil, apiError } - + err = tx.Commit() if err != nil { zap.S().Errorf("Error in commiting transaction for INSERT to notification_channels\n", err) @@ -2602,7 +2602,6 @@ func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *mo fmt.Errorf("error while getting disks. Err=%v", err)} } - zap.S().Infof("Got response: %+v\n", diskItems) return &diskItems, nil @@ -2610,29 +2609,33 @@ func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *mo func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLParams) (*model.GetTTLResponseItem, *model.ApiError) { - parseTTL := func(queryResp string) int { - values := strings.Split(queryResp, " ") - N := len(values) - ttlIdx := -1 + parseTTL := func(queryResp string) (int, int) { - for i := 0; i < N; i++ { - if strings.Contains(values[i], "toIntervalSecond") { - ttlIdx = i - break + zap.S().Debugf("Parsing TTL from: %s", queryResp) + deleteTTLExp := regexp.MustCompile(`toIntervalSecond\(([0-9]*)\)`) + moveTTLExp := regexp.MustCompile(`toIntervalSecond\(([0-9]*)\) TO VOLUME`) + + var delTTL, moveTTL int = -1, -1 + + m := deleteTTLExp.FindStringSubmatch(queryResp) + if len(m) > 1 { + seconds_int, err := strconv.Atoi(m[1]) + if err != nil { + return -1, -1 } - } - if ttlIdx == -1 { - return ttlIdx + delTTL = seconds_int / 3600 } - output := strings.SplitN(values[ttlIdx], "(", 2) - timePart := strings.Trim(output[1], ")") - seconds_int, err := strconv.Atoi(timePart) - if err != nil { - return -1 + m = moveTTLExp.FindStringSubmatch(queryResp) + if len(m) > 1 { + seconds_int, err := strconv.Atoi(m[1]) + if err != nil { + return -1, -1 + } + moveTTL = seconds_int / 3600 } - ttl_hrs := seconds_int / 3600 - return ttl_hrs + + return delTTL, moveTTL } getMetricsTTL := func() (*model.DBResponseTTL, *model.ApiError) { @@ -2671,7 +2674,8 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLPa return nil, err } - return &model.GetTTLResponseItem{TracesTime: parseTTL(dbResp.EngineFull)}, nil + delTTL, moveTTL := parseTTL(dbResp.EngineFull) + return &model.GetTTLResponseItem{TracesTime: delTTL, TracesMoveTime: moveTTL}, nil case constants.MetricsTTL: dbResp, err := getMetricsTTL() @@ -2679,7 +2683,9 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLPa return nil, err } - return &model.GetTTLResponseItem{MetricsTime: parseTTL(dbResp.EngineFull)}, nil + delTTL, moveTTL := parseTTL(dbResp.EngineFull) + return &model.GetTTLResponseItem{MetricsTime: delTTL, MetricsMoveTime: moveTTL}, nil + } db1, err := getTracesTTL() if err != nil { @@ -2690,9 +2696,15 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLPa if err != nil { return nil, err } + tracesDelTTL, tracesMoveTTL := parseTTL(db1.EngineFull) + metricsDelTTL, metricsMoveTTL := parseTTL(db2.EngineFull) - return &model.GetTTLResponseItem{TracesTime: parseTTL(db1.EngineFull), MetricsTime: parseTTL(db2.EngineFull)}, nil - + return &model.GetTTLResponseItem{ + TracesTime: tracesDelTTL, + TracesMoveTime: tracesMoveTTL, + MetricsTime: metricsDelTTL, + MetricsMoveTime: metricsMoveTTL, + }, nil } func (r *ClickHouseReader) GetErrors(ctx context.Context, queryParams *model.GetErrorsParams) (*[]model.Error, *model.ApiError) { diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index 3e3abc6c79..ae9abc5204 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -914,7 +914,7 @@ func parseTTLParams(r *http.Request) (*model.TTLParams, error) { if err != nil { return nil, fmt.Errorf("Not a valid toCold TTL duration %v", toColdDuration) } - if toColdParsed.Seconds() >= durationParsed.Seconds() { + if toColdParsed.Seconds() != 0 && toColdParsed.Seconds() >= durationParsed.Seconds() { return nil, fmt.Errorf("Delete TTL should be greater than cold storage move TTL.") } } diff --git a/pkg/query-service/godruid/queries.go b/pkg/query-service/godruid/queries.go index 5fb518f908..ffdeee002d 100644 --- a/pkg/query-service/godruid/queries.go +++ b/pkg/query-service/godruid/queries.go @@ -62,7 +62,7 @@ type QueryScan struct { Limit int64 `json:"limit,omitempty"` Offset int64 `json:"offset,omitempty"` BatchSize int64 `json:"batchSize,omitempty"` - Order string `json:"order",omitempty` + Order string `json:"order,omitempty"` ResultFormat string `json:"resultFormat"` Context map[string]interface{} `json:"context,omitempty"` @@ -189,7 +189,7 @@ type TimeBoundaryItem struct { type TimeBoundary struct { MinTime string `json:"minTime"` - MaxTime string `json:"minTime"` + MaxTime string `json:"maxTime"` } func (q *QueryTimeBoundary) setup() { q.QueryType = "timeBoundary" } diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index bbee7224e9..8d4bd4b766 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -283,8 +283,10 @@ type DBResponseTTL struct { } type GetTTLResponseItem struct { - MetricsTime int `json:"metrics_ttl_duration_hrs"` - TracesTime int `json:"traces_ttl_duration_hrs"` + MetricsTime int `json:"metrics_ttl_duration_hrs,omitempty"` + MetricsMoveTime int `json:"metrics_move_ttl_duration_hrs,omitempty"` + TracesTime int `json:"traces_ttl_duration_hrs,omitempty"` + TracesMoveTime int `json:"traces_move_ttl_duration_hrs,omitempty"` } type DBResponseMinMaxDuration struct { diff --git a/pkg/query-service/telemetry/ignoredPaths.go b/pkg/query-service/telemetry/ignoredPaths.go index 3fc2f1898a..e6adf197ff 100644 --- a/pkg/query-service/telemetry/ignoredPaths.go +++ b/pkg/query-service/telemetry/ignoredPaths.go @@ -2,7 +2,8 @@ package telemetry func IgnoredPaths() map[string]struct{} { ignoredPaths := map[string]struct{}{ - "/api/v1/tags": struct{}{}, + "/api/v1/tags": struct{}{}, + "/api/v1/version": struct{}{}, } return ignoredPaths diff --git a/pkg/query-service/tests/cold_storage_test.go b/pkg/query-service/tests/cold_storage_test.go index f748db30dc..8159805a56 100644 --- a/pkg/query-service/tests/cold_storage_test.go +++ b/pkg/query-service/tests/cold_storage_test.go @@ -1,6 +1,7 @@ package tests import ( + "encoding/json" "fmt" "io/ioutil" "net/http" @@ -8,6 +9,7 @@ import ( "time" "github.com/stretchr/testify/require" + "go.signoz.io/query-service/model" ) const ( @@ -102,6 +104,76 @@ func TestSetTTL(t *testing.T) { fmt.Printf("=== Found %d objects in Minio\n", count) } +func getTTL(t *testing.T, table string) *model.GetTTLResponseItem { + req := endpoint + fmt.Sprintf("/api/v1/settings/ttl?type=%s", table) + if len(table) == 0 { + req = endpoint + "/api/v1/settings/ttl" + } + + resp, err := client.Get(req) + require.NoError(t, err) + + defer resp.Body.Close() + b, err := ioutil.ReadAll(resp.Body) + require.NoError(t, err) + + res := &model.GetTTLResponseItem{} + require.NoError(t, json.Unmarshal(b, res)) + return res +} + +func TestGetTTL(t *testing.T) { + r, err := setTTL("traces", "s3", "3600s", "7200s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp := getTTL(t, "traces") + require.Equal(t, 1, resp.TracesMoveTime) + require.Equal(t, 2, resp.TracesTime) + + r, err = setTTL("metrics", "s3", "3600s", "7200s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "metrics") + require.Equal(t, 1, resp.MetricsMoveTime) + require.Equal(t, 2, resp.MetricsTime) + + r, err = setTTL("traces", "s3", "36000s", "72000s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "") + require.Equal(t, 10, resp.TracesMoveTime) + require.Equal(t, 20, resp.TracesTime) + require.Equal(t, 1, resp.MetricsMoveTime) + require.Equal(t, 2, resp.MetricsTime) + + r, err = setTTL("metrics", "s3", "15h", "50h") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "") + require.Equal(t, 10, resp.TracesMoveTime) + require.Equal(t, 20, resp.TracesTime) + require.Equal(t, 15, resp.MetricsMoveTime) + require.Equal(t, 50, resp.MetricsTime) + + r, err = setTTL("metrics", "s3", "0s", "0s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + r, err = setTTL("traces", "s3", "0s", "0s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "") + require.Equal(t, 0, resp.TracesMoveTime) + require.Equal(t, 0, resp.TracesTime) + require.Equal(t, 0, resp.MetricsMoveTime) + require.Equal(t, 0, resp.MetricsTime) +} + func TestMain(m *testing.M) { if err := startCluster(); err != nil { fmt.Println(err) diff --git a/pkg/query-service/tests/test-deploy/clickhouse-config.xml b/pkg/query-service/tests/test-deploy/clickhouse-config.xml index a955a6a3fa..06ddb2b723 100644 --- a/pkg/query-service/tests/test-deploy/clickhouse-config.xml +++ b/pkg/query-service/tests/test-deploy/clickhouse-config.xml @@ -1,11 +1,8 @@ - trace - /var/log/clickhouse-server/clickhouse-server.log - /var/log/clickhouse-server/clickhouse-server.err.log - 1000M - 10 + information + 1 8123 diff --git a/pkg/query-service/tests/test-deploy/data/signoz.db b/pkg/query-service/tests/test-deploy/data/signoz.db new file mode 100644 index 0000000000..c19319ab34 Binary files /dev/null and b/pkg/query-service/tests/test-deploy/data/signoz.db differ diff --git a/pkg/query-service/tests/test-deploy/docker-compose.arm.yaml b/pkg/query-service/tests/test-deploy/docker-compose.arm.yaml index 0447c26a35..a5d4e28796 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.arm.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.arm.yaml @@ -5,6 +5,11 @@ services: image: altinity/clickhouse-server:21.12.3.32.altinitydev.arm volumes: - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + restart: on-failure + logging: + options: + max-size: 50m + max-file: "3" healthcheck: # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"] @@ -16,6 +21,7 @@ services: image: signoz/alertmanager:0.6.0 depends_on: - query-service + restart: on-failure command: - --queryService.url=http://query-service:8080 - --storage.path=/data @@ -35,6 +41,11 @@ services: - STORAGE=clickhouse - GODEBUG=netdns=go - TELEMETRY_ENABLED=true + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/version"] + interval: 30s + timeout: 5s + retries: 3 depends_on: clickhouse: condition: service_healthy diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 6edd31b63a..c88e3544ec 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -5,6 +5,11 @@ services: image: yandex/clickhouse-server:21.12.3.32 volumes: - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + restart: on-failure + logging: + options: + max-size: 50m + max-file: "3" healthcheck: # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"] @@ -16,6 +21,7 @@ services: image: signoz/alertmanager:0.6.0 depends_on: - query-service + restart: on-failure command: - --queryService.url=http://query-service:8080 - --storage.path=/data @@ -37,6 +43,11 @@ services: - STORAGE=clickhouse - GODEBUG=netdns=go - TELEMETRY_ENABLED=true + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/version"] + interval: 30s + timeout: 5s + retries: 3 depends_on: clickhouse: condition: service_healthy