From 2008156f37467307a23618e2a6fa4edaca661747 Mon Sep 17 00:00:00 2001 From: Abinand P Date: Sat, 30 Mar 2024 09:15:53 +0000 Subject: [PATCH] initalising the project --- .dockerignore | 6 + .editorconfig | 33 + .gitattributes | 1 + .github/CODEOWNERS | 10 + .github/ISSUE_TEMPLATE/bug_report.md | 34 + .github/ISSUE_TEMPLATE/feature_request.md | 27 + .../performance-issue-report.md | 33 + .github/config.yml | 31 + .github/pull_request_template.md | 17 + .github/release-drafter.yml | 29 + .github/workflows/README.md | 42 + .github/workflows/build.yaml | 84 + .github/workflows/codeball.yml | 17 + .github/workflows/codeql.yaml | 71 + .github/workflows/commitlint.yml | 13 + .../workflows/create-issue-on-pr-merge.yml | 27 + .github/workflows/dependency-review.yml | 22 + .github/workflows/e2e-k3s.yaml | 93 + .github/workflows/playwright.yaml | 24 + .github/workflows/pr_verify_linked_issue.yml | 19 + .github/workflows/push.yaml | 198 + .github/workflows/release-drafter.yml | 35 + .github/workflows/remove-label.yaml | 22 + .github/workflows/sonar.yml | 26 + .github/workflows/staging-deployment.yaml | 42 + .github/workflows/testing-deployment.yaml | 43 + .gitignore | 64 + .gitpod.yml | 36 + .scripts/commentLinesForSetup.sh | 7 + CHANGELOG.md | 0 CODE_OF_CONDUCT.md | 76 + CONTRIBUTING.md | 385 + LICENSE | 699 +- Makefile | 187 + README.de-de.md | 216 + README.md | 230 + README.pt-br.md | 158 + README.zh-cn.md | 227 + SECURITY.md | 18 + deploy/README.md | 82 + .../clickhouse-setup/alertmanager.yml | 35 + .../docker-swarm/clickhouse-setup/alerts.yml | 11 + .../clickhouse-setup/clickhouse-cluster.xml | 75 + .../clickhouse-setup/clickhouse-config.xml | 1139 + .../clickhouse-setup/clickhouse-storage.xml | 41 + .../clickhouse-setup/clickhouse-users.xml | 123 + .../clickhouse-setup/docker-compose.yaml | 287 + .../docker-entrypoint-initdb.d/init-db.sql | 31 + .../otel-collector-config.yaml | 161 + .../otel-collector-opamp-config.yaml | 1 + .../clickhouse-setup/prometheus.yml | 25 + .../common/locust-scripts/locustfile.py | 16 + deploy/docker-swarm/common/nginx-config.conf | 51 + deploy/docker-swarm/dashboards/.gitkeep | 0 .../docker/clickhouse-setup/alertmanager.yml | 35 + deploy/docker/clickhouse-setup/alerts.yml | 11 + .../clickhouse-setup/clickhouse-cluster.xml | 75 + .../clickhouse-setup/clickhouse-config.xml | 1140 + .../clickhouse-setup/clickhouse-storage.xml | 41 + .../clickhouse-setup/clickhouse-users.xml | 123 + .../clickhouse-setup/custom-function.xml | 21 + .../clickhouse-setup/docker-compose-core.yaml | 154 + .../docker-compose-local.yaml | 67 + .../clickhouse-setup/docker-compose.yaml | 306 + .../otel-collector-config.yaml | 169 + .../otel-collector-opamp-config.yaml | 1 + deploy/docker/clickhouse-setup/prometheus.yml | 25 + .../user_scripts/histogramQuantile | Bin 0 -> 1849156 bytes .../user_scripts/histogramQuantile.go | 237 + .../common/locust-scripts/locustfile.py | 16 + deploy/docker/common/nginx-config.conf | 51 + deploy/install.sh | 559 + e2e/package.json | 14 + e2e/playwright.config.ts | 46 + e2e/tests/auth.setup.ts | 37 + e2e/tests/contants.ts | 10 + e2e/tests/navigation.spec.ts | 40 + e2e/yarn.lock | 46 + ee/LICENSE | 37 + ee/query-service/.dockerignore | 4 + ee/query-service/Dockerfile | 31 + ee/query-service/app/api/api.go | 187 + ee/query-service/app/api/auth.go | 338 + ee/query-service/app/api/dashboard.go | 51 + ee/query-service/app/api/domains.go | 90 + ee/query-service/app/api/featureFlags.go | 24 + ee/query-service/app/api/license.go | 272 + ee/query-service/app/api/metrics.go | 236 + ee/query-service/app/api/pat.go | 165 + ee/query-service/app/api/response.go | 12 + ee/query-service/app/api/traces.go | 39 + ee/query-service/app/db/metrics.go | 401 + ee/query-service/app/db/reader.go | 39 + ee/query-service/app/db/trace.go | 222 + ee/query-service/app/server.go | 687 + ee/query-service/auth/auth.go | 56 + ee/query-service/constants/constants.go | 31 + ee/query-service/dao/factory.go | 18 + ee/query-service/dao/interface.go | 44 + ee/query-service/dao/sqlite/auth.go | 197 + ee/query-service/dao/sqlite/domain.go | 253 + ee/query-service/dao/sqlite/modelDao.go | 144 + ee/query-service/dao/sqlite/pat.go | 199 + .../integrations/signozio/response.go | 20 + .../integrations/signozio/signozio.go | 159 + ee/query-service/interfaces/connector.go | 12 + ee/query-service/license/db.go | 205 + ee/query-service/license/manager.go | 336 + ee/query-service/license/sqlite/init.go | 52 + ee/query-service/main.go | 170 + ee/query-service/model/auth.go | 12 + ee/query-service/model/domain.go | 184 + ee/query-service/model/errors.go | 109 + ee/query-service/model/license.go | 106 + ee/query-service/model/pat.go | 32 + ee/query-service/model/plans.go | 316 + ee/query-service/model/sso.go | 68 + ee/query-service/model/trace.go | 22 + ee/query-service/model/usage.go | 34 + ee/query-service/sso/google.go | 92 + ee/query-service/sso/model.go | 31 + ee/query-service/sso/saml/request.go | 107 + ee/query-service/usage/manager.go | 209 + frontend/.babelrc | 16 + frontend/.dockerignore | 3 + frontend/.eslintignore | 4 + frontend/.eslintrc.js | 121 + frontend/.husky/commit-msg | 20 + frontend/.husky/pre-commit | 4 + frontend/.npmrc | 1 + frontend/.nvmrc | 1 + frontend/.prettierignore | 6 + frontend/.prettierrc.json | 8 + frontend/.yarnrc | 2 + frontend/CONTRIBUTIONS.md | 56 + frontend/Dockerfile | 18 + frontend/README.md | 105 + frontend/__mocks__/cssMock.ts | 1 + frontend/babel.config.js | 6 + frontend/bundlesize.config.json | 8 + frontend/commitlint.config.ts | 1 + frontend/conf/default.conf | 33 + frontend/docker-compose.yml | 7 + frontend/example.env | 7 + frontend/i18-generate-hash.js | 20 + frontend/jest.config.ts | 40 + frontend/jest.setup.ts | 30 + frontend/package.json | 236 + frontend/playwright.config.ts | 23 + frontend/public/Icons/awwSnap.svg | 1 + frontend/public/Icons/emptyState.svg | 1 + frontend/public/Icons/loading-plane.gif | Bin 0 -> 89667 bytes frontend/public/Icons/promQL.svg | 1 + frontend/public/Icons/redis-logo.svg | 1 + frontend/public/Icons/tetra-pack.svg | 1 + frontend/public/Images/eyesEmoji.svg | 19 + frontend/public/Images/notFound404.png | Bin 0 -> 43988 bytes frontend/public/Logos/cloudwatch.png | Bin 0 -> 52632 bytes frontend/public/Logos/cmd-terminal.svg | 1 + frontend/public/Logos/docker.svg | 1 + frontend/public/Logos/dotnet.png | Bin 0 -> 3778 bytes frontend/public/Logos/ec2.svg | 18 + frontend/public/Logos/ecs.svg | 18 + frontend/public/Logos/eks.svg | 2 + frontend/public/Logos/elixir.png | Bin 0 -> 4523 bytes frontend/public/Logos/fluent-bit.png | Bin 0 -> 49652 bytes frontend/public/Logos/fluentd.png | Bin 0 -> 20455 bytes frontend/public/Logos/go.png | Bin 0 -> 2968 bytes frontend/public/Logos/heroku.png | Bin 0 -> 957 bytes frontend/public/Logos/http.png | Bin 0 -> 4446 bytes frontend/public/Logos/java.png | Bin 0 -> 3259 bytes frontend/public/Logos/javascript.png | Bin 0 -> 1682 bytes frontend/public/Logos/kubernetes.svg | 1 + frontend/public/Logos/logstash.svg | 1 + frontend/public/Logos/ms-net-framework.png | Bin 0 -> 4252 bytes frontend/public/Logos/node-js.svg | 1 + frontend/public/Logos/php.png | Bin 0 -> 3476 bytes frontend/public/Logos/python.png | Bin 0 -> 2722 bytes frontend/public/Logos/rails.png | Bin 0 -> 1705 bytes frontend/public/Logos/rust.png | Bin 0 -> 15903 bytes frontend/public/Logos/signoz-brand-logo.svg | 11 + frontend/public/Logos/software-window.svg | 1 + frontend/public/Logos/swift.png | Bin 0 -> 13089 bytes frontend/public/Logos/syslogs.svg | 9 + frontend/public/Logos/vercel.png | Bin 0 -> 46474 bytes frontend/public/SigNoz-dark.svg | 1 + frontend/public/SigNoz-white.svg | 1 + frontend/public/favicon.ico | Bin 0 -> 2218 bytes frontend/public/locales/en-GB/alerts.json | 117 + frontend/public/locales/en-GB/channels.json | 58 + frontend/public/locales/en-GB/common.json | 10 + frontend/public/locales/en-GB/dashboard.json | 29 + .../public/locales/en-GB/errorDetails.json | 7 + frontend/public/locales/en-GB/explorer.json | 3 + .../public/locales/en-GB/generalSettings.json | 21 + frontend/public/locales/en-GB/licenses.json | 13 + frontend/public/locales/en-GB/login.json | 22 + frontend/public/locales/en-GB/logs.json | 1 + .../locales/en-GB/organizationsettings.json | 19 + frontend/public/locales/en-GB/routes.json | 15 + frontend/public/locales/en-GB/rules.json | 86 + frontend/public/locales/en-GB/services.json | 3 + frontend/public/locales/en-GB/settings.json | 5 + frontend/public/locales/en-GB/signup.json | 18 + frontend/public/locales/en-GB/titles.json | 42 + frontend/public/locales/en-GB/trace.json | 11 + .../public/locales/en-GB/traceDetails.json | 3 + .../public/locales/en-GB/translation.json | 19 + frontend/public/locales/en/alerts.json | 117 + frontend/public/locales/en/apiKeys.json | 3 + frontend/public/locales/en/channels.json | 74 + frontend/public/locales/en/common.json | 10 + frontend/public/locales/en/dashboard.json | 32 + frontend/public/locales/en/errorDetails.json | 9 + frontend/public/locales/en/explorer.json | 4 + .../public/locales/en/generalSettings.json | 21 + frontend/public/locales/en/licenses.json | 13 + frontend/public/locales/en/login.json | 22 + frontend/public/locales/en/logs.json | 1 + .../locales/en/organizationsettings.json | 19 + frontend/public/locales/en/pipeline.json | 46 + frontend/public/locales/en/routes.json | 15 + frontend/public/locales/en/rules.json | 86 + frontend/public/locales/en/services.json | 3 + frontend/public/locales/en/settings.json | 6 + frontend/public/locales/en/signup.json | 18 + frontend/public/locales/en/titles.json | 52 + frontend/public/locales/en/trace.json | 11 + frontend/public/locales/en/traceDetails.json | 3 + frontend/public/locales/en/translation.json | 19 + frontend/public/locales/en/valueGraph.json | 3 + frontend/public/manifest.json | 25 + frontend/public/robots.txt | 3 + frontend/public/signoz-signup.svg | 9 + frontend/public/signoz.svg | 4 + frontend/scripts/typecheck-staged.sh | 25 + frontend/sonar-project.properties | 6 + frontend/src/AppRoutes/Private.tsx | 231 + frontend/src/AppRoutes/index.tsx | 209 + frontend/src/AppRoutes/pageComponents.ts | 207 + frontend/src/AppRoutes/routes.ts | 400 + frontend/src/AppRoutes/utils.ts | 92 + frontend/src/ReactI18/index.tsx | 35 + frontend/src/api/APIKeys/createAPIKey.ts | 26 + frontend/src/api/APIKeys/deleteAPIKey.ts | 24 + frontend/src/api/APIKeys/getAPIKey.ts | 24 + frontend/src/api/APIKeys/getAllAPIKeys.ts | 6 + frontend/src/api/APIKeys/updateAPIKey.ts | 26 + frontend/src/api/ErrorResponseHandler.ts | 63 + .../api/Integrations/getAllIntegrations.ts | 7 + .../src/api/Integrations/getIntegration.ts | 11 + .../api/Integrations/getIntegrationStatus.ts | 11 + .../api/Integrations/installIntegration.ts | 31 + .../api/Integrations/uninstallIntegration.ts | 31 + frontend/src/api/SAML/deleteDomain.ts | 24 + frontend/src/api/SAML/listAllDomain.ts | 24 + frontend/src/api/SAML/postDomain.ts | 24 + frontend/src/api/SAML/updateDomain.ts | 24 + frontend/src/api/alerts/create.ts | 26 + frontend/src/api/alerts/delete.ts | 24 + frontend/src/api/alerts/get.ts | 24 + frontend/src/api/alerts/getAll.ts | 24 + frontend/src/api/alerts/getGroup.ts | 29 + frontend/src/api/alerts/getTriggered.ts | 29 + frontend/src/api/alerts/patch.ts | 26 + frontend/src/api/alerts/put.ts | 26 + frontend/src/api/alerts/save.ts | 17 + frontend/src/api/alerts/testAlert.ts | 26 + frontend/src/api/apiV1.ts | 8 + frontend/src/api/billing/checkout.ts | 31 + frontend/src/api/billing/getUsage.ts | 35 + frontend/src/api/billing/manage.ts | 30 + frontend/src/api/browser/localstorage/get.ts | 9 + .../src/api/browser/localstorage/remove.ts | 10 + frontend/src/api/browser/localstorage/set.ts | 10 + frontend/src/api/channels/createEmail.ts | 34 + frontend/src/api/channels/createMsTeams.ts | 34 + frontend/src/api/channels/createOpsgenie.ts | 37 + frontend/src/api/channels/createPager.ts | 42 + frontend/src/api/channels/createSlack.ts | 35 + frontend/src/api/channels/createWebhook.ts | 51 + frontend/src/api/channels/delete.ts | 24 + frontend/src/api/channels/editEmail.ts | 34 + frontend/src/api/channels/editMsTeams.ts | 34 + frontend/src/api/channels/editOpsgenie.ts | 38 + frontend/src/api/channels/editPager.ts | 42 + frontend/src/api/channels/editSlack.ts | 35 + frontend/src/api/channels/editWebhook.ts | 50 + frontend/src/api/channels/get.ts | 24 + frontend/src/api/channels/getAll.ts | 24 + frontend/src/api/channels/testEmail.ts | 34 + frontend/src/api/channels/testMsTeams.ts | 34 + frontend/src/api/channels/testOpsgenie.ts | 37 + frontend/src/api/channels/testPager.ts | 42 + frontend/src/api/channels/testSlack.ts | 35 + frontend/src/api/channels/testWebhook.ts | 51 + frontend/src/api/dashboard/create.ts | 27 + frontend/src/api/dashboard/delete.ts | 9 + frontend/src/api/dashboard/get.ts | 11 + frontend/src/api/dashboard/getAll.ts | 8 + frontend/src/api/dashboard/lockDashboard.ts | 11 + .../src/api/dashboard/queryRangeFormat.ts | 15 + frontend/src/api/dashboard/unlockDashboard.ts | 11 + frontend/src/api/dashboard/update.ts | 26 + .../variables/dashboardVariablesQuery.ts | 30 + frontend/src/api/disks/getDisks.ts | 24 + .../api/dynamicConfigs/getDynamicConfigs.ts | 24 + frontend/src/api/errors/getAll.ts | 34 + .../api/errors/getByErrorTypeAndService.ts | 29 + frontend/src/api/errors/getById.ts | 29 + frontend/src/api/errors/getErrorCounts.ts | 30 + frontend/src/api/errors/getNextPrevId.ts | 29 + frontend/src/api/features/getFeatureFlags.ts | 10 + frontend/src/api/index.ts | 144 + frontend/src/api/licenses/apply.ts | 26 + frontend/src/api/licenses/getAll.ts | 24 + frontend/src/api/logs/AddToSelectedField.ts | 23 + frontend/src/api/logs/GetLogs.ts | 26 + frontend/src/api/logs/GetLogsAggregate.ts | 26 + frontend/src/api/logs/GetSearchFields.ts | 24 + .../src/api/logs/RemoveFromSelectedField.ts | 23 + frontend/src/api/logs/livetail.ts | 19 + .../src/api/metrics/ApDex/apDexSettings.ts | 16 + .../src/api/metrics/ApDex/getApDexSettings.ts | 8 + .../src/api/metrics/ApDex/getMetricMeta.ts | 9 + frontend/src/api/metrics/getDBOverView.ts | 26 + .../api/metrics/getExternalAverageDuration.ts | 29 + frontend/src/api/metrics/getExternalError.ts | 26 + .../src/api/metrics/getExternalService.ts | 26 + frontend/src/api/metrics/getMetricName.ts | 27 + frontend/src/api/metrics/getQueryRange.ts | 41 + .../src/api/metrics/getResourceAttributes.ts | 50 + frontend/src/api/metrics/getService.ts | 13 + .../src/api/metrics/getServiceOverview.ts | 16 + .../src/api/metrics/getTopLevelOperations.ts | 12 + frontend/src/api/metrics/getTopOperations.ts | 15 + frontend/src/api/pipeline/get.ts | 25 + frontend/src/api/pipeline/post.ts | 25 + frontend/src/api/pipeline/preview.ts | 21 + .../api/queryBuilder/getAggregateAttribute.ts | 49 + .../src/api/queryBuilder/getAttributeKeys.ts | 51 + .../api/queryBuilder/getAttributesValues.ts | 42 + frontend/src/api/saveView/deleteView.ts | 5 + frontend/src/api/saveView/getAllViews.ts | 9 + frontend/src/api/saveView/saveView.ts | 16 + frontend/src/api/saveView/updateView.ts | 19 + frontend/src/api/settings/getIngestionData.ts | 24 + frontend/src/api/settings/getRetention.ts | 26 + frontend/src/api/settings/setRetention.ts | 30 + frontend/src/api/trace/getFilters.ts | 49 + frontend/src/api/trace/getSpans.ts | 62 + frontend/src/api/trace/getSpansAggregate.ts | 65 + frontend/src/api/trace/getTagFilter.ts | 49 + frontend/src/api/trace/getTagValue.ts | 31 + frontend/src/api/trace/getTraceItem.ts | 32 + frontend/src/api/user/changeMyPassword.ts | 26 + frontend/src/api/user/deleteInvite.ts | 24 + frontend/src/api/user/deleteUser.ts | 24 + frontend/src/api/user/editOrg.ts | 28 + frontend/src/api/user/editUser.ts | 26 + frontend/src/api/user/getInviteDetails.ts | 26 + frontend/src/api/user/getLatestVersion.ts | 25 + frontend/src/api/user/getOrgUser.ts | 24 + frontend/src/api/user/getOrganization.ts | 28 + frontend/src/api/user/getPendingInvites.ts | 24 + frontend/src/api/user/getPreference.ts | 24 + .../src/api/user/getResetPasswordToken.ts | 24 + frontend/src/api/user/getRoles.ts | 28 + frontend/src/api/user/getUser.ts | 28 + frontend/src/api/user/getVersion.ts | 25 + frontend/src/api/user/login.ts | 26 + frontend/src/api/user/loginPrecheck.ts | 28 + frontend/src/api/user/resetPassword.ts | 26 + frontend/src/api/user/sendInvite.ts | 26 + frontend/src/api/user/setFlags.ts | 26 + frontend/src/api/user/signup.ts | 26 + frontend/src/api/user/updateRole.ts | 26 + frontend/src/api/userFeedback/sendFeedback.ts | 21 + frontend/src/api/utils.ts | 76 + frontend/src/api/widgets/getQuery.ts | 26 + frontend/src/assets/Dashboard/BarIcon.tsx | 41 + frontend/src/assets/Dashboard/List.tsx | 30 + frontend/src/assets/Dashboard/Table.tsx | 48 + frontend/src/assets/Dashboard/TimeSeries.tsx | 69 + frontend/src/assets/Dashboard/Value.tsx | 32 + frontend/src/assets/NotFound.tsx | 14 + frontend/src/assets/SomethingWentWrong.tsx | 468 + frontend/src/assets/UnAuthorized.tsx | 26 + .../CustomTimePicker.styles.scss | 121 + .../CustomTimePicker/CustomTimePicker.tsx | 323 + .../CustomTimePickerPopoverContent.tsx | 118 + .../RangePickerModal.styles.scss | 4 + .../CustomTimePicker/RangePickerModal.tsx | 68 + .../components/DraggableTableRow/index.tsx | 54 + .../tests/DraggableTableRow.test.tsx | 38 + .../DraggableTableRow.test.tsx.snap | 103 + .../DraggableTableRow/tests/utils.test.ts | 44 + .../src/components/DraggableTableRow/utils.ts | 15 + .../components/DropDown/DropDown.styles.scss | 11 + frontend/src/components/DropDown/DropDown.tsx | 30 + .../src/components/Editor/Editor.test.tsx | 59 + .../Editor/__snapshots__/Editor.test.tsx.snap | 69 + frontend/src/components/Editor/index.tsx | 56 + .../components/ExplorerCard/ExplorerCard.tsx | 252 + .../ExplorerCard/MenuItemGenerator.tsx | 103 + .../ExplorerCard/SaveViewWithName.tsx | 80 + .../ExplorerCard/__mock__/viewData.ts | 32 + .../src/components/ExplorerCard/constants.ts | 14 + .../src/components/ExplorerCard/styles.ts | 29 + .../ExplorerCard/test/ExplorerCard.test.tsx | 60 + .../test/MenuItemGenerator.test.tsx | 62 + .../test/SaveViewWithName.test.tsx | 63 + frontend/src/components/ExplorerCard/types.ts | 84 + frontend/src/components/ExplorerCard/utils.ts | 195 + .../src/components/Graph/Plugin/DragSelect.ts | 321 + .../src/components/Graph/Plugin/EmptyGraph.ts | 17 + .../Graph/Plugin/IntersectionCursor.ts | 164 + .../src/components/Graph/Plugin/Legend.ts | 114 + .../src/components/Graph/Plugin/Tooltip.ts | 46 + frontend/src/components/Graph/Plugin/index.ts | 1 + frontend/src/components/Graph/Plugin/utils.ts | 20 + .../Graph/__tests__/xAxisConfig.test.ts | 74 + frontend/src/components/Graph/hasData.ts | 19 + frontend/src/components/Graph/helpers.ts | 8 + frontend/src/components/Graph/index.tsx | 209 + frontend/src/components/Graph/styles.ts | 28 + frontend/src/components/Graph/types.ts | 78 + frontend/src/components/Graph/utils.ts | 223 + frontend/src/components/Graph/xAxisConfig.ts | 140 + frontend/src/components/Graph/yAxisConfig.ts | 58 + frontend/src/components/Input/index.tsx | 71 + .../src/components/Loadable/Loadable.test.tsx | 49 + frontend/src/components/Loadable/index.tsx | 15 + .../LogDetail/LogDetail.interfaces.ts | 14 + .../LogDetail/LogDetails.styles.scss | 232 + .../QueryBuilderSearchWrapper.styles.scss | 10 + .../LogDetail/QueryBuilderSearchWrapper.tsx | 77 + .../src/components/LogDetail/constants.ts | 7 + frontend/src/components/LogDetail/index.tsx | 211 + .../components/Logs/AddToQueryHOC.styles.scss | 3 + .../src/components/Logs/AddToQueryHOC.tsx | 38 + .../components/Logs/CategoryHeading/index.tsx | 12 + .../components/Logs/CategoryHeading/styles.ts | 6 + .../src/components/Logs/CopyClipboardHOC.tsx | 41 + .../Logs/ListLogView/ListLogView.styles.scss | 103 + .../src/components/Logs/ListLogView/index.tsx | 225 + .../src/components/Logs/ListLogView/styles.ts | 55 + .../src/components/Logs/ListLogView/util.ts | 1 + .../LogLinesActionButtons.styles.scss | 44 + .../LogLinesActionButtons.tsx | 42 + .../LogStateIndicator.styles.scss | 42 + .../LogStateIndicator.test.tsx | 45 + .../LogStateIndicator/LogStateIndicator.tsx | 57 + .../Logs/LogStateIndicator/utils.test.ts | 89 + .../Logs/LogStateIndicator/utils.ts | 57 + .../Logs/RawLogView/RawLogView.styles.scss | 0 .../src/components/Logs/RawLogView/config.ts | 3 + .../src/components/Logs/RawLogView/index.tsx | 214 + .../src/components/Logs/RawLogView/styles.ts | 83 + .../src/components/Logs/RawLogView/types.ts | 19 + .../src/components/Logs/TableView/config.ts | 31 + .../src/components/Logs/TableView/index.tsx | 24 + .../src/components/Logs/TableView/styles.ts | 23 + .../src/components/Logs/TableView/types.ts | 35 + .../Logs/TableView/useTableView.styles.scss | 27 + .../Logs/TableView/useTableView.tsx | 131 + frontend/src/components/Logs/styles.ts | 8 + .../LogsFormatOptionsMenu.styles.scss | 396 + .../LogsFormatOptionsMenu.tsx | 244 + .../CodeCopyBtn/CodeCopyBtn.scss | 36 + .../CodeCopyBtn/CodeCopyBtn.tsx | 32 + .../MarkdownRenderer/MarkdownRenderer.tsx | 109 + .../components/MessageTip/MessageTip.test.tsx | 47 + .../__snapshots__/MessageTip.test.tsx.snap | 54 + frontend/src/components/MessageTip/index.tsx | 27 + frontend/src/components/MessageTip/styles.ts | 6 + frontend/src/components/Modal.tsx | 36 + .../src/components/NotFound/NotFound.test.tsx | 19 + .../__snapshots__/NotFound.test.tsx.snap | 130 + frontend/src/components/NotFound/constant.ts | 1 + frontend/src/components/NotFound/index.tsx | 53 + frontend/src/components/NotFound/styles.ts | 60 + .../ReleaseNote/ReleaseNoteProps.ts | 4 + .../ReleaseNote/Releases/ReleaseNote0120.tsx | 73 + frontend/src/components/ReleaseNote/index.tsx | 66 + .../ResizeTable/DynamicColumnTable.syles.scss | 31 + .../ResizeTable/DynamicColumnTable.tsx | 113 + .../ResizeTable/ResizableHeader.tsx | 44 + .../components/ResizeTable/ResizeTable.tsx | 89 + .../TableComponent/DateComponent.tsx | 15 + .../ResizeTable/TableComponent/Time.tsx | 16 + frontend/src/components/ResizeTable/config.ts | 1 + .../src/components/ResizeTable/contants.ts | 11 + frontend/src/components/ResizeTable/index.ts | 4 + frontend/src/components/ResizeTable/styles.ts | 17 + frontend/src/components/ResizeTable/types.ts | 43 + frontend/src/components/ResizeTable/utils.ts | 77 + .../src/components/RouteTab/RouteTab.test.tsx | 93 + frontend/src/components/RouteTab/index.tsx | 49 + frontend/src/components/RouteTab/types.ts | 16 + frontend/src/components/Spinner/index.tsx | 28 + frontend/src/components/Spinner/styles.ts | 16 + frontend/src/components/Styled/index.ts | 85 + frontend/src/components/Styled/styles.ts | 42 + frontend/src/components/Styled/types.ts | 5 + .../TabLabel/TabLabel.interfaces.ts | 5 + frontend/src/components/TabLabel/index.tsx | 29 + .../TableRenderer/LabelColumn.styles.scss | 10 + .../components/TableRenderer/LabelColumn.tsx | 54 + .../TableRenderer/TableRenderer.types.ts | 5 + .../TableRenderer/TagWithToolTip.tsx | 36 + .../src/components/TableRenderer/utils.ts | 44 + .../TextToolTip/TextToolTip.style.scss | 3 + .../TextToolTip/TextToolTip.test.tsx | 53 + .../components/TextToolTip/TextToolTip.tsx | 89 + .../src/components/TextToolTip/constant.ts | 1 + frontend/src/components/TextToolTip/index.tsx | 3 + .../TimePreferenceDropDown/config.tsx | 7 + .../TimePreferenceDropDown/index.tsx | 54 + .../TimePreferenceDropDown/styles.ts | 13 + .../src/components/Upgrade/UpgradePrompt.tsx | 31 + .../src/components/Uplot/Uplot.styles.scss | 23 + frontend/src/components/Uplot/Uplot.tsx | 163 + frontend/src/components/Uplot/index.ts | 3 + frontend/src/components/Uplot/utils.ts | 48 + .../ValueGraph/ValueGraph.styles.scss | 31 + frontend/src/components/ValueGraph/index.tsx | 66 + frontend/src/components/ValueGraph/utils.ts | 97 + .../components/WelcomeLeftContainer/index.tsx | 40 + .../components/WelcomeLeftContainer/styles.ts | 23 + frontend/src/constants/alerts.ts | 9 + frontend/src/constants/apDex.ts | 5 + frontend/src/constants/api.ts | 5 + frontend/src/constants/app.ts | 18 + frontend/src/constants/card.ts | 4 + frontend/src/constants/events.ts | 5 + frontend/src/constants/features.ts | 22 + frontend/src/constants/global.ts | 4 + frontend/src/constants/liveTail.ts | 5 + frontend/src/constants/localStorage.ts | 19 + frontend/src/constants/onboarding.ts | 1 + frontend/src/constants/optionsFormatTypes.ts | 5 + frontend/src/constants/panelTypes.ts | 41 + frontend/src/constants/query.ts | 31 + frontend/src/constants/queryBuilder.ts | 401 + .../src/constants/queryBuilderFilterConfig.ts | 1 + .../src/constants/queryBuilderOperators.ts | 427 + .../src/constants/queryFunctionOptions.ts | 137 + frontend/src/constants/reactQueryKeys.ts | 11 + frontend/src/constants/regExp.ts | 7 + frontend/src/constants/resourceAttributes.ts | 18 + frontend/src/constants/routes.ts | 59 + .../constants/shortcuts/DashboardShortcuts.ts | 17 + .../src/constants/shortcuts/QBShortcuts.ts | 17 + .../constants/shortcuts/globalShortcuts.ts | 32 + .../shortcuts/logsExplorerShortcuts.ts | 19 + frontend/src/constants/theme.ts | 105 + .../src/container/APIKeys/APIKeys.styles.scss | 685 + .../src/container/APIKeys/APIKeys.test.tsx | 99 + frontend/src/container/APIKeys/APIKeys.tsx | 864 + .../AllAlertChannels/AlertChannels.tsx | 76 + .../src/container/AllAlertChannels/Delete.tsx | 62 + .../src/container/AllAlertChannels/index.tsx | 68 + .../src/container/AllAlertChannels/styles.ts | 26 + frontend/src/container/AllError/constant.ts | 9 + frontend/src/container/AllError/index.tsx | 432 + frontend/src/container/AllError/types.ts | 9 + frontend/src/container/AllError/utils.test.ts | 109 + frontend/src/container/AllError/utils.ts | 184 + .../container/AppLayout/AppLayout.styles.scss | 61 + frontend/src/container/AppLayout/index.tsx | 346 + frontend/src/container/AppLayout/styles.ts | 24 + frontend/src/container/AppLayout/utils.ts | 9 + .../BillingContainer.styles.scss | 78 + .../BillingContainer.test.tsx | 188 + .../BillingContainer/BillingContainer.tsx | 475 + .../BillingUsageGraph.styles.scss | 29 + .../BillingUsageGraph/BillingUsageGraph.tsx | 190 + .../BillingUsageGraph/utils.ts | 87 + .../ConfigDropdown/Config/ErrorLink.tsx | 33 + .../container/ConfigDropdown/Config/Link.tsx | 23 + .../container/ConfigDropdown/Config/index.tsx | 47 + .../src/container/ConfigDropdown/index.tsx | 75 + .../src/container/ConfigDropdown/styles.ts | 6 + frontend/src/container/Controls/config.ts | 9 + frontend/src/container/Controls/index.tsx | 91 + frontend/src/container/Controls/styles.ts | 7 + .../container/CreateAlertChannels/config.ts | 127 + .../container/CreateAlertChannels/defaults.ts | 448 + .../container/CreateAlertChannels/index.tsx | 529 + .../container/CreateAlertChannels/utils.ts | 4 + .../CreateAlertRule/SelectAlertType/config.ts | 27 + .../CreateAlertRule/SelectAlertType/index.tsx | 53 + .../CreateAlertRule/SelectAlertType/styles.ts | 16 + .../CreateAlertRule/SelectAlertType/types.ts | 7 + .../src/container/CreateAlertRule/config.ts | 8 + .../src/container/CreateAlertRule/defaults.ts | 152 + .../src/container/CreateAlertRule/index.tsx | 85 + .../container/Download/Download.styles.scss | 4 + frontend/src/container/Download/Download.tsx | 77 + .../src/container/Download/Download.types.ts | 10 + .../src/container/EditAlertChannels/index.tsx | 454 + frontend/src/container/EditRules/index.tsx | 30 + frontend/src/container/EditRules/styles.ts | 11 + .../EmptyLogsSearch.styles.scss | 30 + .../EmptyLogsSearch/EmptyLogsSearch.tsx | 21 + frontend/src/container/ErrorDetails/config.ts | 8 + frontend/src/container/ErrorDetails/index.tsx | 183 + .../src/container/ErrorDetails/styles.scss | 3 + frontend/src/container/ErrorDetails/styles.ts | 28 + .../ExplorerControlPanel.interfaces.ts | 8 + .../container/ExplorerControlPanel/index.tsx | 33 + .../container/ExplorerControlPanel/styles.ts | 5 + .../ExplorerOptions.styles.scss | 321 + .../ExplorerOptions/ExplorerOptions.tsx | 434 + .../src/container/ExplorerOptions/types.ts | 28 + .../src/container/ExplorerOptions/utils.ts | 69 + .../src/container/ExplorerOrderBy/index.tsx | 73 + .../ExportPanel/ExportPanelContainer.tsx | 124 + frontend/src/container/ExportPanel/index.tsx | 49 + frontend/src/container/ExportPanel/styles.ts | 33 + frontend/src/container/ExportPanel/utils.ts | 18 + .../FormAlertChannels/Settings/Email.tsx | 48 + .../Settings/LabelFilter.tsx | 61 + .../FormAlertChannels/Settings/MsTeams.tsx | 57 + .../FormAlertChannels/Settings/Opsgenie.tsx | 74 + .../FormAlertChannels/Settings/Pager.tsx | 154 + .../FormAlertChannels/Settings/Slack.tsx | 72 + .../FormAlertChannels/Settings/Webhook.tsx | 59 + .../src/container/FormAlertChannels/index.tsx | 185 + .../src/container/FormAlertChannels/styles.ts | 8 + .../container/FormAlertRules/BasicInfo.tsx | 172 + .../ChQuerySection/ChQuerySection.tsx | 17 + .../FormAlertRules/ChQuerySection/index.ts | 3 + .../FormAlertRules/ChannelSelect/index.tsx | 77 + .../FormAlertRules/ChannelSelect/styles.ts | 6 + .../FormAlertRules/ChartPreview/config.ts | 98 + .../FormAlertRules/ChartPreview/index.tsx | 224 + .../FormAlertRules/ChartPreview/styles.ts | 30 + .../FormAlertRules/ChartPreview/utils.test.ts | 105 + .../FormAlertRules/ChartPreview/utils.ts | 85 + .../FormAlertRules/FormAlertRules.styles.scss | 45 + .../FormAlertRules/PromqlSection.tsx | 21 + .../FormAlertRules/QuerySection.styles.scss | 61 + .../container/FormAlertRules/QuerySection.tsx | 225 + .../container/FormAlertRules/RuleOptions.tsx | 289 + .../FormAlertRules/UserGuide/index.tsx | 165 + .../FormAlertRules/UserGuide/styles.ts | 17 + .../src/container/FormAlertRules/index.tsx | 583 + .../FormAlertRules/labels/Labels.machine.ts | 49 + .../labels/Labels.machine.typegen.ts | 25 + .../FormAlertRules/labels/QueryChip.tsx | 24 + .../container/FormAlertRules/labels/index.tsx | 161 + .../container/FormAlertRules/labels/styles.ts | 34 + .../container/FormAlertRules/labels/types.ts | 9 + .../container/FormAlertRules/labels/utils.ts | 54 + .../src/container/FormAlertRules/styles.ts | 99 + .../container/FormAlertRules/utils.test.ts | 14 + .../src/container/FormAlertRules/utils.ts | 56 + .../FullScreenHeader.styles.scss | 37 + .../FullScreenHeader/FullScreenHeader.tsx | 28 + .../container/GantChart/GantChart.styles.scss | 16 + .../src/container/GantChart/Span/index.tsx | 96 + .../src/container/GantChart/Span/styles.ts | 52 + .../container/GantChart/SpanName/index.tsx | 22 + .../container/GantChart/SpanName/styles.ts | 41 + .../src/container/GantChart/Trace/index.tsx | 231 + .../src/container/GantChart/Trace/styles.ts | 113 + .../src/container/GantChart/Trace/utils.ts | 3 + frontend/src/container/GantChart/index.tsx | 92 + frontend/src/container/GantChart/styles.ts | 48 + frontend/src/container/GantChart/utils.ts | 195 + .../GeneralSettings/GeneralSettings.tsx | 634 + .../container/GeneralSettings/Retention.tsx | 132 + .../GeneralSettings/StatusMessage.tsx | 76 + .../src/container/GeneralSettings/index.tsx | 113 + .../src/container/GeneralSettings/styles.ts | 100 + .../src/container/GeneralSettings/utils.ts | 56 + .../GeneralSettingsCloud.styles.scss | 11 + .../GeneralSettingsCloud.tsx | 16 + .../container/GeneralSettingsCloud/index.tsx | 3 + frontend/src/container/GoToTop/index.tsx | 29 + .../GridCardLayout/EmptyWidget/index.tsx | 15 + .../GridCardLayout/EmptyWidget/styles.ts | 8 + .../GridCard/FullView/GraphManager.tsx | 155 + .../FullView/TableRender/CustomCheckBox.tsx | 41 + .../FullView/TableRender/GetLabel.tsx | 18 + .../TableRender/GraphManagerColumns.tsx | 85 + .../GridCard/FullView/TableRender/Label.tsx | 34 + .../FullView/WidgetFullView.styles.scss | 53 + .../GridCard/FullView/contants.ts | 30 + .../GridCard/FullView/index.tsx | 272 + .../GridCard/FullView/styles.ts | 44 + .../GridCardLayout/GridCard/FullView/types.ts | 89 + .../GridCardLayout/GridCard/FullView/utils.ts | 133 + .../GridCard/WidgetGraphComponent.tsx | 341 + .../GridCard/__mock__/mockChartData.ts | 15 + .../GridCard/__mock__/mockLegendEntryData.ts | 12 + .../GridCardLayout/GridCard/index.tsx | 274 + .../GridCardLayout/GridCard/styles.ts | 23 + .../GridCardLayout/GridCard/types.ts | 59 + .../GridCardLayout/GridCard/utils.ts | 133 + .../GridCardLayout/GridCardLayout.styles.scss | 43 + .../GridCardLayout/GridCardLayout.tsx | 219 + .../WidgetHeader/DisplayThreshold.tsx | 19 + .../WidgetHeader/WidgetHeader.styles.scss | 39 + .../GridCardLayout/WidgetHeader/config.ts | 30 + .../GridCardLayout/WidgetHeader/contants.ts | 15 + .../GridCardLayout/WidgetHeader/index.tsx | 216 + .../GridCardLayout/WidgetHeader/styles.ts | 58 + .../GridCardLayout/WidgetHeader/types.ts | 16 + .../GridCardLayout/WidgetHeader/utils.ts | 22 + .../src/container/GridCardLayout/config.ts | 21 + .../src/container/GridCardLayout/index.tsx | 16 + .../src/container/GridCardLayout/styles.ts | 91 + .../src/container/GridCardLayout/types.ts | 3 + .../src/container/GridCardLayout/utils.ts | 8 + .../src/container/GridPanelSwitch/index.tsx | 103 + .../src/container/GridPanelSwitch/types.ts | 48 + .../src/container/GridPanelSwitch/utils.ts | 12 + .../container/GridTableComponent/config.ts | 9 + .../container/GridTableComponent/index.tsx | 91 + .../container/GridTableComponent/styles.ts | 31 + .../src/container/GridTableComponent/types.ts | 23 + .../src/container/GridTableComponent/utils.ts | 58 + .../container/GridValueComponent/config.ts | 3 + .../container/GridValueComponent/index.tsx | 52 + .../container/GridValueComponent/styles.ts | 19 + .../src/container/GridValueComponent/types.ts | 10 + .../Header/CurrentOrganization/index.tsx | 78 + .../src/container/Header/Header.styles.scss | 25 + .../container/Header/ManageLicense/index.tsx | 49 + .../container/Header/ManageLicense/styles.ts | 19 + .../src/container/Header/SignedIn/index.tsx | 50 + frontend/src/container/Header/index.tsx | 210 + frontend/src/container/Header/styles.ts | 90 + .../IngestionSettings.styles.scss | 3 + .../IngestionSettings/IngestionSettings.tsx | 91 + .../container/Licenses/ApplyLicenseForm.tsx | 116 + .../src/container/Licenses/ListLicenses.tsx | 43 + frontend/src/container/Licenses/index.tsx | 42 + frontend/src/container/Licenses/styles.ts | 30 + .../container/ListAlertRules/DeleteAlert.tsx | 116 + .../container/ListAlertRules/ListAlert.tsx | 373 + .../ListAlertRules/TableComponents/Status.tsx | 32 + .../ListAlertRules/ToggleAlertState.tsx | 110 + .../src/container/ListAlertRules/index.tsx | 66 + .../src/container/ListAlertRules/styles.ts | 31 + .../src/container/ListAlertRules/utils.ts | 25 + .../ListOfDashboard/DashboardsList.tsx | 407 + .../ListOfDashboard/ImportJSON/index.tsx | 195 + .../ListOfDashboard/ImportJSON/styles.ts | 10 + .../SearchFilter/Dashboard.machine.tsx | 50 + .../SearchFilter/Dashboard.machine.typegen.ts | 32 + .../SearchFilter/QueryChip.tsx | 21 + .../SearchFilter/__tests__/utils.test.ts | 67 + .../ListOfDashboard/SearchFilter/index.tsx | 211 + .../ListOfDashboard/SearchFilter/styles.ts | 27 + .../ListOfDashboard/SearchFilter/types.ts | 18 + .../ListOfDashboard/SearchFilter/utils.ts | 154 + .../TableComponents/CreatedBy.tsx | 17 + .../TableComponents/DeleteButton.styles.scss | 5 + .../TableComponents/DeleteButton.tsx | 139 + .../ListOfDashboard/TableComponents/Name.tsx | 28 + .../ListOfDashboard/TableComponents/Tags.tsx | 16 + .../ListOfDashboard/TableComponents/styles.ts | 7 + .../dashboardSearchAndFilter.ts | 24 + .../src/container/ListOfDashboard/index.tsx | 3 + .../src/container/ListOfDashboard/styles.ts | 25 + .../src/container/ListOfDashboard/utils.ts | 27 + .../container/LiveLogs/BackButton/index.tsx | 53 + .../container/LiveLogs/FiltersInput/index.tsx | 78 + .../container/LiveLogs/FiltersInput/styles.ts | 24 + .../LiveLogs/ListViewPanel/index.tsx | 75 + .../LiveLogs/ListViewPanel/styles.ts | 11 + .../LiveLogs/LiveLogsContainer/index.tsx | 203 + .../LiveLogs/LiveLogsContainer/styles.ts | 17 + .../container/LiveLogs/LiveLogsList/index.tsx | 152 + .../container/LiveLogs/LiveLogsList/types.ts | 5 + .../LiveLogs/LiveLogsListChart/index.tsx | 72 + .../LiveLogs/LiveLogsListChart/types.ts | 6 + frontend/src/container/LiveLogs/constants.ts | 53 + frontend/src/container/LiveLogs/types.ts | 6 + frontend/src/container/LiveLogs/utils.ts | 71 + .../src/container/LiveLogsTopNav/index.tsx | 71 + .../src/container/LiveLogsTopNav/styles.ts | 19 + frontend/src/container/LocalTopNav/index.tsx | 50 + frontend/src/container/LocalTopNav/styles.ts | 9 + frontend/src/container/LocalTopNav/types.ts | 6 + frontend/src/container/LogControls/index.tsx | 130 + frontend/src/container/LogControls/styles.ts | 14 + .../container/LogDetailedView/ActionItem.tsx | 61 + .../BodyTitleRenderer.styles.ts | 11 + .../LogDetailedView/BodyTitleRenderer.tsx | 89 + .../ContextLogRenderer.styles.scss | 25 + .../ContextView/ContextLogRenderer.tsx | 129 + .../ContextView/ContextView.styles.scss | 23 + .../ContextView/ContextView.tsx | 36 + .../ContextView/useContextLogData.ts | 163 + .../LogDetailedView/FieldRenderer.styles.scss | 23 + .../LogDetailedView/FieldRenderer.styles.ts | 25 + .../LogDetailedView/FieldRenderer.tsx | 45 + .../LogDetailedView/JsonView.styles.scss | 46 + .../container/LogDetailedView/JsonView.tsx | 93 + .../LogDetailedView/LogContext.styles.scss | 3 + .../container/LogDetailedView/LogContext.tsx | 52 + .../LogDetailedView/LogDetailedView.types.ts | 25 + .../LogDetailedView/Overview.styles.scss | 130 + .../container/LogDetailedView/Overview.tsx | 220 + .../LogDetailedView/TableView.styles.scss | 80 + .../container/LogDetailedView/TableView.tsx | 290 + .../src/container/LogDetailedView/config.ts | 13 + .../src/container/LogDetailedView/constant.ts | 4 + .../src/container/LogDetailedView/index.tsx | 164 + .../container/LogDetailedView/util.test.ts | 187 + .../src/container/LogDetailedView/utils.tsx | 268 + .../LogsExplorerQuerySection.styles.scss | 32 + .../LogExplorerQuerySection/index.tsx | 114 + frontend/src/container/LogLiveTail/config.ts | 26 + frontend/src/container/LogLiveTail/index.tsx | 266 + frontend/src/container/LogLiveTail/styles.ts | 25 + frontend/src/container/Login/index.tsx | 290 + frontend/src/container/Login/styles.ts | 38 + .../src/container/LogsAggregate/index.tsx | 93 + .../src/container/LogsAggregate/styles.ts | 11 + .../LogsContextList.styles.scss | 32 + .../LogsContextList/ShowButton.styles.scss | 31 + .../container/LogsContextList/ShowButton.tsx | 54 + .../src/container/LogsContextList/configs.ts | 9 + .../src/container/LogsContextList/index.tsx | 216 + .../src/container/LogsContextList/styles.ts | 20 + .../src/container/LogsContextList/utils.ts | 53 + .../container/LogsError/LogsError.styles.scss | 42 + .../src/container/LogsError/LogsError.tsx | 39 + .../LogsExplorerChart.interfaces.ts | 8 + .../LogsExplorerChart.styled.ts | 14 + .../src/container/LogsExplorerChart/index.tsx | 118 + .../container/LogsExplorerContext/index.tsx | 115 + .../container/LogsExplorerContext/styles.ts | 34 + .../container/LogsExplorerContext/types.ts | 7 + .../LogsExplorerContext/useInitialQuery.ts | 36 + .../container/LogsExplorerContext/utils.ts | 23 + .../InfinityTableView/LogsCustomTable.tsx | 30 + .../InfinityTableView/TableRow.styles.scss | 4 + .../InfinityTableView/TableRow.tsx | 95 + .../InfinityTableView/config.ts | 7 + .../InfinityTableView/index.tsx | 182 + .../InfinityTableView/styles.ts | 74 + .../InfinityTableView/types.ts | 9 + .../LogsExplorerList.interfaces.ts | 12 + .../LogsExplorerList.style.scss | 11 + .../src/container/LogsExplorerList/index.tsx | 194 + .../src/container/LogsExplorerList/styles.ts | 8 + .../src/container/LogsExplorerList/utils.ts | 11 + .../LogsExplorerTable.interfaces.ts | 7 + .../LogsExplorerTable.styles.scss | 58 + .../src/container/LogsExplorerTable/index.tsx | 37 + .../LogsExplorerViews.styled.ts | 7 + .../LogsExplorerViews.styles.scss | 153 + .../src/container/LogsExplorerViews/index.tsx | 648 + .../src/container/LogsFilters/FieldItem.tsx | 84 + frontend/src/container/LogsFilters/config.ts | 8 + frontend/src/container/LogsFilters/index.tsx | 118 + frontend/src/container/LogsFilters/styles.ts | 29 + frontend/src/container/LogsFilters/types.ts | 36 + frontend/src/container/LogsFilters/utils.ts | 103 + .../src/container/LogsIndexToFields/index.tsx | 7 + .../LogsLoading/LogsLoading.styles.scss | 19 + .../src/container/LogsLoading/LogsLoading.tsx | 22 + .../LogsPanelComponent.styles.scss | 88 + .../LogsPanelTable/LogsPanelComponent.tsx | 313 + .../src/container/LogsPanelTable/utils.tsx | 38 + .../SearchFields/ActionBar.tsx | 27 + .../SearchFields/FieldKey.tsx | 19 + .../QueryBuilder/QueryBuilder.tsx | 252 + .../SearchFields/QueryBuilder/styles.ts | 17 + .../SearchFields/QueryBuilder/utils.ts | 23 + .../SearchFields/Suggestions.tsx | 61 + .../LogsSearchFilter/SearchFields/index.tsx | 122 + .../LogsSearchFilter/SearchFields/styles.tsx | 16 + .../LogsSearchFilter/SearchFields/utils.ts | 122 + .../src/container/LogsSearchFilter/index.tsx | 228 + .../src/container/LogsSearchFilter/styles.ts | 14 + .../LogsSearchFilter/useSearchParser.ts | 79 + .../src/container/LogsSearchFilter/utils.ts | 13 + frontend/src/container/LogsTable/index.tsx | 132 + .../container/LogsTable/logsTable.styles.scss | 3 + frontend/src/container/LogsTable/styles.ts | 20 + frontend/src/container/LogsTopNav/index.tsx | 92 + frontend/src/container/LogsTopNav/styles.ts | 19 + .../MetricsApplication.factory.ts | 27 + .../MetricsPageQueries/DBCallQueries.ts | 137 + .../MetricsPageQueries/ExternalQueries.ts | 285 + .../MetricsPageQueriesFactory.ts | 105 + .../MetricsPageQueries/OverviewQueries.ts | 630 + .../MetricsPageQueries/TopOperationQueries.ts | 153 + .../MetricsApplication/Tabs/DBCall.tsx | 172 + .../MetricsApplication/Tabs/External.tsx | 293 + .../MetricsApplication/Tabs/Overview.tsx | 296 + .../Tabs/Overview/ApDex/ApDexMetrics.tsx | 109 + .../ApDex/ApDexMetricsApplication.tsx | 41 + .../Tabs/Overview/ApDex/ApDexTraces.tsx | 64 + .../Tabs/Overview/ApDex/constants.ts | 1 + .../Tabs/Overview/ApDex/index.tsx | 49 + .../Tabs/Overview/ApDex/types.ts | 18 + .../Tabs/Overview/ServiceOverview.tsx | 115 + .../Overview/TableRenderer/ColumnWithLink.tsx | 39 + .../TableRenderer/TableColumnRenderer.tsx | 11 + .../Tabs/Overview/TopLevelOperations.tsx | 56 + .../Tabs/Overview/TopOperation.tsx | 45 + .../Tabs/Overview/TopOperationMetrics.tsx | 121 + .../Tabs/Overview/config.ts | 1 + .../MetricsApplication/Tabs/styles.ts | 16 + .../MetricsApplication/Tabs/types.ts | 75 + .../container/MetricsApplication/Tabs/util.ts | 89 + .../TopOperationsTable.styles.scss | 9 + .../MetricsApplication/TopOperationsTable.tsx | 168 + .../__mocks__/getTopOperation.ts | 19 + .../container/MetricsApplication/constant.ts | 95 + .../container/MetricsApplication/styles.ts | 50 + .../src/container/MetricsApplication/types.ts | 30 + .../MetricsApplication/utils.test.ts | 70 + .../src/container/MetricsApplication/utils.ts | 57 + .../MySettings/MySettings.styles.scss | 11 + .../container/MySettings/Password/index.tsx | 157 + .../MySettings/UserInfo/UserInfo.styles.scss | 7 + .../container/MySettings/UserInfo/index.tsx | 125 + frontend/src/container/MySettings/index.tsx | 84 + frontend/src/container/MySettings/styles.ts | 14 + .../ComponentsSlider/constants.ts | 89 + .../NewDashboard/ComponentsSlider/index.tsx | 158 + .../ComponentsSlider/menuItems.tsx | 39 + .../NewDashboard/ComponentsSlider/styles.ts | 41 + .../DashboardName/index.tsx | 27 + .../Description.styles.scss | 39 + .../DashboardDescription/SettingsDrawer.tsx | 44 + .../DashboardDescription/ShareModal.tsx | 101 + .../DashboardDescription/index.tsx | 130 + .../DashboardDescription/styles.ts | 24 + .../DashboardDescription/utils.ts | 14 + .../DashboardSettings.styles.scss | 5 + .../General/AddTags/index.tsx | 125 + .../General/AddTags/styles.ts | 26 + .../General/Description/index.tsx | 35 + .../General/Description/styles.ts | 5 + .../DashboardSettings/General/index.tsx | 105 + .../DashboardSettings/General/styles.ts | 20 + .../VariableItem/VariableItem.styles.scss | 8 + .../Variables/VariableItem/VariableItem.tsx | 388 + .../Variables/VariableItem/styles.ts | 11 + .../DashboardSettings/Variables/index.tsx | 412 + .../DashboardSettings/Variables/types.ts | 7 + .../NewDashboard/DashboardSettings/index.tsx | 19 + .../DashboardVariableSelection.styles.scss | 16 + .../DashboardVariableSelection.tsx | 123 + .../VariableItem.test.tsx | 156 + .../VariableItem.tsx | 306 + .../DashboardVariablesSelection/index.tsx | 3 + .../DashboardVariablesSelection/styles.ts | 45 + .../DashboardVariablesSelection/util.ts | 31 + .../DashboardVariablesSelection/utils.test.ts | 33 + .../NewDashboard/GridGraphs/index.tsx | 19 + .../NewDashboard/GridGraphs/styles.ts | 28 + frontend/src/container/NewDashboard/index.tsx | 13 + frontend/src/container/NewDashboard/utils.ts | 16 + .../src/container/NewExplorerCTA/config.ts | 11 + .../src/container/NewExplorerCTA/index.tsx | 61 + .../ExplorerColumnsRenderer.styles.scss | 184 + .../LeftContainer/ExplorerColumnsRenderer.tsx | 328 + .../QueryBuilder/QueryHeader.styles.scss | 7 + .../QuerySection/QueryBuilder/QueryHeader.tsx | 68 + .../QueryBuilder/clickHouse/index.tsx | 31 + .../QueryBuilder/clickHouse/query.tsx | 137 + .../QueryBuilder/clickHouse/types.ts | 9 + .../QueryBuilder/promQL/index.tsx | 35 + .../QueryBuilder/promQL/query.tsx | 83 + .../QuerySection/QueryBuilder/promQL/types.ts | 6 + .../QuerySection/QuerySection.styles.scss | 60 + .../LeftContainer/QuerySection/index.tsx | 257 + .../LeftContainer/QuerySection/styles.ts | 43 + .../NewWidget/LeftContainer/QueryTypeTag.tsx | 39 + .../LeftContainer/WidgetGraph/PlotTag.tsx | 24 + .../WidgetGraph/WidgetGraphContainer.tsx | 104 + .../WidgetGraph/WidgetGraphs.tsx | 193 + .../LeftContainer/WidgetGraph/index.tsx | 74 + .../LeftContainer/WidgetGraph/styles.ts | 45 + .../NewWidget/LeftContainer/index.tsx | 51 + .../NewWidget/LeftContainer/styles.ts | 13 + .../Threshold/ColorSelector.styles.scss | 7 + .../Threshold/ColorSelector.tsx | 94 + .../Threshold/CustomColor.styles.scss | 18 + .../RightContainer/Threshold/CustomColor.tsx | 26 + .../Threshold/ShowCaseValue.styles.scss | 14 + .../Threshold/ShowCaseValue.tsx | 23 + .../Threshold/Threshold.styles.scss | 58 + .../RightContainer/Threshold/Threshold.tsx | 356 + .../Threshold/ThresholdSelector.styles.scss | 18 + .../Threshold/ThresholdSelector.tsx | 111 + .../RightContainer/Threshold/types.ts | 38 + .../RightContainer/YAxisUnitSelector.tsx | 54 + .../RightContainer/alertFomatCategories.ts | 131 + .../NewWidget/RightContainer/constants.ts | 94 + .../RightContainer/dataFormatCategories.ts | 440 + .../NewWidget/RightContainer/index.tsx | 243 + .../NewWidget/RightContainer/styles.ts | 39 + .../NewWidget/RightContainer/timeItems.ts | 67 + .../NewWidget/RightContainer/types.ts | 374 + frontend/src/container/NewWidget/index.tsx | 461 + frontend/src/container/NewWidget/styles.ts | 39 + frontend/src/container/NewWidget/types.ts | 25 + frontend/src/container/NewWidget/utils.ts | 15 + .../src/container/NoLogs/NoLogs.styles.scss | 44 + frontend/src/container/NoLogs/NoLogs.tsx | 50 + .../dotnet-kubernetes-installOtelCollector.md | 24 + ...dotnet-kubernetes-instrumentApplication.md | 65 + .../dotnet-kubernetes-runApplication.md | 10 + ...xamd64-quickStart-instrumentApplication.md | 71 + ...et-linuxamd64-quickStart-runApplication.md | 10 + ...xamd64-recommended-installOtelCollector.md | 98 + ...amd64-recommended-instrumentApplication.md | 67 + ...t-linuxamd64-recommended-runApplication.md | 18 + ...xarm64-quickStart-instrumentApplication.md | 70 + ...et-linuxarm64-quickStart-runApplication.md | 10 + ...xarm64-recommended-installOtelCollector.md | 99 + ...arm64-recommended-instrumentApplication.md | 68 + ...t-linuxarm64-recommended-runApplication.md | 18 + ...samd64-quickStart-instrumentApplication.md | 70 + ...et-macosamd64-quickStart-runApplication.md | 10 + ...samd64-recommended-installOtelCollector.md | 97 + ...amd64-recommended-instrumentApplication.md | 67 + ...t-macosamd64-recommended-runApplication.md | 18 + ...sarm64-quickStart-instrumentApplication.md | 70 + ...et-macosarm64-quickStart-runApplication.md | 10 + ...sarm64-recommended-installOtelCollector.md | 98 + ...arm64-recommended-instrumentApplication.md | 68 + ...t-macosarm64-recommended-runApplication.md | 18 + .../elixir-kubernetes-installOtelCollector.md | 24 + ...elixir-kubernetes-instrumentApplication.md | 57 + .../elixir-kubernetes-runApplication.md | 6 + ...xamd64-quickStart-instrumentApplication.md | 62 + ...ir-linuxamd64-quickStart-runApplication.md | 6 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 57 + ...r-linuxamd64-recommended-runApplication.md | 29 + ...xarm64-quickStart-instrumentApplication.md | 62 + ...ir-linuxarm64-quickStart-runApplication.md | 6 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 57 + ...r-linuxarm64-recommended-runApplication.md | 28 + ...samd64-quickStart-instrumentApplication.md | 62 + ...ir-macosamd64-quickStart-runApplication.md | 6 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 57 + ...r-macosamd64-recommended-runApplication.md | 28 + ...sarm64-quickStart-instrumentApplication.md | 62 + ...ir-macosarm64-quickStart-runApplication.md | 6 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 57 + ...r-macosarm64-recommended-runApplication.md | 28 + .../Modules/APM/GoLang/goLang.md | 414 + .../golang-kubernetes-installOtelCollector.md | 24 + ...golang-kubernetes-instrumentApplication.md | 124 + .../golang-kubernetes-runApplication.md | 7 + ...xamd64-quickStart-instrumentApplication.md | 124 + ...ng-linuxamd64-quickStart-runApplication.md | 6 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 124 + ...g-linuxamd64-recommended-runApplication.md | 27 + ...xarm64-quickStart-instrumentApplication.md | 124 + ...ng-linuxarm64-quickStart-runApplication.md | 6 + ...xarm64-recommended-installOtelCollector.md | 99 + ...arm64-recommended-instrumentApplication.md | 124 + ...g-linuxarm64-recommended-runApplication.md | 27 + ...samd64-quickStart-instrumentApplication.md | 124 + ...ng-macosamd64-quickStart-runApplication.md | 6 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 124 + ...g-macosamd64-recommended-runApplication.md | 27 + ...sarm64-quickStart-instrumentApplication.md | 124 + ...ng-macosarm64-quickStart-runApplication.md | 6 + ...sarm64-recommended-installOtelCollector.md | 94 + ...arm64-recommended-instrumentApplication.md | 124 + ...g-macosarm64-recommended-runApplication.md | 27 + .../jboss-kubernetes-installOtelCollector.md | 24 + .../jboss-kubernetes-instrumentApplication.md | 12 + .../jboss-kubernetes-runApplication.md | 20 + ...xamd64-quickStart-instrumentApplication.md | 10 + ...ss-linuxamd64-quickStart-runApplication.md | 28 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 11 + ...s-linuxamd64-recommended-runApplication.md | 38 + ...xarm64-quickStart-instrumentApplication.md | 10 + ...ss-linuxarm64-quickStart-runApplication.md | 28 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 11 + ...s-linuxarm64-recommended-runApplication.md | 38 + ...samd64-quickStart-instrumentApplication.md | 10 + ...ss-macosamd64-quickStart-runApplication.md | 28 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 11 + ...s-macosamd64-recommended-runApplication.md | 38 + ...sarm64-quickStart-instrumentApplication.md | 10 + ...ss-macosarm64-quickStart-runApplication.md | 28 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 11 + ...s-macosarm64-recommended-runApplication.md | 38 + .../others-kubernetes-installOtelCollector.md | 24 + ...others-kubernetes-instrumentApplication.md | 12 + .../others-kubernetes-runApplication.md | 17 + ...xamd64-quickStart-instrumentApplication.md | 10 + ...rs-linuxamd64-quickStart-runApplication.md | 16 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 11 + ...s-linuxamd64-recommended-runApplication.md | 31 + ...xarm64-quickStart-instrumentApplication.md | 10 + ...rs-linuxarm64-quickStart-runApplication.md | 16 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 11 + ...s-linuxarm64-recommended-runApplication.md | 31 + ...samd64-quickStart-instrumentApplication.md | 10 + ...rs-macosamd64-quickStart-runApplication.md | 16 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 11 + ...s-macosamd64-recommended-runApplication.md | 31 + ...sarm64-quickStart-instrumentApplication.md | 10 + ...rs-macosarm64-quickStart-runApplication.md | 16 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 11 + ...s-macosarm64-recommended-runApplication.md | 29 + ...ingBoot-kubernetes-installOtelCollector.md | 24 + ...ngBoot-kubernetes-instrumentApplication.md | 12 + .../springBoot-kubernetes-runApplication.md | 19 + ...xamd64-quickStart-instrumentApplication.md | 12 + ...ot-linuxamd64-quickStart-runApplication.md | 16 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 11 + ...t-linuxamd64-recommended-runApplication.md | 36 + ...xarm64-quickStart-instrumentApplication.md | 12 + ...ot-linuxarm64-quickStart-runApplication.md | 16 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 11 + ...t-linuxarm64-recommended-runApplication.md | 36 + ...samd64-quickStart-instrumentApplication.md | 12 + ...ot-macosamd64-quickStart-runApplication.md | 16 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 11 + ...t-macosamd64-recommended-runApplication.md | 36 + ...sarm64-quickStart-instrumentApplication.md | 12 + ...ot-macosarm64-quickStart-runApplication.md | 16 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 11 + ...t-macosarm64-recommended-runApplication.md | 36 + .../tomcat-kubernetes-installOtelCollector.md | 24 + ...tomcat-kubernetes-instrumentApplication.md | 11 + .../tomcat-kubernetes-runApplication.md | 24 + ...xamd64-quickStart-instrumentApplication.md | 9 + ...at-linuxamd64-quickStart-runApplication.md | 22 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 11 + ...t-linuxamd64-recommended-runApplication.md | 39 + ...xarm64-quickStart-instrumentApplication.md | 9 + ...at-linuxarm64-quickStart-runApplication.md | 22 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 11 + ...t-linuxarm64-recommended-runApplication.md | 39 + ...samd64-quickStart-instrumentApplication.md | 9 + ...at-macosamd64-quickStart-runApplication.md | 22 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 11 + ...t-macosamd64-recommended-runApplication.md | 39 + ...sarm64-quickStart-instrumentApplication.md | 9 + ...at-macosarm64-quickStart-runApplication.md | 22 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 11 + ...t-macosarm64-recommended-runApplication.md | 39 + .../Modules/APM/Java/md-docs/java.md | 83 + .../Modules/APM/Java/md-docs/jboss.md | 116 + .../Modules/APM/Java/md-docs/spring_boot.md | 80 + .../Modules/APM/Java/md-docs/tomcat.md | 95 + ...express-kubernetes-installOtelCollector.md | 24 + ...xpress-kubernetes-instrumentApplication.md | 56 + .../express-kubernetes-runApplication.md | 12 + ...xamd64-quickStart-instrumentApplication.md | 55 + ...ss-linuxamd64-quickStart-runApplication.md | 11 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 53 + ...s-linuxamd64-recommended-runApplication.md | 33 + ...xarm64-quickStart-instrumentApplication.md | 55 + ...ss-linuxarm64-quickStart-runApplication.md | 11 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 53 + ...s-linuxarm64-recommended-runApplication.md | 33 + ...samd64-quickStart-instrumentApplication.md | 55 + ...ss-macosamd64-quickStart-runApplication.md | 11 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 53 + ...s-macosamd64-recommended-runApplication.md | 33 + ...sarm64-quickStart-instrumentApplication.md | 55 + ...ss-macosarm64-quickStart-runApplication.md | 11 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 53 + ...s-macosarm64-recommended-runApplication.md | 33 + .../nestjs-kubernetes-installOtelCollector.md | 24 + ...nestjs-kubernetes-instrumentApplication.md | 87 + .../nestjs-kubernetes-runApplication.md | 9 + ...xamd64-quickStart-instrumentApplication.md | 84 + ...js-linuxamd64-quickStart-runApplication.md | 9 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 87 + ...s-linuxamd64-recommended-runApplication.md | 31 + ...xarm64-quickStart-instrumentApplication.md | 84 + ...js-linuxarm64-quickStart-runApplication.md | 9 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 87 + ...s-linuxarm64-recommended-runApplication.md | 31 + ...samd64-quickStart-instrumentApplication.md | 84 + ...js-macosamd64-quickStart-runApplication.md | 9 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 87 + ...s-macosamd64-recommended-runApplication.md | 31 + ...sarm64-quickStart-instrumentApplication.md | 84 + ...js-macosarm64-quickStart-runApplication.md | 9 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 87 + ...s-macosarm64-recommended-runApplication.md | 31 + .../nodejs-kubernetes-installOtelCollector.md | 24 + ...nodejs-kubernetes-instrumentApplication.md | 56 + .../nodejs-kubernetes-runApplication.md | 9 + ...xamd64-quickStart-instrumentApplication.md | 57 + ...js-linuxamd64-quickStart-runApplication.md | 9 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 57 + ...s-linuxamd64-recommended-runApplication.md | 32 + ...xarm64-quickStart-instrumentApplication.md | 57 + ...js-linuxarm64-quickStart-runApplication.md | 9 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 57 + ...s-linuxarm64-recommended-runApplication.md | 32 + ...samd64-quickStart-instrumentApplication.md | 57 + ...js-macosamd64-quickStart-runApplication.md | 9 + ...samd64-recommended-installOtelCollector.md | 87 + ...amd64-recommended-instrumentApplication.md | 57 + ...s-macosamd64-recommended-runApplication.md | 32 + ...sarm64-quickStart-instrumentApplication.md | 57 + ...js-macosarm64-quickStart-runApplication.md | 9 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 57 + ...s-macosarm64-recommended-runApplication.md | 32 + .../others-kubernetes-installOtelCollector.md | 24 + ...others-kubernetes-instrumentApplication.md | 68 + .../others-kubernetes-runApplication.md | 18 + ...xamd64-quickStart-instrumentApplication.md | 71 + ...rs-linuxamd64-quickStart-runApplication.md | 18 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 68 + ...s-linuxamd64-recommended-runApplication.md | 43 + ...xarm64-quickStart-instrumentApplication.md | 71 + ...rs-linuxarm64-quickStart-runApplication.md | 18 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 68 + ...s-linuxarm64-recommended-runApplication.md | 43 + ...samd64-quickStart-instrumentApplication.md | 71 + ...rs-macosamd64-quickStart-runApplication.md | 18 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 68 + ...s-macosamd64-recommended-runApplication.md | 43 + ...sarm64-quickStart-instrumentApplication.md | 71 + ...rs-macosarm64-quickStart-runApplication.md | 18 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 68 + ...s-macosarm64-recommended-runApplication.md | 43 + ...reactjs-kubernetes-installOtelCollector.md | 24 + ...eactjs-kubernetes-instrumentApplication.md | 68 + .../reactjs-kubernetes-runApplication.md | 18 + ...xamd64-quickStart-instrumentApplication.md | 71 + ...js-linuxamd64-quickStart-runApplication.md | 18 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 68 + ...s-linuxamd64-recommended-runApplication.md | 43 + ...xarm64-quickStart-instrumentApplication.md | 71 + ...js-linuxarm64-quickStart-runApplication.md | 18 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 68 + ...s-linuxarm64-recommended-runApplication.md | 43 + ...samd64-quickStart-instrumentApplication.md | 71 + ...js-macosamd64-quickStart-runApplication.md | 18 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 68 + ...s-macosamd64-recommended-runApplication.md | 43 + ...sarm64-quickStart-instrumentApplication.md | 71 + ...js-macosarm64-quickStart-runApplication.md | 18 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 68 + ...s-macosarm64-recommended-runApplication.md | 43 + .../Modules/APM/Javascript/md-docs/express.md | 201 + .../APM/Javascript/md-docs/javascript.md | 201 + .../Modules/APM/Javascript/md-docs/nestjs.md | 285 + .../django-kubernetes-installOtelCollector.md | 24 + ...django-kubernetes-instrumentApplication.md | 37 + .../django-kubernetes-runApplication.md | 23 + ...xamd64-quickStart-instrumentApplication.md | 35 + ...go-linuxamd64-quickStart-runApplication.md | 20 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 37 + ...o-linuxamd64-recommended-runApplication.md | 44 + ...xarm64-quickStart-instrumentApplication.md | 35 + ...go-linuxarm64-quickStart-runApplication.md | 20 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 37 + ...o-linuxarm64-recommended-runApplication.md | 44 + ...samd64-quickStart-instrumentApplication.md | 35 + ...go-macosamd64-quickStart-runApplication.md | 20 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 37 + ...o-macosamd64-recommended-runApplication.md | 44 + ...sarm64-quickStart-instrumentApplication.md | 35 + ...go-macosarm64-quickStart-runApplication.md | 20 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 37 + ...o-macosarm64-recommended-runApplication.md | 44 + .../falcon-kubernetes-installOtelCollector.md | 24 + ...falcon-kubernetes-instrumentApplication.md | 35 + .../falcon-kubernetes-runApplication.md | 22 + ...xamd64-quickStart-instrumentApplication.md | 32 + ...on-linuxamd64-quickStart-runApplication.md | 21 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 35 + ...n-linuxamd64-recommended-runApplication.md | 41 + ...xarm64-quickStart-instrumentApplication.md | 32 + ...on-linuxarm64-quickStart-runApplication.md | 21 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 35 + ...n-linuxarm64-recommended-runApplication.md | 41 + ...samd64-quickStart-instrumentApplication.md | 32 + ...on-macosamd64-quickStart-runApplication.md | 21 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 35 + ...n-macosamd64-recommended-runApplication.md | 41 + ...sarm64-quickStart-instrumentApplication.md | 32 + ...on-macosarm64-quickStart-runApplication.md | 21 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 35 + ...n-macosarm64-recommended-runApplication.md | 42 + ...fastapi-kubernetes-installOtelCollector.md | 24 + ...astapi-kubernetes-instrumentApplication.md | 36 + .../fastapi-kubernetes-runApplication.md | 20 + ...xamd64-quickStart-instrumentApplication.md | 34 + ...pi-linuxamd64-quickStart-runApplication.md | 20 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 34 + ...i-linuxamd64-recommended-runApplication.md | 42 + ...xarm64-quickStart-instrumentApplication.md | 34 + ...pi-linuxarm64-quickStart-runApplication.md | 20 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 34 + ...i-linuxarm64-recommended-runApplication.md | 42 + ...samd64-quickStart-instrumentApplication.md | 34 + ...pi-macosamd64-quickStart-runApplication.md | 20 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 34 + ...i-macosamd64-recommended-runApplication.md | 42 + ...sarm64-quickStart-instrumentApplication.md | 34 + ...pi-macosarm64-quickStart-runApplication.md | 20 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 34 + ...i-macosarm64-recommended-runApplication.md | 42 + .../flask-kubernetes-installOtelCollector.md | 24 + .../flask-kubernetes-instrumentApplication.md | 36 + .../flask-kubernetes-runApplication.md | 22 + ...xamd64-quickStart-instrumentApplication.md | 35 + ...sk-linuxamd64-quickStart-runApplication.md | 22 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 34 + ...k-linuxamd64-recommended-runApplication.md | 43 + ...xarm64-quickStart-instrumentApplication.md | 35 + ...sk-linuxarm64-quickStart-runApplication.md | 22 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 34 + ...k-linuxarm64-recommended-runApplication.md | 43 + ...samd64-quickStart-instrumentApplication.md | 35 + ...sk-macosamd64-quickStart-runApplication.md | 22 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 34 + ...k-macosamd64-recommended-runApplication.md | 43 + ...sarm64-quickStart-instrumentApplication.md | 35 + ...sk-macosarm64-quickStart-runApplication.md | 22 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 34 + ...k-macosarm64-recommended-runApplication.md | 43 + .../others-kubernetes-installOtelCollector.md | 24 + ...others-kubernetes-instrumentApplication.md | 35 + .../others-kubernetes-runApplication.md | 20 + ...xamd64-quickStart-instrumentApplication.md | 34 + ...rs-linuxamd64-quickStart-runApplication.md | 20 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 30 + ...s-linuxamd64-recommended-runApplication.md | 40 + ...xarm64-quickStart-instrumentApplication.md | 34 + ...rs-linuxarm64-quickStart-runApplication.md | 20 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 30 + ...s-linuxarm64-recommended-runApplication.md | 40 + ...samd64-quickStart-instrumentApplication.md | 34 + ...rs-macosamd64-quickStart-runApplication.md | 20 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 30 + ...s-macosamd64-recommended-runApplication.md | 40 + ...sarm64-quickStart-instrumentApplication.md | 34 + ...rs-macosarm64-quickStart-runApplication.md | 20 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 30 + ...s-macosarm64-recommended-runApplication.md | 40 + .../Modules/APM/Python/md-docs/django.md | 155 + .../Modules/APM/Python/md-docs/falcon.md | 154 + .../Modules/APM/Python/md-docs/fastAPI.md | 147 + .../Modules/APM/Python/md-docs/flask.md | 153 + .../Modules/APM/Python/md-docs/python.md | 118 + .../Modules/APM/RubyOnRails/RubyOnRails.md | 178 + .../ror-kubernetes-installOtelCollector.md | 24 + .../ror-kubernetes-instrumentApplication.md | 40 + .../ror-kubernetes-runApplication.md | 10 + ...xamd64-quickStart-instrumentApplication.md | 38 + ...or-linuxamd64-quickStart-runApplication.md | 11 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 42 + ...r-linuxamd64-recommended-runApplication.md | 39 + ...xarm64-quickStart-instrumentApplication.md | 38 + ...or-linuxarm64-quickStart-runApplication.md | 11 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 42 + ...r-linuxarm64-recommended-runApplication.md | 39 + ...samd64-quickStart-instrumentApplication.md | 38 + ...or-macosamd64-quickStart-runApplication.md | 11 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 42 + ...r-macosamd64-recommended-runApplication.md | 39 + ...sarm64-quickStart-instrumentApplication.md | 38 + ...or-macosarm64-quickStart-runApplication.md | 11 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 42 + ...r-macosarm64-recommended-runApplication.md | 39 + .../rust-kubernetes-installOtelCollector.md | 24 + .../rust-kubernetes-instrumentApplication.md | 95 + .../rust-kubernetes-runApplication.md | 7 + ...xamd64-quickStart-instrumentApplication.md | 133 + ...st-linuxamd64-quickStart-runApplication.md | 7 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 95 + ...t-linuxamd64-recommended-runApplication.md | 32 + ...xarm64-quickStart-instrumentApplication.md | 133 + ...st-linuxarm64-quickStart-runApplication.md | 7 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 95 + ...t-linuxarm64-recommended-runApplication.md | 32 + ...samd64-quickStart-instrumentApplication.md | 133 + ...st-macosamd64-quickStart-runApplication.md | 7 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 95 + ...t-macosamd64-recommended-runApplication.md | 32 + ...sarm64-quickStart-instrumentApplication.md | 133 + ...st-macosarm64-quickStart-runApplication.md | 7 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 95 + ...t-macosarm64-recommended-runApplication.md | 32 + .../swift-kubernetes-installOtelCollector.md | 24 + .../swift-kubernetes-instrumentApplication.md | 68 + .../swift-kubernetes-runApplication.md | 7 + ...xamd64-quickStart-instrumentApplication.md | 65 + ...ft-linuxamd64-quickStart-runApplication.md | 7 + ...xamd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 70 + ...t-linuxamd64-recommended-runApplication.md | 32 + ...xarm64-quickStart-instrumentApplication.md | 64 + ...ft-linuxarm64-quickStart-runApplication.md | 7 + ...xarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 68 + ...t-linuxarm64-recommended-runApplication.md | 32 + ...samd64-quickStart-instrumentApplication.md | 64 + ...ft-macosamd64-quickStart-runApplication.md | 7 + ...samd64-recommended-installOtelCollector.md | 96 + ...amd64-recommended-instrumentApplication.md | 69 + ...t-macosamd64-recommended-runApplication.md | 32 + ...sarm64-quickStart-instrumentApplication.md | 64 + ...ft-macosarm64-quickStart-runApplication.md | 7 + ...sarm64-recommended-installOtelCollector.md | 96 + ...arm64-recommended-instrumentApplication.md | 66 + ...t-macosarm64-recommended-runApplication.md | 32 + ...cationLogs-linuxamd64-configureReceiver.md | 31 + ...ionLogs-linuxamd64-installOtelCollector.md | 113 + ...icationLogs-linuxamd64-runOtelCollector.md | 15 + ...cationLogs-linuxarm64-configureReceiver.md | 31 + ...ionLogs-linuxarm64-installOtelCollector.md | 114 + ...icationLogs-linuxarm64-runOtelCollector.md | 15 + ...cationLogs-macosamd64-configureReceiver.md | 31 + ...ionLogs-macosamd64-installOtelCollector.md | 113 + ...icationLogs-macosamd64-runOtelCollector.md | 15 + ...cationLogs-macosarm64-configureReceiver.md | 31 + ...ionLogs-macosarm64-installOtelCollector.md | 113 + ...icationLogs-macosarm64-runOtelCollector.md | 15 + .../hostmetrics-configureHostmetricsJson.md | 15 + .../hostmetrics-setupOtelCollector.md | 113 + .../hostmetrics-configureHostmetricsJson.md | 15 + .../hostmetrics-setupOtelCollector.md | 114 + .../hostmetrics-configureHostmetricsJson.md | 15 + .../hostmetrics-setupOtelCollector.md | 113 + .../hostmetrics-configureHostmetricsJson.md | 15 + .../hostmetrics-setupOtelCollector.md | 112 + .../md-docs/ecsEc2-createDaemonService.md | 68 + .../ECSEc2/md-docs/ecsEc2-createOtelConfig.md | 28 + .../ECSEc2/md-docs/ecsEc2-sendData.md | 90 + .../md-docs/ecsEc2-setupDeamonService.md | 21 + .../ecsExternal-createDaemonService.md | 68 + .../md-docs/ecsExternal-createOtelConfig.md | 28 + .../md-docs/ecsExternal-sendData.md | 106 + .../md-docs/ecsExternal-setupDeamonService.md | 21 + .../md-docs/ecsFargate-createOtelConfig.md | 30 + ...Fargate-createSidecarCollectorContainer.md | 143 + .../ecsFargate-deployTaskDefinition.md | 10 + .../ECSFargate/md-docs/ecsFargate-sendData.md | 84 + .../ECSFargate/md-docs/ecsFargate-sendLogs.md | 133 + .../EKS/eks-installOtelCollector.md | 24 + .../EKS/eks-monitorUsingDashboard.md | 16 + .../hostmetrics-configureHostmetricsJson.md | 13 + .../hostmetrics-setupOtelCollector.md | 108 + .../hostmetrics-configureHostmetricsJson.md | 13 + .../hostmetrics-setupOtelCollector.md | 109 + .../hostmetrics-configureHostmetricsJson.md | 13 + .../hostmetrics-setupOtelCollector.md | 108 + .../hostmetrics-configureHostmetricsJson.md | 13 + .../hostmetrics-setupOtelCollector.md | 107 + .../md-docs/kubernetes-plotMetrics.md | 4 + .../md-docs/kubernetes-setupOtelCollector.md | 21 + .../otherMetrics-configureReceiver.md | 101 + .../otherMetrics-setupOtelCollector.md | 108 + .../otherMetrics-configureReceiver.md | 101 + .../otherMetrics-setupOtelCollector.md | 109 + .../otherMetrics-configureReceiver.md | 101 + .../otherMetrics-setupOtelCollector.md | 108 + .../otherMetrics-configureReceiver.md | 101 + .../otherMetrics-setupOtelCollector.md | 108 + .../applicationLogsFromLogFile.md | 39 + .../applicationLogsUsingJavaOtelSDK.md | 33 + .../applicationLogsUsingPythonOtelSDK.md | 3 + ...cationLogs-linuxamd64-configureReceiver.md | 28 + ...ionLogs-linuxamd64-installOtelCollector.md | 108 + ...icationLogs-linuxamd64-runOtelCollector.md | 15 + ...cationLogs-linuxarm64-configureReceiver.md | 28 + ...ionLogs-linuxarm64-installOtelCollector.md | 109 + ...icationLogs-linuxarm64-runOtelCollector.md | 15 + ...cationLogs-macosamd64-configureReceiver.md | 28 + ...ionLogs-macosamd64-installOtelCollector.md | 108 + ...icationLogs-macosamd64-runOtelCollector.md | 15 + ...cationLogs-macosarm64-configureReceiver.md | 28 + ...ionLogs-macosarm64-installOtelCollector.md | 108 + ...icationLogs-macosarm64-runOtelCollector.md | 15 + .../cloudwatch-linuxamd64-configureAws.md | 39 + ...cloudwatch-linuxamd64-configureReceiver.md | 50 + ...udwatch-linuxamd64-installOtelCollector.md | 94 + .../cloudwatch-linuxamd64-sendLogs.md | 38 + .../cloudwatch-linuxarm64-configureAws.md | 39 + ...cloudwatch-linuxarm64-configureReceiver.md | 50 + ...udwatch-linuxarm64-installOtelCollector.md | 95 + .../cloudwatch-linuxarm64-sendLogs.md | 38 + .../cloudwatch-macosamd64-configureAws.md | 39 + ...cloudwatch-macosamd64-configureReceiver.md | 50 + ...udwatch-macosamd64-installOtelCollector.md | 94 + .../cloudwatch-macosamd64-sendLogs.md | 38 + .../cloudwatch-macosarm64-configureAws.md | 39 + ...cloudwatch-macosarm64-configureReceiver.md | 50 + ...udwatch-macosarm64-installOtelCollector.md | 93 + .../cloudwatch-macosarm64-sendLogs.md | 38 + .../Modules/LogsManagement/Docker/docker.md | 30 + .../Docker/md-docs/docker-cloneRepository.md | 18 + .../Docker/md-docs/docker-startContainers.md | 6 + .../fluentbit-linuxamd64-configureReceiver.md | 37 + ...uentbit-linuxamd64-installOtelCollector.md | 108 + ...uentbit-linuxamd64-restartOtelCollector.md | 17 + .../fluentbit-linuxarm64-configureReceiver.md | 37 + ...uentbit-linuxarm64-installOtelCollector.md | 109 + ...uentbit-linuxarm64-restartOtelCollector.md | 17 + .../fluentbit-macosamd64-configureReceiver.md | 37 + ...uentbit-macosamd64-installOtelCollector.md | 108 + ...uentbit-macosamd64-restartOtelCollector.md | 17 + ...uentBit-macosarm64-restartOtelCollector.md | 17 + .../fluentbit-macosarm64-configureReceiver.md | 37 + ...uentbit-macosarm64-installOtelCollector.md | 107 + .../fluentd-linuxamd64-configureReceiver.md | 44 + ...fluentd-linuxamd64-installOtelCollector.md | 108 + ...fluentd-linuxamd64-restartOtelCollector.md | 17 + .../fluentd-linuxarm64-configureReceiver.md | 44 + ...fluentd-linuxarm64-installOtelCollector.md | 109 + ...fluentd-linuxarm64-restartOtelCollector.md | 17 + .../fluentd-macosamd64-configureReceiver.md | 44 + ...fluentd-macosamd64-installOtelCollector.md | 108 + ...fluentd-macosamd64-restartOtelCollector.md | 17 + .../fluentd-macosarm64-configureReceiver.md | 44 + ...fluentd-macosarm64-installOtelCollector.md | 107 + ...fluentd-macosarm64-restartOtelCollector.md | 17 + .../Heroku/md-docs/heroku-addHttpDrain.md | 29 + .../Http/md-docs/httpJsonPayload.md | 95 + .../LogsManagement/Kubernetes/kubernetes.md | 6 + .../kubernetes-installOtelCollector.md | 24 + .../logstash-linuxamd64-configureReceiver.md | 45 + ...ogstash-linuxamd64-installOtelCollector.md | 114 + ...ogstash-linuxamd64-restartOtelCollector.md | 20 + .../logstash-linuxarm64-configureReceiver.md | 45 + ...ogstash-linuxarm64-installOtelCollector.md | 113 + ...ogstash-linuxarm64-restartOtelCollector.md | 20 + .../logstash-macosamd64-configureReceiver.md | 45 + ...ogstash-macosamd64-installOtelCollector.md | 113 + ...ogstash-macosamd64-restartOtelCollector.md | 20 + .../logstash-macosarm64-configureReceiver.md | 45 + ...ogstash-macosarm64-installOtelCollector.md | 112 + ...ogstash-macosarm64-restartOtelCollector.md | 20 + .../Modules/LogsManagement/Nodejs/nodejs.md | 29 + .../syslogs-linuxamd64-checkServiceStatus.md | 11 + .../syslogs-linuxamd64-configureReceiver.md | 65 + ...syslogs-linuxamd64-installOtelCollector.md | 108 + .../syslogs-linuxarm64-checkServiceStatus.md | 11 + .../syslogs-linuxarm64-configureReceiver.md | 65 + ...syslogs-linuxarm64-installOtelCollector.md | 109 + .../syslogs-macosamd64-checkServiceStatus.md | 11 + .../syslogs-macosamd64-configureReceiver.md | 65 + ...syslogs-macosamd64-installOtelCollector.md | 108 + .../syslogs-macosarm64-checkServiceStatus.md | 11 + .../syslogs-macosarm64-configureReceiver.md | 65 + ...syslogs-macosarm64-installOtelCollector.md | 107 + .../Modules/LogsManagement/SysLogs/syslogs.md | 75 + .../Vercel/md-docs/vercel-setupLogDrains.md | 50 + .../Onboarding.styles.scss | 139 + .../OnboardingContainer.tsx | 330 + .../ConnectionStatus.styles.scss | 96 + .../ConnectionStatus/ConnectionStatus.tsx | 286 + .../Steps/DataSource/DataSource.styles.scss | 38 + .../Steps/DataSource/DataSource.tsx | 167 + .../EnvironmentDetails/EnvironmentDetails.tsx | 90 + .../LogsConnectionStatus.styles.scss | 98 + .../LogsConnectionStatus.tsx | 276 + .../Steps/MarkdownStep/MarkdownStep.tsx | 94 + .../Steps/SelectMethod/SelectMethod.tsx | 37 + .../common/Header/Header.tsx | 33 + .../ModuleStepsContainer.styles.scss | 200 + .../ModuleStepsContainer.tsx | 459 + .../constants/apmDocFilePaths.ts | 1549 ++ .../constants/awsMonitoringDocFilePaths.ts | 127 + .../constants/infraMonitoringDocFilePaths.ts | 90 + .../constants/logsManagementDocFilePaths.ts | 304 + .../constants/stepsConfig.tsx | 203 + .../context/OnboardingContext.tsx | 153 + .../container/OnboardingContainer/index.tsx | 3 + .../OnboardingContainer/typings.d.ts | 4 + .../utils/dataSourceUtils.ts | 339 + .../OnboardingContainer/utils/getSteps.ts | 151 + .../OptionsMenu/AddColumnField/index.tsx | 59 + .../OptionsMenu/AddColumnField/styles.ts | 39 + .../OptionsMenu/FormatField/index.tsx | 44 + .../OptionsMenu/FormatField/styles.ts | 17 + .../OptionsMenu/MaxLinesField/index.tsx | 29 + .../OptionsMenu/MaxLinesField/styles.ts | 13 + .../src/container/OptionsMenu/constants.ts | 9 + frontend/src/container/OptionsMenu/index.tsx | 60 + frontend/src/container/OptionsMenu/styles.ts | 19 + frontend/src/container/OptionsMenu/types.ts | 29 + .../container/OptionsMenu/useOptionsMenu.ts | 310 + frontend/src/container/OptionsMenu/utils.ts | 16 + .../AuthDomains/AddDomain/index.tsx | 114 + .../AuthDomains/Create/Row/index.tsx | 39 + .../AuthDomains/Create/Row/styles.ts | 16 + .../AuthDomains/Create/index.tsx | 90 + .../AuthDomains/Create/styles.ts | 14 + .../AuthDomains/Edit/EditGoogleAuth.tsx | 49 + .../AuthDomains/Edit/EditSAML.tsx | 43 + .../AuthDomains/Edit/helpers.ts | 40 + .../AuthDomains/Edit/index.tsx | 102 + .../AuthDomains/Switch/index.tsx | 46 + .../AuthDomains/helpers.test.ts | 74 + .../AuthDomains/helpers.ts | 45 + .../AuthDomains/index.tsx | 338 + .../AuthDomains/styles.ts | 14 + .../DeleteMembersDetails/index.tsx | 32 + .../DisplayName/index.tsx | 116 + .../EditMembersDetails/index.tsx | 179 + .../EditMembersDetails/styles.ts | 16 + .../InviteTeamMembers/index.tsx | 79 + .../InviteTeamMembers/styles.ts | 21 + .../OrganizationSettings/Members/index.tsx | 341 + .../PendingInvitesContainer/index.tsx | 333 + .../PendingInvitesContainer/styles.tsx | 8 + .../container/OrganizationSettings/index.tsx | 48 + .../PageSizeSelect.interfaces.ts | 4 + .../src/container/PageSizeSelect/index.tsx | 51 + .../Layouts/ChangeHistory/DeploymentStage.tsx | 14 + .../Layouts/ChangeHistory/DeploymentTime.tsx | 9 + .../Layouts/ChangeHistory/index.tsx | 25 + .../Layouts/ChangeHistory/utils.tsx | 44 + .../Layouts/Pipeline/CreatePipelineButton.tsx | 77 + .../Pipeline/PipelinesSearchSection.tsx | 31 + .../PipelinePage/Layouts/Pipeline/index.tsx | 39 + .../container/PipelinePage/Layouts/config.ts | 3 + .../container/PipelinePage/Layouts/utils.ts | 4 + .../FormFields/DescriptionTextArea.tsx | 27 + .../FormFields/FilterInput/index.tsx | 87 + .../FormFields/FilterInput/styles.scss | 3 + .../AddNewPipeline/FormFields/NameInput.tsx | 27 + .../FormFields/ProcessorTags.tsx | 36 + .../AddNewPipeline/index.tsx | 150 + .../AddNewPipeline/styles.ts | 7 + .../AddNewPipeline/utils.tsx | 7 + .../AddNewProcessor/FormFields/CSVInput.tsx | 25 + .../AddNewProcessor/FormFields/NameInput.tsx | 37 + .../FormFields/ParsingRulesTextArea.tsx | 33 + .../AddNewProcessor/FormFields/TypeSelect.tsx | 44 + .../AddNewProcessor/ProcessorForm.tsx | 107 + .../AddNewProcessor/config.ts | 460 + .../AddNewProcessor/index.tsx | 210 + .../AddNewProcessor/styles.scss | 27 + .../AddNewProcessor/styles.ts | 46 + .../ModeAndConfiguration.tsx | 24 + .../PipelineListsView/PipelineExpandView.tsx | 276 + .../PipelineListsView/PipelineListsView.tsx | 550 + .../Preview/LogsFilterPreview/index.tsx | 43 + .../Preview/LogsFilterPreview/styles.scss | 18 + .../components/LogsProcessingSimulator.tsx | 39 + .../PipelineSimulationResult/index.tsx | 47 + .../PipelineSimulationResult/styles.scss | 3 + .../PipelineProcessingPreview/index.tsx | 55 + .../PipelineProcessingPreview/styles.scss | 19 + .../Preview/components/LogsList/index.tsx | 54 + .../Preview/components/LogsList/styles.scss | 46 + .../components/LogsCountInInterval/index.tsx | 59 + .../LogsCountInInterval/styles.scss | 3 + .../PreviewIntervalSelector/index.tsx | 45 + .../PreviewIntervalSelector/styles.scss | 4 + .../SampleLogs/SampleLogsResponseDisplay.tsx | 32 + .../Preview/components/SampleLogs/index.tsx | 16 + .../Preview/components/SampleLogs/styles.scss | 7 + .../Preview/hooks/usePipelinePreview.ts | 60 + .../Preview/hooks/useSampleLogs.ts | 73 + .../PipelineListsView/SaveConfigButton.tsx | 37 + .../TableComponents/DragAction.tsx | 21 + .../components/PreviewAction.tsx | 54 + .../TableComponents/PipelineActions/index.tsx | 27 + .../PipelineFilterSummary/index.tsx | 24 + .../PipelineFilterSummary/styles.scss | 10 + .../TableComponents/ProcessorActions.tsx | 21 + .../TableActions/DeleteAction.tsx | 23 + .../TableActions/EditAction.tsx | 23 + .../TableComponents/TableExpandIcon.tsx | 28 + .../TableComponents/Tags.tsx | 19 + .../TableComponents/index.tsx | 45 + .../PipelinePage/PipelineListsView/config.ts | 132 + .../PipelinePage/PipelineListsView/index.tsx | 3 + .../PipelineListsView/styles.scss | 13 + .../PipelinePage/PipelineListsView/styles.ts | 114 + .../PipelinePage/PipelineListsView/utils.tsx | 95 + .../PipelinePage/components/TagInput.tsx | 157 + .../PipelinePage/components/styles.ts | 6 + frontend/src/container/PipelinePage/config.ts | 7 + .../container/PipelinePage/mocks/pipeline.ts | 181 + frontend/src/container/PipelinePage/styles.ts | 32 + .../tests/AddNewPipeline.test.tsx | 65 + .../tests/AddNewProcessor.test.tsx | 46 + .../tests/CreatePipelineButton.test.tsx | 29 + .../PipelinePage/tests/DeleteAction.test.tsx | 22 + .../PipelinePage/tests/DragAction.test.tsx | 22 + .../PipelinePage/tests/EditAction.test.tsx | 22 + .../tests/PipelineActions.test.tsx | 28 + .../tests/PipelineExpandView.test.tsx | 38 + .../tests/PipelinePageLayout.test.tsx | 62 + .../tests/PipelinesSearchSection.test.tsx | 23 + .../tests/TableExpandIcon.test.tsx | 27 + .../PipelinePage/tests/TagInput.test.tsx | 23 + .../PipelinePage/tests/Tags.test.tsx | 24 + .../AddNewPipeline.test.tsx.snap | 3 + .../AddNewProcessor.test.tsx.snap | 5 + .../CreatePipelineButton.test.tsx.snap | 81 + .../__snapshots__/DeleteAction.test.tsx.snap | 27 + .../__snapshots__/DragAction.test.tsx.snap | 66 + .../__snapshots__/EditAction.test.tsx.snap | 27 + .../PipelineActions.test.tsx.snap | 85 + .../PipelineExpandView.test.tsx.snap | 141 + .../PipelinePageLayout.test.tsx.snap | 136 + .../PipelinesSearchSection.test.tsx.snap | 46 + .../TableExpandIcon.test.tsx.snap | 26 + .../__snapshots__/TagInput.test.tsx.snap | 32 + .../tests/__snapshots__/Tags.test.tsx.snap | 18 + .../PipelinePage/tests/utils.test.ts | 88 + .../QueryBuilder/QueryBuilder.interfaces.ts | 32 + .../QueryBuilder/QueryBuilder.styles.scss | 220 + .../container/QueryBuilder/QueryBuilder.tsx | 263 + .../AdditionalFiltersToggler.interfaces.ts | 6 + .../AdditionalFiltersToggler.styled.ts | 37 + .../AdditionalFiltersToggler.styles.scss | 15 + .../AdditionalFiltersToggler.tsx | 65 + .../AdditionalFiltersToggler/index.ts | 1 + .../DataSourceDropdown.interfaces.ts | 7 + .../DataSourceDropdown/DataSourceDropdown.tsx | 39 + .../components/DataSourceDropdown/index.ts | 1 + .../FilterLabel/FilterLabel.interfaces.ts | 6 + .../FilterLabel/FilterLabel.styled.ts | 14 + .../components/FilterLabel/FilterLabel.tsx | 26 + .../components/FilterLabel/index.ts | 1 + .../components/Formula/Formula.interfaces.ts | 13 + .../components/Formula/Formula.styles.scss | 4 + .../components/Formula/Formula.tsx | 194 + .../QueryBuilder/components/Formula/index.ts | 1 + .../HavingFilterTag.interfaces.ts | 12 + .../HavingFilterTag/HavingFilterTag.styled.ts | 13 + .../HavingFilterTag/HavingFilterTag.tsx | 21 + .../components/HavingFilterTag/index.ts | 1 + .../ListItemWrapper.interfaces.ts | 6 + .../ListItemWrapper/ListItemWrapper.styled.ts | 26 + .../ListItemWrapper/ListItemWrapper.tsx | 16 + .../components/ListItemWrapper/index.ts | 1 + .../ListMarker/ListMarker.interfaces.ts | 11 + .../ListMarker/ListMarker.styled.ts | 12 + .../components/ListMarker/ListMarker.tsx | 39 + .../components/ListMarker/index.ts | 1 + .../QBEntityOptions.styles.scss | 88 + .../QBEntityOptions/QBEntityOptions.tsx | 136 + .../components/Query/Query.interfaces.ts | 12 + .../components/Query/Query.styles.scss | 8 + .../QueryBuilder/components/Query/Query.tsx | 531 + .../QueryBuilder/components/Query/index.ts | 1 + .../components/QueryFunctions/Function.tsx | 89 + .../QueryFunctions/QueryFunctions.styles.scss | 151 + .../QueryFunctions/QueryFunctions.tsx | 181 + .../SpaceAggregationOptions.tsx | 67 + .../ToolbarActions/LeftToolbarActions.tsx | 85 + .../ToolbarActions/RightToolbarActions.tsx | 38 + .../ToolbarActions/ToolbarActions.styles.scss | 71 + .../QueryBuilder/components/index.ts | 8 + .../filters/AggregateEveryFilter/index.tsx | 45 + .../AggregatorFilter.intefaces.ts | 8 + .../AggregatorFilter/AggregatorFilter.tsx | 205 + .../filters/AggregatorFilter/index.ts | 1 + .../BuilderUnitsFilter/BuilderUnits.tsx | 51 + .../filters/BuilderUnitsFilter/config.ts | 10 + .../filters/BuilderUnitsFilter/index.ts | 1 + .../filters/BuilderUnitsFilter/styles.ts | 11 + .../filters/BuilderUnitsFilter/types.ts | 4 + .../filters/BuilderUnitsFilter/utils.ts | 6 + .../filters/Formula/Having/HavingFilter.tsx | 198 + .../filters/Formula/Having/types.ts | 12 + .../filters/Formula/Limit/Limit.tsx | 20 + .../filters/Formula/Limit/types.ts | 6 + .../filters/Formula/OrderBy/OrderByFilter.tsx | 84 + .../filters/Formula/OrderBy/types.ts | 12 + .../OrderBy/useOrderByFormulaFilter.tsx | 127 + .../filters/Formula/OrderBy/utils.ts | 26 + .../GroupByFilter/GroupByFilter.interfaces.ts | 8 + .../filters/GroupByFilter/GroupByFilter.tsx | 207 + .../filters/GroupByFilter/index.ts | 1 + .../filters/GroupByFilter/utils.ts | 14 + .../HavingFilter/HavingFilter.interfaces.ts | 7 + .../filters/HavingFilter/HavingFilter.tsx | 236 + .../HavingFilter/__tests__/utils.test.tsx | 181 + .../filters/HavingFilter/index.ts | 1 + .../filters/LimitFilter/LimitFilter.tsx | 31 + .../OperatorsSelect.interfaces.ts | 8 + .../OperatorsSelect/OperatorsSelect.tsx | 25 + .../filters/OperatorsSelect/index.ts | 1 + .../OrderByFilter/OrderByFilter.interfaces.ts | 18 + .../filters/OrderByFilter/OrderByFilter.tsx | 80 + .../filters/OrderByFilter/config.ts | 4 + .../filters/OrderByFilter/constants.ts | 1 + .../filters/OrderByFilter/index.ts | 1 + .../filters/OrderByFilter/useOrderByFilter.ts | 148 + .../filters/OrderByFilter/utils.ts | 77 + .../QueryBuilderSearch/OptionRenderer.tsx | 48 + .../QueryBuilderSearch.styles.scss | 24 + .../filters/QueryBuilderSearch/config.ts | 1 + .../filters/QueryBuilderSearch/constant.ts | 2 + .../filters/QueryBuilderSearch/index.tsx | 305 + .../filters/QueryBuilderSearch/style.ts | 35 + .../filters/QueryBuilderSearch/utils.ts | 164 + .../ReduceToFilter.interfaces.ts | 8 + .../filters/ReduceToFilter/ReduceToFilter.tsx | 34 + .../filters/ReduceToFilter/index.ts | 1 + .../container/QueryBuilder/filters/index.ts | 7 + .../container/QueryBuilder/filters/utils.ts | 94 + frontend/src/container/QueryBuilder/index.ts | 1 + frontend/src/container/QueryBuilder/type.ts | 18 + .../QueryTable/QueryTable.intefaces.ts | 21 + .../QueryTable/QueryTable.styles.scss | 10 + .../src/container/QueryTable/QueryTable.tsx | 72 + frontend/src/container/QueryTable/config.ts | 3 + frontend/src/container/QueryTable/index.ts | 1 + frontend/src/container/QueryTable/utils.ts | 14 + .../ResetPassword/ResetPassword.test.tsx | 72 + .../src/container/ResetPassword/index.tsx | 196 + .../src/container/ResetPassword/styles.ts | 26 + .../ResourceAttributesFilter.tsx | 90 + .../components/QueryChip/QueryChip.tsx | 22 + .../components/QueryChip/index.ts | 3 + .../components/QueryChip/types.ts | 6 + .../ResourceAttributesFilter/index.ts | 3 + .../ResourceAttributesFilter/styles.ts | 28 + .../Columns/BaseColumnOptions.ts | 36 + .../Columns/ColumnContants.ts | 24 + .../Columns/GetColumnSearchProps.tsx | 38 + .../Columns/ServiceColumn.ts | 58 + .../Filter/FilterDropdown.tsx | 41 + .../ServiceMetrics/ServiceMetricTable.tsx | 109 + .../ServiceMetrics/ServiceMetrics.test.tsx | 48 + .../ServiceMetricsApplication.tsx | 36 + .../ServiceMetrics/ServiceMetricsQuery.ts | 217 + .../ServiceMetrics/index.tsx | 59 + .../ServiceTraces/ServicTraces.test.tsx | 59 + .../ServiceTraces/Service.test.tsx | 29 + .../ServiceTraces/ServiceTracesTable.tsx | 66 + .../ServiceTraces/__mocks__/getServices.ts | 22 + .../ServiceTraces/index.tsx | 60 + .../SkipOnBoardModal/index.tsx | 48 + .../container/ServiceApplication/index.tsx | 23 + .../container/ServiceApplication/styles.ts | 14 + .../src/container/ServiceApplication/types.ts | 35 + .../src/container/ServiceApplication/utils.ts | 99 + .../ServiceTable/Columns/ColumnContants.ts | 24 + .../Columns/GetColumnSearchProps.tsx | 34 + .../ServiceTable/Columns/ServiceColumn.ts | 46 + .../ServiceTable/Filter/FilterDropdown.tsx | 41 + .../container/ServiceTable/Service.test.tsx | 29 + .../ServiceTable/SkipOnBoardModal/index.tsx | 48 + .../ServiceTable/__mock__/servicesListMock.ts | 22 + frontend/src/container/ServiceTable/index.tsx | 26 + frontend/src/container/ServiceTable/styles.ts | 14 + frontend/src/container/ServiceTable/types.ts | 6 + .../SideNav/NavItem/NavItem.styles.scss | 113 + .../src/container/SideNav/NavItem/NavItem.tsx | 38 + .../src/container/SideNav/SideNav.styles.scss | 171 + frontend/src/container/SideNav/SideNav.tsx | 457 + frontend/src/container/SideNav/Slack.tsx | 54 + frontend/src/container/SideNav/config.ts | 51 + frontend/src/container/SideNav/helper.test.ts | 34 + frontend/src/container/SideNav/helper.ts | 10 + frontend/src/container/SideNav/index.ts | 3 + frontend/src/container/SideNav/menuItems.tsx | 133 + .../src/container/SideNav/sideNav.types.ts | 21 + .../src/container/SideNav/sideNav.utils.ts | 11 + frontend/src/container/SideNav/styles.ts | 66 + .../TimeSeriesView/TimeSeriesView.styles.scss | 0 .../TimeSeriesView/TimeSeriesView.tsx | 124 + .../src/container/TimeSeriesView/index.tsx | 92 + .../src/container/TimeSeriesView/styles.ts | 15 + .../src/container/TimeSeriesView/utils.ts | 26 + frontend/src/container/Timeline/index.tsx | 111 + frontend/src/container/Timeline/styles.ts | 19 + frontend/src/container/Timeline/types.ts | 4 + frontend/src/container/Timeline/utils.ts | 92 + .../src/container/Toolbar/Toolbar.styles.scss | 29 + frontend/src/container/Toolbar/Toolbar.tsx | 35 + .../container/TopNav/AutoRefresh/config.ts | 77 + .../container/TopNav/AutoRefresh/index.tsx | 202 + .../container/TopNav/AutoRefresh/styles.ts | 13 + .../AutoRefreshV2/AutoRefreshV2.styles.scss | 79 + .../container/TopNav/AutoRefreshV2/config.ts | 77 + .../container/TopNav/AutoRefreshV2/index.tsx | 201 + .../container/TopNav/AutoRefreshV2/styles.ts | 13 + .../container/TopNav/Breadcrumbs/index.tsx | 63 + .../CustomDateTimeModal.test.tsx | 42 + .../TopNav/CustomDateTimeModal/index.tsx | 60 + .../DateTimeSelection.styles.scss | 3 + .../TopNav/DateTimeSelection/Refresh.tsx | 36 + .../TopNav/DateTimeSelection/config.ts | 112 + .../TopNav/DateTimeSelection/index.tsx | 424 + .../TopNav/DateTimeSelection/styles.ts | 34 + .../DateTimeSelectionV2.styles.scss | 237 + .../TopNav/DateTimeSelectionV2/Refresh.tsx | 36 + .../TopNav/DateTimeSelectionV2/config.ts | 162 + .../TopNav/DateTimeSelectionV2/index.tsx | 486 + .../TopNav/DateTimeSelectionV2/styles.ts | 34 + frontend/src/container/TopNav/index.tsx | 54 + frontend/src/container/TopNav/styles.ts | 8 + .../Panel/PanelBody/Common/Checkbox.tsx | 196 + .../Filters/Panel/PanelBody/Common/styles.ts | 19 + .../Panel/PanelBody/CommonCheckBox/index.tsx | 102 + .../Panel/PanelBody/Duration/index.tsx | 222 + .../Panel/PanelBody/Duration/styles.ts | 40 + .../Filters/Panel/PanelBody/Duration/util.ts | 13 + .../Panel/PanelBody/SearchTraceID/index.tsx | 132 + .../Trace/Filters/Panel/PanelBody/index.tsx | 43 + .../Trace/Filters/Panel/PanelBody/styles.ts | 3 + .../Filters/Panel/PanelHeading/index.tsx | 357 + .../Filters/Panel/PanelHeading/styles.ts | 50 + .../container/Trace/Filters/Panel/index.tsx | 29 + .../src/container/Trace/Filters/index.tsx | 31 + .../src/container/Trace/Filters/styles.ts | 20 + frontend/src/container/Trace/Graph/config.ts | 125 + frontend/src/container/Trace/Graph/index.tsx | 61 + frontend/src/container/Trace/Graph/styles.ts | 23 + .../Trace/Search/AllTags/Tag/TagKey.tsx | 95 + .../Trace/Search/AllTags/Tag/TagValue.tsx | 153 + .../Trace/Search/AllTags/Tag/index.tsx | 185 + .../Trace/Search/AllTags/Tag/styles.ts | 38 + .../Trace/Search/AllTags/Tag/utils.ts | 232 + .../container/Trace/Search/AllTags/index.tsx | 150 + .../container/Trace/Search/AllTags/styles.ts | 39 + frontend/src/container/Trace/Search/config.ts | 1 + frontend/src/container/Trace/Search/index.tsx | 146 + frontend/src/container/Trace/Search/styles.ts | 19 + frontend/src/container/Trace/Search/util.ts | 158 + .../Trace/TraceGraphFilter/config.ts | 111 + .../Trace/TraceGraphFilter/index.tsx | 101 + .../Trace/TraceGraphFilter/styles.ts | 8 + .../Trace/TraceGraphFilter/utils.test.ts | 27 + .../container/Trace/TraceGraphFilter/utils.ts | 88 + .../src/container/Trace/TraceTable/index.tsx | 223 + .../src/container/Trace/TraceTable/util.ts | 19 + .../container/TraceDetail/Missingtrace.tsx | 31 + .../SelectedSpanDetails/EllipsedButton.tsx | 52 + .../SelectedSpanDetails/Events/Event.tsx | 94 + .../Events/EventStartTime.tsx | 30 + .../Events/RelativeStartTime.tsx | 41 + .../SelectedSpanDetails/Events/index.tsx | 37 + .../SelectedSpanDetails/Tags/Tag.tsx | 62 + .../SelectedSpanDetails/Tags/index.tsx | 99 + .../SelectedSpanDetails/Tags/styles.ts | 8 + .../TraceDetail/SelectedSpanDetails/config.ts | 74 + .../TraceDetail/SelectedSpanDetails/index.tsx | 158 + .../TraceDetail/SelectedSpanDetails/styles.ts | 103 + .../TraceDetail/TraceDetails.styles.scss | 13 + frontend/src/container/TraceDetail/index.tsx | 280 + frontend/src/container/TraceDetail/styles.ts | 123 + .../src/container/TraceDetail/utils.test.ts | 56 + frontend/src/container/TraceDetail/utils.ts | 142 + .../__tests__/TraceFlameGraph.test.tsx | 46 + .../TraceFlameGraph.test.tsx.snap | 34 + .../src/container/TraceFlameGraph/index.tsx | 182 + .../src/container/TraceFlameGraph/styles.ts | 38 + .../TracesExplorer/Controls/index.tsx | 57 + .../TracesExplorer/Controls/styles.ts | 8 + .../TracesExplorer/ListView/configs.tsx | 11 + .../TracesExplorer/ListView/index.tsx | 153 + .../TracesExplorer/ListView/styles.ts | 21 + .../TracesExplorer/ListView/utils.tsx | 58 + .../TracesExplorer/QuerySection/index.tsx | 69 + .../TracesExplorer/QuerySection/styles.ts | 16 + .../TracesExplorer/TableView/index.tsx | 55 + .../TracesExplorer/TracesView/configs.tsx | 51 + .../TracesExplorer/TracesView/index.tsx | 94 + .../TracesExplorer/TracesView/styles.ts | 13 + .../TracesTableComponent.styles.scss | 73 + .../TracesTableComponent.tsx | 173 + .../src/container/TriggeredAlerts/Filter.tsx | 136 + .../FilteredTable/ExapandableRow.tsx | 74 + .../FilteredTable/TableRow.tsx | 54 + .../TriggeredAlerts/FilteredTable/index.tsx | 75 + .../TriggeredAlerts/FilteredTable/styles.ts | 64 + .../TriggeredAlerts/NoFilterTable.tsx | 112 + .../TableComponents/AlertStatus.tsx | 27 + .../TriggeredAlerts/TriggeredAlert.tsx | 50 + .../src/container/TriggeredAlerts/index.tsx | 43 + .../src/container/TriggeredAlerts/styles.ts | 29 + .../src/container/TriggeredAlerts/utils.ts | 51 + frontend/src/container/Version/constant.ts | 1 + frontend/src/container/Version/index.tsx | 107 + frontend/src/container/Version/styles.ts | 8 + .../src/hooks/APIKeys/useGetAllAPIKeys.ts | 13 + .../Integrations/useGetAllIntegrations.ts | 13 + .../hooks/Integrations/useGetIntegration.ts | 18 + .../Integrations/useGetIntegrationStatus.ts | 20 + .../src/hooks/ResizeTable/useSortableTable.ts | 44 + frontend/src/hooks/analytics/useAnalytics.tsx | 48 + .../src/hooks/apDex/useGetApDexSettings.ts | 12 + frontend/src/hooks/apDex/useGetMetricMeta.ts | 13 + .../src/hooks/apDex/useSetApDexSettings.ts | 21 + .../useDashboardFromLocalStorage.tsx | 100 + .../hooks/dashboard/useDeleteDashboard.tsx | 15 + .../hooks/dashboard/useGetAllDashboard.tsx | 10 + .../hooks/dashboard/useUpdateDashboard.tsx | 25 + frontend/src/hooks/dashboard/utils.ts | 43 + .../__tests__/useKeyboardHotkeys.test.tsx | 75 + .../src/hooks/hotkeys/useKeyboardHotkeys.tsx | 124 + frontend/src/hooks/logs/configs.ts | 1 + frontend/src/hooks/logs/types.ts | 31 + frontend/src/hooks/logs/useActiveLog.ts | 151 + frontend/src/hooks/logs/useCopyLogLink.ts | 99 + .../src/hooks/queryBuilder/useAutoComplete.ts | 163 + .../hooks/queryBuilder/useCreateAlerts.tsx | 70 + .../queryBuilder/useFetchKeysAndValues.ts | 164 + .../hooks/queryBuilder/useGetAggregateKeys.ts | 34 + .../queryBuilder/useGetCompositeQueryParam.ts | 23 + .../queryBuilder/useGetExplorerQueryRange.ts | 59 + .../useGetPanelTypesQueryParam.ts | 16 + .../hooks/queryBuilder/useGetQueriesRange.ts | 36 + .../hooks/queryBuilder/useGetQueryRange.ts | 52 + .../queryBuilder/useGetSearchQueryParam.ts | 15 + .../queryBuilder/useGetWidgetQueryRange.ts | 54 + .../hooks/queryBuilder/useIsValidTag.test.ts | 41 + .../src/hooks/queryBuilder/useIsValidTag.ts | 22 + .../src/hooks/queryBuilder/useOperatorType.ts | 31 + .../src/hooks/queryBuilder/useOperators.ts | 24 + frontend/src/hooks/queryBuilder/useOptions.ts | 183 + .../src/hooks/queryBuilder/useQueryBuilder.ts | 7 + .../queryBuilder/useQueryBuilderOperations.ts | 362 + .../useSetCurrentKeyAndOperator.ts | 32 + .../hooks/queryBuilder/useShareBuilderUrl.ts | 28 + .../src/hooks/queryBuilder/useStepInterval.ts | 37 + frontend/src/hooks/queryBuilder/useTag.ts | 113 + .../hooks/queryBuilder/useTagValidation.ts | 33 + frontend/src/hooks/queryPagination/config.ts | 1 + frontend/src/hooks/queryPagination/index.ts | 2 + frontend/src/hooks/queryPagination/types.ts | 4 + .../queryPagination/useQueryPagination.ts | 89 + frontend/src/hooks/queryPagination/utils.ts | 20 + frontend/src/hooks/saveViews/useDeleteView.ts | 11 + .../src/hooks/saveViews/useGetAllViews.ts | 13 + frontend/src/hooks/saveViews/useSaveView.ts | 20 + frontend/src/hooks/saveViews/useUpdateView.ts | 30 + frontend/src/hooks/useAxiosError.tsx | 19 + frontend/src/hooks/useChartMutable.ts | 22 + frontend/src/hooks/useClickOutside.tsx | 28 + .../src/hooks/useComponentPermission.test.ts | 100 + frontend/src/hooks/useComponentPermission.ts | 21 + frontend/src/hooks/useDarkMode/constant.ts | 4 + frontend/src/hooks/useDarkMode/index.tsx | 112 + frontend/src/hooks/useDebounce.tsx | 17 + frontend/src/hooks/useDebouncedFunction.ts | 37 + frontend/src/hooks/useDimensions.ts | 39 + frontend/src/hooks/useDragColumns/configs.ts | 7 + frontend/src/hooks/useDragColumns/index.ts | 75 + frontend/src/hooks/useDragColumns/types.ts | 10 + frontend/src/hooks/useDragColumns/utils.ts | 37 + frontend/src/hooks/useErrorNotification.ts | 17 + .../src/hooks/useEventSourceEvent/index.ts | 31 + frontend/src/hooks/useFeatureFlag/constant.ts | 9 + frontend/src/hooks/useFeatureFlag/index.ts | 7 + .../hooks/useFeatureFlag/useFeatureFlag.ts | 29 + .../useFeatureFlag/useIsFeatureDisabled.ts | 11 + .../src/hooks/useFeatureFlag/utils.test.ts | 13 + frontend/src/hooks/useFeatureFlag/utils.ts | 4 + frontend/src/hooks/useFetch.ts | 80 + frontend/src/hooks/useFontObserver.tsx | 69 + frontend/src/hooks/useGetFeatureFlag.tsx | 23 + .../src/hooks/useGetTopLevelOperations.ts | 16 + .../src/hooks/useHandleExplorerTabChange.ts | 95 + frontend/src/hooks/useIntersectionObserver.ts | 38 + frontend/src/hooks/useInterval.test.ts | 93 + frontend/src/hooks/useInterval.ts | 35 + frontend/src/hooks/useLicense/constant.ts | 8 + frontend/src/hooks/useLicense/index.ts | 6 + frontend/src/hooks/useLicense/useLicense.tsx | 25 + frontend/src/hooks/useLogsData.ts | 198 + frontend/src/hooks/useMountedState.ts | 18 + frontend/src/hooks/useNotifications.tsx | 43 + frontend/src/hooks/usePreviousValue.test.tsx | 43 + frontend/src/hooks/usePreviousValue.ts | 13 + frontend/src/hooks/useQueryService.ts | 33 + .../useResourceAttribute/ResourceProvider.tsx | 188 + .../src/hooks/useResourceAttribute/config.ts | 5 + .../src/hooks/useResourceAttribute/context.ts | 7 + .../src/hooks/useResourceAttribute/index.ts | 7 + .../src/hooks/useResourceAttribute/machine.ts | 61 + .../useResourceAttribute/machine.typegen.ts | 32 + .../src/hooks/useResourceAttribute/types.ts | 31 + .../useResourceAttribute.tsx | 9 + .../src/hooks/useResourceAttribute/utils.ts | 177 + frontend/src/hooks/useScrollToTop/index.tsx | 29 + frontend/src/hooks/useScrollToTop/types.ts | 4 + .../useScrollToTop/useScrollToTop.test.ts | 58 + frontend/src/hooks/useTabFocus.tsx | 28 + frontend/src/hooks/useUrlQuery.test.tsx | 56 + frontend/src/hooks/useUrlQuery.ts | 10 + frontend/src/hooks/useUrlQueryData.ts | 44 + frontend/src/hooks/useUsage/useUsage.tsx | 25 + frontend/src/index.html.ejs | 209 + frontend/src/index.tsx | 61 + frontend/src/lib/JSXtoHTML.ts | 9 + .../src/lib/__fixtures__/getRandomColor.ts | 50 + frontend/src/lib/__fixtures__/logql.ts | 209 + frontend/src/lib/__tests__/getStep.test.ts | 73 + .../src/lib/__tests__/logql/parser.test.ts | 15 + .../lib/__tests__/logql/reverseParser.test.ts | 14 + .../src/lib/__tests__/logql/splitter.test.ts | 10 + frontend/src/lib/convertDateToAmAndPm.ts | 9 + frontend/src/lib/convertIntoHr.ts | 15 + .../src/lib/convertToNanoSecondsToSecond.ts | 4 + frontend/src/lib/covertIntoEpoc.ts | 4 + frontend/src/lib/createIdFromObjectFields.ts | 6 + frontend/src/lib/createQueryParams.ts | 10 + .../customCommaValuesParser.ts | 20 + .../getDashboardVariables.ts | 35 + .../dashbaordVariables/sortVariableValues.ts | 15 + frontend/src/lib/dashboard/getQueryResults.ts | 77 + .../src/lib/dashboard/getUpdatedLayout.ts | 23 + .../lib/dashboard/prepareQueryRangePayload.ts | 108 + frontend/src/lib/getChartData.ts | 137 + frontend/src/lib/getConvertedValue.ts | 293 + frontend/src/lib/getFormatedDate.ts | 13 + .../src/lib/getGeneratedFilterQueryString.ts | 24 + .../src/lib/getGlobalDropDownFormatedDate.ts | 11 + frontend/src/lib/getLabelName.ts | 52 + frontend/src/lib/getMaxMinTime.ts | 28 + frontend/src/lib/getMinMax.ts | 90 + frontend/src/lib/getRandomColor.test.ts | 29 + frontend/src/lib/getRandomColor.ts | 74 + frontend/src/lib/getSettingsPeroid.ts | 36 + .../lib/getStartAndEndTime/getMicroSeconds.ts | 8 + .../src/lib/getStartAndEndTime/getMinAgo.ts | 11 + frontend/src/lib/getStartAndEndTime/index.ts | 76 + frontend/src/lib/getStartEndRangeTime.ts | 49 + frontend/src/lib/getStep.test.ts | 137 + frontend/src/lib/getStep.ts | 52 + frontend/src/lib/getTimeString.ts | 15 + frontend/src/lib/history.ts | 3 + .../src/lib/logql/errors/ConvertToFullText.ts | 7 + .../src/lib/logql/errors/InvalidQueryPair.ts | 7 + frontend/src/lib/logql/errors/index.ts | 2 + frontend/src/lib/logql/index.ts | 4 + frontend/src/lib/logql/parser.ts | 150 + frontend/src/lib/logql/reverseParser.ts | 39 + frontend/src/lib/logql/splitter.ts | 52 + frontend/src/lib/logql/tokens.ts | 83 + frontend/src/lib/logql/types.ts | 4 + frontend/src/lib/logs/fieldSearch.ts | 9 + frontend/src/lib/logs/flatLogData.ts | 17 + frontend/src/lib/logs/generateFilterQuery.ts | 24 + .../chooseAutocompleteFromCustomValue.ts | 27 + .../newQueryBuilder/convertNewDataToOld.ts | 43 + .../createNewBuilderItemName.ts | 17 + .../getAutocompleteValueAndType.ts | 15 + .../getMetricsOperatorsByAttributeType.ts | 31 + .../getOperatorsBySourceAndPanelType.ts | 44 + .../newQueryBuilder/getPaginationQueryData.ts | 72 + .../mapCompositeQueryFromQuery.ts | 109 + .../mapQueryDataFromApi.ts | 38 + .../queryBuilderMappers/mapQueryDataToApi.ts | 35 + .../transformQueryBuilderDataModel.ts | 30 + frontend/src/lib/query/GetFormulaName.ts | 29 + frontend/src/lib/query/GetQueryName.ts | 27 + .../src/lib/query/convertObjectIntoParams.ts | 14 + .../lib/query/createTableColumnsFromQuery.ts | 508 + .../src/lib/query/findDataTypeOfOperator.ts | 22 + .../lib/query/transformQueryBuilderData.ts | 34 + .../lib/query/transformStringWithPrefix.ts | 16 + frontend/src/lib/removeJSONStringifyQuotes.ts | 10 + .../src/lib/replaceIncorrectObjectFields.ts | 28 + frontend/src/lib/toCapitalize.ts | 5 + .../src/lib/uPlotLib/getUplotChartOptions.ts | 229 + frontend/src/lib/uPlotLib/placement.ts | 114 + .../src/lib/uPlotLib/plugins/onClickPlugin.ts | 40 + .../src/lib/uPlotLib/plugins/tooltipPlugin.ts | 257 + .../src/lib/uPlotLib/uPlotLib.styles.scss | 39 + frontend/src/lib/uPlotLib/utils/constants.ts | 15 + .../src/lib/uPlotLib/utils/generateColor.ts | 20 + frontend/src/lib/uPlotLib/utils/getAxes.ts | 64 + .../src/lib/uPlotLib/utils/getGridColor.ts | 8 + .../src/lib/uPlotLib/utils/getRenderer.ts | 22 + .../src/lib/uPlotLib/utils/getSeriesData.ts | 92 + .../lib/uPlotLib/utils/getUplotChartData.ts | 78 + .../src/lib/uPlotLib/utils/getXAxisScale.ts | 53 + .../lib/uPlotLib/utils/getYAxisScale.test.ts | 211 + .../src/lib/uPlotLib/utils/getYAxisScale.ts | 238 + .../utils/tests/__mocks__/seriesData.ts | 889 + .../tests/__mocks__/uplotChartOptionsData.ts | 453 + .../utils/tests/getSeriesData.test.ts | 32 + .../utils/tests/getUplotChartOptions.test.ts | 68 + .../src/mocks-server/__mockdata__/apiKeys.ts | 541 + .../src/mocks-server/__mockdata__/billing.ts | 54 + .../src/mocks-server/__mockdata__/licenses.ts | 124 + .../__mockdata__/logs_query_range.ts | 45 + .../mocks-server/__mockdata__/query_range.ts | 79 + .../src/mocks-server/__mockdata__/services.ts | 22 + .../__mockdata__/top_level_operations.ts | 5 + frontend/src/mocks-server/handlers.ts | 88 + frontend/src/mocks-server/server.ts | 7 + frontend/src/mocks-server/setupTests.ts | 10 + frontend/src/modules/Servicemap/Map.tsx | 62 + .../src/modules/Servicemap/ServiceMap.tsx | 130 + frontend/src/modules/Servicemap/index.tsx | 6 + frontend/src/modules/Servicemap/utils.ts | 138 + frontend/src/modules/Usage/UsageExplorer.tsx | 224 + frontend/src/modules/Usage/index.tsx | 7 + frontend/src/modules/Usage/styles.ts | 13 + .../src/pages/AlertChannelCreate/config.tsx | 22 + .../src/pages/AlertChannelCreate/index.tsx | 19 + frontend/src/pages/AlertList/index.tsx | 26 + frontend/src/pages/AllErrors/config.ts | 12 + frontend/src/pages/AllErrors/index.tsx | 19 + .../src/pages/Billing/BillingPage.styles.scss | 7 + frontend/src/pages/Billing/BillingPage.tsx | 13 + frontend/src/pages/Billing/index.tsx | 3 + frontend/src/pages/ChannelsEdit/index.tsx | 136 + frontend/src/pages/CreateAlert/index.tsx | 7 + frontend/src/pages/CreateAlert/styles.ts | 18 + frontend/src/pages/DashboardWidget/index.tsx | 69 + .../DashboardsListPage/DashboardsListPage.tsx | 17 + .../src/pages/DashboardsListPage/index.tsx | 3 + frontend/src/pages/EditRules/index.tsx | 63 + .../ErrorBoundaryFallback.styles.scss | 16 + .../ErrorBoundaryFallback.tsx | 54 + frontend/src/pages/ErrorDetails/index.tsx | 95 + frontend/src/pages/ErrorDetails/utils.ts | 8 + frontend/src/pages/GettingStarted/DocCard.tsx | 27 + frontend/src/pages/GettingStarted/Section.tsx | 42 + frontend/src/pages/GettingStarted/index.tsx | 20 + .../src/pages/GettingStarted/renderConfig.tsx | 174 + frontend/src/pages/GettingStarted/styles.ts | 30 + frontend/src/pages/GettingStarted/types.ts | 10 + .../src/pages/GettingStarted/utmParams.ts | 3 + frontend/src/pages/Integrations/Header.tsx | 37 + .../IntegrationDetailContent.tsx | 79 + .../Configure.tsx | 48 + .../DataCollected.tsx | 85 + .../IntegrationDetailContentTabs.styles.scss | 296 + .../IntegrationDetailContentTabs/Overview.tsx | 63 + .../IntegrationDetailHeader.tsx | 190 + .../IntegrationDetailPage.styles.scss | 652 + .../IntegrationDetailPage.tsx | 156 + .../IntegrationsUninstallBar.tsx | 89 + .../IntegrationDetailPage/TestConnection.tsx | 35 + .../IntegrationDetailPage/utils.ts | 55 + .../Integrations/Integrations.styles.scss | 228 + .../src/pages/Integrations/Integrations.tsx | 41 + .../pages/Integrations/IntegrationsList.tsx | 120 + frontend/src/pages/Integrations/index.ts | 3 + frontend/src/pages/Integrations/utils.ts | 9 + .../IntegrationsMarketPlace.tsx | 9 + .../pages/IntegrationsMarketPlace/index.ts | 3 + .../IntegrationsModulePage.styles.scss | 27 + .../IntegrationsModulePage.tsx | 21 + .../IntegrationsModulePage/constants.tsx | 15 + .../src/pages/IntegrationsModulePage/index.ts | 3 + frontend/src/pages/License/index.tsx | 7 + frontend/src/pages/LiveLogs/index.tsx | 25 + frontend/src/pages/Login/index.tsx | 63 + frontend/src/pages/Logs/PopoverContent.tsx | 27 + frontend/src/pages/Logs/config.ts | 48 + frontend/src/pages/Logs/hooks.ts | 78 + frontend/src/pages/Logs/index.tsx | 151 + frontend/src/pages/Logs/logs.styles.scss | 4 + frontend/src/pages/Logs/styles.ts | 10 + frontend/src/pages/Logs/types.ts | 17 + frontend/src/pages/Logs/utils.ts | 29 + .../LogsExplorer/LogsExplorer.styles.scss | 11 + .../__tests__/LogsExplorer.test.tsx | 147 + frontend/src/pages/LogsExplorer/index.tsx | 108 + frontend/src/pages/LogsExplorer/styles.ts | 14 + frontend/src/pages/LogsExplorer/utils.ts | 19 + .../LogsModulePage/LogsModulePage.styles.scss | 57 + .../pages/LogsModulePage/LogsModulePage.tsx | 20 + .../src/pages/LogsModulePage/constants.tsx | 39 + frontend/src/pages/LogsModulePage/index.tsx | 3 + frontend/src/pages/LogsModulePage/utils.ts | 7 + .../LogsSettings/components/TabLabel.tsx | 14 + frontend/src/pages/LogsSettings/config.tsx | 21 + frontend/src/pages/LogsSettings/constant.ts | 11 + frontend/src/pages/LogsSettings/index.tsx | 18 + frontend/src/pages/LogsSettings/types.ts | 1 + .../ApDex/ApDexApplication.test.tsx | 65 + .../ApDex/ApDexApplication.tsx | 58 + .../ApDex/ApDexSettings.test.tsx | 65 + .../ApDex/ApDexSettings.tsx | 119 + .../axiosResponseMockThresholdData.ts | 9 + .../ApDex/__mock__/thresholdMockData.ts | 7 + .../pages/MetricsApplication/ApDex/types.ts | 10 + .../src/pages/MetricsApplication/config.ts | 8 + .../src/pages/MetricsApplication/constants.ts | 1 + .../src/pages/MetricsApplication/index.tsx | 63 + .../src/pages/MetricsApplication/styles.ts | 42 + .../src/pages/MetricsApplication/types.ts | 31 + .../useMetricsApplicationTabKey.tsx | 13 + .../src/pages/MetricsApplication/utils.ts | 46 + frontend/src/pages/MySettings/index.tsx | 6 + .../src/pages/NewDashboard/DashboardPage.tsx | 35 + frontend/src/pages/NewDashboard/index.tsx | 3 + .../OnboardingPage/OnboardingPage.styles.scss | 7 + .../pages/OnboardingPage/OnboardingPage.tsx | 16 + frontend/src/pages/OnboardingPage/index.tsx | 3 + .../src/pages/Pipelines/Pipelines.styles.scss | 5 + frontend/src/pages/Pipelines/index.tsx | 95 + frontend/src/pages/ResetPassword/index.tsx | 49 + .../src/pages/SaveView/SaveView.styles.scss | 346 + frontend/src/pages/SaveView/constants.ts | 15 + frontend/src/pages/SaveView/index.tsx | 370 + frontend/src/pages/SaveView/utils.ts | 37 + frontend/src/pages/Services/Metrics.test.tsx | 74 + frontend/src/pages/Services/index.tsx | 18 + frontend/src/pages/Settings/config.tsx | 88 + frontend/src/pages/Settings/index.tsx | 31 + frontend/src/pages/Settings/utils.ts | 39 + .../src/pages/Shortcuts/Shortcuts.styles.scss | 23 + frontend/src/pages/Shortcuts/Shortcuts.tsx | 36 + frontend/src/pages/Shortcuts/index.ts | 3 + frontend/src/pages/Shortcuts/utils.ts | 62 + frontend/src/pages/SignUp/SignUp.tsx | 465 + frontend/src/pages/SignUp/index.tsx | 50 + frontend/src/pages/SignUp/styles.ts | 39 + frontend/src/pages/SignUp/utils.ts | 13 + .../src/pages/SomethingWentWrong/index.tsx | 24 + frontend/src/pages/Status/index.tsx | 7 + .../src/pages/Support/Support.styles.scss | 53 + frontend/src/pages/Support/Support.tsx | 174 + frontend/src/pages/Support/index.tsx | 3 + frontend/src/pages/Trace/index.tsx | 199 + frontend/src/pages/Trace/styles.ts | 36 + frontend/src/pages/TraceDetail/constants.ts | 4 + frontend/src/pages/TraceDetail/index.tsx | 53 + .../TracesExplorer/TracesExplorer.styles.scss | 13 + frontend/src/pages/TracesExplorer/index.tsx | 223 + frontend/src/pages/TracesExplorer/styles.ts | 10 + frontend/src/pages/TracesExplorer/utils.tsx | 51 + .../TracesModulePage.styles.scss | 27 + .../TracesModulePage/TracesModulePage.tsx | 22 + .../src/pages/TracesModulePage/constants.tsx | 27 + frontend/src/pages/TracesModulePage/index.tsx | 3 + frontend/src/pages/UnAuthorized/index.tsx | 22 + .../WorkspaceLocked.styles.scss | 27 + .../WorkspaceLocked/WorkspaceLocked.test.tsx | 72 + .../pages/WorkspaceLocked/WorkspaceLocked.tsx | 160 + frontend/src/pages/WorkspaceLocked/index.tsx | 3 + frontend/src/periscope.scss | 47 + .../src/providers/Dashboard/Dashboard.tsx | 357 + frontend/src/providers/Dashboard/types.ts | 33 + frontend/src/providers/Dashboard/util.ts | 22 + frontend/src/providers/EventSource.tsx | 162 + frontend/src/providers/QueryBuilder.tsx | 734 + .../test/MockQueryClientProvider.tsx | 21 + frontend/src/reportWebVitals.ts | 15 + frontend/src/setupProxy.js | 14 + frontend/src/store/actions/app/index.ts | 1 + .../src/store/actions/app/sideBarCollapse.ts | 16 + frontend/src/store/actions/global.ts | 32 + frontend/src/store/actions/index.ts | 6 + .../store/actions/logs/addToSelectedField.ts | 15 + frontend/src/store/actions/logs/getFields.ts | 23 + frontend/src/store/actions/logs/getLogs.ts | 34 + .../store/actions/logs/getLogsAggregate.ts | 42 + .../src/store/actions/logs/setLInesPerRow.ts | 14 + .../src/store/actions/logs/setViewMode.ts | 15 + .../src/store/actions/metrics/getService.ts | 62 + frontend/src/store/actions/metrics/index.ts | 1 + .../store/actions/metrics/resetInitialData.ts | 12 + frontend/src/store/actions/serviceMap.ts | 62 + .../store/actions/trace/getInitialFilter.ts | 211 + .../actions/trace/getInitialSpansAggregate.ts | 145 + frontend/src/store/actions/trace/getSpans.ts | 87 + .../store/actions/trace/parseFilter/filter.ts | 44 + .../trace/parseFilter/filterToFetchData.ts | 38 + .../store/actions/trace/parseFilter/index.ts | 11 + .../trace/parseFilter/isFilterExclude.ts | 45 + .../actions/trace/parseFilter/minMaxTime.ts | 20 + .../trace/parseFilter/parseSpanKind.ts | 39 + .../trace/parseFilter/selectedFilter.ts | 44 + .../actions/trace/parseFilter/selectedTags.ts | 38 + .../trace/parseFilter/skippedSelected.ts | 33 + .../parseFilter/spanAggregateCurrentPage.ts | 37 + .../spanAggregateCurrentPageSize.ts | 37 + .../trace/parseFilter/spanAggregateOrder.ts | 39 + .../parseFilter/spanAggregateOrderParam.ts | 39 + .../store/actions/trace/selectTraceFilter.ts | 52 + .../store/actions/trace/updateIsTagsError.ts | 15 + .../actions/trace/updateTagPanelVisiblity.ts | 15 + .../store/actions/trace/updateTagsSelected.ts | 15 + frontend/src/store/actions/trace/util.ts | 91 + frontend/src/store/actions/types.ts | 17 + frontend/src/store/actions/usage.ts | 34 + frontend/src/store/index.ts | 27 + frontend/src/store/reducers/app.ts | 238 + frontend/src/store/reducers/global.ts | 88 + frontend/src/store/reducers/index.ts | 23 + frontend/src/store/reducers/logs.ts | 268 + frontend/src/store/reducers/metric.ts | 111 + frontend/src/store/reducers/serviceMap.ts | 27 + frontend/src/store/reducers/trace.ts | 286 + frontend/src/store/reducers/usage.ts | 14 + frontend/src/store/utils.ts | 22 + frontend/src/styles.scss | 244 + frontend/src/tests/test-utils.tsx | 100 + frontend/src/types/actions/app.ts | 152 + frontend/src/types/actions/globalTime.ts | 45 + frontend/src/types/actions/index.ts | 14 + frontend/src/types/actions/logs.ts | 175 + frontend/src/types/actions/metrics.ts | 54 + frontend/src/types/actions/trace.ts | 220 + frontend/src/types/api/SAML/deleteDomain.ts | 5 + frontend/src/types/api/SAML/listDomain.ts | 47 + frontend/src/types/api/SAML/postDomain.ts | 8 + frontend/src/types/api/SAML/updateDomain.ts | 5 + frontend/src/types/api/alerts/alertTypes.ts | 7 + .../src/types/api/alerts/compositeQuery.ts | 17 + frontend/src/types/api/alerts/create.ts | 10 + frontend/src/types/api/alerts/def.ts | 41 + frontend/src/types/api/alerts/delete.ts | 10 + frontend/src/types/api/alerts/get.ts | 20 + frontend/src/types/api/alerts/getAll.ts | 3 + frontend/src/types/api/alerts/getGroups.ts | 17 + frontend/src/types/api/alerts/getTriggered.ts | 39 + frontend/src/types/api/alerts/patch.ts | 12 + frontend/src/types/api/alerts/save.ts | 11 + frontend/src/types/api/alerts/testAlert.ts | 10 + frontend/src/types/api/billing/checkout.ts | 9 + .../src/types/api/channels/createEmail.ts | 8 + .../src/types/api/channels/createMsTeams.ts | 8 + .../src/types/api/channels/createOpsgenie.ts | 8 + .../src/types/api/channels/createPager.ts | 8 + .../src/types/api/channels/createSlack.ts | 8 + .../src/types/api/channels/createWebhook.ts | 8 + frontend/src/types/api/channels/delete.ts | 10 + frontend/src/types/api/channels/editEmail.ts | 10 + .../src/types/api/channels/editMsTeams.ts | 10 + .../src/types/api/channels/editOpsgenie.ts | 10 + frontend/src/types/api/channels/editPager.ts | 10 + frontend/src/types/api/channels/editSlack.ts | 10 + .../src/types/api/channels/editWebhook.ts | 10 + frontend/src/types/api/channels/get.ts | 7 + frontend/src/types/api/channels/getAll.ts | 10 + frontend/src/types/api/dashboard/create.ts | 11 + frontend/src/types/api/dashboard/delete.ts | 9 + frontend/src/types/api/dashboard/get.ts | 7 + frontend/src/types/api/dashboard/getAll.ts | 99 + frontend/src/types/api/dashboard/update.ts | 8 + .../types/api/dashboard/variables/query.ts | 15 + frontend/src/types/api/disks/getDisks.ts | 5 + .../api/dynamicConfigs/getDynamicConfigs.ts | 14 + frontend/src/types/api/errors/getAll.ts | 34 + frontend/src/types/api/errors/getByErrorId.ts | 9 + .../api/errors/getByErrorTypeAndService.ts | 17 + .../src/types/api/errors/getErrorCounts.ts | 12 + .../src/types/api/errors/getNextPrevId.ts | 13 + .../types/api/features/getFeaturesFlags.ts | 11 + frontend/src/types/api/index.ts | 18 + frontend/src/types/api/integrations/types.ts | 105 + frontend/src/types/api/licenses/apply.ts | 10 + frontend/src/types/api/licenses/def.ts | 8 + frontend/src/types/api/licenses/getAll.ts | 11 + .../src/types/api/logs/addToSelectedFields.ts | 4 + frontend/src/types/api/logs/fields.ts | 16 + frontend/src/types/api/logs/getLogs.ts | 13 + .../src/types/api/logs/getLogsAggregate.ts | 15 + .../src/types/api/logs/getSearchFields.ts | 3 + frontend/src/types/api/logs/liveTail.ts | 1 + frontend/src/types/api/logs/log.ts | 34 + frontend/src/types/api/logs/logAggregate.ts | 5 + frontend/src/types/api/logs/operator.ts | 15 + .../src/types/api/logs/removeSelectedField.ts | 4 + frontend/src/types/api/metrics/getApDex.ts | 14 + .../src/types/api/metrics/getDBOverview.ts | 19 + .../api/metrics/getExternalAverageDuration.ts | 12 + .../src/types/api/metrics/getExternalError.ts | 13 + .../types/api/metrics/getExternalService.ts | 15 + .../src/types/api/metrics/getMetricName.ts | 4 + .../src/types/api/metrics/getQueryRange.ts | 41 + .../api/metrics/getResourceAttributes.ts | 15 + frontend/src/types/api/metrics/getService.ts | 26 + .../types/api/metrics/getServiceOverview.ts | 16 + .../api/metrics/getTopLevelOperations.ts | 7 + .../src/types/api/metrics/getTopOperations.ts | 11 + frontend/src/types/api/pat/types.ts | 53 + frontend/src/types/api/pipeline/def.ts | 93 + frontend/src/types/api/pipeline/get.ts | 3 + frontend/src/types/api/pipeline/post.ts | 5 + .../queryBuilder/getAggregatorAttribute.ts | 7 + .../api/queryBuilder/getAttributeKeys.ts | 11 + .../api/queryBuilder/getAttributesValues.ts | 19 + .../queryBuilder/queryAutocompleteResponse.ts | 28 + .../api/queryBuilder/queryBuilderData.ts | 119 + frontend/src/types/api/saveViews/types.ts | 51 + frontend/src/types/api/settings/common.ts | 1 + .../src/types/api/settings/getRetention.ts | 35 + frontend/src/types/api/settings/ingestion.ts | 18 + .../src/types/api/settings/setRetention.ts | 12 + frontend/src/types/api/trace/getFilters.ts | 16 + .../src/types/api/trace/getSpanAggregate.ts | 19 + frontend/src/types/api/trace/getSpans.ts | 23 + frontend/src/types/api/trace/getTagFilters.ts | 17 + frontend/src/types/api/trace/getTagValue.ts | 18 + frontend/src/types/api/trace/getTraceItem.ts | 69 + .../src/types/api/user/changeMyPassword.ts | 11 + frontend/src/types/api/user/deleteInvite.ts | 9 + frontend/src/types/api/user/deleteUser.ts | 9 + frontend/src/types/api/user/editOrg.ts | 10 + frontend/src/types/api/user/editUser.ts | 10 + .../src/types/api/user/getInviteDetails.ts | 19 + .../src/types/api/user/getLatestVersion.ts | 18 + frontend/src/types/api/user/getOrgMembers.ts | 18 + .../src/types/api/user/getOrganization.ts | 9 + .../src/types/api/user/getPendingInvites.ts | 12 + .../types/api/user/getResetPasswordToken.ts | 10 + frontend/src/types/api/user/getUser.ts | 20 + .../src/types/api/user/getUserPreference.ts | 6 + frontend/src/types/api/user/getUserRole.ts | 12 + frontend/src/types/api/user/getVersion.ts | 5 + frontend/src/types/api/user/login.ts | 13 + frontend/src/types/api/user/loginPrecheck.ts | 11 + frontend/src/types/api/user/resetPassword.ts | 8 + frontend/src/types/api/user/setFlags.ts | 12 + frontend/src/types/api/user/setInvite.ts | 13 + .../src/types/api/user/setUserPreference.ts | 8 + frontend/src/types/api/user/signup.ts | 8 + frontend/src/types/api/user/updateRole.ts | 10 + .../types/api/userFeedback/sendResponse.ts | 4 + frontend/src/types/api/widgets/getQuery.ts | 43 + frontend/src/types/common/dashboard.ts | 5 + frontend/src/types/common/index.ts | 32 + frontend/src/types/common/operations.types.ts | 53 + frontend/src/types/common/queryBuilder.ts | 247 + frontend/src/types/common/select.ts | 14 + frontend/src/types/global.d.ts | 17 + frontend/src/types/reducer/app.ts | 38 + frontend/src/types/reducer/globalTime.ts | 12 + frontend/src/types/reducer/logs.ts | 32 + frontend/src/types/reducer/metrics.ts | 24 + frontend/src/types/reducer/trace.ts | 145 + frontend/src/types/roles.ts | 13 + .../src/typings/chartjs-adapter-date-fns.d.ts | 1 + frontend/src/typings/environment.ts | 10 + frontend/src/typings/window.ts | 14 + .../__snapshots__/spanToTree.test.ts.snap | 216 + .../src/utils/__tests__/spanToTree.test.ts | 21 + frontend/src/utils/app.ts | 33 + .../generateExportToDashboardLink.ts | 26 + frontend/src/utils/fixtures/TraceData.ts | 52 + .../src/utils/form/requireErrorMessage.ts | 2 + frontend/src/utils/getAlphaColor.ts | 14 + frontend/src/utils/getEventEmitter.ts | 3 + frontend/src/utils/getFormatedLegend.ts | 2 + frontend/src/utils/getGraphType.ts | 9 + frontend/src/utils/getSortedSeriesData.ts | 20 + frontend/src/utils/getSpanTreeMetadata.ts | 60 + frontend/src/utils/getTimeRange.ts | 36 + frontend/src/utils/getUserOS.ts | 14 + frontend/src/utils/logs.ts | 31 + frontend/src/utils/permission/index.ts | 102 + frontend/src/utils/selectPopupContainer.ts | 5 + frontend/src/utils/services.ts | 4 + frontend/src/utils/spanToTree.ts | 142 + frontend/src/utils/timeUtils.ts | 30 + frontend/src/utils/toFixed.ts | 6 + frontend/src/utils/token.ts | 3 + frontend/src/utils/transformToUpperCase.ts | 2 + frontend/tests/auth.json | 38 + frontend/tests/dashboards/index.spec.ts | 143 + frontend/tests/dashboards/utils.ts | 181 + frontend/tests/expectionDetails/index.spec.ts | 101 + ...-200-from-details-page-1-Signoz-darwin.png | Bin 0 -> 187092 bytes ...nd-when-api-return-404-1-Signoz-darwin.png | Bin 0 -> 39506 bytes frontend/tests/expections/index.spec.ts | 148 + ...ve-a-valid-Breadcrumbs-1-Signoz-darwin.png | Bin 0 -> 61476 bytes ...uld-have-a-valid-route-1-Signoz-darwin.png | Bin 0 -> 43130 bytes ...der-data-in-antd-table-1-Signoz-darwin.png | Bin 0 -> 117914 bytes ...e-page-with-404-status-1-Signoz-darwin.png | Bin 0 -> 61476 bytes ...ith-no-data-antd-table-1-Signoz-darwin.png | Bin 0 -> 59550 bytes .../tests/fixtures/api/allErrors/200.json | 92 + .../dashboard/createNewDashboardPost200.json | 16 + ...shboardGetCallWithTimeSeriesWidget200.json | 91 + .../dashboard/getDashboardListEmpty200.json | 4 + .../dashboard/getIndividualDashboard200.json | 16 + .../putDashboardWithTimeSeries200.json | 91 + .../dashboard/putNewDashboardUpdate200.json | 18 + .../tests/fixtures/api/errorDetails/200.json | 12 + .../tests/fixtures/api/errorDetails/404.json | 5 + .../tests/fixtures/api/getNextPrev/200.json | 7 + frontend/tests/fixtures/api/login/200.json | 7 + .../tests/fixtures/api/organisation/201.json | 3 + frontend/tests/fixtures/api/register/200.json | 1 + frontend/tests/fixtures/api/register/401.json | 5 + frontend/tests/fixtures/api/services/200.json | 68 + .../fixtures/api/traces/attributeKeys200.json | 14 + .../traces/attributeKeysDurationNano200.json | 14 + .../traces/attributeKeysHttpMethod200.json | 21 + .../api/traces/attributeKeysName200.json | 56 + .../attributeKeysResponseStatusCode200.json | 14 + .../traces/attributeKeysServiceName200.json | 14 + .../fixtures/api/traces/queryRange200.json | 26 + .../api/traces/traceExplorerViewPost200.json | 4 + .../api/traces/traceExplorerViews200.json | 52 + .../fixtures/api/traces/tracesRange200.json | 24 + .../api/traces/tracesTableView200.json | 24 + frontend/tests/fixtures/api/userId/200.json | 11 + frontend/tests/fixtures/common.ts | 43 + frontend/tests/fixtures/constant.ts | 10 + frontend/tests/login/fail.spec.ts | 29 + ...e-Something-went-wrong-1-Signoz-darwin.png | Bin 0 -> 8076 bytes frontend/tests/login/index.spec.ts | 50 + ...n-when-api-returns-200-1-Signoz-darwin.png | Bin 0 -> 47462 bytes frontend/tests/service/index.spec.ts | 32 + ...erice-Page-is-rendered-1-Signoz-darwin.png | Bin 0 -> 41410 bytes .../tests/service/servicesLanding.spec.ts | 150 + frontend/tests/service/utils.ts | 6 + frontend/tests/signup/index.spec.ts | 232 + ...ame-with-valid-details-1-Signoz-darwin.png | Bin 0 -> 71480 bytes ...ail-with-valid-details-1-Signoz-darwin.png | Bin 0 -> 71623 bytes ...ord-with-valid-details-1-Signoz-darwin.png | Bin 0 -> 70414 bytes ...ame-with-valid-details-1-Signoz-darwin.png | Bin 0 -> 71414 bytes ...Invite-link-validation-1-Signoz-darwin.png | Bin 0 -> 67365 bytes ...ord-with-valid-details-1-Signoz-darwin.png | Bin 0 -> 73701 bytes ...-up-with-valid-details-1-Signoz-darwin.png | Bin 0 -> 55058 bytes ...edirected-to-dashboard-1-Signoz-darwin.png | Bin 0 -> 71503 bytes .../tests/traces/newTracesExplorer.spec.ts | 118 + frontend/tests/traces/utils.ts | 116 + frontend/tsconfig.json | 54 + frontend/webpack.config.js | 173 + frontend/webpack.config.prod.js | 209 + frontend/yarn.lock | 18116 ++++++++++++++++ go.mod | 206 + go.sum | 1522 ++ pkg/query-service/.dockerignore | 3 + pkg/query-service/Dockerfile | 31 + pkg/query-service/README.md | 81 + pkg/query-service/agentConf/Readme.md | 5 + pkg/query-service/agentConf/agent_features.go | 25 + pkg/query-service/agentConf/db.go | 284 + pkg/query-service/agentConf/manager.go | 357 + pkg/query-service/agentConf/sqlite/init.go | 65 + pkg/query-service/agentConf/version.go | 72 + pkg/query-service/app/apdex.go | 47 + pkg/query-service/app/auth.go | 122 + .../app/clickhouseReader/options.go | 208 + .../app/clickhouseReader/reader.go | 5087 +++++ .../app/clickhouseReader/reader_test.go | 29 + .../app/clickhouseReader/wrapper.go | 82 + pkg/query-service/app/dashboards/model.go | 714 + pkg/query-service/app/dashboards/provision.go | 65 + pkg/query-service/app/explorer/db.go | 230 + pkg/query-service/app/formula.go | 251 + pkg/query-service/app/formula_test.go | 1583 ++ pkg/query-service/app/having.go | 90 + pkg/query-service/app/having_test.go | 283 + pkg/query-service/app/http_handler.go | 3686 ++++ pkg/query-service/app/http_handler_test.go | 132 + pkg/query-service/app/http_utils.go | 22 + pkg/query-service/app/ingestion_key.go | 33 + pkg/query-service/app/integrations/Readme.md | 1 + pkg/query-service/app/integrations/builtin.go | 225 + .../mongo/assets/dashboards/overview.json | 797 + .../mongo/assets/pipelines/log-parser.json | 33 + .../mongo/config/configure-otel-collector.md | 74 + .../mongo/config/prerequisites.md | 22 + .../builtin_integrations/mongo/icon.svg | 28 + .../mongo/integration.json | 88 + .../builtin_integrations/mongo/overview.md | 3 + .../nginx/assets/pipelines/log-parser.json | 62 + .../nginx/config/configure-otel-collector.md | 1 + .../nginx/config/prepare-nginx.md | 1 + .../builtin_integrations/nginx/icon.svg | 8 + .../nginx/integration.json | 87 + .../builtin_integrations/nginx/overview.md | 3 + .../postgres/assets/dashboards/overview.json | 1869 ++ .../postgres/assets/pipelines/log-parser.json | 33 + .../config/configure-otel-collector.md | 72 + .../postgres/config/prerequisites.md | 26 + .../builtin_integrations/postgres/icon.svg | 13 + .../postgres/integration.json | 88 + .../builtin_integrations/postgres/overview.md | 3 + .../redis/assets/dashboards/overview.json | 924 + .../redis/assets/pipelines/log-parser.json | 33 + .../redis/config/configure-otel-collector.md | 63 + .../redis/config/prerequisites.md | 5 + .../builtin_integrations/redis/icon.svg | 11 + .../redis/integration.json | 88 + .../builtin_integrations/redis/overview.md | 3 + .../app/integrations/builtin_test.go | 32 + .../app/integrations/controller.go | 129 + pkg/query-service/app/integrations/manager.go | 428 + .../app/integrations/manager_test.go | 78 + .../app/integrations/pipeline_utils.go | 33 + pkg/query-service/app/integrations/repo.go | 58 + .../app/integrations/sqlite_repo.go | 169 + .../app/integrations/test_utils.go | 213 + pkg/query-service/app/limit.go | 81 + pkg/query-service/app/limit_test.go | 624 + .../app/logparsingpipeline/agent_feature.go | 5 + .../logparsingpipeline/collector_config.go | 221 + .../collector_config_test.go | 206 + .../app/logparsingpipeline/controller.go | 248 + .../app/logparsingpipeline/db.go | 209 + .../app/logparsingpipeline/model.go | 104 + .../app/logparsingpipeline/pipelineBuilder.go | 380 + .../pipelineBuilder_test.go | 878 + .../logparsingpipeline/postablePipeline.go | 238 + .../postablePipeline_test.go | 377 + .../app/logparsingpipeline/preview.go | 222 + .../app/logparsingpipeline/preview_test.go | 353 + .../severity_parser_test.go | 221 + .../app/logparsingpipeline/sqlite/init.go | 35 + .../app/logparsingpipeline/time_parser.go | 120 + .../logparsingpipeline/time_parser_test.go | 136 + pkg/query-service/app/logs/parser.go | 396 + pkg/query-service/app/logs/parser_test.go | 434 + pkg/query-service/app/logs/v3/enrich_query.go | 223 + .../app/logs/v3/enrich_query_test.go | 626 + pkg/query-service/app/logs/v3/json_filter.go | 163 + .../app/logs/v3/json_filter_test.go | 330 + .../app/logs/v3/query_builder.go | 552 + .../app/logs/v3/query_builder_test.go | 1472 ++ pkg/query-service/app/logs/validator.go | 40 + .../app/metrics/query_builder.go | 523 + .../app/metrics/query_builder_test.go | 261 + .../app/metrics/v3/cumulative_table.go | 212 + .../app/metrics/v3/cumulative_table_test.go | 99 + pkg/query-service/app/metrics/v3/delta.go | 174 + .../app/metrics/v3/delta_table.go | 148 + .../app/metrics/v3/delta_table_test.go | 99 + .../app/metrics/v3/query_builder.go | 515 + .../app/metrics/v3/query_builder_test.go | 522 + .../app/metrics/v4/cumulative/table.go | 50 + .../app/metrics/v4/cumulative/table_test.go | 112 + .../app/metrics/v4/cumulative/timeseries.go | 213 + .../metrics/v4/cumulative/timeseries_test.go | 229 + .../app/metrics/v4/delta/table.go | 55 + .../app/metrics/v4/delta/table_test.go | 114 + .../app/metrics/v4/delta/time_series_test.go | 263 + .../app/metrics/v4/delta/timeseries.go | 218 + .../app/metrics/v4/helpers/clauses.go | 77 + .../app/metrics/v4/helpers/sub_query.go | 119 + .../app/metrics/v4/query_builder.go | 95 + .../app/metrics/v4/query_builder_test.go | 534 + .../app/opamp/config_provider.go | 12 + .../app/opamp/config_provider_test.go | 249 + .../app/opamp/configure_ingestionRules.go | 151 + pkg/query-service/app/opamp/logger.go | 15 + pkg/query-service/app/opamp/mocks.go | 147 + pkg/query-service/app/opamp/model/agent.go | 363 + pkg/query-service/app/opamp/model/agents.go | 163 + pkg/query-service/app/opamp/model/config.go | 20 + .../app/opamp/model/constants.go | 4 + .../app/opamp/model/coordinator.go | 66 + pkg/query-service/app/opamp/opamp_server.go | 128 + .../app/opamp/otelconfig/config_parser.go | 195 + .../opamp/otelconfig/config_parser_test.go | 58 + .../otelconfig/filterprocessor/config.go | 11 + .../opamp/otelconfig/otlpreceiver/config.go | 6 + .../otelconfig/otlpreceiver/grpcSettings.go | 14 + .../otelconfig/otlpreceiver/httpSettings.go | 9 + .../app/opamp/otelconfig/otlpreceiver/tls.go | 38 + .../opamp/otelconfig/tailsampler/config.go | 82 + .../app/opamp/otelconfig/testdata/basic.yaml | 76 + .../opamp/otelconfig/testdata/service.yaml | 11 + .../app/opamp/pipeline_builder.go | 196 + pkg/query-service/app/opamp/signal.go | 9 + pkg/query-service/app/parser.go | 1132 + pkg/query-service/app/parser/metrics.go | 117 + pkg/query-service/app/parser_test.go | 1227 ++ pkg/query-service/app/querier/helper.go | 413 + pkg/query-service/app/querier/querier.go | 540 + pkg/query-service/app/querier/querier_test.go | 953 + pkg/query-service/app/querier/v2/helper.go | 314 + pkg/query-service/app/querier/v2/querier.go | 520 + .../app/queryBuilder/functions.go | 301 + .../app/queryBuilder/functions_test.go | 604 + .../app/queryBuilder/query_builder.go | 434 + .../app/queryBuilder/query_builder_test.go | 582 + pkg/query-service/app/reduce_to.go | 71 + pkg/query-service/app/reduce_to_test.go | 99 + pkg/query-service/app/server.go | 709 + pkg/query-service/app/server_test.go | 41 + pkg/query-service/app/services/map.go | 63 + .../app/traces/v3/query_builder.go | 544 + .../app/traces/v3/query_builder_test.go | 1381 ++ pkg/query-service/auth/auth.go | 528 + pkg/query-service/auth/jwt.go | 109 + pkg/query-service/auth/rbac.go | 75 + pkg/query-service/auth/utils.go | 45 + pkg/query-service/cache/cache.go | 69 + pkg/query-service/cache/cache_test.go | 52 + pkg/query-service/cache/inmemory/cache.go | 73 + .../cache/inmemory/cache_test.go | 102 + pkg/query-service/cache/inmemory/options.go | 23 + pkg/query-service/cache/redis/options.go | 24 + pkg/query-service/cache/redis/redis.go | 126 + pkg/query-service/cache/redis/redis_test.go | 92 + pkg/query-service/cache/status/status.go | 33 + pkg/query-service/cache/status/status_test.go | 43 + pkg/query-service/cache/testdata/cache.yaml | 2 + .../collectorsimulator/collectorsimulator.go | 257 + .../inmemoryexporter/config.go | 16 + .../inmemoryexporter/config_test.go | 48 + .../inmemoryexporter/exporter.go | 86 + .../inmemoryexporter/exporter_test.go | 67 + .../inmemoryexporter/factory.go | 34 + .../inmemoryexporter/factory_test.go | 28 + .../inmemoryreceiver/config.go | 16 + .../inmemoryreceiver/config_test.go | 48 + .../inmemoryreceiver/factory.go | 41 + .../inmemoryreceiver/factory_test.go | 29 + .../inmemoryreceiver/receiver.go | 64 + .../inmemoryreceiver/receiver_test.go | 68 + pkg/query-service/collectorsimulator/logs.go | 122 + .../collectorsimulator/logs_test.go | 159 + pkg/query-service/common/metrics.go | 25 + pkg/query-service/common/user.go | 16 + pkg/query-service/config/alerts.yml | 11 + pkg/query-service/config/cache-config.yml | 4 + pkg/query-service/config/prometheus.yml | 25 + pkg/query-service/constants/auth.go | 7 + pkg/query-service/constants/constants.go | 395 + pkg/query-service/constants/constants_test.go | 35 + pkg/query-service/converter/bool.go | 20 + pkg/query-service/converter/converter.go | 190 + pkg/query-service/converter/data.go | 101 + pkg/query-service/converter/data_rate.go | 118 + pkg/query-service/converter/data_rate_test.go | 67 + pkg/query-service/converter/data_test.go | 47 + .../converter/percent_converter.go | 30 + .../converter/percent_converter_test.go | 16 + pkg/query-service/converter/throughput.go | 21 + pkg/query-service/converter/time.go | 62 + pkg/query-service/converter/time_test.go | 55 + pkg/query-service/dao/factory.go | 38 + pkg/query-service/dao/interface.go | 69 + pkg/query-service/dao/sqlite/apdex.go | 70 + pkg/query-service/dao/sqlite/connection.go | 189 + pkg/query-service/dao/sqlite/ingestion.go | 39 + pkg/query-service/dao/sqlite/rbac.go | 614 + pkg/query-service/featureManager/manager.go | 66 + pkg/query-service/formatter/bool.go | 49 + pkg/query-service/formatter/data.go | 54 + pkg/query-service/formatter/data_rate.go | 74 + pkg/query-service/formatter/data_test.go | 23 + pkg/query-service/formatter/formatter.go | 36 + pkg/query-service/formatter/none.go | 17 + pkg/query-service/formatter/percent.go | 32 + pkg/query-service/formatter/scale.go | 136 + pkg/query-service/formatter/scale_test.go | 15 + pkg/query-service/formatter/throughput.go | 48 + .../formatter/throughput_test.go | 15 + pkg/query-service/formatter/time.go | 175 + pkg/query-service/formatter/time_test.go | 29 + pkg/query-service/healthcheck/handler.go | 12 + .../integrations/alertManager/manager.go | 182 + .../integrations/alertManager/model.go | 79 + .../integrations/alertManager/notifier.go | 310 + .../integrations/signozio/dynamic_config.go | 75 + .../integrations/signozio/response.go | 54 + pkg/query-service/interfaces/featureLookup.go | 13 + pkg/query-service/interfaces/interface.go | 113 + pkg/query-service/main.go | 124 + pkg/query-service/model/auth.go | 71 + pkg/query-service/model/config.go | 57 + pkg/query-service/model/dashboards.go | 254 + pkg/query-service/model/db.go | 100 + pkg/query-service/model/errors.go | 36 + pkg/query-service/model/featureSet.go | 118 + pkg/query-service/model/queryParams.go | 562 + pkg/query-service/model/response.go | 665 + pkg/query-service/model/response_easyjson.go | 328 + pkg/query-service/model/v3/v3.go | 1044 + pkg/query-service/pqlEngine/engine.go | 126 + .../queryBuilderToExpr/queryBuilderToExpr.go | 159 + .../queryBuilderToExpr_test.go | 163 + pkg/query-service/rules/alerting.go | 252 + pkg/query-service/rules/apiParams.go | 272 + pkg/query-service/rules/db.go | 204 + pkg/query-service/rules/manager.go | 921 + pkg/query-service/rules/promRule.go | 629 + pkg/query-service/rules/promRuleTask.go | 356 + pkg/query-service/rules/promrule_test.go | 622 + pkg/query-service/rules/queriers.go | 21 + pkg/query-service/rules/resultTypes.go | 57 + pkg/query-service/rules/rule.go | 38 + pkg/query-service/rules/ruleTask.go | 347 + pkg/query-service/rules/task.go | 37 + pkg/query-service/rules/templates.go | 296 + pkg/query-service/rules/thresholdRule.go | 1307 ++ pkg/query-service/rules/thresholdRule_test.go | 422 + pkg/query-service/telemetry/ignored.go | 24 + pkg/query-service/telemetry/telemetry.go | 563 + .../templates/invitation_email_template.html | 14 + pkg/query-service/tests/auth_test.go | 126 + pkg/query-service/tests/cold_storage_test.go | 223 + pkg/query-service/tests/docker.go | 118 + .../integration/logparsingpipeline_test.go | 786 + .../integration/signoz_integrations_test.go | 586 + .../tests/integration/test_utils.go | 200 + .../tests/test-deploy/alertmanager.yml | 35 + .../tests/test-deploy/alerts.yml | 11 + .../tests/test-deploy/clickhouse-cluster.xml | 75 + .../tests/test-deploy/clickhouse-config.xml | 1139 + .../tests/test-deploy/clickhouse-storage.xml | 29 + .../tests/test-deploy/clickhouse-users.xml | 123 + .../tests/test-deploy/docker-compose.yaml | 282 + .../test-deploy/otel-collector-config.yaml | 147 + .../otel-collector-opamp-config.yaml | 1 + .../tests/test-deploy/prometheus.yml | 25 + .../utils/encryption/encryption.go | 45 + pkg/query-service/utils/format.go | 263 + pkg/query-service/utils/format_test.go | 467 + pkg/query-service/utils/labels/interface.go | 13 + pkg/query-service/utils/labels/labels.go | 314 + pkg/query-service/utils/pass.go | 10 + pkg/query-service/utils/port.go | 21 + pkg/query-service/utils/queryTemplate/vars.go | 42 + pkg/query-service/utils/random.go | 14 + pkg/query-service/utils/slices.go | 29 + pkg/query-service/utils/smtpService/smtp.go | 57 + pkg/query-service/utils/testutils.go | 31 + pkg/query-service/utils/time.go | 20 + pkg/query-service/utils/times/time.go | 35 + .../utils/timestamp/timestamp.go | 13 + pkg/query-service/utils/value/value.go | 40 + pkg/query-service/version/version.go | 48 + sample-apps/hotrod/README.md | 37 + sample-apps/hotrod/hotrod-delete.sh | 20 + sample-apps/hotrod/hotrod-install.sh | 52 + sample-apps/hotrod/hotrod-template.yaml | 202 + sample-apps/hotrod/hotrod.yaml | 202 + 2998 files changed, 254491 insertions(+), 674 deletions(-) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/performance-issue-report.md create mode 100644 .github/config.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/README.md create mode 100644 .github/workflows/build.yaml create mode 100644 .github/workflows/codeball.yml create mode 100644 .github/workflows/codeql.yaml create mode 100644 .github/workflows/commitlint.yml create mode 100644 .github/workflows/create-issue-on-pr-merge.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/e2e-k3s.yaml create mode 100644 .github/workflows/playwright.yaml create mode 100644 .github/workflows/pr_verify_linked_issue.yml create mode 100644 .github/workflows/push.yaml create mode 100644 .github/workflows/release-drafter.yml create mode 100644 .github/workflows/remove-label.yaml create mode 100644 .github/workflows/sonar.yml create mode 100644 .github/workflows/staging-deployment.yaml create mode 100644 .github/workflows/testing-deployment.yaml create mode 100644 .gitignore create mode 100644 .gitpod.yml create mode 100644 .scripts/commentLinesForSetup.sh create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 Makefile create mode 100644 README.de-de.md create mode 100644 README.md create mode 100644 README.pt-br.md create mode 100644 README.zh-cn.md create mode 100644 SECURITY.md create mode 100644 deploy/README.md create mode 100644 deploy/docker-swarm/clickhouse-setup/alertmanager.yml create mode 100644 deploy/docker-swarm/clickhouse-setup/alerts.yml create mode 100644 deploy/docker-swarm/clickhouse-setup/clickhouse-cluster.xml create mode 100644 deploy/docker-swarm/clickhouse-setup/clickhouse-config.xml create mode 100644 deploy/docker-swarm/clickhouse-setup/clickhouse-storage.xml create mode 100644 deploy/docker-swarm/clickhouse-setup/clickhouse-users.xml create mode 100644 deploy/docker-swarm/clickhouse-setup/docker-compose.yaml create mode 100644 deploy/docker-swarm/clickhouse-setup/docker-entrypoint-initdb.d/init-db.sql create mode 100644 deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml create mode 100644 deploy/docker-swarm/clickhouse-setup/otel-collector-opamp-config.yaml create mode 100644 deploy/docker-swarm/clickhouse-setup/prometheus.yml create mode 100644 deploy/docker-swarm/common/locust-scripts/locustfile.py create mode 100644 deploy/docker-swarm/common/nginx-config.conf create mode 100644 deploy/docker-swarm/dashboards/.gitkeep create mode 100644 deploy/docker/clickhouse-setup/alertmanager.yml create mode 100644 deploy/docker/clickhouse-setup/alerts.yml create mode 100644 deploy/docker/clickhouse-setup/clickhouse-cluster.xml create mode 100644 deploy/docker/clickhouse-setup/clickhouse-config.xml create mode 100644 deploy/docker/clickhouse-setup/clickhouse-storage.xml create mode 100644 deploy/docker/clickhouse-setup/clickhouse-users.xml create mode 100644 deploy/docker/clickhouse-setup/custom-function.xml create mode 100644 deploy/docker/clickhouse-setup/docker-compose-core.yaml create mode 100644 deploy/docker/clickhouse-setup/docker-compose-local.yaml create mode 100644 deploy/docker/clickhouse-setup/docker-compose.yaml create mode 100644 deploy/docker/clickhouse-setup/otel-collector-config.yaml create mode 100644 deploy/docker/clickhouse-setup/otel-collector-opamp-config.yaml create mode 100644 deploy/docker/clickhouse-setup/prometheus.yml create mode 100755 deploy/docker/clickhouse-setup/user_scripts/histogramQuantile create mode 100644 deploy/docker/clickhouse-setup/user_scripts/histogramQuantile.go create mode 100644 deploy/docker/common/locust-scripts/locustfile.py create mode 100644 deploy/docker/common/nginx-config.conf create mode 100755 deploy/install.sh create mode 100644 e2e/package.json create mode 100644 e2e/playwright.config.ts create mode 100644 e2e/tests/auth.setup.ts create mode 100644 e2e/tests/contants.ts create mode 100644 e2e/tests/navigation.spec.ts create mode 100644 e2e/yarn.lock create mode 100644 ee/LICENSE create mode 100644 ee/query-service/.dockerignore create mode 100644 ee/query-service/Dockerfile create mode 100644 ee/query-service/app/api/api.go create mode 100644 ee/query-service/app/api/auth.go create mode 100644 ee/query-service/app/api/dashboard.go create mode 100644 ee/query-service/app/api/domains.go create mode 100644 ee/query-service/app/api/featureFlags.go create mode 100644 ee/query-service/app/api/license.go create mode 100644 ee/query-service/app/api/metrics.go create mode 100644 ee/query-service/app/api/pat.go create mode 100644 ee/query-service/app/api/response.go create mode 100644 ee/query-service/app/api/traces.go create mode 100644 ee/query-service/app/db/metrics.go create mode 100644 ee/query-service/app/db/reader.go create mode 100644 ee/query-service/app/db/trace.go create mode 100644 ee/query-service/app/server.go create mode 100644 ee/query-service/auth/auth.go create mode 100644 ee/query-service/constants/constants.go create mode 100644 ee/query-service/dao/factory.go create mode 100644 ee/query-service/dao/interface.go create mode 100644 ee/query-service/dao/sqlite/auth.go create mode 100644 ee/query-service/dao/sqlite/domain.go create mode 100644 ee/query-service/dao/sqlite/modelDao.go create mode 100644 ee/query-service/dao/sqlite/pat.go create mode 100644 ee/query-service/integrations/signozio/response.go create mode 100644 ee/query-service/integrations/signozio/signozio.go create mode 100644 ee/query-service/interfaces/connector.go create mode 100644 ee/query-service/license/db.go create mode 100644 ee/query-service/license/manager.go create mode 100644 ee/query-service/license/sqlite/init.go create mode 100644 ee/query-service/main.go create mode 100644 ee/query-service/model/auth.go create mode 100644 ee/query-service/model/domain.go create mode 100644 ee/query-service/model/errors.go create mode 100644 ee/query-service/model/license.go create mode 100644 ee/query-service/model/pat.go create mode 100644 ee/query-service/model/plans.go create mode 100644 ee/query-service/model/sso.go create mode 100644 ee/query-service/model/trace.go create mode 100644 ee/query-service/model/usage.go create mode 100644 ee/query-service/sso/google.go create mode 100644 ee/query-service/sso/model.go create mode 100644 ee/query-service/sso/saml/request.go create mode 100644 ee/query-service/usage/manager.go create mode 100644 frontend/.babelrc create mode 100644 frontend/.dockerignore create mode 100644 frontend/.eslintignore create mode 100644 frontend/.eslintrc.js create mode 100755 frontend/.husky/commit-msg create mode 100755 frontend/.husky/pre-commit create mode 100644 frontend/.npmrc create mode 100644 frontend/.nvmrc create mode 100644 frontend/.prettierignore create mode 100644 frontend/.prettierrc.json create mode 100644 frontend/.yarnrc create mode 100644 frontend/CONTRIBUTIONS.md create mode 100644 frontend/Dockerfile create mode 100644 frontend/README.md create mode 100644 frontend/__mocks__/cssMock.ts create mode 100644 frontend/babel.config.js create mode 100644 frontend/bundlesize.config.json create mode 100644 frontend/commitlint.config.ts create mode 100644 frontend/conf/default.conf create mode 100644 frontend/docker-compose.yml create mode 100644 frontend/example.env create mode 100644 frontend/i18-generate-hash.js create mode 100644 frontend/jest.config.ts create mode 100644 frontend/jest.setup.ts create mode 100644 frontend/package.json create mode 100644 frontend/playwright.config.ts create mode 100644 frontend/public/Icons/awwSnap.svg create mode 100644 frontend/public/Icons/emptyState.svg create mode 100644 frontend/public/Icons/loading-plane.gif create mode 100644 frontend/public/Icons/promQL.svg create mode 100644 frontend/public/Icons/redis-logo.svg create mode 100644 frontend/public/Icons/tetra-pack.svg create mode 100644 frontend/public/Images/eyesEmoji.svg create mode 100644 frontend/public/Images/notFound404.png create mode 100644 frontend/public/Logos/cloudwatch.png create mode 100644 frontend/public/Logos/cmd-terminal.svg create mode 100644 frontend/public/Logos/docker.svg create mode 100644 frontend/public/Logos/dotnet.png create mode 100644 frontend/public/Logos/ec2.svg create mode 100644 frontend/public/Logos/ecs.svg create mode 100644 frontend/public/Logos/eks.svg create mode 100644 frontend/public/Logos/elixir.png create mode 100644 frontend/public/Logos/fluent-bit.png create mode 100644 frontend/public/Logos/fluentd.png create mode 100644 frontend/public/Logos/go.png create mode 100644 frontend/public/Logos/heroku.png create mode 100644 frontend/public/Logos/http.png create mode 100644 frontend/public/Logos/java.png create mode 100644 frontend/public/Logos/javascript.png create mode 100644 frontend/public/Logos/kubernetes.svg create mode 100644 frontend/public/Logos/logstash.svg create mode 100644 frontend/public/Logos/ms-net-framework.png create mode 100644 frontend/public/Logos/node-js.svg create mode 100644 frontend/public/Logos/php.png create mode 100644 frontend/public/Logos/python.png create mode 100644 frontend/public/Logos/rails.png create mode 100644 frontend/public/Logos/rust.png create mode 100644 frontend/public/Logos/signoz-brand-logo.svg create mode 100644 frontend/public/Logos/software-window.svg create mode 100644 frontend/public/Logos/swift.png create mode 100644 frontend/public/Logos/syslogs.svg create mode 100644 frontend/public/Logos/vercel.png create mode 100644 frontend/public/SigNoz-dark.svg create mode 100644 frontend/public/SigNoz-white.svg create mode 100644 frontend/public/favicon.ico create mode 100644 frontend/public/locales/en-GB/alerts.json create mode 100644 frontend/public/locales/en-GB/channels.json create mode 100644 frontend/public/locales/en-GB/common.json create mode 100644 frontend/public/locales/en-GB/dashboard.json create mode 100644 frontend/public/locales/en-GB/errorDetails.json create mode 100644 frontend/public/locales/en-GB/explorer.json create mode 100644 frontend/public/locales/en-GB/generalSettings.json create mode 100644 frontend/public/locales/en-GB/licenses.json create mode 100644 frontend/public/locales/en-GB/login.json create mode 100644 frontend/public/locales/en-GB/logs.json create mode 100644 frontend/public/locales/en-GB/organizationsettings.json create mode 100644 frontend/public/locales/en-GB/routes.json create mode 100644 frontend/public/locales/en-GB/rules.json create mode 100644 frontend/public/locales/en-GB/services.json create mode 100644 frontend/public/locales/en-GB/settings.json create mode 100644 frontend/public/locales/en-GB/signup.json create mode 100644 frontend/public/locales/en-GB/titles.json create mode 100644 frontend/public/locales/en-GB/trace.json create mode 100644 frontend/public/locales/en-GB/traceDetails.json create mode 100644 frontend/public/locales/en-GB/translation.json create mode 100644 frontend/public/locales/en/alerts.json create mode 100644 frontend/public/locales/en/apiKeys.json create mode 100644 frontend/public/locales/en/channels.json create mode 100644 frontend/public/locales/en/common.json create mode 100644 frontend/public/locales/en/dashboard.json create mode 100644 frontend/public/locales/en/errorDetails.json create mode 100644 frontend/public/locales/en/explorer.json create mode 100644 frontend/public/locales/en/generalSettings.json create mode 100644 frontend/public/locales/en/licenses.json create mode 100644 frontend/public/locales/en/login.json create mode 100644 frontend/public/locales/en/logs.json create mode 100644 frontend/public/locales/en/organizationsettings.json create mode 100644 frontend/public/locales/en/pipeline.json create mode 100644 frontend/public/locales/en/routes.json create mode 100644 frontend/public/locales/en/rules.json create mode 100644 frontend/public/locales/en/services.json create mode 100644 frontend/public/locales/en/settings.json create mode 100644 frontend/public/locales/en/signup.json create mode 100644 frontend/public/locales/en/titles.json create mode 100644 frontend/public/locales/en/trace.json create mode 100644 frontend/public/locales/en/traceDetails.json create mode 100644 frontend/public/locales/en/translation.json create mode 100644 frontend/public/locales/en/valueGraph.json create mode 100644 frontend/public/manifest.json create mode 100644 frontend/public/robots.txt create mode 100644 frontend/public/signoz-signup.svg create mode 100644 frontend/public/signoz.svg create mode 100644 frontend/scripts/typecheck-staged.sh create mode 100644 frontend/sonar-project.properties create mode 100644 frontend/src/AppRoutes/Private.tsx create mode 100644 frontend/src/AppRoutes/index.tsx create mode 100644 frontend/src/AppRoutes/pageComponents.ts create mode 100644 frontend/src/AppRoutes/routes.ts create mode 100644 frontend/src/AppRoutes/utils.ts create mode 100644 frontend/src/ReactI18/index.tsx create mode 100644 frontend/src/api/APIKeys/createAPIKey.ts create mode 100644 frontend/src/api/APIKeys/deleteAPIKey.ts create mode 100644 frontend/src/api/APIKeys/getAPIKey.ts create mode 100644 frontend/src/api/APIKeys/getAllAPIKeys.ts create mode 100644 frontend/src/api/APIKeys/updateAPIKey.ts create mode 100644 frontend/src/api/ErrorResponseHandler.ts create mode 100644 frontend/src/api/Integrations/getAllIntegrations.ts create mode 100644 frontend/src/api/Integrations/getIntegration.ts create mode 100644 frontend/src/api/Integrations/getIntegrationStatus.ts create mode 100644 frontend/src/api/Integrations/installIntegration.ts create mode 100644 frontend/src/api/Integrations/uninstallIntegration.ts create mode 100644 frontend/src/api/SAML/deleteDomain.ts create mode 100644 frontend/src/api/SAML/listAllDomain.ts create mode 100644 frontend/src/api/SAML/postDomain.ts create mode 100644 frontend/src/api/SAML/updateDomain.ts create mode 100644 frontend/src/api/alerts/create.ts create mode 100644 frontend/src/api/alerts/delete.ts create mode 100644 frontend/src/api/alerts/get.ts create mode 100644 frontend/src/api/alerts/getAll.ts create mode 100644 frontend/src/api/alerts/getGroup.ts create mode 100644 frontend/src/api/alerts/getTriggered.ts create mode 100644 frontend/src/api/alerts/patch.ts create mode 100644 frontend/src/api/alerts/put.ts create mode 100644 frontend/src/api/alerts/save.ts create mode 100644 frontend/src/api/alerts/testAlert.ts create mode 100644 frontend/src/api/apiV1.ts create mode 100644 frontend/src/api/billing/checkout.ts create mode 100644 frontend/src/api/billing/getUsage.ts create mode 100644 frontend/src/api/billing/manage.ts create mode 100644 frontend/src/api/browser/localstorage/get.ts create mode 100644 frontend/src/api/browser/localstorage/remove.ts create mode 100644 frontend/src/api/browser/localstorage/set.ts create mode 100644 frontend/src/api/channels/createEmail.ts create mode 100644 frontend/src/api/channels/createMsTeams.ts create mode 100644 frontend/src/api/channels/createOpsgenie.ts create mode 100644 frontend/src/api/channels/createPager.ts create mode 100644 frontend/src/api/channels/createSlack.ts create mode 100644 frontend/src/api/channels/createWebhook.ts create mode 100644 frontend/src/api/channels/delete.ts create mode 100644 frontend/src/api/channels/editEmail.ts create mode 100644 frontend/src/api/channels/editMsTeams.ts create mode 100644 frontend/src/api/channels/editOpsgenie.ts create mode 100644 frontend/src/api/channels/editPager.ts create mode 100644 frontend/src/api/channels/editSlack.ts create mode 100644 frontend/src/api/channels/editWebhook.ts create mode 100644 frontend/src/api/channels/get.ts create mode 100644 frontend/src/api/channels/getAll.ts create mode 100644 frontend/src/api/channels/testEmail.ts create mode 100644 frontend/src/api/channels/testMsTeams.ts create mode 100644 frontend/src/api/channels/testOpsgenie.ts create mode 100644 frontend/src/api/channels/testPager.ts create mode 100644 frontend/src/api/channels/testSlack.ts create mode 100644 frontend/src/api/channels/testWebhook.ts create mode 100644 frontend/src/api/dashboard/create.ts create mode 100644 frontend/src/api/dashboard/delete.ts create mode 100644 frontend/src/api/dashboard/get.ts create mode 100644 frontend/src/api/dashboard/getAll.ts create mode 100644 frontend/src/api/dashboard/lockDashboard.ts create mode 100644 frontend/src/api/dashboard/queryRangeFormat.ts create mode 100644 frontend/src/api/dashboard/unlockDashboard.ts create mode 100644 frontend/src/api/dashboard/update.ts create mode 100644 frontend/src/api/dashboard/variables/dashboardVariablesQuery.ts create mode 100644 frontend/src/api/disks/getDisks.ts create mode 100644 frontend/src/api/dynamicConfigs/getDynamicConfigs.ts create mode 100644 frontend/src/api/errors/getAll.ts create mode 100644 frontend/src/api/errors/getByErrorTypeAndService.ts create mode 100644 frontend/src/api/errors/getById.ts create mode 100644 frontend/src/api/errors/getErrorCounts.ts create mode 100644 frontend/src/api/errors/getNextPrevId.ts create mode 100644 frontend/src/api/features/getFeatureFlags.ts create mode 100644 frontend/src/api/index.ts create mode 100644 frontend/src/api/licenses/apply.ts create mode 100644 frontend/src/api/licenses/getAll.ts create mode 100644 frontend/src/api/logs/AddToSelectedField.ts create mode 100644 frontend/src/api/logs/GetLogs.ts create mode 100644 frontend/src/api/logs/GetLogsAggregate.ts create mode 100644 frontend/src/api/logs/GetSearchFields.ts create mode 100644 frontend/src/api/logs/RemoveFromSelectedField.ts create mode 100644 frontend/src/api/logs/livetail.ts create mode 100644 frontend/src/api/metrics/ApDex/apDexSettings.ts create mode 100644 frontend/src/api/metrics/ApDex/getApDexSettings.ts create mode 100644 frontend/src/api/metrics/ApDex/getMetricMeta.ts create mode 100644 frontend/src/api/metrics/getDBOverView.ts create mode 100644 frontend/src/api/metrics/getExternalAverageDuration.ts create mode 100644 frontend/src/api/metrics/getExternalError.ts create mode 100644 frontend/src/api/metrics/getExternalService.ts create mode 100644 frontend/src/api/metrics/getMetricName.ts create mode 100644 frontend/src/api/metrics/getQueryRange.ts create mode 100644 frontend/src/api/metrics/getResourceAttributes.ts create mode 100644 frontend/src/api/metrics/getService.ts create mode 100644 frontend/src/api/metrics/getServiceOverview.ts create mode 100644 frontend/src/api/metrics/getTopLevelOperations.ts create mode 100644 frontend/src/api/metrics/getTopOperations.ts create mode 100644 frontend/src/api/pipeline/get.ts create mode 100644 frontend/src/api/pipeline/post.ts create mode 100644 frontend/src/api/pipeline/preview.ts create mode 100644 frontend/src/api/queryBuilder/getAggregateAttribute.ts create mode 100644 frontend/src/api/queryBuilder/getAttributeKeys.ts create mode 100644 frontend/src/api/queryBuilder/getAttributesValues.ts create mode 100644 frontend/src/api/saveView/deleteView.ts create mode 100644 frontend/src/api/saveView/getAllViews.ts create mode 100644 frontend/src/api/saveView/saveView.ts create mode 100644 frontend/src/api/saveView/updateView.ts create mode 100644 frontend/src/api/settings/getIngestionData.ts create mode 100644 frontend/src/api/settings/getRetention.ts create mode 100644 frontend/src/api/settings/setRetention.ts create mode 100644 frontend/src/api/trace/getFilters.ts create mode 100644 frontend/src/api/trace/getSpans.ts create mode 100644 frontend/src/api/trace/getSpansAggregate.ts create mode 100644 frontend/src/api/trace/getTagFilter.ts create mode 100644 frontend/src/api/trace/getTagValue.ts create mode 100644 frontend/src/api/trace/getTraceItem.ts create mode 100644 frontend/src/api/user/changeMyPassword.ts create mode 100644 frontend/src/api/user/deleteInvite.ts create mode 100644 frontend/src/api/user/deleteUser.ts create mode 100644 frontend/src/api/user/editOrg.ts create mode 100644 frontend/src/api/user/editUser.ts create mode 100644 frontend/src/api/user/getInviteDetails.ts create mode 100644 frontend/src/api/user/getLatestVersion.ts create mode 100644 frontend/src/api/user/getOrgUser.ts create mode 100644 frontend/src/api/user/getOrganization.ts create mode 100644 frontend/src/api/user/getPendingInvites.ts create mode 100644 frontend/src/api/user/getPreference.ts create mode 100644 frontend/src/api/user/getResetPasswordToken.ts create mode 100644 frontend/src/api/user/getRoles.ts create mode 100644 frontend/src/api/user/getUser.ts create mode 100644 frontend/src/api/user/getVersion.ts create mode 100644 frontend/src/api/user/login.ts create mode 100644 frontend/src/api/user/loginPrecheck.ts create mode 100644 frontend/src/api/user/resetPassword.ts create mode 100644 frontend/src/api/user/sendInvite.ts create mode 100644 frontend/src/api/user/setFlags.ts create mode 100644 frontend/src/api/user/signup.ts create mode 100644 frontend/src/api/user/updateRole.ts create mode 100644 frontend/src/api/userFeedback/sendFeedback.ts create mode 100644 frontend/src/api/utils.ts create mode 100644 frontend/src/api/widgets/getQuery.ts create mode 100644 frontend/src/assets/Dashboard/BarIcon.tsx create mode 100644 frontend/src/assets/Dashboard/List.tsx create mode 100644 frontend/src/assets/Dashboard/Table.tsx create mode 100644 frontend/src/assets/Dashboard/TimeSeries.tsx create mode 100644 frontend/src/assets/Dashboard/Value.tsx create mode 100644 frontend/src/assets/NotFound.tsx create mode 100644 frontend/src/assets/SomethingWentWrong.tsx create mode 100644 frontend/src/assets/UnAuthorized.tsx create mode 100644 frontend/src/components/CustomTimePicker/CustomTimePicker.styles.scss create mode 100644 frontend/src/components/CustomTimePicker/CustomTimePicker.tsx create mode 100644 frontend/src/components/CustomTimePicker/CustomTimePickerPopoverContent.tsx create mode 100644 frontend/src/components/CustomTimePicker/RangePickerModal.styles.scss create mode 100644 frontend/src/components/CustomTimePicker/RangePickerModal.tsx create mode 100644 frontend/src/components/DraggableTableRow/index.tsx create mode 100644 frontend/src/components/DraggableTableRow/tests/DraggableTableRow.test.tsx create mode 100644 frontend/src/components/DraggableTableRow/tests/__snapshots__/DraggableTableRow.test.tsx.snap create mode 100644 frontend/src/components/DraggableTableRow/tests/utils.test.ts create mode 100644 frontend/src/components/DraggableTableRow/utils.ts create mode 100644 frontend/src/components/DropDown/DropDown.styles.scss create mode 100644 frontend/src/components/DropDown/DropDown.tsx create mode 100644 frontend/src/components/Editor/Editor.test.tsx create mode 100644 frontend/src/components/Editor/__snapshots__/Editor.test.tsx.snap create mode 100644 frontend/src/components/Editor/index.tsx create mode 100644 frontend/src/components/ExplorerCard/ExplorerCard.tsx create mode 100644 frontend/src/components/ExplorerCard/MenuItemGenerator.tsx create mode 100644 frontend/src/components/ExplorerCard/SaveViewWithName.tsx create mode 100644 frontend/src/components/ExplorerCard/__mock__/viewData.ts create mode 100644 frontend/src/components/ExplorerCard/constants.ts create mode 100644 frontend/src/components/ExplorerCard/styles.ts create mode 100644 frontend/src/components/ExplorerCard/test/ExplorerCard.test.tsx create mode 100644 frontend/src/components/ExplorerCard/test/MenuItemGenerator.test.tsx create mode 100644 frontend/src/components/ExplorerCard/test/SaveViewWithName.test.tsx create mode 100644 frontend/src/components/ExplorerCard/types.ts create mode 100644 frontend/src/components/ExplorerCard/utils.ts create mode 100644 frontend/src/components/Graph/Plugin/DragSelect.ts create mode 100644 frontend/src/components/Graph/Plugin/EmptyGraph.ts create mode 100644 frontend/src/components/Graph/Plugin/IntersectionCursor.ts create mode 100644 frontend/src/components/Graph/Plugin/Legend.ts create mode 100644 frontend/src/components/Graph/Plugin/Tooltip.ts create mode 100644 frontend/src/components/Graph/Plugin/index.ts create mode 100644 frontend/src/components/Graph/Plugin/utils.ts create mode 100644 frontend/src/components/Graph/__tests__/xAxisConfig.test.ts create mode 100644 frontend/src/components/Graph/hasData.ts create mode 100644 frontend/src/components/Graph/helpers.ts create mode 100644 frontend/src/components/Graph/index.tsx create mode 100644 frontend/src/components/Graph/styles.ts create mode 100644 frontend/src/components/Graph/types.ts create mode 100644 frontend/src/components/Graph/utils.ts create mode 100644 frontend/src/components/Graph/xAxisConfig.ts create mode 100644 frontend/src/components/Graph/yAxisConfig.ts create mode 100644 frontend/src/components/Input/index.tsx create mode 100644 frontend/src/components/Loadable/Loadable.test.tsx create mode 100644 frontend/src/components/Loadable/index.tsx create mode 100644 frontend/src/components/LogDetail/LogDetail.interfaces.ts create mode 100644 frontend/src/components/LogDetail/LogDetails.styles.scss create mode 100644 frontend/src/components/LogDetail/QueryBuilderSearchWrapper.styles.scss create mode 100644 frontend/src/components/LogDetail/QueryBuilderSearchWrapper.tsx create mode 100644 frontend/src/components/LogDetail/constants.ts create mode 100644 frontend/src/components/LogDetail/index.tsx create mode 100644 frontend/src/components/Logs/AddToQueryHOC.styles.scss create mode 100644 frontend/src/components/Logs/AddToQueryHOC.tsx create mode 100644 frontend/src/components/Logs/CategoryHeading/index.tsx create mode 100644 frontend/src/components/Logs/CategoryHeading/styles.ts create mode 100644 frontend/src/components/Logs/CopyClipboardHOC.tsx create mode 100644 frontend/src/components/Logs/ListLogView/ListLogView.styles.scss create mode 100644 frontend/src/components/Logs/ListLogView/index.tsx create mode 100644 frontend/src/components/Logs/ListLogView/styles.ts create mode 100644 frontend/src/components/Logs/ListLogView/util.ts create mode 100644 frontend/src/components/Logs/LogLinesActionButtons/LogLinesActionButtons.styles.scss create mode 100644 frontend/src/components/Logs/LogLinesActionButtons/LogLinesActionButtons.tsx create mode 100644 frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.styles.scss create mode 100644 frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.test.tsx create mode 100644 frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.tsx create mode 100644 frontend/src/components/Logs/LogStateIndicator/utils.test.ts create mode 100644 frontend/src/components/Logs/LogStateIndicator/utils.ts create mode 100644 frontend/src/components/Logs/RawLogView/RawLogView.styles.scss create mode 100644 frontend/src/components/Logs/RawLogView/config.ts create mode 100644 frontend/src/components/Logs/RawLogView/index.tsx create mode 100644 frontend/src/components/Logs/RawLogView/styles.ts create mode 100644 frontend/src/components/Logs/RawLogView/types.ts create mode 100644 frontend/src/components/Logs/TableView/config.ts create mode 100644 frontend/src/components/Logs/TableView/index.tsx create mode 100644 frontend/src/components/Logs/TableView/styles.ts create mode 100644 frontend/src/components/Logs/TableView/types.ts create mode 100644 frontend/src/components/Logs/TableView/useTableView.styles.scss create mode 100644 frontend/src/components/Logs/TableView/useTableView.tsx create mode 100644 frontend/src/components/Logs/styles.ts create mode 100644 frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss create mode 100644 frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx create mode 100644 frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.scss create mode 100644 frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx create mode 100644 frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx create mode 100644 frontend/src/components/MessageTip/MessageTip.test.tsx create mode 100644 frontend/src/components/MessageTip/__snapshots__/MessageTip.test.tsx.snap create mode 100644 frontend/src/components/MessageTip/index.tsx create mode 100644 frontend/src/components/MessageTip/styles.ts create mode 100644 frontend/src/components/Modal.tsx create mode 100644 frontend/src/components/NotFound/NotFound.test.tsx create mode 100644 frontend/src/components/NotFound/__snapshots__/NotFound.test.tsx.snap create mode 100644 frontend/src/components/NotFound/constant.ts create mode 100644 frontend/src/components/NotFound/index.tsx create mode 100644 frontend/src/components/NotFound/styles.ts create mode 100644 frontend/src/components/ReleaseNote/ReleaseNoteProps.ts create mode 100644 frontend/src/components/ReleaseNote/Releases/ReleaseNote0120.tsx create mode 100644 frontend/src/components/ReleaseNote/index.tsx create mode 100644 frontend/src/components/ResizeTable/DynamicColumnTable.syles.scss create mode 100644 frontend/src/components/ResizeTable/DynamicColumnTable.tsx create mode 100644 frontend/src/components/ResizeTable/ResizableHeader.tsx create mode 100644 frontend/src/components/ResizeTable/ResizeTable.tsx create mode 100644 frontend/src/components/ResizeTable/TableComponent/DateComponent.tsx create mode 100644 frontend/src/components/ResizeTable/TableComponent/Time.tsx create mode 100644 frontend/src/components/ResizeTable/config.ts create mode 100644 frontend/src/components/ResizeTable/contants.ts create mode 100644 frontend/src/components/ResizeTable/index.ts create mode 100644 frontend/src/components/ResizeTable/styles.ts create mode 100644 frontend/src/components/ResizeTable/types.ts create mode 100644 frontend/src/components/ResizeTable/utils.ts create mode 100644 frontend/src/components/RouteTab/RouteTab.test.tsx create mode 100644 frontend/src/components/RouteTab/index.tsx create mode 100644 frontend/src/components/RouteTab/types.ts create mode 100644 frontend/src/components/Spinner/index.tsx create mode 100644 frontend/src/components/Spinner/styles.ts create mode 100644 frontend/src/components/Styled/index.ts create mode 100644 frontend/src/components/Styled/styles.ts create mode 100644 frontend/src/components/Styled/types.ts create mode 100644 frontend/src/components/TabLabel/TabLabel.interfaces.ts create mode 100644 frontend/src/components/TabLabel/index.tsx create mode 100644 frontend/src/components/TableRenderer/LabelColumn.styles.scss create mode 100644 frontend/src/components/TableRenderer/LabelColumn.tsx create mode 100644 frontend/src/components/TableRenderer/TableRenderer.types.ts create mode 100644 frontend/src/components/TableRenderer/TagWithToolTip.tsx create mode 100644 frontend/src/components/TableRenderer/utils.ts create mode 100644 frontend/src/components/TextToolTip/TextToolTip.style.scss create mode 100644 frontend/src/components/TextToolTip/TextToolTip.test.tsx create mode 100644 frontend/src/components/TextToolTip/TextToolTip.tsx create mode 100644 frontend/src/components/TextToolTip/constant.ts create mode 100644 frontend/src/components/TextToolTip/index.tsx create mode 100644 frontend/src/components/TimePreferenceDropDown/config.tsx create mode 100644 frontend/src/components/TimePreferenceDropDown/index.tsx create mode 100644 frontend/src/components/TimePreferenceDropDown/styles.ts create mode 100644 frontend/src/components/Upgrade/UpgradePrompt.tsx create mode 100644 frontend/src/components/Uplot/Uplot.styles.scss create mode 100644 frontend/src/components/Uplot/Uplot.tsx create mode 100644 frontend/src/components/Uplot/index.ts create mode 100644 frontend/src/components/Uplot/utils.ts create mode 100644 frontend/src/components/ValueGraph/ValueGraph.styles.scss create mode 100644 frontend/src/components/ValueGraph/index.tsx create mode 100644 frontend/src/components/ValueGraph/utils.ts create mode 100644 frontend/src/components/WelcomeLeftContainer/index.tsx create mode 100644 frontend/src/components/WelcomeLeftContainer/styles.ts create mode 100644 frontend/src/constants/alerts.ts create mode 100644 frontend/src/constants/apDex.ts create mode 100644 frontend/src/constants/api.ts create mode 100644 frontend/src/constants/app.ts create mode 100644 frontend/src/constants/card.ts create mode 100644 frontend/src/constants/events.ts create mode 100644 frontend/src/constants/features.ts create mode 100644 frontend/src/constants/global.ts create mode 100644 frontend/src/constants/liveTail.ts create mode 100644 frontend/src/constants/localStorage.ts create mode 100644 frontend/src/constants/onboarding.ts create mode 100644 frontend/src/constants/optionsFormatTypes.ts create mode 100644 frontend/src/constants/panelTypes.ts create mode 100644 frontend/src/constants/query.ts create mode 100644 frontend/src/constants/queryBuilder.ts create mode 100644 frontend/src/constants/queryBuilderFilterConfig.ts create mode 100644 frontend/src/constants/queryBuilderOperators.ts create mode 100644 frontend/src/constants/queryFunctionOptions.ts create mode 100644 frontend/src/constants/reactQueryKeys.ts create mode 100644 frontend/src/constants/regExp.ts create mode 100644 frontend/src/constants/resourceAttributes.ts create mode 100644 frontend/src/constants/routes.ts create mode 100644 frontend/src/constants/shortcuts/DashboardShortcuts.ts create mode 100644 frontend/src/constants/shortcuts/QBShortcuts.ts create mode 100644 frontend/src/constants/shortcuts/globalShortcuts.ts create mode 100644 frontend/src/constants/shortcuts/logsExplorerShortcuts.ts create mode 100644 frontend/src/constants/theme.ts create mode 100644 frontend/src/container/APIKeys/APIKeys.styles.scss create mode 100644 frontend/src/container/APIKeys/APIKeys.test.tsx create mode 100644 frontend/src/container/APIKeys/APIKeys.tsx create mode 100644 frontend/src/container/AllAlertChannels/AlertChannels.tsx create mode 100644 frontend/src/container/AllAlertChannels/Delete.tsx create mode 100644 frontend/src/container/AllAlertChannels/index.tsx create mode 100644 frontend/src/container/AllAlertChannels/styles.ts create mode 100644 frontend/src/container/AllError/constant.ts create mode 100644 frontend/src/container/AllError/index.tsx create mode 100644 frontend/src/container/AllError/types.ts create mode 100644 frontend/src/container/AllError/utils.test.ts create mode 100644 frontend/src/container/AllError/utils.ts create mode 100644 frontend/src/container/AppLayout/AppLayout.styles.scss create mode 100644 frontend/src/container/AppLayout/index.tsx create mode 100644 frontend/src/container/AppLayout/styles.ts create mode 100644 frontend/src/container/AppLayout/utils.ts create mode 100644 frontend/src/container/BillingContainer/BillingContainer.styles.scss create mode 100644 frontend/src/container/BillingContainer/BillingContainer.test.tsx create mode 100644 frontend/src/container/BillingContainer/BillingContainer.tsx create mode 100644 frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.styles.scss create mode 100644 frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx create mode 100644 frontend/src/container/BillingContainer/BillingUsageGraph/utils.ts create mode 100644 frontend/src/container/ConfigDropdown/Config/ErrorLink.tsx create mode 100644 frontend/src/container/ConfigDropdown/Config/Link.tsx create mode 100644 frontend/src/container/ConfigDropdown/Config/index.tsx create mode 100644 frontend/src/container/ConfigDropdown/index.tsx create mode 100644 frontend/src/container/ConfigDropdown/styles.ts create mode 100644 frontend/src/container/Controls/config.ts create mode 100644 frontend/src/container/Controls/index.tsx create mode 100644 frontend/src/container/Controls/styles.ts create mode 100644 frontend/src/container/CreateAlertChannels/config.ts create mode 100644 frontend/src/container/CreateAlertChannels/defaults.ts create mode 100644 frontend/src/container/CreateAlertChannels/index.tsx create mode 100644 frontend/src/container/CreateAlertChannels/utils.ts create mode 100644 frontend/src/container/CreateAlertRule/SelectAlertType/config.ts create mode 100644 frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx create mode 100644 frontend/src/container/CreateAlertRule/SelectAlertType/styles.ts create mode 100644 frontend/src/container/CreateAlertRule/SelectAlertType/types.ts create mode 100644 frontend/src/container/CreateAlertRule/config.ts create mode 100644 frontend/src/container/CreateAlertRule/defaults.ts create mode 100644 frontend/src/container/CreateAlertRule/index.tsx create mode 100644 frontend/src/container/Download/Download.styles.scss create mode 100644 frontend/src/container/Download/Download.tsx create mode 100644 frontend/src/container/Download/Download.types.ts create mode 100644 frontend/src/container/EditAlertChannels/index.tsx create mode 100644 frontend/src/container/EditRules/index.tsx create mode 100644 frontend/src/container/EditRules/styles.ts create mode 100644 frontend/src/container/EmptyLogsSearch/EmptyLogsSearch.styles.scss create mode 100644 frontend/src/container/EmptyLogsSearch/EmptyLogsSearch.tsx create mode 100644 frontend/src/container/ErrorDetails/config.ts create mode 100644 frontend/src/container/ErrorDetails/index.tsx create mode 100644 frontend/src/container/ErrorDetails/styles.scss create mode 100644 frontend/src/container/ErrorDetails/styles.ts create mode 100644 frontend/src/container/ExplorerControlPanel/ExplorerControlPanel.interfaces.ts create mode 100644 frontend/src/container/ExplorerControlPanel/index.tsx create mode 100644 frontend/src/container/ExplorerControlPanel/styles.ts create mode 100644 frontend/src/container/ExplorerOptions/ExplorerOptions.styles.scss create mode 100644 frontend/src/container/ExplorerOptions/ExplorerOptions.tsx create mode 100644 frontend/src/container/ExplorerOptions/types.ts create mode 100644 frontend/src/container/ExplorerOptions/utils.ts create mode 100644 frontend/src/container/ExplorerOrderBy/index.tsx create mode 100644 frontend/src/container/ExportPanel/ExportPanelContainer.tsx create mode 100644 frontend/src/container/ExportPanel/index.tsx create mode 100644 frontend/src/container/ExportPanel/styles.ts create mode 100644 frontend/src/container/ExportPanel/utils.ts create mode 100644 frontend/src/container/FormAlertChannels/Settings/Email.tsx create mode 100644 frontend/src/container/FormAlertChannels/Settings/LabelFilter.tsx create mode 100644 frontend/src/container/FormAlertChannels/Settings/MsTeams.tsx create mode 100644 frontend/src/container/FormAlertChannels/Settings/Opsgenie.tsx create mode 100644 frontend/src/container/FormAlertChannels/Settings/Pager.tsx create mode 100644 frontend/src/container/FormAlertChannels/Settings/Slack.tsx create mode 100644 frontend/src/container/FormAlertChannels/Settings/Webhook.tsx create mode 100644 frontend/src/container/FormAlertChannels/index.tsx create mode 100644 frontend/src/container/FormAlertChannels/styles.ts create mode 100644 frontend/src/container/FormAlertRules/BasicInfo.tsx create mode 100644 frontend/src/container/FormAlertRules/ChQuerySection/ChQuerySection.tsx create mode 100644 frontend/src/container/FormAlertRules/ChQuerySection/index.ts create mode 100644 frontend/src/container/FormAlertRules/ChannelSelect/index.tsx create mode 100644 frontend/src/container/FormAlertRules/ChannelSelect/styles.ts create mode 100644 frontend/src/container/FormAlertRules/ChartPreview/config.ts create mode 100644 frontend/src/container/FormAlertRules/ChartPreview/index.tsx create mode 100644 frontend/src/container/FormAlertRules/ChartPreview/styles.ts create mode 100644 frontend/src/container/FormAlertRules/ChartPreview/utils.test.ts create mode 100644 frontend/src/container/FormAlertRules/ChartPreview/utils.ts create mode 100644 frontend/src/container/FormAlertRules/FormAlertRules.styles.scss create mode 100644 frontend/src/container/FormAlertRules/PromqlSection.tsx create mode 100644 frontend/src/container/FormAlertRules/QuerySection.styles.scss create mode 100644 frontend/src/container/FormAlertRules/QuerySection.tsx create mode 100644 frontend/src/container/FormAlertRules/RuleOptions.tsx create mode 100644 frontend/src/container/FormAlertRules/UserGuide/index.tsx create mode 100644 frontend/src/container/FormAlertRules/UserGuide/styles.ts create mode 100644 frontend/src/container/FormAlertRules/index.tsx create mode 100644 frontend/src/container/FormAlertRules/labels/Labels.machine.ts create mode 100644 frontend/src/container/FormAlertRules/labels/Labels.machine.typegen.ts create mode 100644 frontend/src/container/FormAlertRules/labels/QueryChip.tsx create mode 100644 frontend/src/container/FormAlertRules/labels/index.tsx create mode 100644 frontend/src/container/FormAlertRules/labels/styles.ts create mode 100644 frontend/src/container/FormAlertRules/labels/types.ts create mode 100644 frontend/src/container/FormAlertRules/labels/utils.ts create mode 100644 frontend/src/container/FormAlertRules/styles.ts create mode 100644 frontend/src/container/FormAlertRules/utils.test.ts create mode 100644 frontend/src/container/FormAlertRules/utils.ts create mode 100644 frontend/src/container/FullScreenHeader/FullScreenHeader.styles.scss create mode 100644 frontend/src/container/FullScreenHeader/FullScreenHeader.tsx create mode 100644 frontend/src/container/GantChart/GantChart.styles.scss create mode 100644 frontend/src/container/GantChart/Span/index.tsx create mode 100644 frontend/src/container/GantChart/Span/styles.ts create mode 100644 frontend/src/container/GantChart/SpanName/index.tsx create mode 100644 frontend/src/container/GantChart/SpanName/styles.ts create mode 100644 frontend/src/container/GantChart/Trace/index.tsx create mode 100644 frontend/src/container/GantChart/Trace/styles.ts create mode 100644 frontend/src/container/GantChart/Trace/utils.ts create mode 100644 frontend/src/container/GantChart/index.tsx create mode 100644 frontend/src/container/GantChart/styles.ts create mode 100644 frontend/src/container/GantChart/utils.ts create mode 100644 frontend/src/container/GeneralSettings/GeneralSettings.tsx create mode 100644 frontend/src/container/GeneralSettings/Retention.tsx create mode 100644 frontend/src/container/GeneralSettings/StatusMessage.tsx create mode 100644 frontend/src/container/GeneralSettings/index.tsx create mode 100644 frontend/src/container/GeneralSettings/styles.ts create mode 100644 frontend/src/container/GeneralSettings/utils.ts create mode 100644 frontend/src/container/GeneralSettingsCloud/GeneralSettingsCloud.styles.scss create mode 100644 frontend/src/container/GeneralSettingsCloud/GeneralSettingsCloud.tsx create mode 100644 frontend/src/container/GeneralSettingsCloud/index.tsx create mode 100644 frontend/src/container/GoToTop/index.tsx create mode 100644 frontend/src/container/GridCardLayout/EmptyWidget/index.tsx create mode 100644 frontend/src/container/GridCardLayout/EmptyWidget/styles.ts create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/GraphManager.tsx create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/CustomCheckBox.tsx create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GetLabel.tsx create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/GraphManagerColumns.tsx create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/TableRender/Label.tsx create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/WidgetFullView.styles.scss create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/contants.ts create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/types.ts create mode 100644 frontend/src/container/GridCardLayout/GridCard/FullView/utils.ts create mode 100644 frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx create mode 100644 frontend/src/container/GridCardLayout/GridCard/__mock__/mockChartData.ts create mode 100644 frontend/src/container/GridCardLayout/GridCard/__mock__/mockLegendEntryData.ts create mode 100644 frontend/src/container/GridCardLayout/GridCard/index.tsx create mode 100644 frontend/src/container/GridCardLayout/GridCard/styles.ts create mode 100644 frontend/src/container/GridCardLayout/GridCard/types.ts create mode 100644 frontend/src/container/GridCardLayout/GridCard/utils.ts create mode 100644 frontend/src/container/GridCardLayout/GridCardLayout.styles.scss create mode 100644 frontend/src/container/GridCardLayout/GridCardLayout.tsx create mode 100644 frontend/src/container/GridCardLayout/WidgetHeader/DisplayThreshold.tsx create mode 100644 frontend/src/container/GridCardLayout/WidgetHeader/WidgetHeader.styles.scss create mode 100644 frontend/src/container/GridCardLayout/WidgetHeader/config.ts create mode 100644 frontend/src/container/GridCardLayout/WidgetHeader/contants.ts create mode 100644 frontend/src/container/GridCardLayout/WidgetHeader/index.tsx create mode 100644 frontend/src/container/GridCardLayout/WidgetHeader/styles.ts create mode 100644 frontend/src/container/GridCardLayout/WidgetHeader/types.ts create mode 100644 frontend/src/container/GridCardLayout/WidgetHeader/utils.ts create mode 100644 frontend/src/container/GridCardLayout/config.ts create mode 100644 frontend/src/container/GridCardLayout/index.tsx create mode 100644 frontend/src/container/GridCardLayout/styles.ts create mode 100644 frontend/src/container/GridCardLayout/types.ts create mode 100644 frontend/src/container/GridCardLayout/utils.ts create mode 100644 frontend/src/container/GridPanelSwitch/index.tsx create mode 100644 frontend/src/container/GridPanelSwitch/types.ts create mode 100644 frontend/src/container/GridPanelSwitch/utils.ts create mode 100644 frontend/src/container/GridTableComponent/config.ts create mode 100644 frontend/src/container/GridTableComponent/index.tsx create mode 100644 frontend/src/container/GridTableComponent/styles.ts create mode 100644 frontend/src/container/GridTableComponent/types.ts create mode 100644 frontend/src/container/GridTableComponent/utils.ts create mode 100644 frontend/src/container/GridValueComponent/config.ts create mode 100644 frontend/src/container/GridValueComponent/index.tsx create mode 100644 frontend/src/container/GridValueComponent/styles.ts create mode 100644 frontend/src/container/GridValueComponent/types.ts create mode 100644 frontend/src/container/Header/CurrentOrganization/index.tsx create mode 100644 frontend/src/container/Header/Header.styles.scss create mode 100644 frontend/src/container/Header/ManageLicense/index.tsx create mode 100644 frontend/src/container/Header/ManageLicense/styles.ts create mode 100644 frontend/src/container/Header/SignedIn/index.tsx create mode 100644 frontend/src/container/Header/index.tsx create mode 100644 frontend/src/container/Header/styles.ts create mode 100644 frontend/src/container/IngestionSettings/IngestionSettings.styles.scss create mode 100644 frontend/src/container/IngestionSettings/IngestionSettings.tsx create mode 100644 frontend/src/container/Licenses/ApplyLicenseForm.tsx create mode 100644 frontend/src/container/Licenses/ListLicenses.tsx create mode 100644 frontend/src/container/Licenses/index.tsx create mode 100644 frontend/src/container/Licenses/styles.ts create mode 100644 frontend/src/container/ListAlertRules/DeleteAlert.tsx create mode 100644 frontend/src/container/ListAlertRules/ListAlert.tsx create mode 100644 frontend/src/container/ListAlertRules/TableComponents/Status.tsx create mode 100644 frontend/src/container/ListAlertRules/ToggleAlertState.tsx create mode 100644 frontend/src/container/ListAlertRules/index.tsx create mode 100644 frontend/src/container/ListAlertRules/styles.ts create mode 100644 frontend/src/container/ListAlertRules/utils.ts create mode 100644 frontend/src/container/ListOfDashboard/DashboardsList.tsx create mode 100644 frontend/src/container/ListOfDashboard/ImportJSON/index.tsx create mode 100644 frontend/src/container/ListOfDashboard/ImportJSON/styles.ts create mode 100644 frontend/src/container/ListOfDashboard/SearchFilter/Dashboard.machine.tsx create mode 100644 frontend/src/container/ListOfDashboard/SearchFilter/Dashboard.machine.typegen.ts create mode 100644 frontend/src/container/ListOfDashboard/SearchFilter/QueryChip.tsx create mode 100644 frontend/src/container/ListOfDashboard/SearchFilter/__tests__/utils.test.ts create mode 100644 frontend/src/container/ListOfDashboard/SearchFilter/index.tsx create mode 100644 frontend/src/container/ListOfDashboard/SearchFilter/styles.ts create mode 100644 frontend/src/container/ListOfDashboard/SearchFilter/types.ts create mode 100644 frontend/src/container/ListOfDashboard/SearchFilter/utils.ts create mode 100644 frontend/src/container/ListOfDashboard/TableComponents/CreatedBy.tsx create mode 100644 frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.styles.scss create mode 100644 frontend/src/container/ListOfDashboard/TableComponents/DeleteButton.tsx create mode 100644 frontend/src/container/ListOfDashboard/TableComponents/Name.tsx create mode 100644 frontend/src/container/ListOfDashboard/TableComponents/Tags.tsx create mode 100644 frontend/src/container/ListOfDashboard/TableComponents/styles.ts create mode 100644 frontend/src/container/ListOfDashboard/dashboardSearchAndFilter.ts create mode 100644 frontend/src/container/ListOfDashboard/index.tsx create mode 100644 frontend/src/container/ListOfDashboard/styles.ts create mode 100644 frontend/src/container/ListOfDashboard/utils.ts create mode 100644 frontend/src/container/LiveLogs/BackButton/index.tsx create mode 100644 frontend/src/container/LiveLogs/FiltersInput/index.tsx create mode 100644 frontend/src/container/LiveLogs/FiltersInput/styles.ts create mode 100644 frontend/src/container/LiveLogs/ListViewPanel/index.tsx create mode 100644 frontend/src/container/LiveLogs/ListViewPanel/styles.ts create mode 100644 frontend/src/container/LiveLogs/LiveLogsContainer/index.tsx create mode 100644 frontend/src/container/LiveLogs/LiveLogsContainer/styles.ts create mode 100644 frontend/src/container/LiveLogs/LiveLogsList/index.tsx create mode 100644 frontend/src/container/LiveLogs/LiveLogsList/types.ts create mode 100644 frontend/src/container/LiveLogs/LiveLogsListChart/index.tsx create mode 100644 frontend/src/container/LiveLogs/LiveLogsListChart/types.ts create mode 100644 frontend/src/container/LiveLogs/constants.ts create mode 100644 frontend/src/container/LiveLogs/types.ts create mode 100644 frontend/src/container/LiveLogs/utils.ts create mode 100644 frontend/src/container/LiveLogsTopNav/index.tsx create mode 100644 frontend/src/container/LiveLogsTopNav/styles.ts create mode 100644 frontend/src/container/LocalTopNav/index.tsx create mode 100644 frontend/src/container/LocalTopNav/styles.ts create mode 100644 frontend/src/container/LocalTopNav/types.ts create mode 100644 frontend/src/container/LogControls/index.tsx create mode 100644 frontend/src/container/LogControls/styles.ts create mode 100644 frontend/src/container/LogDetailedView/ActionItem.tsx create mode 100644 frontend/src/container/LogDetailedView/BodyTitleRenderer.styles.ts create mode 100644 frontend/src/container/LogDetailedView/BodyTitleRenderer.tsx create mode 100644 frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.styles.scss create mode 100644 frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx create mode 100644 frontend/src/container/LogDetailedView/ContextView/ContextView.styles.scss create mode 100644 frontend/src/container/LogDetailedView/ContextView/ContextView.tsx create mode 100644 frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts create mode 100644 frontend/src/container/LogDetailedView/FieldRenderer.styles.scss create mode 100644 frontend/src/container/LogDetailedView/FieldRenderer.styles.ts create mode 100644 frontend/src/container/LogDetailedView/FieldRenderer.tsx create mode 100644 frontend/src/container/LogDetailedView/JsonView.styles.scss create mode 100644 frontend/src/container/LogDetailedView/JsonView.tsx create mode 100644 frontend/src/container/LogDetailedView/LogContext.styles.scss create mode 100644 frontend/src/container/LogDetailedView/LogContext.tsx create mode 100644 frontend/src/container/LogDetailedView/LogDetailedView.types.ts create mode 100644 frontend/src/container/LogDetailedView/Overview.styles.scss create mode 100644 frontend/src/container/LogDetailedView/Overview.tsx create mode 100644 frontend/src/container/LogDetailedView/TableView.styles.scss create mode 100644 frontend/src/container/LogDetailedView/TableView.tsx create mode 100644 frontend/src/container/LogDetailedView/config.ts create mode 100644 frontend/src/container/LogDetailedView/constant.ts create mode 100644 frontend/src/container/LogDetailedView/index.tsx create mode 100644 frontend/src/container/LogDetailedView/util.test.ts create mode 100644 frontend/src/container/LogDetailedView/utils.tsx create mode 100644 frontend/src/container/LogExplorerQuerySection/LogsExplorerQuerySection.styles.scss create mode 100644 frontend/src/container/LogExplorerQuerySection/index.tsx create mode 100644 frontend/src/container/LogLiveTail/config.ts create mode 100644 frontend/src/container/LogLiveTail/index.tsx create mode 100644 frontend/src/container/LogLiveTail/styles.ts create mode 100644 frontend/src/container/Login/index.tsx create mode 100644 frontend/src/container/Login/styles.ts create mode 100644 frontend/src/container/LogsAggregate/index.tsx create mode 100644 frontend/src/container/LogsAggregate/styles.ts create mode 100644 frontend/src/container/LogsContextList/LogsContextList.styles.scss create mode 100644 frontend/src/container/LogsContextList/ShowButton.styles.scss create mode 100644 frontend/src/container/LogsContextList/ShowButton.tsx create mode 100644 frontend/src/container/LogsContextList/configs.ts create mode 100644 frontend/src/container/LogsContextList/index.tsx create mode 100644 frontend/src/container/LogsContextList/styles.ts create mode 100644 frontend/src/container/LogsContextList/utils.ts create mode 100644 frontend/src/container/LogsError/LogsError.styles.scss create mode 100644 frontend/src/container/LogsError/LogsError.tsx create mode 100644 frontend/src/container/LogsExplorerChart/LogsExplorerChart.interfaces.ts create mode 100644 frontend/src/container/LogsExplorerChart/LogsExplorerChart.styled.ts create mode 100644 frontend/src/container/LogsExplorerChart/index.tsx create mode 100644 frontend/src/container/LogsExplorerContext/index.tsx create mode 100644 frontend/src/container/LogsExplorerContext/styles.ts create mode 100644 frontend/src/container/LogsExplorerContext/types.ts create mode 100644 frontend/src/container/LogsExplorerContext/useInitialQuery.ts create mode 100644 frontend/src/container/LogsExplorerContext/utils.ts create mode 100644 frontend/src/container/LogsExplorerList/InfinityTableView/LogsCustomTable.tsx create mode 100644 frontend/src/container/LogsExplorerList/InfinityTableView/TableRow.styles.scss create mode 100644 frontend/src/container/LogsExplorerList/InfinityTableView/TableRow.tsx create mode 100644 frontend/src/container/LogsExplorerList/InfinityTableView/config.ts create mode 100644 frontend/src/container/LogsExplorerList/InfinityTableView/index.tsx create mode 100644 frontend/src/container/LogsExplorerList/InfinityTableView/styles.ts create mode 100644 frontend/src/container/LogsExplorerList/InfinityTableView/types.ts create mode 100644 frontend/src/container/LogsExplorerList/LogsExplorerList.interfaces.ts create mode 100644 frontend/src/container/LogsExplorerList/LogsExplorerList.style.scss create mode 100644 frontend/src/container/LogsExplorerList/index.tsx create mode 100644 frontend/src/container/LogsExplorerList/styles.ts create mode 100644 frontend/src/container/LogsExplorerList/utils.ts create mode 100644 frontend/src/container/LogsExplorerTable/LogsExplorerTable.interfaces.ts create mode 100644 frontend/src/container/LogsExplorerTable/LogsExplorerTable.styles.scss create mode 100644 frontend/src/container/LogsExplorerTable/index.tsx create mode 100644 frontend/src/container/LogsExplorerViews/LogsExplorerViews.styled.ts create mode 100644 frontend/src/container/LogsExplorerViews/LogsExplorerViews.styles.scss create mode 100644 frontend/src/container/LogsExplorerViews/index.tsx create mode 100644 frontend/src/container/LogsFilters/FieldItem.tsx create mode 100644 frontend/src/container/LogsFilters/config.ts create mode 100644 frontend/src/container/LogsFilters/index.tsx create mode 100644 frontend/src/container/LogsFilters/styles.ts create mode 100644 frontend/src/container/LogsFilters/types.ts create mode 100644 frontend/src/container/LogsFilters/utils.ts create mode 100644 frontend/src/container/LogsIndexToFields/index.tsx create mode 100644 frontend/src/container/LogsLoading/LogsLoading.styles.scss create mode 100644 frontend/src/container/LogsLoading/LogsLoading.tsx create mode 100644 frontend/src/container/LogsPanelTable/LogsPanelComponent.styles.scss create mode 100644 frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx create mode 100644 frontend/src/container/LogsPanelTable/utils.tsx create mode 100644 frontend/src/container/LogsSearchFilter/SearchFields/ActionBar.tsx create mode 100644 frontend/src/container/LogsSearchFilter/SearchFields/FieldKey.tsx create mode 100644 frontend/src/container/LogsSearchFilter/SearchFields/QueryBuilder/QueryBuilder.tsx create mode 100644 frontend/src/container/LogsSearchFilter/SearchFields/QueryBuilder/styles.ts create mode 100644 frontend/src/container/LogsSearchFilter/SearchFields/QueryBuilder/utils.ts create mode 100644 frontend/src/container/LogsSearchFilter/SearchFields/Suggestions.tsx create mode 100644 frontend/src/container/LogsSearchFilter/SearchFields/index.tsx create mode 100644 frontend/src/container/LogsSearchFilter/SearchFields/styles.tsx create mode 100644 frontend/src/container/LogsSearchFilter/SearchFields/utils.ts create mode 100644 frontend/src/container/LogsSearchFilter/index.tsx create mode 100644 frontend/src/container/LogsSearchFilter/styles.ts create mode 100644 frontend/src/container/LogsSearchFilter/useSearchParser.ts create mode 100644 frontend/src/container/LogsSearchFilter/utils.ts create mode 100644 frontend/src/container/LogsTable/index.tsx create mode 100644 frontend/src/container/LogsTable/logsTable.styles.scss create mode 100644 frontend/src/container/LogsTable/styles.ts create mode 100644 frontend/src/container/LogsTopNav/index.tsx create mode 100644 frontend/src/container/LogsTopNav/styles.ts create mode 100644 frontend/src/container/MetricsApplication/MetricsApplication.factory.ts create mode 100644 frontend/src/container/MetricsApplication/MetricsPageQueries/DBCallQueries.ts create mode 100644 frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts create mode 100644 frontend/src/container/MetricsApplication/MetricsPageQueries/MetricsPageQueriesFactory.ts create mode 100644 frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts create mode 100644 frontend/src/container/MetricsApplication/MetricsPageQueries/TopOperationQueries.ts create mode 100644 frontend/src/container/MetricsApplication/Tabs/DBCall.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/External.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetricsApplication.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexTraces.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/constants.ts create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/index.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/types.ts create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/TableRenderer/ColumnWithLink.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/TableRenderer/TableColumnRenderer.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/TopOperation.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/TopOperationMetrics.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/config.ts create mode 100644 frontend/src/container/MetricsApplication/Tabs/styles.ts create mode 100644 frontend/src/container/MetricsApplication/Tabs/types.ts create mode 100644 frontend/src/container/MetricsApplication/Tabs/util.ts create mode 100644 frontend/src/container/MetricsApplication/TopOperationsTable.styles.scss create mode 100644 frontend/src/container/MetricsApplication/TopOperationsTable.tsx create mode 100644 frontend/src/container/MetricsApplication/__mocks__/getTopOperation.ts create mode 100644 frontend/src/container/MetricsApplication/constant.ts create mode 100644 frontend/src/container/MetricsApplication/styles.ts create mode 100644 frontend/src/container/MetricsApplication/types.ts create mode 100644 frontend/src/container/MetricsApplication/utils.test.ts create mode 100644 frontend/src/container/MetricsApplication/utils.ts create mode 100644 frontend/src/container/MySettings/MySettings.styles.scss create mode 100644 frontend/src/container/MySettings/Password/index.tsx create mode 100644 frontend/src/container/MySettings/UserInfo/UserInfo.styles.scss create mode 100644 frontend/src/container/MySettings/UserInfo/index.tsx create mode 100644 frontend/src/container/MySettings/index.tsx create mode 100644 frontend/src/container/MySettings/styles.ts create mode 100644 frontend/src/container/NewDashboard/ComponentsSlider/constants.ts create mode 100644 frontend/src/container/NewDashboard/ComponentsSlider/index.tsx create mode 100644 frontend/src/container/NewDashboard/ComponentsSlider/menuItems.tsx create mode 100644 frontend/src/container/NewDashboard/ComponentsSlider/styles.ts create mode 100644 frontend/src/container/NewDashboard/DashboardDescription/DashboardName/index.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardDescription/Description.styles.scss create mode 100644 frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardDescription/ShareModal.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardDescription/index.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardDescription/styles.ts create mode 100644 frontend/src/container/NewDashboard/DashboardDescription/utils.ts create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/DashboardSettings.styles.scss create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/General/AddTags/index.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/General/AddTags/styles.ts create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/General/Description/index.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/General/Description/styles.ts create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/General/index.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/General/styles.ts create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/Variables/VariableItem/VariableItem.styles.scss create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/Variables/VariableItem/VariableItem.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/Variables/VariableItem/styles.ts create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/Variables/types.ts create mode 100644 frontend/src/container/NewDashboard/DashboardSettings/index.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.styles.scss create mode 100644 frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardVariablesSelection/index.tsx create mode 100644 frontend/src/container/NewDashboard/DashboardVariablesSelection/styles.ts create mode 100644 frontend/src/container/NewDashboard/DashboardVariablesSelection/util.ts create mode 100644 frontend/src/container/NewDashboard/DashboardVariablesSelection/utils.test.ts create mode 100644 frontend/src/container/NewDashboard/GridGraphs/index.tsx create mode 100644 frontend/src/container/NewDashboard/GridGraphs/styles.ts create mode 100644 frontend/src/container/NewDashboard/index.tsx create mode 100644 frontend/src/container/NewDashboard/utils.ts create mode 100644 frontend/src/container/NewExplorerCTA/config.ts create mode 100644 frontend/src/container/NewExplorerCTA/index.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/ExplorerColumnsRenderer.styles.scss create mode 100644 frontend/src/container/NewWidget/LeftContainer/ExplorerColumnsRenderer.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/QueryHeader.styles.scss create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/QueryHeader.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/index.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/query.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/types.ts create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/promQL/index.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/promQL/query.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/promQL/types.ts create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/QuerySection.styles.scss create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/styles.ts create mode 100644 frontend/src/container/NewWidget/LeftContainer/QueryTypeTag.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/WidgetGraph/PlotTag.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphContainer.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/WidgetGraph/index.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/WidgetGraph/styles.ts create mode 100644 frontend/src/container/NewWidget/LeftContainer/index.tsx create mode 100644 frontend/src/container/NewWidget/LeftContainer/styles.ts create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/ColorSelector.styles.scss create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/ColorSelector.tsx create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/CustomColor.styles.scss create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/CustomColor.tsx create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.styles.scss create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.tsx create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.styles.scss create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.tsx create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/ThresholdSelector.styles.scss create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/ThresholdSelector.tsx create mode 100644 frontend/src/container/NewWidget/RightContainer/Threshold/types.ts create mode 100644 frontend/src/container/NewWidget/RightContainer/YAxisUnitSelector.tsx create mode 100644 frontend/src/container/NewWidget/RightContainer/alertFomatCategories.ts create mode 100644 frontend/src/container/NewWidget/RightContainer/constants.ts create mode 100644 frontend/src/container/NewWidget/RightContainer/dataFormatCategories.ts create mode 100644 frontend/src/container/NewWidget/RightContainer/index.tsx create mode 100644 frontend/src/container/NewWidget/RightContainer/styles.ts create mode 100644 frontend/src/container/NewWidget/RightContainer/timeItems.ts create mode 100644 frontend/src/container/NewWidget/RightContainer/types.ts create mode 100644 frontend/src/container/NewWidget/index.tsx create mode 100644 frontend/src/container/NewWidget/styles.ts create mode 100644 frontend/src/container/NewWidget/types.ts create mode 100644 frontend/src/container/NewWidget/utils.ts create mode 100644 frontend/src/container/NoLogs/NoLogs.styles.scss create mode 100644 frontend/src/container/NoLogs/NoLogs.tsx create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Kubernetes/dotnet-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Kubernetes/dotnet-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Kubernetes/dotnet-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/LinuxAMD64/QuickStart/dotnet-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/LinuxAMD64/QuickStart/dotnet-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/LinuxAMD64/Recommended/dotnet-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/LinuxAMD64/Recommended/dotnet-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/LinuxAMD64/Recommended/dotnet-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/LinuxARM64/QuickStart/dotnet-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/LinuxARM64/QuickStart/dotnet-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/LinuxARM64/Recommended/dotnet-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/LinuxARM64/Recommended/dotnet-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/LinuxARM64/Recommended/dotnet-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/MacOsAMD64/QuickStart/dotnet-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/MacOsAMD64/QuickStart/dotnet-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/MacOsAMD64/Recommended/dotnet-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/MacOsAMD64/Recommended/dotnet-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/MacOsAMD64/Recommended/dotnet-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/MacOsARM64/QuickStart/dotnet-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/MacOsARM64/QuickStart/dotnet-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/MacOsARM64/Recommended/dotnet-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/MacOsARM64/Recommended/dotnet-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/MacOsARM64/Recommended/dotnet-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Kubernetes/elixir-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Kubernetes/elixir-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Kubernetes/elixir-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/LinuxAMD64/QuickStart/elixir-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/LinuxAMD64/QuickStart/elixir-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/LinuxAMD64/Recommended/elixir-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/LinuxAMD64/Recommended/elixir-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/LinuxAMD64/Recommended/elixir-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/LinuxARM64/QuickStart/elixir-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/LinuxARM64/QuickStart/elixir-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/LinuxARM64/Recommended/elixir-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/LinuxARM64/Recommended/elixir-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/LinuxARM64/Recommended/elixir-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/MacOsAMD64/QuickStart/elixir-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/MacOsAMD64/QuickStart/elixir-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/MacOsAMD64/Recommended/elixir-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/MacOsAMD64/Recommended/elixir-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/MacOsAMD64/Recommended/elixir-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/MacOsARM64/QuickStart/elixir-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/MacOsARM64/QuickStart/elixir-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/MacOsARM64/Recommended/elixir-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/MacOsARM64/Recommended/elixir-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/MacOsARM64/Recommended/elixir-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/goLang.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Kubernetes/golang-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Kubernetes/golang-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Kubernetes/golang-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/LinuxAMD64/QuickStart/golang-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/LinuxAMD64/QuickStart/golang-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/LinuxAMD64/Recommended/golang-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/LinuxAMD64/Recommended/golang-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/LinuxAMD64/Recommended/golang-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/LinuxARM64/QuickStart/golang-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/LinuxARM64/QuickStart/golang-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/LinuxARM64/Recommended/golang-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/LinuxARM64/Recommended/golang-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/LinuxARM64/Recommended/golang-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/MacOsAMD64/QuickStart/golang-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/MacOsAMD64/QuickStart/golang-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/MacOsAMD64/Recommended/golang-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/MacOsAMD64/Recommended/golang-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/MacOsAMD64/Recommended/golang-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/MacOsARM64/QuickStart/golang-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/MacOsARM64/QuickStart/golang-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/MacOsARM64/Recommended/golang-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/MacOsARM64/Recommended/golang-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/MacOsARM64/Recommended/golang-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Kubernetes/jboss-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Kubernetes/jboss-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Kubernetes/jboss-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/LinuxAMD64/QuickStart/jboss-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/LinuxAMD64/QuickStart/jboss-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/LinuxAMD64/Recommended/jboss-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/LinuxAMD64/Recommended/jboss-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/LinuxAMD64/Recommended/jboss-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/LinuxARM64/QuickStart/jboss-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/LinuxARM64/QuickStart/jboss-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/LinuxARM64/Recommended/jboss-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/LinuxARM64/Recommended/jboss-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/LinuxARM64/Recommended/jboss-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/MacOsAMD64/QuickStart/jboss-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/MacOsAMD64/QuickStart/jboss-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/MacOsAMD64/Recommended/jboss-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/MacOsAMD64/Recommended/jboss-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/MacOsAMD64/Recommended/jboss-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/MacOsARM64/QuickStart/jboss-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/MacOsARM64/QuickStart/jboss-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/MacOsARM64/Recommended/jboss-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/MacOsARM64/Recommended/jboss-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/MacOsARM64/Recommended/jboss-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Kubernetes/others-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Kubernetes/others-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Kubernetes/others-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/LinuxAMD64/QuickStart/others-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/LinuxAMD64/QuickStart/others-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/LinuxAMD64/Recommended/others-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/LinuxAMD64/Recommended/others-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/LinuxAMD64/Recommended/others-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/LinuxARM64/QuickStart/others-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/LinuxARM64/QuickStart/others-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/LinuxARM64/Recommended/others-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/LinuxARM64/Recommended/others-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/LinuxARM64/Recommended/others-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/MacOsAMD64/QuickStart/others-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/MacOsAMD64/QuickStart/others-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/MacOsAMD64/Recommended/others-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/MacOsAMD64/Recommended/others-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/MacOsAMD64/Recommended/others-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/MacOsARM64/QuickStart/others-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/MacOsARM64/QuickStart/others-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/MacOsARM64/Recommended/others-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/MacOsARM64/Recommended/others-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/MacOsARM64/Recommended/others-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/LinuxAMD64/QuickStart/springBoot-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/LinuxAMD64/QuickStart/springBoot-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/LinuxAMD64/Recommended/springBoot-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/LinuxAMD64/Recommended/springBoot-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/LinuxAMD64/Recommended/springBoot-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/LinuxARM64/QuickStart/springBoot-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/LinuxARM64/QuickStart/springBoot-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/LinuxARM64/Recommended/springBoot-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/LinuxARM64/Recommended/springBoot-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/LinuxARM64/Recommended/springBoot-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/MacOsAMD64/QuickStart/springBoot-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/MacOsAMD64/QuickStart/springBoot-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/MacOsAMD64/Recommended/springBoot-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/MacOsAMD64/Recommended/springBoot-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/MacOsAMD64/Recommended/springBoot-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/MacOsARM64/QuickStart/springBoot-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/MacOsARM64/QuickStart/springBoot-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/MacOsARM64/Recommended/springBoot-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/MacOsARM64/Recommended/springBoot-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/MacOsARM64/Recommended/springBoot-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Kubernetes/tomcat-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Kubernetes/tomcat-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Kubernetes/tomcat-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/LinuxAMD64/QuickStart/tomcat-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/LinuxAMD64/QuickStart/tomcat-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/LinuxAMD64/Recommended/tomcat-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/LinuxAMD64/Recommended/tomcat-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/LinuxAMD64/Recommended/tomcat-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/LinuxARM64/QuickStart/tomcat-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/LinuxARM64/QuickStart/tomcat-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/LinuxARM64/Recommended/tomcat-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/LinuxARM64/Recommended/tomcat-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/LinuxARM64/Recommended/tomcat-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/MacOsAMD64/QuickStart/tomcat-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/MacOsAMD64/QuickStart/tomcat-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/MacOsAMD64/Recommended/tomcat-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/MacOsAMD64/Recommended/tomcat-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/MacOsAMD64/Recommended/tomcat-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/MacOsARM64/QuickStart/tomcat-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/MacOsARM64/QuickStart/tomcat-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/MacOsARM64/Recommended/tomcat-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/MacOsARM64/Recommended/tomcat-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/MacOsARM64/Recommended/tomcat-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/java.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/jboss.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/spring_boot.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/tomcat.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Kubernetes/express-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Kubernetes/express-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Kubernetes/express-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/LinuxAMD64/QuickStart/express-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/LinuxAMD64/QuickStart/express-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/LinuxAMD64/Recommended/express-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/LinuxAMD64/Recommended/express-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/LinuxAMD64/Recommended/express-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/LinuxARM64/QuickStart/express-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/LinuxARM64/QuickStart/express-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/LinuxARM64/Recommended/express-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/LinuxARM64/Recommended/express-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/LinuxARM64/Recommended/express-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/MacOsAMD64/QuickStart/express-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/MacOsAMD64/QuickStart/express-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/MacOsAMD64/Recommended/express-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/MacOsAMD64/Recommended/express-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/MacOsAMD64/Recommended/express-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/MacOsARM64/QuickStart/express-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/MacOsARM64/QuickStart/express-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/MacOsARM64/Recommended/express-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/MacOsARM64/Recommended/express-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/MacOsARM64/Recommended/express-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Kubernetes/nestjs-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Kubernetes/nestjs-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Kubernetes/nestjs-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/LinuxAMD64/QuickStart/nestjs-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/LinuxAMD64/QuickStart/nestjs-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/LinuxAMD64/Recommended/nestjs-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/LinuxAMD64/Recommended/nestjs-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/LinuxAMD64/Recommended/nestjs-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/LinuxARM64/QuickStart/nestjs-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/LinuxARM64/QuickStart/nestjs-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/LinuxARM64/Recommended/nestjs-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/LinuxARM64/Recommended/nestjs-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/LinuxARM64/Recommended/nestjs-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/MacOsAMD64/QuickStart/nestjs-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/MacOsAMD64/QuickStart/nestjs-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/MacOsAMD64/Recommended/nestjs-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/MacOsAMD64/Recommended/nestjs-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/MacOsAMD64/Recommended/nestjs-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/MacOsARM64/QuickStart/nestjs-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/MacOsARM64/QuickStart/nestjs-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/MacOsARM64/Recommended/nestjs-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/MacOsARM64/Recommended/nestjs-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/MacOsARM64/Recommended/nestjs-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Kubernetes/nodejs-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Kubernetes/nodejs-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Kubernetes/nodejs-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/LinuxAMD64/QuickStart/nodejs-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/LinuxAMD64/QuickStart/nodejs-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/LinuxAMD64/Recommended/nodejs-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/LinuxAMD64/Recommended/nodejs-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/LinuxAMD64/Recommended/nodejs-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/LinuxARM64/QuickStart/nodejs-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/LinuxARM64/QuickStart/nodejs-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/LinuxARM64/Recommended/nodejs-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/LinuxARM64/Recommended/nodejs-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/LinuxARM64/Recommended/nodejs-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/MacOsAMD64/QuickStart/nodejs-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/MacOsAMD64/QuickStart/nodejs-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/MacOsAMD64/Recommended/nodejs-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/MacOsAMD64/Recommended/nodejs-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/MacOsAMD64/Recommended/nodejs-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/MacOsARM64/QuickStart/nodejs-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/MacOsARM64/QuickStart/nodejs-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/MacOsARM64/Recommended/nodejs-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/MacOsARM64/Recommended/nodejs-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/MacOsARM64/Recommended/nodejs-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Kubernetes/others-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Kubernetes/others-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Kubernetes/others-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/LinuxAMD64/QuickStart/others-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/LinuxAMD64/QuickStart/others-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/LinuxAMD64/Recommended/others-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/LinuxAMD64/Recommended/others-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/LinuxAMD64/Recommended/others-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/LinuxARM64/QuickStart/others-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/LinuxARM64/QuickStart/others-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/LinuxARM64/Recommended/others-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/LinuxARM64/Recommended/others-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/LinuxARM64/Recommended/others-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/MacOsAMD64/QuickStart/others-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/MacOsAMD64/QuickStart/others-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/MacOsAMD64/Recommended/others-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/MacOsAMD64/Recommended/others-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/MacOsAMD64/Recommended/others-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/MacOsARM64/QuickStart/others-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/MacOsARM64/QuickStart/others-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/MacOsARM64/Recommended/others-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/MacOsARM64/Recommended/others-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/MacOsARM64/Recommended/others-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Kubernetes/reactjs-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Kubernetes/reactjs-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Kubernetes/reactjs-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/LinuxAMD64/QuickStart/reactjs-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/LinuxAMD64/QuickStart/reactjs-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/LinuxAMD64/Recommended/reactjs-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/LinuxAMD64/Recommended/reactjs-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/LinuxAMD64/Recommended/reactjs-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/LinuxARM64/QuickStart/reactjs-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/LinuxARM64/QuickStart/reactjs-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/LinuxARM64/Recommended/reactjs-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/LinuxARM64/Recommended/reactjs-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/LinuxARM64/Recommended/reactjs-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/MacOsAMD64/QuickStart/reactjs-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/MacOsAMD64/QuickStart/reactjs-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/MacOsAMD64/Recommended/reactjs-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/MacOsAMD64/Recommended/reactjs-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/MacOsAMD64/Recommended/reactjs-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/MacOsARM64/QuickStart/reactjs-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/MacOsARM64/QuickStart/reactjs-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/MacOsARM64/Recommended/reactjs-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/MacOsARM64/Recommended/reactjs-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/MacOsARM64/Recommended/reactjs-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/express.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/javascript.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/nestjs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Kubernetes/django-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Kubernetes/django-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Kubernetes/django-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/LinuxAMD64/QuickStart/django-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/LinuxAMD64/QuickStart/django-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/LinuxAMD64/Recommended/django-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/LinuxAMD64/Recommended/django-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/LinuxAMD64/Recommended/django-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/LinuxARM64/QuickStart/django-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/LinuxARM64/QuickStart/django-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/LinuxARM64/Recommended/django-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/LinuxARM64/Recommended/django-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/LinuxARM64/Recommended/django-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/MacOsAMD64/QuickStart/django-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/MacOsAMD64/QuickStart/django-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/MacOsAMD64/Recommended/django-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/MacOsAMD64/Recommended/django-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/MacOsAMD64/Recommended/django-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/MacOsARM64/QuickStart/django-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/MacOsARM64/QuickStart/django-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/MacOsARM64/Recommended/django-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/MacOsARM64/Recommended/django-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/MacOsARM64/Recommended/django-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Kubernetes/falcon-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Kubernetes/falcon-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Kubernetes/falcon-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/LinuxAMD64/QuickStart/falcon-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/LinuxAMD64/QuickStart/falcon-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/LinuxAMD64/Recommended/falcon-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/LinuxAMD64/Recommended/falcon-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/LinuxAMD64/Recommended/falcon-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/LinuxARM64/QuickStart/falcon-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/LinuxARM64/QuickStart/falcon-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/LinuxARM64/Recommended/falcon-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/LinuxARM64/Recommended/falcon-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/LinuxARM64/Recommended/falcon-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/MacOsAMD64/QuickStart/falcon-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/MacOsAMD64/QuickStart/falcon-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/MacOsAMD64/Recommended/falcon-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/MacOsAMD64/Recommended/falcon-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/MacOsAMD64/Recommended/falcon-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/MacOsARM64/QuickStart/falcon-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/MacOsARM64/QuickStart/falcon-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/MacOsARM64/Recommended/falcon-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/MacOsARM64/Recommended/falcon-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/MacOsARM64/Recommended/falcon-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Kubernetes/fastapi-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Kubernetes/fastapi-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Kubernetes/fastapi-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/LinuxAMD64/QuickStart/fastapi-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/LinuxAMD64/QuickStart/fastapi-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/LinuxAMD64/Recommended/fastapi-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/LinuxAMD64/Recommended/fastapi-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/LinuxAMD64/Recommended/fastapi-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/LinuxARM64/QuickStart/fastapi-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/LinuxARM64/QuickStart/fastapi-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/LinuxARM64/Recommended/fastapi-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/LinuxARM64/Recommended/fastapi-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/LinuxARM64/Recommended/fastapi-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/MacOsAMD64/QuickStart/fastapi-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/MacOsAMD64/QuickStart/fastapi-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/MacOsAMD64/Recommended/fastapi-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/MacOsAMD64/Recommended/fastapi-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/MacOsAMD64/Recommended/fastapi-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/MacOsARM64/QuickStart/fastapi-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/MacOsARM64/QuickStart/fastapi-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/MacOsARM64/Recommended/fastapi-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/MacOsARM64/Recommended/fastapi-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/MacOsARM64/Recommended/fastapi-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Kubernetes/flask-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Kubernetes/flask-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Kubernetes/flask-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/LinuxAMD64/QuickStart/flask-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/LinuxAMD64/QuickStart/flask-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/LinuxAMD64/Recommended/flask-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/LinuxAMD64/Recommended/flask-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/LinuxAMD64/Recommended/flask-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/LinuxARM64/QuickStart/flask-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/LinuxARM64/QuickStart/flask-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/LinuxARM64/Recommended/flask-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/LinuxARM64/Recommended/flask-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/LinuxARM64/Recommended/flask-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/MacOsAMD64/QuickStart/flask-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/MacOsAMD64/QuickStart/flask-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/MacOsAMD64/Recommended/flask-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/MacOsAMD64/Recommended/flask-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/MacOsAMD64/Recommended/flask-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/MacOsARM64/QuickStart/flask-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/MacOsARM64/QuickStart/flask-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/MacOsARM64/Recommended/flask-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/MacOsARM64/Recommended/flask-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/MacOsARM64/Recommended/flask-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Kubernetes/others-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Kubernetes/others-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Kubernetes/others-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/LinuxAMD64/QuickStart/others-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/LinuxAMD64/QuickStart/others-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/LinuxAMD64/Recommended/others-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/LinuxAMD64/Recommended/others-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/LinuxAMD64/Recommended/others-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/LinuxARM64/QuickStart/others-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/LinuxARM64/QuickStart/others-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/LinuxARM64/Recommended/others-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/LinuxARM64/Recommended/others-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/LinuxARM64/Recommended/others-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/MacOsAMD64/QuickStart/others-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/MacOsAMD64/QuickStart/others-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/MacOsAMD64/Recommended/others-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/MacOsAMD64/Recommended/others-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/MacOsAMD64/Recommended/others-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/MacOsARM64/QuickStart/others-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/MacOsARM64/QuickStart/others-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/MacOsARM64/Recommended/others-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/MacOsARM64/Recommended/others-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/MacOsARM64/Recommended/others-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/django.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/falcon.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/fastAPI.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/flask.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/python.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/RubyOnRails.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Kubernetes/ror-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Kubernetes/ror-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Kubernetes/ror-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/LinuxAMD64/QuickStart/ror-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/LinuxAMD64/QuickStart/ror-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/LinuxAMD64/Recommended/ror-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/LinuxAMD64/Recommended/ror-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/LinuxAMD64/Recommended/ror-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/LinuxARM64/QuickStart/ror-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/LinuxARM64/QuickStart/ror-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/LinuxARM64/Recommended/ror-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/LinuxARM64/Recommended/ror-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/LinuxARM64/Recommended/ror-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/MacOsAMD64/QuickStart/ror-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/MacOsAMD64/QuickStart/ror-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/MacOsAMD64/Recommended/ror-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/MacOsAMD64/Recommended/ror-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/MacOsAMD64/Recommended/ror-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/MacOsARM64/QuickStart/ror-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/MacOsARM64/QuickStart/ror-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/MacOsARM64/Recommended/ror-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/MacOsARM64/Recommended/ror-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/MacOsARM64/Recommended/ror-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Kubernetes/rust-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Kubernetes/rust-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Kubernetes/rust-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/LinuxAMD64/QuickStart/rust-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/LinuxAMD64/QuickStart/rust-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/LinuxAMD64/Recommended/rust-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/LinuxAMD64/Recommended/rust-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/LinuxAMD64/Recommended/rust-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/LinuxARM64/QuickStart/rust-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/LinuxARM64/QuickStart/rust-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/LinuxARM64/Recommended/rust-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/LinuxARM64/Recommended/rust-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/LinuxARM64/Recommended/rust-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/MacOsAMD64/QuickStart/rust-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/MacOsAMD64/QuickStart/rust-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/MacOsAMD64/Recommended/rust-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/MacOsAMD64/Recommended/rust-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/MacOsAMD64/Recommended/rust-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/MacOsARM64/QuickStart/rust-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/MacOsARM64/QuickStart/rust-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/MacOsARM64/Recommended/rust-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/MacOsARM64/Recommended/rust-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/MacOsARM64/Recommended/rust-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Kubernetes/swift-kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Kubernetes/swift-kubernetes-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Kubernetes/swift-kubernetes-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/LinuxAMD64/QuickStart/swift-linuxamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/LinuxAMD64/QuickStart/swift-linuxamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/LinuxAMD64/Recommended/swift-linuxamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/LinuxAMD64/Recommended/swift-linuxamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/LinuxAMD64/Recommended/swift-linuxamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/LinuxARM64/QuickStart/swift-linuxarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/LinuxARM64/QuickStart/swift-linuxarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/LinuxARM64/Recommended/swift-linuxarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/LinuxARM64/Recommended/swift-linuxarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/LinuxARM64/Recommended/swift-linuxarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/MacOsAMD64/QuickStart/swift-macosamd64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/MacOsAMD64/QuickStart/swift-macosamd64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/MacOsAMD64/Recommended/swift-macosamd64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/MacOsAMD64/Recommended/swift-macosamd64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/MacOsAMD64/Recommended/swift-macosamd64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/MacOsARM64/QuickStart/swift-macosarm64-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/MacOsARM64/QuickStart/swift-macosarm64-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/MacOsARM64/Recommended/swift-macosarm64-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/MacOsARM64/Recommended/swift-macosarm64-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/MacOsARM64/Recommended/swift-macosarm64-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/LinuxAMD64/appplicationLogs-linuxamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/LinuxAMD64/appplicationLogs-linuxamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/LinuxAMD64/appplicationLogs-linuxamd64-runOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/LinuxARM64/appplicationLogs-linuxarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/LinuxARM64/appplicationLogs-linuxarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/LinuxARM64/appplicationLogs-linuxarm64-runOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/MacOsAMD64/appplicationLogs-macosamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/MacOsAMD64/appplicationLogs-macosamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/MacOsAMD64/appplicationLogs-macosamd64-runOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/MacOsARM64/appplicationLogs-macosarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/MacOsARM64/appplicationLogs-macosarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2ApplicationLogs/md-docs/MacOsARM64/appplicationLogs-macosarm64-runOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2InfrastructureMetrics/md-docs/LinuxAMD64/hostmetrics-configureHostmetricsJson.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2InfrastructureMetrics/md-docs/LinuxAMD64/hostmetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2InfrastructureMetrics/md-docs/LinuxARM64/hostmetrics-configureHostmetricsJson.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2InfrastructureMetrics/md-docs/LinuxARM64/hostmetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2InfrastructureMetrics/md-docs/MacOsAMD64/hostmetrics-configureHostmetricsJson.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2InfrastructureMetrics/md-docs/MacOsAMD64/hostmetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2InfrastructureMetrics/md-docs/MacOsARM64/hostmetrics-configureHostmetricsJson.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EC2InfrastructureMetrics/md-docs/MacOsARM64/hostmetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSEc2/md-docs/ecsEc2-createDaemonService.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSEc2/md-docs/ecsEc2-createOtelConfig.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSEc2/md-docs/ecsEc2-sendData.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSEc2/md-docs/ecsEc2-setupDeamonService.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSExternal/md-docs/ecsExternal-createDaemonService.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSExternal/md-docs/ecsExternal-createOtelConfig.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSExternal/md-docs/ecsExternal-sendData.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSExternal/md-docs/ecsExternal-setupDeamonService.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSFargate/md-docs/ecsFargate-createOtelConfig.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSFargate/md-docs/ecsFargate-createSidecarCollectorContainer.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSFargate/md-docs/ecsFargate-deployTaskDefinition.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSFargate/md-docs/ecsFargate-sendData.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/ECSFargate/md-docs/ecsFargate-sendLogs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EKS/eks-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AwsMonitoring/EKS/eks-monitorUsingDashboard.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/Hostmetrics/md-docs/LinuxAMD64/hostmetrics-configureHostmetricsJson.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/Hostmetrics/md-docs/LinuxAMD64/hostmetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/Hostmetrics/md-docs/LinuxARM64/hostmetrics-configureHostmetricsJson.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/Hostmetrics/md-docs/LinuxARM64/hostmetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/Hostmetrics/md-docs/MacOsAMD64/hostmetrics-configureHostmetricsJson.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/Hostmetrics/md-docs/MacOsAMD64/hostmetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/Hostmetrics/md-docs/MacOsARM64/hostmetrics-configureHostmetricsJson.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/Hostmetrics/md-docs/MacOsARM64/hostmetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/KubernetesInfraMetrics/md-docs/kubernetes-plotMetrics.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/KubernetesInfraMetrics/md-docs/kubernetes-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/OtherMetrics/md-docs/LinuxAMD64/otherMetrics-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/OtherMetrics/md-docs/LinuxAMD64/otherMetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/OtherMetrics/md-docs/LinuxARM64/otherMetrics-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/OtherMetrics/md-docs/LinuxARM64/otherMetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/OtherMetrics/md-docs/MacOsAMD64/otherMetrics-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/OtherMetrics/md-docs/MacOsAMD64/otherMetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/OtherMetrics/md-docs/MacOsARM64/otherMetrics-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/InfrastructureMonitoring/OtherMetrics/md-docs/MacOsARM64/otherMetrics-setupOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/applicationLogsFromLogFile.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/applicationLogsUsingJavaOtelSDK.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/applicationLogsUsingPythonOtelSDK.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/LinuxAMD64/appplicationLogs-linuxamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/LinuxAMD64/appplicationLogs-linuxamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/LinuxAMD64/appplicationLogs-linuxamd64-runOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/LinuxARM64/appplicationLogs-linuxarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/LinuxARM64/appplicationLogs-linuxarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/LinuxARM64/appplicationLogs-linuxarm64-runOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/MacOsAMD64/appplicationLogs-macosamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/MacOsAMD64/appplicationLogs-macosamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/MacOsAMD64/appplicationLogs-macosamd64-runOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/MacOsARM64/appplicationLogs-macosarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/MacOsARM64/appplicationLogs-macosarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/ApplicationLogs/md-docs/MacOsARM64/appplicationLogs-macosarm64-runOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/LinuxAMD64/cloudwatch-linuxamd64-configureAws.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/LinuxAMD64/cloudwatch-linuxamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/LinuxAMD64/cloudwatch-linuxamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/LinuxAMD64/cloudwatch-linuxamd64-sendLogs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/LinuxARM64/cloudwatch-linuxarm64-configureAws.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/LinuxARM64/cloudwatch-linuxarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/LinuxARM64/cloudwatch-linuxarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/LinuxARM64/cloudwatch-linuxarm64-sendLogs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/MacOsAMD64/cloudwatch-macosamd64-configureAws.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/MacOsAMD64/cloudwatch-macosamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/MacOsAMD64/cloudwatch-macosamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/MacOsAMD64/cloudwatch-macosamd64-sendLogs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/MacOsARM64/cloudwatch-macosarm64-configureAws.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/MacOsARM64/cloudwatch-macosarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/MacOsARM64/cloudwatch-macosarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Cloudwatch/md-docs/MacOsARM64/cloudwatch-macosarm64-sendLogs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Docker/docker.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Docker/md-docs/docker-cloneRepository.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Docker/md-docs/docker-startContainers.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/LinuxAMD64/fluentbit-linuxamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/LinuxAMD64/fluentbit-linuxamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/LinuxAMD64/fluentbit-linuxamd64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/LinuxARM64/fluentbit-linuxarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/LinuxARM64/fluentbit-linuxarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/LinuxARM64/fluentbit-linuxarm64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/MacOsAMD64/fluentbit-macosamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/MacOsAMD64/fluentbit-macosamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/MacOsAMD64/fluentbit-macosamd64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/MacOsARM64/fluentBit-macosarm64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/MacOsARM64/fluentbit-macosarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentBit/md-docs/MacOsARM64/fluentbit-macosarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/LinuxAMD64/fluentd-linuxamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/LinuxAMD64/fluentd-linuxamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/LinuxAMD64/fluentd-linuxamd64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/LinuxARM64/fluentd-linuxarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/LinuxARM64/fluentd-linuxarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/LinuxARM64/fluentd-linuxarm64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/MacOsAMD64/fluentd-macosamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/MacOsAMD64/fluentd-macosamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/MacOsAMD64/fluentd-macosamd64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/MacOsARM64/fluentd-macosarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/MacOsARM64/fluentd-macosarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/FluentD/md-docs/MacOsARM64/fluentd-macosarm64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Heroku/md-docs/heroku-addHttpDrain.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Http/md-docs/httpJsonPayload.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Kubernetes/kubernetes.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Kubernetes/md-docs/kubernetes-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/LinuxAMD64/logstash-linuxamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/LinuxAMD64/logstash-linuxamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/LinuxAMD64/logstash-linuxamd64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/LinuxARM64/logstash-linuxarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/LinuxARM64/logstash-linuxarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/LinuxARM64/logstash-linuxarm64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/MacOsAMD64/logstash-macosamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/MacOsAMD64/logstash-macosamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/MacOsAMD64/logstash-macosamd64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/MacOsARM64/logstash-macosarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/MacOsARM64/logstash-macosarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Logstash/md-docs/MacOsARM64/logstash-macosarm64-restartOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Nodejs/nodejs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/LinuxAMD64/syslogs-linuxamd64-checkServiceStatus.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/LinuxAMD64/syslogs-linuxamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/LinuxAMD64/syslogs-linuxamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/LinuxARM64/syslogs-linuxarm64-checkServiceStatus.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/LinuxARM64/syslogs-linuxarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/LinuxARM64/syslogs-linuxarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/MacOsAMD64/syslogs-macosamd64-checkServiceStatus.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/MacOsAMD64/syslogs-macosamd64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/MacOsAMD64/syslogs-macosamd64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/MacOsARM64/syslogs-macosarm64-checkServiceStatus.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/MacOsARM64/syslogs-macosarm64-configureReceiver.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/md-docs/MacOsARM64/syslogs-macosarm64-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/SysLogs/syslogs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/LogsManagement/Vercel/md-docs/vercel-setupLogDrains.md create mode 100644 frontend/src/container/OnboardingContainer/Onboarding.styles.scss create mode 100644 frontend/src/container/OnboardingContainer/OnboardingContainer.tsx create mode 100644 frontend/src/container/OnboardingContainer/Steps/ConnectionStatus/ConnectionStatus.styles.scss create mode 100644 frontend/src/container/OnboardingContainer/Steps/ConnectionStatus/ConnectionStatus.tsx create mode 100644 frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.styles.scss create mode 100644 frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx create mode 100644 frontend/src/container/OnboardingContainer/Steps/EnvironmentDetails/EnvironmentDetails.tsx create mode 100644 frontend/src/container/OnboardingContainer/Steps/LogsConnectionStatus/LogsConnectionStatus.styles.scss create mode 100644 frontend/src/container/OnboardingContainer/Steps/LogsConnectionStatus/LogsConnectionStatus.tsx create mode 100644 frontend/src/container/OnboardingContainer/Steps/MarkdownStep/MarkdownStep.tsx create mode 100644 frontend/src/container/OnboardingContainer/Steps/SelectMethod/SelectMethod.tsx create mode 100644 frontend/src/container/OnboardingContainer/common/Header/Header.tsx create mode 100644 frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.styles.scss create mode 100644 frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx create mode 100644 frontend/src/container/OnboardingContainer/constants/apmDocFilePaths.ts create mode 100644 frontend/src/container/OnboardingContainer/constants/awsMonitoringDocFilePaths.ts create mode 100644 frontend/src/container/OnboardingContainer/constants/infraMonitoringDocFilePaths.ts create mode 100644 frontend/src/container/OnboardingContainer/constants/logsManagementDocFilePaths.ts create mode 100644 frontend/src/container/OnboardingContainer/constants/stepsConfig.tsx create mode 100644 frontend/src/container/OnboardingContainer/context/OnboardingContext.tsx create mode 100644 frontend/src/container/OnboardingContainer/index.tsx create mode 100644 frontend/src/container/OnboardingContainer/typings.d.ts create mode 100644 frontend/src/container/OnboardingContainer/utils/dataSourceUtils.ts create mode 100644 frontend/src/container/OnboardingContainer/utils/getSteps.ts create mode 100644 frontend/src/container/OptionsMenu/AddColumnField/index.tsx create mode 100644 frontend/src/container/OptionsMenu/AddColumnField/styles.ts create mode 100644 frontend/src/container/OptionsMenu/FormatField/index.tsx create mode 100644 frontend/src/container/OptionsMenu/FormatField/styles.ts create mode 100644 frontend/src/container/OptionsMenu/MaxLinesField/index.tsx create mode 100644 frontend/src/container/OptionsMenu/MaxLinesField/styles.ts create mode 100644 frontend/src/container/OptionsMenu/constants.ts create mode 100644 frontend/src/container/OptionsMenu/index.tsx create mode 100644 frontend/src/container/OptionsMenu/styles.ts create mode 100644 frontend/src/container/OptionsMenu/types.ts create mode 100644 frontend/src/container/OptionsMenu/useOptionsMenu.ts create mode 100644 frontend/src/container/OptionsMenu/utils.ts create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/AddDomain/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/Create/Row/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/Create/Row/styles.ts create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/Create/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/Create/styles.ts create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/Edit/EditGoogleAuth.tsx create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/Edit/EditSAML.tsx create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/Edit/helpers.ts create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/Edit/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/Switch/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/helpers.test.ts create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/helpers.ts create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/AuthDomains/styles.ts create mode 100644 frontend/src/container/OrganizationSettings/DeleteMembersDetails/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/DisplayName/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/EditMembersDetails/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/EditMembersDetails/styles.ts create mode 100644 frontend/src/container/OrganizationSettings/InviteTeamMembers/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/InviteTeamMembers/styles.ts create mode 100644 frontend/src/container/OrganizationSettings/Members/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/PendingInvitesContainer/index.tsx create mode 100644 frontend/src/container/OrganizationSettings/PendingInvitesContainer/styles.tsx create mode 100644 frontend/src/container/OrganizationSettings/index.tsx create mode 100644 frontend/src/container/PageSizeSelect/PageSizeSelect.interfaces.ts create mode 100644 frontend/src/container/PageSizeSelect/index.tsx create mode 100644 frontend/src/container/PipelinePage/Layouts/ChangeHistory/DeploymentStage.tsx create mode 100644 frontend/src/container/PipelinePage/Layouts/ChangeHistory/DeploymentTime.tsx create mode 100644 frontend/src/container/PipelinePage/Layouts/ChangeHistory/index.tsx create mode 100644 frontend/src/container/PipelinePage/Layouts/ChangeHistory/utils.tsx create mode 100644 frontend/src/container/PipelinePage/Layouts/Pipeline/CreatePipelineButton.tsx create mode 100644 frontend/src/container/PipelinePage/Layouts/Pipeline/PipelinesSearchSection.tsx create mode 100644 frontend/src/container/PipelinePage/Layouts/Pipeline/index.tsx create mode 100644 frontend/src/container/PipelinePage/Layouts/config.ts create mode 100644 frontend/src/container/PipelinePage/Layouts/utils.ts create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/DescriptionTextArea.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/FilterInput/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/NameInput.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/FormFields/ProcessorTags.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/styles.ts create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewPipeline/utils.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/CSVInput.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/NameInput.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/ParsingRulesTextArea.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/TypeSelect.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/ProcessorForm.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/config.ts create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/styles.ts create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/ModeAndConfiguration.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/LogsFilterPreview/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/LogsFilterPreview/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/PipelineProcessingPreview/components/LogsProcessingSimulator.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/PipelineProcessingPreview/components/PipelineSimulationResult/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/PipelineProcessingPreview/components/PipelineSimulationResult/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/PipelineProcessingPreview/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/PipelineProcessingPreview/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/components/LogsCountInInterval/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/components/LogsCountInInterval/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/PreviewIntervalSelector/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/SampleLogsResponseDisplay.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/components/SampleLogs/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/hooks/usePipelinePreview.ts create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/Preview/hooks/useSampleLogs.ts create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/SaveConfigButton.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/DragAction.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineActions/components/PreviewAction.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineActions/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterSummary/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineFilterSummary/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/ProcessorActions.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/TableActions/DeleteAction.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/TableActions/EditAction.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/TableExpandIcon.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/Tags.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/TableComponents/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/config.ts create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/index.tsx create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/styles.scss create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/styles.ts create mode 100644 frontend/src/container/PipelinePage/PipelineListsView/utils.tsx create mode 100644 frontend/src/container/PipelinePage/components/TagInput.tsx create mode 100644 frontend/src/container/PipelinePage/components/styles.ts create mode 100644 frontend/src/container/PipelinePage/config.ts create mode 100644 frontend/src/container/PipelinePage/mocks/pipeline.ts create mode 100644 frontend/src/container/PipelinePage/styles.ts create mode 100644 frontend/src/container/PipelinePage/tests/AddNewPipeline.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/AddNewProcessor.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/CreatePipelineButton.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/DeleteAction.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/DragAction.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/EditAction.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/PipelineActions.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/PipelineExpandView.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/PipelinePageLayout.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/PipelinesSearchSection.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/TableExpandIcon.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/TagInput.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/Tags.test.tsx create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/AddNewPipeline.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/AddNewProcessor.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/CreatePipelineButton.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/DeleteAction.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/DragAction.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/EditAction.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/PipelineActions.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/PipelineExpandView.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/PipelinePageLayout.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/PipelinesSearchSection.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/TableExpandIcon.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/TagInput.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/__snapshots__/Tags.test.tsx.snap create mode 100644 frontend/src/container/PipelinePage/tests/utils.test.ts create mode 100644 frontend/src/container/QueryBuilder/QueryBuilder.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/QueryBuilder.styles.scss create mode 100644 frontend/src/container/QueryBuilder/QueryBuilder.tsx create mode 100644 frontend/src/container/QueryBuilder/components/AdditionalFiltersToggler/AdditionalFiltersToggler.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/components/AdditionalFiltersToggler/AdditionalFiltersToggler.styled.ts create mode 100644 frontend/src/container/QueryBuilder/components/AdditionalFiltersToggler/AdditionalFiltersToggler.styles.scss create mode 100644 frontend/src/container/QueryBuilder/components/AdditionalFiltersToggler/AdditionalFiltersToggler.tsx create mode 100644 frontend/src/container/QueryBuilder/components/AdditionalFiltersToggler/index.ts create mode 100644 frontend/src/container/QueryBuilder/components/DataSourceDropdown/DataSourceDropdown.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/components/DataSourceDropdown/DataSourceDropdown.tsx create mode 100644 frontend/src/container/QueryBuilder/components/DataSourceDropdown/index.ts create mode 100644 frontend/src/container/QueryBuilder/components/FilterLabel/FilterLabel.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/components/FilterLabel/FilterLabel.styled.ts create mode 100644 frontend/src/container/QueryBuilder/components/FilterLabel/FilterLabel.tsx create mode 100644 frontend/src/container/QueryBuilder/components/FilterLabel/index.ts create mode 100644 frontend/src/container/QueryBuilder/components/Formula/Formula.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/components/Formula/Formula.styles.scss create mode 100644 frontend/src/container/QueryBuilder/components/Formula/Formula.tsx create mode 100644 frontend/src/container/QueryBuilder/components/Formula/index.ts create mode 100644 frontend/src/container/QueryBuilder/components/HavingFilterTag/HavingFilterTag.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/components/HavingFilterTag/HavingFilterTag.styled.ts create mode 100644 frontend/src/container/QueryBuilder/components/HavingFilterTag/HavingFilterTag.tsx create mode 100644 frontend/src/container/QueryBuilder/components/HavingFilterTag/index.ts create mode 100644 frontend/src/container/QueryBuilder/components/ListItemWrapper/ListItemWrapper.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/components/ListItemWrapper/ListItemWrapper.styled.ts create mode 100644 frontend/src/container/QueryBuilder/components/ListItemWrapper/ListItemWrapper.tsx create mode 100644 frontend/src/container/QueryBuilder/components/ListItemWrapper/index.ts create mode 100644 frontend/src/container/QueryBuilder/components/ListMarker/ListMarker.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/components/ListMarker/ListMarker.styled.ts create mode 100644 frontend/src/container/QueryBuilder/components/ListMarker/ListMarker.tsx create mode 100644 frontend/src/container/QueryBuilder/components/ListMarker/index.ts create mode 100644 frontend/src/container/QueryBuilder/components/QBEntityOptions/QBEntityOptions.styles.scss create mode 100644 frontend/src/container/QueryBuilder/components/QBEntityOptions/QBEntityOptions.tsx create mode 100644 frontend/src/container/QueryBuilder/components/Query/Query.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/components/Query/Query.styles.scss create mode 100644 frontend/src/container/QueryBuilder/components/Query/Query.tsx create mode 100644 frontend/src/container/QueryBuilder/components/Query/index.ts create mode 100644 frontend/src/container/QueryBuilder/components/QueryFunctions/Function.tsx create mode 100644 frontend/src/container/QueryBuilder/components/QueryFunctions/QueryFunctions.styles.scss create mode 100644 frontend/src/container/QueryBuilder/components/QueryFunctions/QueryFunctions.tsx create mode 100644 frontend/src/container/QueryBuilder/components/SpaceAggregationOptions/SpaceAggregationOptions.tsx create mode 100644 frontend/src/container/QueryBuilder/components/ToolbarActions/LeftToolbarActions.tsx create mode 100644 frontend/src/container/QueryBuilder/components/ToolbarActions/RightToolbarActions.tsx create mode 100644 frontend/src/container/QueryBuilder/components/ToolbarActions/ToolbarActions.styles.scss create mode 100644 frontend/src/container/QueryBuilder/components/index.ts create mode 100644 frontend/src/container/QueryBuilder/filters/AggregateEveryFilter/index.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/AggregatorFilter/AggregatorFilter.intefaces.ts create mode 100644 frontend/src/container/QueryBuilder/filters/AggregatorFilter/AggregatorFilter.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/AggregatorFilter/index.ts create mode 100644 frontend/src/container/QueryBuilder/filters/BuilderUnitsFilter/BuilderUnits.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/BuilderUnitsFilter/config.ts create mode 100644 frontend/src/container/QueryBuilder/filters/BuilderUnitsFilter/index.ts create mode 100644 frontend/src/container/QueryBuilder/filters/BuilderUnitsFilter/styles.ts create mode 100644 frontend/src/container/QueryBuilder/filters/BuilderUnitsFilter/types.ts create mode 100644 frontend/src/container/QueryBuilder/filters/BuilderUnitsFilter/utils.ts create mode 100644 frontend/src/container/QueryBuilder/filters/Formula/Having/HavingFilter.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/Formula/Having/types.ts create mode 100644 frontend/src/container/QueryBuilder/filters/Formula/Limit/Limit.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/Formula/Limit/types.ts create mode 100644 frontend/src/container/QueryBuilder/filters/Formula/OrderBy/OrderByFilter.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/Formula/OrderBy/types.ts create mode 100644 frontend/src/container/QueryBuilder/filters/Formula/OrderBy/useOrderByFormulaFilter.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/Formula/OrderBy/utils.ts create mode 100644 frontend/src/container/QueryBuilder/filters/GroupByFilter/GroupByFilter.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/filters/GroupByFilter/GroupByFilter.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/GroupByFilter/index.ts create mode 100644 frontend/src/container/QueryBuilder/filters/GroupByFilter/utils.ts create mode 100644 frontend/src/container/QueryBuilder/filters/HavingFilter/HavingFilter.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/filters/HavingFilter/HavingFilter.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/HavingFilter/__tests__/utils.test.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/HavingFilter/index.ts create mode 100644 frontend/src/container/QueryBuilder/filters/LimitFilter/LimitFilter.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/OperatorsSelect/OperatorsSelect.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/filters/OperatorsSelect/OperatorsSelect.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/OperatorsSelect/index.ts create mode 100644 frontend/src/container/QueryBuilder/filters/OrderByFilter/OrderByFilter.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/filters/OrderByFilter/OrderByFilter.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/OrderByFilter/config.ts create mode 100644 frontend/src/container/QueryBuilder/filters/OrderByFilter/constants.ts create mode 100644 frontend/src/container/QueryBuilder/filters/OrderByFilter/index.ts create mode 100644 frontend/src/container/QueryBuilder/filters/OrderByFilter/useOrderByFilter.ts create mode 100644 frontend/src/container/QueryBuilder/filters/OrderByFilter/utils.ts create mode 100644 frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/OptionRenderer.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/QueryBuilderSearch.styles.scss create mode 100644 frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/config.ts create mode 100644 frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/constant.ts create mode 100644 frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/index.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/style.ts create mode 100644 frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/utils.ts create mode 100644 frontend/src/container/QueryBuilder/filters/ReduceToFilter/ReduceToFilter.interfaces.ts create mode 100644 frontend/src/container/QueryBuilder/filters/ReduceToFilter/ReduceToFilter.tsx create mode 100644 frontend/src/container/QueryBuilder/filters/ReduceToFilter/index.ts create mode 100644 frontend/src/container/QueryBuilder/filters/index.ts create mode 100644 frontend/src/container/QueryBuilder/filters/utils.ts create mode 100644 frontend/src/container/QueryBuilder/index.ts create mode 100644 frontend/src/container/QueryBuilder/type.ts create mode 100644 frontend/src/container/QueryTable/QueryTable.intefaces.ts create mode 100644 frontend/src/container/QueryTable/QueryTable.styles.scss create mode 100644 frontend/src/container/QueryTable/QueryTable.tsx create mode 100644 frontend/src/container/QueryTable/config.ts create mode 100644 frontend/src/container/QueryTable/index.ts create mode 100644 frontend/src/container/QueryTable/utils.ts create mode 100644 frontend/src/container/ResetPassword/ResetPassword.test.tsx create mode 100644 frontend/src/container/ResetPassword/index.tsx create mode 100644 frontend/src/container/ResetPassword/styles.ts create mode 100644 frontend/src/container/ResourceAttributesFilter/ResourceAttributesFilter.tsx create mode 100644 frontend/src/container/ResourceAttributesFilter/components/QueryChip/QueryChip.tsx create mode 100644 frontend/src/container/ResourceAttributesFilter/components/QueryChip/index.ts create mode 100644 frontend/src/container/ResourceAttributesFilter/components/QueryChip/types.ts create mode 100644 frontend/src/container/ResourceAttributesFilter/index.ts create mode 100644 frontend/src/container/ResourceAttributesFilter/styles.ts create mode 100644 frontend/src/container/ServiceApplication/Columns/BaseColumnOptions.ts create mode 100644 frontend/src/container/ServiceApplication/Columns/ColumnContants.ts create mode 100644 frontend/src/container/ServiceApplication/Columns/GetColumnSearchProps.tsx create mode 100644 frontend/src/container/ServiceApplication/Columns/ServiceColumn.ts create mode 100644 frontend/src/container/ServiceApplication/Filter/FilterDropdown.tsx create mode 100644 frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx create mode 100644 frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetrics.test.tsx create mode 100644 frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricsApplication.tsx create mode 100644 frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricsQuery.ts create mode 100644 frontend/src/container/ServiceApplication/ServiceMetrics/index.tsx create mode 100644 frontend/src/container/ServiceApplication/ServiceTraces/ServicTraces.test.tsx create mode 100644 frontend/src/container/ServiceApplication/ServiceTraces/Service.test.tsx create mode 100644 frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx create mode 100644 frontend/src/container/ServiceApplication/ServiceTraces/__mocks__/getServices.ts create mode 100644 frontend/src/container/ServiceApplication/ServiceTraces/index.tsx create mode 100644 frontend/src/container/ServiceApplication/SkipOnBoardModal/index.tsx create mode 100644 frontend/src/container/ServiceApplication/index.tsx create mode 100644 frontend/src/container/ServiceApplication/styles.ts create mode 100644 frontend/src/container/ServiceApplication/types.ts create mode 100644 frontend/src/container/ServiceApplication/utils.ts create mode 100644 frontend/src/container/ServiceTable/Columns/ColumnContants.ts create mode 100644 frontend/src/container/ServiceTable/Columns/GetColumnSearchProps.tsx create mode 100644 frontend/src/container/ServiceTable/Columns/ServiceColumn.ts create mode 100644 frontend/src/container/ServiceTable/Filter/FilterDropdown.tsx create mode 100644 frontend/src/container/ServiceTable/Service.test.tsx create mode 100644 frontend/src/container/ServiceTable/SkipOnBoardModal/index.tsx create mode 100644 frontend/src/container/ServiceTable/__mock__/servicesListMock.ts create mode 100644 frontend/src/container/ServiceTable/index.tsx create mode 100644 frontend/src/container/ServiceTable/styles.ts create mode 100644 frontend/src/container/ServiceTable/types.ts create mode 100644 frontend/src/container/SideNav/NavItem/NavItem.styles.scss create mode 100644 frontend/src/container/SideNav/NavItem/NavItem.tsx create mode 100644 frontend/src/container/SideNav/SideNav.styles.scss create mode 100644 frontend/src/container/SideNav/SideNav.tsx create mode 100644 frontend/src/container/SideNav/Slack.tsx create mode 100644 frontend/src/container/SideNav/config.ts create mode 100644 frontend/src/container/SideNav/helper.test.ts create mode 100644 frontend/src/container/SideNav/helper.ts create mode 100644 frontend/src/container/SideNav/index.ts create mode 100644 frontend/src/container/SideNav/menuItems.tsx create mode 100644 frontend/src/container/SideNav/sideNav.types.ts create mode 100644 frontend/src/container/SideNav/sideNav.utils.ts create mode 100644 frontend/src/container/SideNav/styles.ts create mode 100644 frontend/src/container/TimeSeriesView/TimeSeriesView.styles.scss create mode 100644 frontend/src/container/TimeSeriesView/TimeSeriesView.tsx create mode 100644 frontend/src/container/TimeSeriesView/index.tsx create mode 100644 frontend/src/container/TimeSeriesView/styles.ts create mode 100644 frontend/src/container/TimeSeriesView/utils.ts create mode 100644 frontend/src/container/Timeline/index.tsx create mode 100644 frontend/src/container/Timeline/styles.ts create mode 100644 frontend/src/container/Timeline/types.ts create mode 100644 frontend/src/container/Timeline/utils.ts create mode 100644 frontend/src/container/Toolbar/Toolbar.styles.scss create mode 100644 frontend/src/container/Toolbar/Toolbar.tsx create mode 100644 frontend/src/container/TopNav/AutoRefresh/config.ts create mode 100644 frontend/src/container/TopNav/AutoRefresh/index.tsx create mode 100644 frontend/src/container/TopNav/AutoRefresh/styles.ts create mode 100644 frontend/src/container/TopNav/AutoRefreshV2/AutoRefreshV2.styles.scss create mode 100644 frontend/src/container/TopNav/AutoRefreshV2/config.ts create mode 100644 frontend/src/container/TopNav/AutoRefreshV2/index.tsx create mode 100644 frontend/src/container/TopNav/AutoRefreshV2/styles.ts create mode 100644 frontend/src/container/TopNav/Breadcrumbs/index.tsx create mode 100644 frontend/src/container/TopNav/CustomDateTimeModal/CustomDateTimeModal.test.tsx create mode 100644 frontend/src/container/TopNav/CustomDateTimeModal/index.tsx create mode 100644 frontend/src/container/TopNav/DateTimeSelection/DateTimeSelection.styles.scss create mode 100644 frontend/src/container/TopNav/DateTimeSelection/Refresh.tsx create mode 100644 frontend/src/container/TopNav/DateTimeSelection/config.ts create mode 100644 frontend/src/container/TopNav/DateTimeSelection/index.tsx create mode 100644 frontend/src/container/TopNav/DateTimeSelection/styles.ts create mode 100644 frontend/src/container/TopNav/DateTimeSelectionV2/DateTimeSelectionV2.styles.scss create mode 100644 frontend/src/container/TopNav/DateTimeSelectionV2/Refresh.tsx create mode 100644 frontend/src/container/TopNav/DateTimeSelectionV2/config.ts create mode 100644 frontend/src/container/TopNav/DateTimeSelectionV2/index.tsx create mode 100644 frontend/src/container/TopNav/DateTimeSelectionV2/styles.ts create mode 100644 frontend/src/container/TopNav/index.tsx create mode 100644 frontend/src/container/TopNav/styles.ts create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelBody/Common/styles.ts create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelBody/CommonCheckBox/index.tsx create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/styles.ts create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/util.ts create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelBody/SearchTraceID/index.tsx create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelBody/index.tsx create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelBody/styles.ts create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx create mode 100644 frontend/src/container/Trace/Filters/Panel/PanelHeading/styles.ts create mode 100644 frontend/src/container/Trace/Filters/Panel/index.tsx create mode 100644 frontend/src/container/Trace/Filters/index.tsx create mode 100644 frontend/src/container/Trace/Filters/styles.ts create mode 100644 frontend/src/container/Trace/Graph/config.ts create mode 100644 frontend/src/container/Trace/Graph/index.tsx create mode 100644 frontend/src/container/Trace/Graph/styles.ts create mode 100644 frontend/src/container/Trace/Search/AllTags/Tag/TagKey.tsx create mode 100644 frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx create mode 100644 frontend/src/container/Trace/Search/AllTags/Tag/index.tsx create mode 100644 frontend/src/container/Trace/Search/AllTags/Tag/styles.ts create mode 100644 frontend/src/container/Trace/Search/AllTags/Tag/utils.ts create mode 100644 frontend/src/container/Trace/Search/AllTags/index.tsx create mode 100644 frontend/src/container/Trace/Search/AllTags/styles.ts create mode 100644 frontend/src/container/Trace/Search/config.ts create mode 100644 frontend/src/container/Trace/Search/index.tsx create mode 100644 frontend/src/container/Trace/Search/styles.ts create mode 100644 frontend/src/container/Trace/Search/util.ts create mode 100644 frontend/src/container/Trace/TraceGraphFilter/config.ts create mode 100644 frontend/src/container/Trace/TraceGraphFilter/index.tsx create mode 100644 frontend/src/container/Trace/TraceGraphFilter/styles.ts create mode 100644 frontend/src/container/Trace/TraceGraphFilter/utils.test.ts create mode 100644 frontend/src/container/Trace/TraceGraphFilter/utils.ts create mode 100644 frontend/src/container/Trace/TraceTable/index.tsx create mode 100644 frontend/src/container/Trace/TraceTable/util.ts create mode 100644 frontend/src/container/TraceDetail/Missingtrace.tsx create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/EllipsedButton.tsx create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/Events/Event.tsx create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/Events/EventStartTime.tsx create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/Events/RelativeStartTime.tsx create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/Events/index.tsx create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/Tags/Tag.tsx create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/Tags/index.tsx create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/Tags/styles.ts create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/config.ts create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/styles.ts create mode 100644 frontend/src/container/TraceDetail/TraceDetails.styles.scss create mode 100644 frontend/src/container/TraceDetail/index.tsx create mode 100644 frontend/src/container/TraceDetail/styles.ts create mode 100644 frontend/src/container/TraceDetail/utils.test.ts create mode 100644 frontend/src/container/TraceDetail/utils.ts create mode 100644 frontend/src/container/TraceFlameGraph/__tests__/TraceFlameGraph.test.tsx create mode 100644 frontend/src/container/TraceFlameGraph/__tests__/__snapshots__/TraceFlameGraph.test.tsx.snap create mode 100644 frontend/src/container/TraceFlameGraph/index.tsx create mode 100644 frontend/src/container/TraceFlameGraph/styles.ts create mode 100644 frontend/src/container/TracesExplorer/Controls/index.tsx create mode 100644 frontend/src/container/TracesExplorer/Controls/styles.ts create mode 100644 frontend/src/container/TracesExplorer/ListView/configs.tsx create mode 100644 frontend/src/container/TracesExplorer/ListView/index.tsx create mode 100644 frontend/src/container/TracesExplorer/ListView/styles.ts create mode 100644 frontend/src/container/TracesExplorer/ListView/utils.tsx create mode 100644 frontend/src/container/TracesExplorer/QuerySection/index.tsx create mode 100644 frontend/src/container/TracesExplorer/QuerySection/styles.ts create mode 100644 frontend/src/container/TracesExplorer/TableView/index.tsx create mode 100644 frontend/src/container/TracesExplorer/TracesView/configs.tsx create mode 100644 frontend/src/container/TracesExplorer/TracesView/index.tsx create mode 100644 frontend/src/container/TracesExplorer/TracesView/styles.ts create mode 100644 frontend/src/container/TracesTableComponent/TracesTableComponent.styles.scss create mode 100644 frontend/src/container/TracesTableComponent/TracesTableComponent.tsx create mode 100644 frontend/src/container/TriggeredAlerts/Filter.tsx create mode 100644 frontend/src/container/TriggeredAlerts/FilteredTable/ExapandableRow.tsx create mode 100644 frontend/src/container/TriggeredAlerts/FilteredTable/TableRow.tsx create mode 100644 frontend/src/container/TriggeredAlerts/FilteredTable/index.tsx create mode 100644 frontend/src/container/TriggeredAlerts/FilteredTable/styles.ts create mode 100644 frontend/src/container/TriggeredAlerts/NoFilterTable.tsx create mode 100644 frontend/src/container/TriggeredAlerts/TableComponents/AlertStatus.tsx create mode 100644 frontend/src/container/TriggeredAlerts/TriggeredAlert.tsx create mode 100644 frontend/src/container/TriggeredAlerts/index.tsx create mode 100644 frontend/src/container/TriggeredAlerts/styles.ts create mode 100644 frontend/src/container/TriggeredAlerts/utils.ts create mode 100644 frontend/src/container/Version/constant.ts create mode 100644 frontend/src/container/Version/index.tsx create mode 100644 frontend/src/container/Version/styles.ts create mode 100644 frontend/src/hooks/APIKeys/useGetAllAPIKeys.ts create mode 100644 frontend/src/hooks/Integrations/useGetAllIntegrations.ts create mode 100644 frontend/src/hooks/Integrations/useGetIntegration.ts create mode 100644 frontend/src/hooks/Integrations/useGetIntegrationStatus.ts create mode 100644 frontend/src/hooks/ResizeTable/useSortableTable.ts create mode 100644 frontend/src/hooks/analytics/useAnalytics.tsx create mode 100644 frontend/src/hooks/apDex/useGetApDexSettings.ts create mode 100644 frontend/src/hooks/apDex/useGetMetricMeta.ts create mode 100644 frontend/src/hooks/apDex/useSetApDexSettings.ts create mode 100644 frontend/src/hooks/dashboard/useDashboardFromLocalStorage.tsx create mode 100644 frontend/src/hooks/dashboard/useDeleteDashboard.tsx create mode 100644 frontend/src/hooks/dashboard/useGetAllDashboard.tsx create mode 100644 frontend/src/hooks/dashboard/useUpdateDashboard.tsx create mode 100644 frontend/src/hooks/dashboard/utils.ts create mode 100644 frontend/src/hooks/hotkeys/__tests__/useKeyboardHotkeys.test.tsx create mode 100644 frontend/src/hooks/hotkeys/useKeyboardHotkeys.tsx create mode 100644 frontend/src/hooks/logs/configs.ts create mode 100644 frontend/src/hooks/logs/types.ts create mode 100644 frontend/src/hooks/logs/useActiveLog.ts create mode 100644 frontend/src/hooks/logs/useCopyLogLink.ts create mode 100644 frontend/src/hooks/queryBuilder/useAutoComplete.ts create mode 100644 frontend/src/hooks/queryBuilder/useCreateAlerts.tsx create mode 100644 frontend/src/hooks/queryBuilder/useFetchKeysAndValues.ts create mode 100644 frontend/src/hooks/queryBuilder/useGetAggregateKeys.ts create mode 100644 frontend/src/hooks/queryBuilder/useGetCompositeQueryParam.ts create mode 100644 frontend/src/hooks/queryBuilder/useGetExplorerQueryRange.ts create mode 100644 frontend/src/hooks/queryBuilder/useGetPanelTypesQueryParam.ts create mode 100644 frontend/src/hooks/queryBuilder/useGetQueriesRange.ts create mode 100644 frontend/src/hooks/queryBuilder/useGetQueryRange.ts create mode 100644 frontend/src/hooks/queryBuilder/useGetSearchQueryParam.ts create mode 100644 frontend/src/hooks/queryBuilder/useGetWidgetQueryRange.ts create mode 100644 frontend/src/hooks/queryBuilder/useIsValidTag.test.ts create mode 100644 frontend/src/hooks/queryBuilder/useIsValidTag.ts create mode 100644 frontend/src/hooks/queryBuilder/useOperatorType.ts create mode 100644 frontend/src/hooks/queryBuilder/useOperators.ts create mode 100644 frontend/src/hooks/queryBuilder/useOptions.ts create mode 100644 frontend/src/hooks/queryBuilder/useQueryBuilder.ts create mode 100644 frontend/src/hooks/queryBuilder/useQueryBuilderOperations.ts create mode 100644 frontend/src/hooks/queryBuilder/useSetCurrentKeyAndOperator.ts create mode 100644 frontend/src/hooks/queryBuilder/useShareBuilderUrl.ts create mode 100644 frontend/src/hooks/queryBuilder/useStepInterval.ts create mode 100644 frontend/src/hooks/queryBuilder/useTag.ts create mode 100644 frontend/src/hooks/queryBuilder/useTagValidation.ts create mode 100644 frontend/src/hooks/queryPagination/config.ts create mode 100644 frontend/src/hooks/queryPagination/index.ts create mode 100644 frontend/src/hooks/queryPagination/types.ts create mode 100644 frontend/src/hooks/queryPagination/useQueryPagination.ts create mode 100644 frontend/src/hooks/queryPagination/utils.ts create mode 100644 frontend/src/hooks/saveViews/useDeleteView.ts create mode 100644 frontend/src/hooks/saveViews/useGetAllViews.ts create mode 100644 frontend/src/hooks/saveViews/useSaveView.ts create mode 100644 frontend/src/hooks/saveViews/useUpdateView.ts create mode 100644 frontend/src/hooks/useAxiosError.tsx create mode 100644 frontend/src/hooks/useChartMutable.ts create mode 100644 frontend/src/hooks/useClickOutside.tsx create mode 100644 frontend/src/hooks/useComponentPermission.test.ts create mode 100644 frontend/src/hooks/useComponentPermission.ts create mode 100644 frontend/src/hooks/useDarkMode/constant.ts create mode 100644 frontend/src/hooks/useDarkMode/index.tsx create mode 100644 frontend/src/hooks/useDebounce.tsx create mode 100644 frontend/src/hooks/useDebouncedFunction.ts create mode 100644 frontend/src/hooks/useDimensions.ts create mode 100644 frontend/src/hooks/useDragColumns/configs.ts create mode 100644 frontend/src/hooks/useDragColumns/index.ts create mode 100644 frontend/src/hooks/useDragColumns/types.ts create mode 100644 frontend/src/hooks/useDragColumns/utils.ts create mode 100644 frontend/src/hooks/useErrorNotification.ts create mode 100644 frontend/src/hooks/useEventSourceEvent/index.ts create mode 100644 frontend/src/hooks/useFeatureFlag/constant.ts create mode 100644 frontend/src/hooks/useFeatureFlag/index.ts create mode 100644 frontend/src/hooks/useFeatureFlag/useFeatureFlag.ts create mode 100644 frontend/src/hooks/useFeatureFlag/useIsFeatureDisabled.ts create mode 100644 frontend/src/hooks/useFeatureFlag/utils.test.ts create mode 100644 frontend/src/hooks/useFeatureFlag/utils.ts create mode 100644 frontend/src/hooks/useFetch.ts create mode 100644 frontend/src/hooks/useFontObserver.tsx create mode 100644 frontend/src/hooks/useGetFeatureFlag.tsx create mode 100644 frontend/src/hooks/useGetTopLevelOperations.ts create mode 100644 frontend/src/hooks/useHandleExplorerTabChange.ts create mode 100644 frontend/src/hooks/useIntersectionObserver.ts create mode 100644 frontend/src/hooks/useInterval.test.ts create mode 100644 frontend/src/hooks/useInterval.ts create mode 100644 frontend/src/hooks/useLicense/constant.ts create mode 100644 frontend/src/hooks/useLicense/index.ts create mode 100644 frontend/src/hooks/useLicense/useLicense.tsx create mode 100644 frontend/src/hooks/useLogsData.ts create mode 100644 frontend/src/hooks/useMountedState.ts create mode 100644 frontend/src/hooks/useNotifications.tsx create mode 100644 frontend/src/hooks/usePreviousValue.test.tsx create mode 100644 frontend/src/hooks/usePreviousValue.ts create mode 100644 frontend/src/hooks/useQueryService.ts create mode 100644 frontend/src/hooks/useResourceAttribute/ResourceProvider.tsx create mode 100644 frontend/src/hooks/useResourceAttribute/config.ts create mode 100644 frontend/src/hooks/useResourceAttribute/context.ts create mode 100644 frontend/src/hooks/useResourceAttribute/index.ts create mode 100644 frontend/src/hooks/useResourceAttribute/machine.ts create mode 100644 frontend/src/hooks/useResourceAttribute/machine.typegen.ts create mode 100644 frontend/src/hooks/useResourceAttribute/types.ts create mode 100644 frontend/src/hooks/useResourceAttribute/useResourceAttribute.tsx create mode 100644 frontend/src/hooks/useResourceAttribute/utils.ts create mode 100644 frontend/src/hooks/useScrollToTop/index.tsx create mode 100644 frontend/src/hooks/useScrollToTop/types.ts create mode 100644 frontend/src/hooks/useScrollToTop/useScrollToTop.test.ts create mode 100644 frontend/src/hooks/useTabFocus.tsx create mode 100644 frontend/src/hooks/useUrlQuery.test.tsx create mode 100644 frontend/src/hooks/useUrlQuery.ts create mode 100644 frontend/src/hooks/useUrlQueryData.ts create mode 100644 frontend/src/hooks/useUsage/useUsage.tsx create mode 100644 frontend/src/index.html.ejs create mode 100644 frontend/src/index.tsx create mode 100644 frontend/src/lib/JSXtoHTML.ts create mode 100644 frontend/src/lib/__fixtures__/getRandomColor.ts create mode 100644 frontend/src/lib/__fixtures__/logql.ts create mode 100644 frontend/src/lib/__tests__/getStep.test.ts create mode 100644 frontend/src/lib/__tests__/logql/parser.test.ts create mode 100644 frontend/src/lib/__tests__/logql/reverseParser.test.ts create mode 100644 frontend/src/lib/__tests__/logql/splitter.test.ts create mode 100644 frontend/src/lib/convertDateToAmAndPm.ts create mode 100644 frontend/src/lib/convertIntoHr.ts create mode 100644 frontend/src/lib/convertToNanoSecondsToSecond.ts create mode 100644 frontend/src/lib/covertIntoEpoc.ts create mode 100644 frontend/src/lib/createIdFromObjectFields.ts create mode 100644 frontend/src/lib/createQueryParams.ts create mode 100644 frontend/src/lib/dashbaordVariables/customCommaValuesParser.ts create mode 100644 frontend/src/lib/dashbaordVariables/getDashboardVariables.ts create mode 100644 frontend/src/lib/dashbaordVariables/sortVariableValues.ts create mode 100644 frontend/src/lib/dashboard/getQueryResults.ts create mode 100644 frontend/src/lib/dashboard/getUpdatedLayout.ts create mode 100644 frontend/src/lib/dashboard/prepareQueryRangePayload.ts create mode 100644 frontend/src/lib/getChartData.ts create mode 100644 frontend/src/lib/getConvertedValue.ts create mode 100644 frontend/src/lib/getFormatedDate.ts create mode 100644 frontend/src/lib/getGeneratedFilterQueryString.ts create mode 100644 frontend/src/lib/getGlobalDropDownFormatedDate.ts create mode 100644 frontend/src/lib/getLabelName.ts create mode 100644 frontend/src/lib/getMaxMinTime.ts create mode 100644 frontend/src/lib/getMinMax.ts create mode 100644 frontend/src/lib/getRandomColor.test.ts create mode 100644 frontend/src/lib/getRandomColor.ts create mode 100644 frontend/src/lib/getSettingsPeroid.ts create mode 100644 frontend/src/lib/getStartAndEndTime/getMicroSeconds.ts create mode 100644 frontend/src/lib/getStartAndEndTime/getMinAgo.ts create mode 100644 frontend/src/lib/getStartAndEndTime/index.ts create mode 100644 frontend/src/lib/getStartEndRangeTime.ts create mode 100644 frontend/src/lib/getStep.test.ts create mode 100644 frontend/src/lib/getStep.ts create mode 100644 frontend/src/lib/getTimeString.ts create mode 100644 frontend/src/lib/history.ts create mode 100644 frontend/src/lib/logql/errors/ConvertToFullText.ts create mode 100644 frontend/src/lib/logql/errors/InvalidQueryPair.ts create mode 100644 frontend/src/lib/logql/errors/index.ts create mode 100644 frontend/src/lib/logql/index.ts create mode 100644 frontend/src/lib/logql/parser.ts create mode 100644 frontend/src/lib/logql/reverseParser.ts create mode 100644 frontend/src/lib/logql/splitter.ts create mode 100644 frontend/src/lib/logql/tokens.ts create mode 100644 frontend/src/lib/logql/types.ts create mode 100644 frontend/src/lib/logs/fieldSearch.ts create mode 100644 frontend/src/lib/logs/flatLogData.ts create mode 100644 frontend/src/lib/logs/generateFilterQuery.ts create mode 100644 frontend/src/lib/newQueryBuilder/chooseAutocompleteFromCustomValue.ts create mode 100644 frontend/src/lib/newQueryBuilder/convertNewDataToOld.ts create mode 100644 frontend/src/lib/newQueryBuilder/createNewBuilderItemName.ts create mode 100644 frontend/src/lib/newQueryBuilder/getAutocompleteValueAndType.ts create mode 100644 frontend/src/lib/newQueryBuilder/getMetricsOperatorsByAttributeType.ts create mode 100644 frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts create mode 100644 frontend/src/lib/newQueryBuilder/getPaginationQueryData.ts create mode 100644 frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery.ts create mode 100644 frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi.ts create mode 100644 frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi.ts create mode 100644 frontend/src/lib/newQueryBuilder/transformQueryBuilderDataModel.ts create mode 100644 frontend/src/lib/query/GetFormulaName.ts create mode 100644 frontend/src/lib/query/GetQueryName.ts create mode 100644 frontend/src/lib/query/convertObjectIntoParams.ts create mode 100644 frontend/src/lib/query/createTableColumnsFromQuery.ts create mode 100644 frontend/src/lib/query/findDataTypeOfOperator.ts create mode 100644 frontend/src/lib/query/transformQueryBuilderData.ts create mode 100644 frontend/src/lib/query/transformStringWithPrefix.ts create mode 100644 frontend/src/lib/removeJSONStringifyQuotes.ts create mode 100644 frontend/src/lib/replaceIncorrectObjectFields.ts create mode 100644 frontend/src/lib/toCapitalize.ts create mode 100644 frontend/src/lib/uPlotLib/getUplotChartOptions.ts create mode 100644 frontend/src/lib/uPlotLib/placement.ts create mode 100644 frontend/src/lib/uPlotLib/plugins/onClickPlugin.ts create mode 100644 frontend/src/lib/uPlotLib/plugins/tooltipPlugin.ts create mode 100644 frontend/src/lib/uPlotLib/uPlotLib.styles.scss create mode 100644 frontend/src/lib/uPlotLib/utils/constants.ts create mode 100644 frontend/src/lib/uPlotLib/utils/generateColor.ts create mode 100644 frontend/src/lib/uPlotLib/utils/getAxes.ts create mode 100644 frontend/src/lib/uPlotLib/utils/getGridColor.ts create mode 100644 frontend/src/lib/uPlotLib/utils/getRenderer.ts create mode 100644 frontend/src/lib/uPlotLib/utils/getSeriesData.ts create mode 100644 frontend/src/lib/uPlotLib/utils/getUplotChartData.ts create mode 100644 frontend/src/lib/uPlotLib/utils/getXAxisScale.ts create mode 100644 frontend/src/lib/uPlotLib/utils/getYAxisScale.test.ts create mode 100644 frontend/src/lib/uPlotLib/utils/getYAxisScale.ts create mode 100644 frontend/src/lib/uPlotLib/utils/tests/__mocks__/seriesData.ts create mode 100644 frontend/src/lib/uPlotLib/utils/tests/__mocks__/uplotChartOptionsData.ts create mode 100644 frontend/src/lib/uPlotLib/utils/tests/getSeriesData.test.ts create mode 100644 frontend/src/lib/uPlotLib/utils/tests/getUplotChartOptions.test.ts create mode 100644 frontend/src/mocks-server/__mockdata__/apiKeys.ts create mode 100644 frontend/src/mocks-server/__mockdata__/billing.ts create mode 100644 frontend/src/mocks-server/__mockdata__/licenses.ts create mode 100644 frontend/src/mocks-server/__mockdata__/logs_query_range.ts create mode 100644 frontend/src/mocks-server/__mockdata__/query_range.ts create mode 100644 frontend/src/mocks-server/__mockdata__/services.ts create mode 100644 frontend/src/mocks-server/__mockdata__/top_level_operations.ts create mode 100644 frontend/src/mocks-server/handlers.ts create mode 100644 frontend/src/mocks-server/server.ts create mode 100644 frontend/src/mocks-server/setupTests.ts create mode 100644 frontend/src/modules/Servicemap/Map.tsx create mode 100644 frontend/src/modules/Servicemap/ServiceMap.tsx create mode 100644 frontend/src/modules/Servicemap/index.tsx create mode 100644 frontend/src/modules/Servicemap/utils.ts create mode 100644 frontend/src/modules/Usage/UsageExplorer.tsx create mode 100644 frontend/src/modules/Usage/index.tsx create mode 100644 frontend/src/modules/Usage/styles.ts create mode 100644 frontend/src/pages/AlertChannelCreate/config.tsx create mode 100644 frontend/src/pages/AlertChannelCreate/index.tsx create mode 100644 frontend/src/pages/AlertList/index.tsx create mode 100644 frontend/src/pages/AllErrors/config.ts create mode 100644 frontend/src/pages/AllErrors/index.tsx create mode 100644 frontend/src/pages/Billing/BillingPage.styles.scss create mode 100644 frontend/src/pages/Billing/BillingPage.tsx create mode 100644 frontend/src/pages/Billing/index.tsx create mode 100644 frontend/src/pages/ChannelsEdit/index.tsx create mode 100644 frontend/src/pages/CreateAlert/index.tsx create mode 100644 frontend/src/pages/CreateAlert/styles.ts create mode 100644 frontend/src/pages/DashboardWidget/index.tsx create mode 100644 frontend/src/pages/DashboardsListPage/DashboardsListPage.tsx create mode 100644 frontend/src/pages/DashboardsListPage/index.tsx create mode 100644 frontend/src/pages/EditRules/index.tsx create mode 100644 frontend/src/pages/ErrorBoundaryFallback/ErrorBoundaryFallback.styles.scss create mode 100644 frontend/src/pages/ErrorBoundaryFallback/ErrorBoundaryFallback.tsx create mode 100644 frontend/src/pages/ErrorDetails/index.tsx create mode 100644 frontend/src/pages/ErrorDetails/utils.ts create mode 100644 frontend/src/pages/GettingStarted/DocCard.tsx create mode 100644 frontend/src/pages/GettingStarted/Section.tsx create mode 100644 frontend/src/pages/GettingStarted/index.tsx create mode 100644 frontend/src/pages/GettingStarted/renderConfig.tsx create mode 100644 frontend/src/pages/GettingStarted/styles.ts create mode 100644 frontend/src/pages/GettingStarted/types.ts create mode 100644 frontend/src/pages/GettingStarted/utmParams.ts create mode 100644 frontend/src/pages/Integrations/Header.tsx create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContent.tsx create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/Configure.tsx create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/DataCollected.tsx create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/IntegrationDetailContentTabs.styles.scss create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/Overview.tsx create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailHeader.tsx create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.styles.scss create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar.tsx create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/TestConnection.tsx create mode 100644 frontend/src/pages/Integrations/IntegrationDetailPage/utils.ts create mode 100644 frontend/src/pages/Integrations/Integrations.styles.scss create mode 100644 frontend/src/pages/Integrations/Integrations.tsx create mode 100644 frontend/src/pages/Integrations/IntegrationsList.tsx create mode 100644 frontend/src/pages/Integrations/index.ts create mode 100644 frontend/src/pages/Integrations/utils.ts create mode 100644 frontend/src/pages/IntegrationsMarketPlace/IntegrationsMarketPlace.tsx create mode 100644 frontend/src/pages/IntegrationsMarketPlace/index.ts create mode 100644 frontend/src/pages/IntegrationsModulePage/IntegrationsModulePage.styles.scss create mode 100644 frontend/src/pages/IntegrationsModulePage/IntegrationsModulePage.tsx create mode 100644 frontend/src/pages/IntegrationsModulePage/constants.tsx create mode 100644 frontend/src/pages/IntegrationsModulePage/index.ts create mode 100644 frontend/src/pages/License/index.tsx create mode 100644 frontend/src/pages/LiveLogs/index.tsx create mode 100644 frontend/src/pages/Login/index.tsx create mode 100644 frontend/src/pages/Logs/PopoverContent.tsx create mode 100644 frontend/src/pages/Logs/config.ts create mode 100644 frontend/src/pages/Logs/hooks.ts create mode 100644 frontend/src/pages/Logs/index.tsx create mode 100644 frontend/src/pages/Logs/logs.styles.scss create mode 100644 frontend/src/pages/Logs/styles.ts create mode 100644 frontend/src/pages/Logs/types.ts create mode 100644 frontend/src/pages/Logs/utils.ts create mode 100644 frontend/src/pages/LogsExplorer/LogsExplorer.styles.scss create mode 100644 frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx create mode 100644 frontend/src/pages/LogsExplorer/index.tsx create mode 100644 frontend/src/pages/LogsExplorer/styles.ts create mode 100644 frontend/src/pages/LogsExplorer/utils.ts create mode 100644 frontend/src/pages/LogsModulePage/LogsModulePage.styles.scss create mode 100644 frontend/src/pages/LogsModulePage/LogsModulePage.tsx create mode 100644 frontend/src/pages/LogsModulePage/constants.tsx create mode 100644 frontend/src/pages/LogsModulePage/index.tsx create mode 100644 frontend/src/pages/LogsModulePage/utils.ts create mode 100644 frontend/src/pages/LogsSettings/components/TabLabel.tsx create mode 100644 frontend/src/pages/LogsSettings/config.tsx create mode 100644 frontend/src/pages/LogsSettings/constant.ts create mode 100644 frontend/src/pages/LogsSettings/index.tsx create mode 100644 frontend/src/pages/LogsSettings/types.ts create mode 100644 frontend/src/pages/MetricsApplication/ApDex/ApDexApplication.test.tsx create mode 100644 frontend/src/pages/MetricsApplication/ApDex/ApDexApplication.tsx create mode 100644 frontend/src/pages/MetricsApplication/ApDex/ApDexSettings.test.tsx create mode 100644 frontend/src/pages/MetricsApplication/ApDex/ApDexSettings.tsx create mode 100644 frontend/src/pages/MetricsApplication/ApDex/__mock__/axiosResponseMockThresholdData.ts create mode 100644 frontend/src/pages/MetricsApplication/ApDex/__mock__/thresholdMockData.ts create mode 100644 frontend/src/pages/MetricsApplication/ApDex/types.ts create mode 100644 frontend/src/pages/MetricsApplication/config.ts create mode 100644 frontend/src/pages/MetricsApplication/constants.ts create mode 100644 frontend/src/pages/MetricsApplication/index.tsx create mode 100644 frontend/src/pages/MetricsApplication/styles.ts create mode 100644 frontend/src/pages/MetricsApplication/types.ts create mode 100644 frontend/src/pages/MetricsApplication/useMetricsApplicationTabKey.tsx create mode 100644 frontend/src/pages/MetricsApplication/utils.ts create mode 100644 frontend/src/pages/MySettings/index.tsx create mode 100644 frontend/src/pages/NewDashboard/DashboardPage.tsx create mode 100644 frontend/src/pages/NewDashboard/index.tsx create mode 100644 frontend/src/pages/OnboardingPage/OnboardingPage.styles.scss create mode 100644 frontend/src/pages/OnboardingPage/OnboardingPage.tsx create mode 100644 frontend/src/pages/OnboardingPage/index.tsx create mode 100644 frontend/src/pages/Pipelines/Pipelines.styles.scss create mode 100644 frontend/src/pages/Pipelines/index.tsx create mode 100644 frontend/src/pages/ResetPassword/index.tsx create mode 100644 frontend/src/pages/SaveView/SaveView.styles.scss create mode 100644 frontend/src/pages/SaveView/constants.ts create mode 100644 frontend/src/pages/SaveView/index.tsx create mode 100644 frontend/src/pages/SaveView/utils.ts create mode 100644 frontend/src/pages/Services/Metrics.test.tsx create mode 100644 frontend/src/pages/Services/index.tsx create mode 100644 frontend/src/pages/Settings/config.tsx create mode 100644 frontend/src/pages/Settings/index.tsx create mode 100644 frontend/src/pages/Settings/utils.ts create mode 100644 frontend/src/pages/Shortcuts/Shortcuts.styles.scss create mode 100644 frontend/src/pages/Shortcuts/Shortcuts.tsx create mode 100644 frontend/src/pages/Shortcuts/index.ts create mode 100644 frontend/src/pages/Shortcuts/utils.ts create mode 100644 frontend/src/pages/SignUp/SignUp.tsx create mode 100644 frontend/src/pages/SignUp/index.tsx create mode 100644 frontend/src/pages/SignUp/styles.ts create mode 100644 frontend/src/pages/SignUp/utils.ts create mode 100644 frontend/src/pages/SomethingWentWrong/index.tsx create mode 100644 frontend/src/pages/Status/index.tsx create mode 100644 frontend/src/pages/Support/Support.styles.scss create mode 100644 frontend/src/pages/Support/Support.tsx create mode 100644 frontend/src/pages/Support/index.tsx create mode 100644 frontend/src/pages/Trace/index.tsx create mode 100644 frontend/src/pages/Trace/styles.ts create mode 100644 frontend/src/pages/TraceDetail/constants.ts create mode 100644 frontend/src/pages/TraceDetail/index.tsx create mode 100644 frontend/src/pages/TracesExplorer/TracesExplorer.styles.scss create mode 100644 frontend/src/pages/TracesExplorer/index.tsx create mode 100644 frontend/src/pages/TracesExplorer/styles.ts create mode 100644 frontend/src/pages/TracesExplorer/utils.tsx create mode 100644 frontend/src/pages/TracesModulePage/TracesModulePage.styles.scss create mode 100644 frontend/src/pages/TracesModulePage/TracesModulePage.tsx create mode 100644 frontend/src/pages/TracesModulePage/constants.tsx create mode 100644 frontend/src/pages/TracesModulePage/index.tsx create mode 100644 frontend/src/pages/UnAuthorized/index.tsx create mode 100644 frontend/src/pages/WorkspaceLocked/WorkspaceLocked.styles.scss create mode 100644 frontend/src/pages/WorkspaceLocked/WorkspaceLocked.test.tsx create mode 100644 frontend/src/pages/WorkspaceLocked/WorkspaceLocked.tsx create mode 100644 frontend/src/pages/WorkspaceLocked/index.tsx create mode 100644 frontend/src/periscope.scss create mode 100644 frontend/src/providers/Dashboard/Dashboard.tsx create mode 100644 frontend/src/providers/Dashboard/types.ts create mode 100644 frontend/src/providers/Dashboard/util.ts create mode 100644 frontend/src/providers/EventSource.tsx create mode 100644 frontend/src/providers/QueryBuilder.tsx create mode 100644 frontend/src/providers/test/MockQueryClientProvider.tsx create mode 100644 frontend/src/reportWebVitals.ts create mode 100644 frontend/src/setupProxy.js create mode 100644 frontend/src/store/actions/app/index.ts create mode 100644 frontend/src/store/actions/app/sideBarCollapse.ts create mode 100644 frontend/src/store/actions/global.ts create mode 100644 frontend/src/store/actions/index.ts create mode 100644 frontend/src/store/actions/logs/addToSelectedField.ts create mode 100644 frontend/src/store/actions/logs/getFields.ts create mode 100644 frontend/src/store/actions/logs/getLogs.ts create mode 100644 frontend/src/store/actions/logs/getLogsAggregate.ts create mode 100644 frontend/src/store/actions/logs/setLInesPerRow.ts create mode 100644 frontend/src/store/actions/logs/setViewMode.ts create mode 100644 frontend/src/store/actions/metrics/getService.ts create mode 100644 frontend/src/store/actions/metrics/index.ts create mode 100644 frontend/src/store/actions/metrics/resetInitialData.ts create mode 100644 frontend/src/store/actions/serviceMap.ts create mode 100644 frontend/src/store/actions/trace/getInitialFilter.ts create mode 100644 frontend/src/store/actions/trace/getInitialSpansAggregate.ts create mode 100644 frontend/src/store/actions/trace/getSpans.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/filter.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/filterToFetchData.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/index.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/isFilterExclude.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/minMaxTime.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/parseSpanKind.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/selectedFilter.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/selectedTags.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/skippedSelected.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/spanAggregateCurrentPage.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/spanAggregateCurrentPageSize.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/spanAggregateOrder.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/spanAggregateOrderParam.ts create mode 100644 frontend/src/store/actions/trace/selectTraceFilter.ts create mode 100644 frontend/src/store/actions/trace/updateIsTagsError.ts create mode 100644 frontend/src/store/actions/trace/updateTagPanelVisiblity.ts create mode 100644 frontend/src/store/actions/trace/updateTagsSelected.ts create mode 100644 frontend/src/store/actions/trace/util.ts create mode 100644 frontend/src/store/actions/types.ts create mode 100644 frontend/src/store/actions/usage.ts create mode 100644 frontend/src/store/index.ts create mode 100644 frontend/src/store/reducers/app.ts create mode 100644 frontend/src/store/reducers/global.ts create mode 100644 frontend/src/store/reducers/index.ts create mode 100644 frontend/src/store/reducers/logs.ts create mode 100644 frontend/src/store/reducers/metric.ts create mode 100644 frontend/src/store/reducers/serviceMap.ts create mode 100644 frontend/src/store/reducers/trace.ts create mode 100644 frontend/src/store/reducers/usage.ts create mode 100644 frontend/src/store/utils.ts create mode 100644 frontend/src/styles.scss create mode 100644 frontend/src/tests/test-utils.tsx create mode 100644 frontend/src/types/actions/app.ts create mode 100644 frontend/src/types/actions/globalTime.ts create mode 100644 frontend/src/types/actions/index.ts create mode 100644 frontend/src/types/actions/logs.ts create mode 100644 frontend/src/types/actions/metrics.ts create mode 100644 frontend/src/types/actions/trace.ts create mode 100644 frontend/src/types/api/SAML/deleteDomain.ts create mode 100644 frontend/src/types/api/SAML/listDomain.ts create mode 100644 frontend/src/types/api/SAML/postDomain.ts create mode 100644 frontend/src/types/api/SAML/updateDomain.ts create mode 100644 frontend/src/types/api/alerts/alertTypes.ts create mode 100644 frontend/src/types/api/alerts/compositeQuery.ts create mode 100644 frontend/src/types/api/alerts/create.ts create mode 100644 frontend/src/types/api/alerts/def.ts create mode 100644 frontend/src/types/api/alerts/delete.ts create mode 100644 frontend/src/types/api/alerts/get.ts create mode 100644 frontend/src/types/api/alerts/getAll.ts create mode 100644 frontend/src/types/api/alerts/getGroups.ts create mode 100644 frontend/src/types/api/alerts/getTriggered.ts create mode 100644 frontend/src/types/api/alerts/patch.ts create mode 100644 frontend/src/types/api/alerts/save.ts create mode 100644 frontend/src/types/api/alerts/testAlert.ts create mode 100644 frontend/src/types/api/billing/checkout.ts create mode 100644 frontend/src/types/api/channels/createEmail.ts create mode 100644 frontend/src/types/api/channels/createMsTeams.ts create mode 100644 frontend/src/types/api/channels/createOpsgenie.ts create mode 100644 frontend/src/types/api/channels/createPager.ts create mode 100644 frontend/src/types/api/channels/createSlack.ts create mode 100644 frontend/src/types/api/channels/createWebhook.ts create mode 100644 frontend/src/types/api/channels/delete.ts create mode 100644 frontend/src/types/api/channels/editEmail.ts create mode 100644 frontend/src/types/api/channels/editMsTeams.ts create mode 100644 frontend/src/types/api/channels/editOpsgenie.ts create mode 100644 frontend/src/types/api/channels/editPager.ts create mode 100644 frontend/src/types/api/channels/editSlack.ts create mode 100644 frontend/src/types/api/channels/editWebhook.ts create mode 100644 frontend/src/types/api/channels/get.ts create mode 100644 frontend/src/types/api/channels/getAll.ts create mode 100644 frontend/src/types/api/dashboard/create.ts create mode 100644 frontend/src/types/api/dashboard/delete.ts create mode 100644 frontend/src/types/api/dashboard/get.ts create mode 100644 frontend/src/types/api/dashboard/getAll.ts create mode 100644 frontend/src/types/api/dashboard/update.ts create mode 100644 frontend/src/types/api/dashboard/variables/query.ts create mode 100644 frontend/src/types/api/disks/getDisks.ts create mode 100644 frontend/src/types/api/dynamicConfigs/getDynamicConfigs.ts create mode 100644 frontend/src/types/api/errors/getAll.ts create mode 100644 frontend/src/types/api/errors/getByErrorId.ts create mode 100644 frontend/src/types/api/errors/getByErrorTypeAndService.ts create mode 100644 frontend/src/types/api/errors/getErrorCounts.ts create mode 100644 frontend/src/types/api/errors/getNextPrevId.ts create mode 100644 frontend/src/types/api/features/getFeaturesFlags.ts create mode 100644 frontend/src/types/api/index.ts create mode 100644 frontend/src/types/api/integrations/types.ts create mode 100644 frontend/src/types/api/licenses/apply.ts create mode 100644 frontend/src/types/api/licenses/def.ts create mode 100644 frontend/src/types/api/licenses/getAll.ts create mode 100644 frontend/src/types/api/logs/addToSelectedFields.ts create mode 100644 frontend/src/types/api/logs/fields.ts create mode 100644 frontend/src/types/api/logs/getLogs.ts create mode 100644 frontend/src/types/api/logs/getLogsAggregate.ts create mode 100644 frontend/src/types/api/logs/getSearchFields.ts create mode 100644 frontend/src/types/api/logs/liveTail.ts create mode 100644 frontend/src/types/api/logs/log.ts create mode 100644 frontend/src/types/api/logs/logAggregate.ts create mode 100644 frontend/src/types/api/logs/operator.ts create mode 100644 frontend/src/types/api/logs/removeSelectedField.ts create mode 100644 frontend/src/types/api/metrics/getApDex.ts create mode 100644 frontend/src/types/api/metrics/getDBOverview.ts create mode 100644 frontend/src/types/api/metrics/getExternalAverageDuration.ts create mode 100644 frontend/src/types/api/metrics/getExternalError.ts create mode 100644 frontend/src/types/api/metrics/getExternalService.ts create mode 100644 frontend/src/types/api/metrics/getMetricName.ts create mode 100644 frontend/src/types/api/metrics/getQueryRange.ts create mode 100644 frontend/src/types/api/metrics/getResourceAttributes.ts create mode 100644 frontend/src/types/api/metrics/getService.ts create mode 100644 frontend/src/types/api/metrics/getServiceOverview.ts create mode 100644 frontend/src/types/api/metrics/getTopLevelOperations.ts create mode 100644 frontend/src/types/api/metrics/getTopOperations.ts create mode 100644 frontend/src/types/api/pat/types.ts create mode 100644 frontend/src/types/api/pipeline/def.ts create mode 100644 frontend/src/types/api/pipeline/get.ts create mode 100644 frontend/src/types/api/pipeline/post.ts create mode 100644 frontend/src/types/api/queryBuilder/getAggregatorAttribute.ts create mode 100644 frontend/src/types/api/queryBuilder/getAttributeKeys.ts create mode 100644 frontend/src/types/api/queryBuilder/getAttributesValues.ts create mode 100644 frontend/src/types/api/queryBuilder/queryAutocompleteResponse.ts create mode 100644 frontend/src/types/api/queryBuilder/queryBuilderData.ts create mode 100644 frontend/src/types/api/saveViews/types.ts create mode 100644 frontend/src/types/api/settings/common.ts create mode 100644 frontend/src/types/api/settings/getRetention.ts create mode 100644 frontend/src/types/api/settings/ingestion.ts create mode 100644 frontend/src/types/api/settings/setRetention.ts create mode 100644 frontend/src/types/api/trace/getFilters.ts create mode 100644 frontend/src/types/api/trace/getSpanAggregate.ts create mode 100644 frontend/src/types/api/trace/getSpans.ts create mode 100644 frontend/src/types/api/trace/getTagFilters.ts create mode 100644 frontend/src/types/api/trace/getTagValue.ts create mode 100644 frontend/src/types/api/trace/getTraceItem.ts create mode 100644 frontend/src/types/api/user/changeMyPassword.ts create mode 100644 frontend/src/types/api/user/deleteInvite.ts create mode 100644 frontend/src/types/api/user/deleteUser.ts create mode 100644 frontend/src/types/api/user/editOrg.ts create mode 100644 frontend/src/types/api/user/editUser.ts create mode 100644 frontend/src/types/api/user/getInviteDetails.ts create mode 100644 frontend/src/types/api/user/getLatestVersion.ts create mode 100644 frontend/src/types/api/user/getOrgMembers.ts create mode 100644 frontend/src/types/api/user/getOrganization.ts create mode 100644 frontend/src/types/api/user/getPendingInvites.ts create mode 100644 frontend/src/types/api/user/getResetPasswordToken.ts create mode 100644 frontend/src/types/api/user/getUser.ts create mode 100644 frontend/src/types/api/user/getUserPreference.ts create mode 100644 frontend/src/types/api/user/getUserRole.ts create mode 100644 frontend/src/types/api/user/getVersion.ts create mode 100644 frontend/src/types/api/user/login.ts create mode 100644 frontend/src/types/api/user/loginPrecheck.ts create mode 100644 frontend/src/types/api/user/resetPassword.ts create mode 100644 frontend/src/types/api/user/setFlags.ts create mode 100644 frontend/src/types/api/user/setInvite.ts create mode 100644 frontend/src/types/api/user/setUserPreference.ts create mode 100644 frontend/src/types/api/user/signup.ts create mode 100644 frontend/src/types/api/user/updateRole.ts create mode 100644 frontend/src/types/api/userFeedback/sendResponse.ts create mode 100644 frontend/src/types/api/widgets/getQuery.ts create mode 100644 frontend/src/types/common/dashboard.ts create mode 100644 frontend/src/types/common/index.ts create mode 100644 frontend/src/types/common/operations.types.ts create mode 100644 frontend/src/types/common/queryBuilder.ts create mode 100644 frontend/src/types/common/select.ts create mode 100644 frontend/src/types/global.d.ts create mode 100644 frontend/src/types/reducer/app.ts create mode 100644 frontend/src/types/reducer/globalTime.ts create mode 100644 frontend/src/types/reducer/logs.ts create mode 100644 frontend/src/types/reducer/metrics.ts create mode 100644 frontend/src/types/reducer/trace.ts create mode 100644 frontend/src/types/roles.ts create mode 100644 frontend/src/typings/chartjs-adapter-date-fns.d.ts create mode 100644 frontend/src/typings/environment.ts create mode 100644 frontend/src/typings/window.ts create mode 100644 frontend/src/utils/__tests__/__snapshots__/spanToTree.test.ts.snap create mode 100644 frontend/src/utils/__tests__/spanToTree.test.ts create mode 100644 frontend/src/utils/app.ts create mode 100644 frontend/src/utils/dashboard/generateExportToDashboardLink.ts create mode 100644 frontend/src/utils/fixtures/TraceData.ts create mode 100644 frontend/src/utils/form/requireErrorMessage.ts create mode 100644 frontend/src/utils/getAlphaColor.ts create mode 100644 frontend/src/utils/getEventEmitter.ts create mode 100644 frontend/src/utils/getFormatedLegend.ts create mode 100644 frontend/src/utils/getGraphType.ts create mode 100644 frontend/src/utils/getSortedSeriesData.ts create mode 100644 frontend/src/utils/getSpanTreeMetadata.ts create mode 100644 frontend/src/utils/getTimeRange.ts create mode 100644 frontend/src/utils/getUserOS.ts create mode 100644 frontend/src/utils/logs.ts create mode 100644 frontend/src/utils/permission/index.ts create mode 100644 frontend/src/utils/selectPopupContainer.ts create mode 100644 frontend/src/utils/services.ts create mode 100644 frontend/src/utils/spanToTree.ts create mode 100644 frontend/src/utils/timeUtils.ts create mode 100644 frontend/src/utils/toFixed.ts create mode 100644 frontend/src/utils/token.ts create mode 100644 frontend/src/utils/transformToUpperCase.ts create mode 100644 frontend/tests/auth.json create mode 100644 frontend/tests/dashboards/index.spec.ts create mode 100644 frontend/tests/dashboards/utils.ts create mode 100644 frontend/tests/expectionDetails/index.spec.ts create mode 100644 frontend/tests/expectionDetails/index.spec.ts-snapshots/Expections-Details-Render-Success-Data-when-200-from-details-page-1-Signoz-darwin.png create mode 100644 frontend/tests/expectionDetails/index.spec.ts-snapshots/Expections-Details-Should-have-not-found-when-api-return-404-1-Signoz-darwin.png create mode 100644 frontend/tests/expections/index.spec.ts create mode 100644 frontend/tests/expections/index.spec.ts-snapshots/Expections-page-Should-have-a-valid-Breadcrumbs-1-Signoz-darwin.png create mode 100644 frontend/tests/expections/index.spec.ts-snapshots/Expections-page-Should-have-a-valid-route-1-Signoz-darwin.png create mode 100644 frontend/tests/expections/index.spec.ts-snapshots/Expections-page-Should-render-data-in-antd-table-1-Signoz-darwin.png create mode 100644 frontend/tests/expections/index.spec.ts-snapshots/Expections-page-Should-render-the-page-with-404-status-1-Signoz-darwin.png create mode 100644 frontend/tests/expections/index.spec.ts-snapshots/Expections-page-Should-render-the-page-with-50-26a88--in-antd-notification-with-no-data-antd-table-1-Signoz-darwin.png create mode 100644 frontend/tests/fixtures/api/allErrors/200.json create mode 100644 frontend/tests/fixtures/api/dashboard/createNewDashboardPost200.json create mode 100644 frontend/tests/fixtures/api/dashboard/dashboardGetCallWithTimeSeriesWidget200.json create mode 100644 frontend/tests/fixtures/api/dashboard/getDashboardListEmpty200.json create mode 100644 frontend/tests/fixtures/api/dashboard/getIndividualDashboard200.json create mode 100644 frontend/tests/fixtures/api/dashboard/putDashboardWithTimeSeries200.json create mode 100644 frontend/tests/fixtures/api/dashboard/putNewDashboardUpdate200.json create mode 100644 frontend/tests/fixtures/api/errorDetails/200.json create mode 100644 frontend/tests/fixtures/api/errorDetails/404.json create mode 100644 frontend/tests/fixtures/api/getNextPrev/200.json create mode 100644 frontend/tests/fixtures/api/login/200.json create mode 100644 frontend/tests/fixtures/api/organisation/201.json create mode 100644 frontend/tests/fixtures/api/register/200.json create mode 100644 frontend/tests/fixtures/api/register/401.json create mode 100644 frontend/tests/fixtures/api/services/200.json create mode 100644 frontend/tests/fixtures/api/traces/attributeKeys200.json create mode 100644 frontend/tests/fixtures/api/traces/attributeKeysDurationNano200.json create mode 100644 frontend/tests/fixtures/api/traces/attributeKeysHttpMethod200.json create mode 100644 frontend/tests/fixtures/api/traces/attributeKeysName200.json create mode 100644 frontend/tests/fixtures/api/traces/attributeKeysResponseStatusCode200.json create mode 100644 frontend/tests/fixtures/api/traces/attributeKeysServiceName200.json create mode 100644 frontend/tests/fixtures/api/traces/queryRange200.json create mode 100644 frontend/tests/fixtures/api/traces/traceExplorerViewPost200.json create mode 100644 frontend/tests/fixtures/api/traces/traceExplorerViews200.json create mode 100644 frontend/tests/fixtures/api/traces/tracesRange200.json create mode 100644 frontend/tests/fixtures/api/traces/tracesTableView200.json create mode 100644 frontend/tests/fixtures/api/userId/200.json create mode 100644 frontend/tests/fixtures/common.ts create mode 100644 frontend/tests/fixtures/constant.ts create mode 100644 frontend/tests/login/fail.spec.ts create mode 100644 frontend/tests/login/fail.spec.ts-snapshots/Version-API-fail-while-loading-login-page-Something-went-wrong-1-Signoz-darwin.png create mode 100644 frontend/tests/login/index.spec.ts create mode 100644 frontend/tests/login/index.spec.ts-snapshots/Login-Page-Version-of-the-application-when-api-returns-200-1-Signoz-darwin.png create mode 100644 frontend/tests/service/index.spec.ts create mode 100644 frontend/tests/service/index.spec.ts-snapshots/Service-Page-Serice-Page-is-rendered-1-Signoz-darwin.png create mode 100644 frontend/tests/service/servicesLanding.spec.ts create mode 100644 frontend/tests/service/utils.ts create mode 100644 frontend/tests/signup/index.spec.ts create mode 100644 frontend/tests/signup/index.spec.ts-snapshots/Sign-Up-Page-Empty-Company-name-with-valid-details-1-Signoz-darwin.png create mode 100644 frontend/tests/signup/index.spec.ts-snapshots/Sign-Up-Page-Empty-Email-with-valid-details-1-Signoz-darwin.png create mode 100644 frontend/tests/signup/index.spec.ts-snapshots/Sign-Up-Page-Empty-Password-and-confirm-password-with-valid-details-1-Signoz-darwin.png create mode 100644 frontend/tests/signup/index.spec.ts-snapshots/Sign-Up-Page-Empty-name-with-valid-details-1-Signoz-darwin.png create mode 100644 frontend/tests/signup/index.spec.ts-snapshots/Sign-Up-Page-Invite-link-validation-1-Signoz-darwin.png create mode 100644 frontend/tests/signup/index.spec.ts-snapshots/Sign-Up-Page-Miss-Match-Password-and-confirm-password-with-valid-details-1-Signoz-darwin.png create mode 100644 frontend/tests/signup/index.spec.ts-snapshots/Sign-Up-Page-User-Sign-up-with-valid-details-1-Signoz-darwin.png create mode 100644 frontend/tests/signup/index.spec.ts-snapshots/Sign-Up-Page-When-User-successfull-signup-and-logged-in-he-should-be-redirected-to-dashboard-1-Signoz-darwin.png create mode 100644 frontend/tests/traces/newTracesExplorer.spec.ts create mode 100644 frontend/tests/traces/utils.ts create mode 100644 frontend/tsconfig.json create mode 100644 frontend/webpack.config.js create mode 100644 frontend/webpack.config.prod.js create mode 100644 frontend/yarn.lock create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/query-service/.dockerignore create mode 100644 pkg/query-service/Dockerfile create mode 100644 pkg/query-service/README.md create mode 100644 pkg/query-service/agentConf/Readme.md create mode 100644 pkg/query-service/agentConf/agent_features.go create mode 100644 pkg/query-service/agentConf/db.go create mode 100644 pkg/query-service/agentConf/manager.go create mode 100644 pkg/query-service/agentConf/sqlite/init.go create mode 100644 pkg/query-service/agentConf/version.go create mode 100644 pkg/query-service/app/apdex.go create mode 100644 pkg/query-service/app/auth.go create mode 100644 pkg/query-service/app/clickhouseReader/options.go create mode 100644 pkg/query-service/app/clickhouseReader/reader.go create mode 100644 pkg/query-service/app/clickhouseReader/reader_test.go create mode 100644 pkg/query-service/app/clickhouseReader/wrapper.go create mode 100644 pkg/query-service/app/dashboards/model.go create mode 100644 pkg/query-service/app/dashboards/provision.go create mode 100644 pkg/query-service/app/explorer/db.go create mode 100644 pkg/query-service/app/formula.go create mode 100644 pkg/query-service/app/formula_test.go create mode 100644 pkg/query-service/app/having.go create mode 100644 pkg/query-service/app/having_test.go create mode 100644 pkg/query-service/app/http_handler.go create mode 100644 pkg/query-service/app/http_handler_test.go create mode 100644 pkg/query-service/app/http_utils.go create mode 100644 pkg/query-service/app/ingestion_key.go create mode 100644 pkg/query-service/app/integrations/Readme.md create mode 100644 pkg/query-service/app/integrations/builtin.go create mode 100644 pkg/query-service/app/integrations/builtin_integrations/mongo/assets/dashboards/overview.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/mongo/assets/pipelines/log-parser.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/mongo/config/configure-otel-collector.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/mongo/config/prerequisites.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/mongo/icon.svg create mode 100644 pkg/query-service/app/integrations/builtin_integrations/mongo/integration.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/mongo/overview.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/nginx/assets/pipelines/log-parser.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/nginx/config/configure-otel-collector.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/nginx/config/prepare-nginx.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/nginx/icon.svg create mode 100644 pkg/query-service/app/integrations/builtin_integrations/nginx/integration.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/nginx/overview.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/postgres/assets/dashboards/overview.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/postgres/assets/pipelines/log-parser.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/postgres/config/configure-otel-collector.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/postgres/config/prerequisites.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/postgres/icon.svg create mode 100644 pkg/query-service/app/integrations/builtin_integrations/postgres/integration.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/postgres/overview.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/redis/assets/dashboards/overview.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/redis/assets/pipelines/log-parser.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/redis/config/configure-otel-collector.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/redis/config/prerequisites.md create mode 100644 pkg/query-service/app/integrations/builtin_integrations/redis/icon.svg create mode 100644 pkg/query-service/app/integrations/builtin_integrations/redis/integration.json create mode 100644 pkg/query-service/app/integrations/builtin_integrations/redis/overview.md create mode 100644 pkg/query-service/app/integrations/builtin_test.go create mode 100644 pkg/query-service/app/integrations/controller.go create mode 100644 pkg/query-service/app/integrations/manager.go create mode 100644 pkg/query-service/app/integrations/manager_test.go create mode 100644 pkg/query-service/app/integrations/pipeline_utils.go create mode 100644 pkg/query-service/app/integrations/repo.go create mode 100644 pkg/query-service/app/integrations/sqlite_repo.go create mode 100644 pkg/query-service/app/integrations/test_utils.go create mode 100644 pkg/query-service/app/limit.go create mode 100644 pkg/query-service/app/limit_test.go create mode 100644 pkg/query-service/app/logparsingpipeline/agent_feature.go create mode 100644 pkg/query-service/app/logparsingpipeline/collector_config.go create mode 100644 pkg/query-service/app/logparsingpipeline/collector_config_test.go create mode 100644 pkg/query-service/app/logparsingpipeline/controller.go create mode 100644 pkg/query-service/app/logparsingpipeline/db.go create mode 100644 pkg/query-service/app/logparsingpipeline/model.go create mode 100644 pkg/query-service/app/logparsingpipeline/pipelineBuilder.go create mode 100644 pkg/query-service/app/logparsingpipeline/pipelineBuilder_test.go create mode 100644 pkg/query-service/app/logparsingpipeline/postablePipeline.go create mode 100644 pkg/query-service/app/logparsingpipeline/postablePipeline_test.go create mode 100644 pkg/query-service/app/logparsingpipeline/preview.go create mode 100644 pkg/query-service/app/logparsingpipeline/preview_test.go create mode 100644 pkg/query-service/app/logparsingpipeline/severity_parser_test.go create mode 100644 pkg/query-service/app/logparsingpipeline/sqlite/init.go create mode 100644 pkg/query-service/app/logparsingpipeline/time_parser.go create mode 100644 pkg/query-service/app/logparsingpipeline/time_parser_test.go create mode 100644 pkg/query-service/app/logs/parser.go create mode 100644 pkg/query-service/app/logs/parser_test.go create mode 100644 pkg/query-service/app/logs/v3/enrich_query.go create mode 100644 pkg/query-service/app/logs/v3/enrich_query_test.go create mode 100644 pkg/query-service/app/logs/v3/json_filter.go create mode 100644 pkg/query-service/app/logs/v3/json_filter_test.go create mode 100644 pkg/query-service/app/logs/v3/query_builder.go create mode 100644 pkg/query-service/app/logs/v3/query_builder_test.go create mode 100644 pkg/query-service/app/logs/validator.go create mode 100644 pkg/query-service/app/metrics/query_builder.go create mode 100644 pkg/query-service/app/metrics/query_builder_test.go create mode 100644 pkg/query-service/app/metrics/v3/cumulative_table.go create mode 100644 pkg/query-service/app/metrics/v3/cumulative_table_test.go create mode 100644 pkg/query-service/app/metrics/v3/delta.go create mode 100644 pkg/query-service/app/metrics/v3/delta_table.go create mode 100644 pkg/query-service/app/metrics/v3/delta_table_test.go create mode 100644 pkg/query-service/app/metrics/v3/query_builder.go create mode 100644 pkg/query-service/app/metrics/v3/query_builder_test.go create mode 100644 pkg/query-service/app/metrics/v4/cumulative/table.go create mode 100644 pkg/query-service/app/metrics/v4/cumulative/table_test.go create mode 100644 pkg/query-service/app/metrics/v4/cumulative/timeseries.go create mode 100644 pkg/query-service/app/metrics/v4/cumulative/timeseries_test.go create mode 100644 pkg/query-service/app/metrics/v4/delta/table.go create mode 100644 pkg/query-service/app/metrics/v4/delta/table_test.go create mode 100644 pkg/query-service/app/metrics/v4/delta/time_series_test.go create mode 100644 pkg/query-service/app/metrics/v4/delta/timeseries.go create mode 100644 pkg/query-service/app/metrics/v4/helpers/clauses.go create mode 100644 pkg/query-service/app/metrics/v4/helpers/sub_query.go create mode 100644 pkg/query-service/app/metrics/v4/query_builder.go create mode 100644 pkg/query-service/app/metrics/v4/query_builder_test.go create mode 100644 pkg/query-service/app/opamp/config_provider.go create mode 100644 pkg/query-service/app/opamp/config_provider_test.go create mode 100644 pkg/query-service/app/opamp/configure_ingestionRules.go create mode 100644 pkg/query-service/app/opamp/logger.go create mode 100644 pkg/query-service/app/opamp/mocks.go create mode 100644 pkg/query-service/app/opamp/model/agent.go create mode 100644 pkg/query-service/app/opamp/model/agents.go create mode 100644 pkg/query-service/app/opamp/model/config.go create mode 100644 pkg/query-service/app/opamp/model/constants.go create mode 100644 pkg/query-service/app/opamp/model/coordinator.go create mode 100644 pkg/query-service/app/opamp/opamp_server.go create mode 100644 pkg/query-service/app/opamp/otelconfig/config_parser.go create mode 100644 pkg/query-service/app/opamp/otelconfig/config_parser_test.go create mode 100644 pkg/query-service/app/opamp/otelconfig/filterprocessor/config.go create mode 100644 pkg/query-service/app/opamp/otelconfig/otlpreceiver/config.go create mode 100644 pkg/query-service/app/opamp/otelconfig/otlpreceiver/grpcSettings.go create mode 100644 pkg/query-service/app/opamp/otelconfig/otlpreceiver/httpSettings.go create mode 100644 pkg/query-service/app/opamp/otelconfig/otlpreceiver/tls.go create mode 100644 pkg/query-service/app/opamp/otelconfig/tailsampler/config.go create mode 100644 pkg/query-service/app/opamp/otelconfig/testdata/basic.yaml create mode 100644 pkg/query-service/app/opamp/otelconfig/testdata/service.yaml create mode 100644 pkg/query-service/app/opamp/pipeline_builder.go create mode 100644 pkg/query-service/app/opamp/signal.go create mode 100644 pkg/query-service/app/parser.go create mode 100644 pkg/query-service/app/parser/metrics.go create mode 100644 pkg/query-service/app/parser_test.go create mode 100644 pkg/query-service/app/querier/helper.go create mode 100644 pkg/query-service/app/querier/querier.go create mode 100644 pkg/query-service/app/querier/querier_test.go create mode 100644 pkg/query-service/app/querier/v2/helper.go create mode 100644 pkg/query-service/app/querier/v2/querier.go create mode 100644 pkg/query-service/app/queryBuilder/functions.go create mode 100644 pkg/query-service/app/queryBuilder/functions_test.go create mode 100644 pkg/query-service/app/queryBuilder/query_builder.go create mode 100644 pkg/query-service/app/queryBuilder/query_builder_test.go create mode 100644 pkg/query-service/app/reduce_to.go create mode 100644 pkg/query-service/app/reduce_to_test.go create mode 100644 pkg/query-service/app/server.go create mode 100644 pkg/query-service/app/server_test.go create mode 100644 pkg/query-service/app/services/map.go create mode 100644 pkg/query-service/app/traces/v3/query_builder.go create mode 100644 pkg/query-service/app/traces/v3/query_builder_test.go create mode 100644 pkg/query-service/auth/auth.go create mode 100644 pkg/query-service/auth/jwt.go create mode 100644 pkg/query-service/auth/rbac.go create mode 100644 pkg/query-service/auth/utils.go create mode 100644 pkg/query-service/cache/cache.go create mode 100644 pkg/query-service/cache/cache_test.go create mode 100644 pkg/query-service/cache/inmemory/cache.go create mode 100644 pkg/query-service/cache/inmemory/cache_test.go create mode 100644 pkg/query-service/cache/inmemory/options.go create mode 100644 pkg/query-service/cache/redis/options.go create mode 100644 pkg/query-service/cache/redis/redis.go create mode 100644 pkg/query-service/cache/redis/redis_test.go create mode 100644 pkg/query-service/cache/status/status.go create mode 100644 pkg/query-service/cache/status/status_test.go create mode 100644 pkg/query-service/cache/testdata/cache.yaml create mode 100644 pkg/query-service/collectorsimulator/collectorsimulator.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/config.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/config_test.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/exporter.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/exporter_test.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/factory.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryexporter/factory_test.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/config.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/config_test.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/factory.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/factory_test.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/receiver.go create mode 100644 pkg/query-service/collectorsimulator/inmemoryreceiver/receiver_test.go create mode 100644 pkg/query-service/collectorsimulator/logs.go create mode 100644 pkg/query-service/collectorsimulator/logs_test.go create mode 100644 pkg/query-service/common/metrics.go create mode 100644 pkg/query-service/common/user.go create mode 100644 pkg/query-service/config/alerts.yml create mode 100644 pkg/query-service/config/cache-config.yml create mode 100644 pkg/query-service/config/prometheus.yml create mode 100644 pkg/query-service/constants/auth.go create mode 100644 pkg/query-service/constants/constants.go create mode 100644 pkg/query-service/constants/constants_test.go create mode 100644 pkg/query-service/converter/bool.go create mode 100644 pkg/query-service/converter/converter.go create mode 100644 pkg/query-service/converter/data.go create mode 100644 pkg/query-service/converter/data_rate.go create mode 100644 pkg/query-service/converter/data_rate_test.go create mode 100644 pkg/query-service/converter/data_test.go create mode 100644 pkg/query-service/converter/percent_converter.go create mode 100644 pkg/query-service/converter/percent_converter_test.go create mode 100644 pkg/query-service/converter/throughput.go create mode 100644 pkg/query-service/converter/time.go create mode 100644 pkg/query-service/converter/time_test.go create mode 100644 pkg/query-service/dao/factory.go create mode 100644 pkg/query-service/dao/interface.go create mode 100644 pkg/query-service/dao/sqlite/apdex.go create mode 100644 pkg/query-service/dao/sqlite/connection.go create mode 100644 pkg/query-service/dao/sqlite/ingestion.go create mode 100644 pkg/query-service/dao/sqlite/rbac.go create mode 100644 pkg/query-service/featureManager/manager.go create mode 100644 pkg/query-service/formatter/bool.go create mode 100644 pkg/query-service/formatter/data.go create mode 100644 pkg/query-service/formatter/data_rate.go create mode 100644 pkg/query-service/formatter/data_test.go create mode 100644 pkg/query-service/formatter/formatter.go create mode 100644 pkg/query-service/formatter/none.go create mode 100644 pkg/query-service/formatter/percent.go create mode 100644 pkg/query-service/formatter/scale.go create mode 100644 pkg/query-service/formatter/scale_test.go create mode 100644 pkg/query-service/formatter/throughput.go create mode 100644 pkg/query-service/formatter/throughput_test.go create mode 100644 pkg/query-service/formatter/time.go create mode 100644 pkg/query-service/formatter/time_test.go create mode 100644 pkg/query-service/healthcheck/handler.go create mode 100644 pkg/query-service/integrations/alertManager/manager.go create mode 100644 pkg/query-service/integrations/alertManager/model.go create mode 100644 pkg/query-service/integrations/alertManager/notifier.go create mode 100644 pkg/query-service/integrations/signozio/dynamic_config.go create mode 100644 pkg/query-service/integrations/signozio/response.go create mode 100644 pkg/query-service/interfaces/featureLookup.go create mode 100644 pkg/query-service/interfaces/interface.go create mode 100644 pkg/query-service/main.go create mode 100644 pkg/query-service/model/auth.go create mode 100644 pkg/query-service/model/config.go create mode 100644 pkg/query-service/model/dashboards.go create mode 100644 pkg/query-service/model/db.go create mode 100644 pkg/query-service/model/errors.go create mode 100644 pkg/query-service/model/featureSet.go create mode 100644 pkg/query-service/model/queryParams.go create mode 100644 pkg/query-service/model/response.go create mode 100644 pkg/query-service/model/response_easyjson.go create mode 100644 pkg/query-service/model/v3/v3.go create mode 100644 pkg/query-service/pqlEngine/engine.go create mode 100644 pkg/query-service/queryBuilderToExpr/queryBuilderToExpr.go create mode 100644 pkg/query-service/queryBuilderToExpr/queryBuilderToExpr_test.go create mode 100644 pkg/query-service/rules/alerting.go create mode 100644 pkg/query-service/rules/apiParams.go create mode 100644 pkg/query-service/rules/db.go create mode 100644 pkg/query-service/rules/manager.go create mode 100644 pkg/query-service/rules/promRule.go create mode 100644 pkg/query-service/rules/promRuleTask.go create mode 100644 pkg/query-service/rules/promrule_test.go create mode 100644 pkg/query-service/rules/queriers.go create mode 100644 pkg/query-service/rules/resultTypes.go create mode 100644 pkg/query-service/rules/rule.go create mode 100644 pkg/query-service/rules/ruleTask.go create mode 100644 pkg/query-service/rules/task.go create mode 100644 pkg/query-service/rules/templates.go create mode 100644 pkg/query-service/rules/thresholdRule.go create mode 100644 pkg/query-service/rules/thresholdRule_test.go create mode 100644 pkg/query-service/telemetry/ignored.go create mode 100644 pkg/query-service/telemetry/telemetry.go create mode 100644 pkg/query-service/templates/invitation_email_template.html create mode 100644 pkg/query-service/tests/auth_test.go create mode 100644 pkg/query-service/tests/cold_storage_test.go create mode 100644 pkg/query-service/tests/docker.go create mode 100644 pkg/query-service/tests/integration/logparsingpipeline_test.go create mode 100644 pkg/query-service/tests/integration/signoz_integrations_test.go create mode 100644 pkg/query-service/tests/integration/test_utils.go create mode 100644 pkg/query-service/tests/test-deploy/alertmanager.yml create mode 100644 pkg/query-service/tests/test-deploy/alerts.yml create mode 100644 pkg/query-service/tests/test-deploy/clickhouse-cluster.xml create mode 100644 pkg/query-service/tests/test-deploy/clickhouse-config.xml create mode 100644 pkg/query-service/tests/test-deploy/clickhouse-storage.xml create mode 100644 pkg/query-service/tests/test-deploy/clickhouse-users.xml create mode 100644 pkg/query-service/tests/test-deploy/docker-compose.yaml create mode 100644 pkg/query-service/tests/test-deploy/otel-collector-config.yaml create mode 100644 pkg/query-service/tests/test-deploy/otel-collector-opamp-config.yaml create mode 100644 pkg/query-service/tests/test-deploy/prometheus.yml create mode 100644 pkg/query-service/utils/encryption/encryption.go create mode 100644 pkg/query-service/utils/format.go create mode 100644 pkg/query-service/utils/format_test.go create mode 100644 pkg/query-service/utils/labels/interface.go create mode 100644 pkg/query-service/utils/labels/labels.go create mode 100644 pkg/query-service/utils/pass.go create mode 100644 pkg/query-service/utils/port.go create mode 100644 pkg/query-service/utils/queryTemplate/vars.go create mode 100644 pkg/query-service/utils/random.go create mode 100644 pkg/query-service/utils/slices.go create mode 100644 pkg/query-service/utils/smtpService/smtp.go create mode 100644 pkg/query-service/utils/testutils.go create mode 100644 pkg/query-service/utils/time.go create mode 100644 pkg/query-service/utils/times/time.go create mode 100644 pkg/query-service/utils/timestamp/timestamp.go create mode 100644 pkg/query-service/utils/value/value.go create mode 100644 pkg/query-service/version/version.go create mode 100644 sample-apps/hotrod/README.md create mode 100755 sample-apps/hotrod/hotrod-delete.sh create mode 100755 sample-apps/hotrod/hotrod-install.sh create mode 100644 sample-apps/hotrod/hotrod-template.yaml create mode 100644 sample-apps/hotrod/hotrod.yaml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..028b1e4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +.github +.vscode +README.md +deploy +sample-apps \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..81ed7f2 --- /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/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8c64395 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.css linguist-detectable=false \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..573be5f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,10 @@ +# CODEOWNERS info: https://help.github.com/en/articles/about-code-owners +# Owners are automatically requested for review for PRs that changes code +# that they own. + +/frontend/ @YounixM +/frontend/src/container/MetricsApplication @srikanthccv +/frontend/src/container/NewWidget/RightContainer/types.ts @srikanthccv +/deploy/ @prashant-shahi +/sample-apps/ @prashant-shahi +.github @prashant-shahi diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1c959c1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +## Bug description + +*Please describe.* +*If this affects the front-end, screenshots would be of great help.* + +## Expected behavior + + + +## How to reproduce + +1. +2. +3. + +## Version information +* **Signoz version**: +* **Browser version**: +* **Your OS and version**: +* **Your CPU Architecture**(ARM/Intel): + +## Additional context + + +#### *Thank you* for your bug report – we love squashing them! diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..f5c6e06 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,27 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +## Is your feature request related to a problem? + +*Please describe.* + +## Describe the solution you'd like + + + +## Describe alternatives you've considered + + + +## Additional context +Add any other context or screenshots about the feature request here. + + + +#### *Thank you* for your feature request – we love each and every one! \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/performance-issue-report.md b/.github/ISSUE_TEMPLATE/performance-issue-report.md new file mode 100644 index 0000000..c038877 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/performance-issue-report.md @@ -0,0 +1,33 @@ +--- +name: Performance issue report +about: Long response times, high resource usage? Ensuring that SigNoz is scalable + is our top priority +title: '' +labels: '' +assignees: '' + +--- + +## In what situation are you experiencing subpar performance? + +*Please describe.* + +## How to reproduce + +1. +2. +3. + +## Your Environment + +- [ ] Linux +- [ ] Mac +- [ ] Windows + +Please provide details of OS version etc. + +## Additional context + + + +#### *Thank you* for your performance issue report – we want SigNoz to be blazing fast! diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 0000000..ecbab95 --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,31 @@ +# Configuration for welcome - https://github.com/behaviorbot/welcome + +# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome +# Comment to be posted to on first time issues +newIssueWelcomeComment: > + Thanks for opening this issue. A team member should give feedback soon. + In the meantime, feel free to check out the [contributing guidelines](https://github.com/signoz/signoz/blob/main/CONTRIBUTING.md). + + +# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome +# Comment to be posted to on PRs from first time contributors in your repository +newPRWelcomeComment: > + Welcome to the SigNoz community! Thank you for your first pull request and making this project better. 🤗 + + +# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge +# Comment to be posted to on pull requests merged by a first time user +firstPRMergeComment: > + Congrats on merging your first pull request! + + ![minion-party](https://i.imgur.com/Xlg59lP.gif) + + We here at SigNoz are proud of you! 🥳 + + +# Configuration for request-info - https://github.com/behaviorbot/request-info +# Comment to be posted in issues or pull requests, when no description is provided. +requestInfoReplyComment: > + We would appreciate it if you could provide us with more info about this issue/pr! + +requestInfoLabelToAdd: request-more-info diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b5de546 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,17 @@ +### Summary + + + +#### Related Issues / PR's + + + +#### Screenshots + +NA + + + +#### Affected Areas and Manually Tested Areas + + diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..c3a8521 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,29 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +template: | + # What's Changed + $CHANGES +autolabeler: + - label: 'chore' + title: + - '/chore/i' + - label: 'bug' + title: + - '/fix/i' + - label: 'enhancement' + title: + - '/feat/i' + +categories: + - title: '🚀 Features' + label: 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' + - title: 'Breaking' + label: 'breaking' + +exclude-labels: + - 'skip-changelog' \ No newline at end of file diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..f1f24a1 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,42 @@ +# Github actions + +## Testing the UI manually on each PR + +First we need to make sure the UI is ready +* Check the `Start tunnel` step in `e2e-k8s/deploy-on-k3s-cluster` job and make sure you see `your url is: https://pull--signoz.loca.lt` +* This job will run until the PR is merged or closed to keep the local tunneling alive + - github will cancel this job if the PR wasn't merged after 6h + - if the job was cancel, go to the action and press `Re-run all jobs` + +Now you can open your browser at https://pull--signoz.loca.lt and check the UI. + +## Environment Variables + +To run GitHub workflow, a few environment variables needs to add in GitHub secrets + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..1d8d4e7 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,84 @@ +name: build-pipeline + +on: + pull_request: + branches: + - develop + - main + - release/v* + +jobs: + build-frontend: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install dependencies + run: cd frontend && yarn install + - name: Run ESLint + run: cd frontend && npm run lint + - name: Run Jest + run: cd frontend && npm run jest + - name: TSC + run: yarn tsc + working-directory: ./frontend + - name: Build frontend docker image + shell: bash + run: | + make build-frontend-amd64 + + build-frontend-ee: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create .env file + run: | + echo 'INTERCOM_APP_ID="${{ secrets.INTERCOM_APP_ID }}"' > frontend/.env + echo 'SEGMENT_ID="${{ secrets.SEGMENT_ID }}"' >> frontend/.env + echo 'CLARITY_PROJECT_ID="${{ secrets.CLARITY_PROJECT_ID }}"' >> frontend/.env + - name: Install dependencies + run: cd frontend && yarn install + - name: Run ESLint + run: cd frontend && npm run lint + - name: Run Jest + run: cd frontend && npm run jest + - name: TSC + run: yarn tsc + working-directory: ./frontend + - name: Build frontend docker image + shell: bash + run: | + make build-frontend-amd64 + + build-query-service: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup golang + uses: actions/setup-go@v4 + with: + go-version: "1.21" + - name: Run tests + shell: bash + run: | + make test + - name: Build query-service image + shell: bash + run: | + make build-query-service-amd64 + + build-ee-query-service: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup golang + uses: actions/setup-go@v4 + with: + go-version: "1.21" + - name: Build EE query-service image + shell: bash + run: | + make build-ee-query-service-amd64 diff --git a/.github/workflows/codeball.yml b/.github/workflows/codeball.yml new file mode 100644 index 0000000..ed69d5c --- /dev/null +++ b/.github/workflows/codeball.yml @@ -0,0 +1,17 @@ +name: Codeball +on: [pull_request] + +jobs: + codeball_job: + runs-on: ubuntu-latest + name: Codeball + steps: + # Run Codeball on all new Pull Requests 🚀 + # For customizations and more documentation, see https://github.com/sturdy-dev/codeball-action + - name: Codeball + uses: sturdy-dev/codeball-action@v2 + with: + approvePullRequests: "true" + labelPullRequestsWhenApproved: "true" + labelPullRequestsWhenReviewNeeded: "false" + failJobsWhenReviewNeeded: "false" diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 0000000..be02f3b --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,71 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main, v* ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '32 5 * * 5' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'go', 'javascript', 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml new file mode 100644 index 0000000..3a38338 --- /dev/null +++ b/.github/workflows/commitlint.yml @@ -0,0 +1,13 @@ +name: commitlint +on: [pull_request] +defaults: + run: + working-directory: frontend +jobs: + lint-commits: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: wagoid/commitlint-github-action@v5 diff --git a/.github/workflows/create-issue-on-pr-merge.yml b/.github/workflows/create-issue-on-pr-merge.yml new file mode 100644 index 0000000..2a79618 --- /dev/null +++ b/.github/workflows/create-issue-on-pr-merge.yml @@ -0,0 +1,27 @@ +on: + pull_request_target: + types: + - closed + +env: + GITHUB_ACCESS_TOKEN: ${{ secrets.CI_BOT_TOKEN }} + PR_NUMBER: ${{ github.event.number }} +jobs: + create_issue_on_merge: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + steps: + - name: Checkout Codebase + uses: actions/checkout@v4 + with: + repository: signoz/gh-bot + - name: Use Node v16 + uses: actions/setup-node@v4 + with: + node-version: 16 + - name: Setup Cache & Install Dependencies + uses: bahmutov/npm-install@v1 + with: + install-command: yarn --frozen-lockfile + - name: Comment on PR + run: node create-issue.js diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..4cb3979 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,22 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v4 + - name: 'Dependency Review' + with: + fail-on-severity: high + uses: actions/dependency-review-action@v3 diff --git a/.github/workflows/e2e-k3s.yaml b/.github/workflows/e2e-k3s.yaml new file mode 100644 index 0000000..ef5911f --- /dev/null +++ b/.github/workflows/e2e-k3s.yaml @@ -0,0 +1,93 @@ +name: e2e-k3s + +on: + pull_request: + types: [labeled] + +jobs: + + e2e-k3s: + runs-on: ubuntu-latest + if: ${{ github.event.label.name == 'ok-to-test' }} + env: + DOCKER_TAG: pull-${{ github.event.number }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup golang + uses: actions/setup-go@v4 + with: + go-version: "1.21" + + - name: Build query-service image + env: + DEV_BUILD: 1 + run: make build-ee-query-service-amd64 + + - name: Build frontend image + run: make build-frontend-amd64 + + - name: Create a k3s cluster + uses: AbsaOSS/k3d-action@v2 + with: + cluster-name: "signoz" + + - name: Inject the images to the cluster + run: k3d image import signoz/query-service:$DOCKER_TAG signoz/frontend:$DOCKER_TAG -c signoz + + - name: Set up HotROD sample-app + run: | + # create sample-application namespace + kubectl create ns sample-application + + # apply hotrod k8s manifest file + kubectl -n sample-application apply -f https://raw.githubusercontent.com/SigNoz/signoz/develop/sample-apps/hotrod/hotrod.yaml + + # wait for all deployments in sample-application namespace to be READY + kubectl -n sample-application get deploy --output name | xargs -r -n1 -t kubectl -n sample-application rollout status --timeout=300s + + - name: Deploy the app + run: | + # add signoz helm repository + helm repo add signoz https://charts.signoz.io + + # create platform namespace + kubectl create ns platform + + # installing signoz using helm + helm install my-release signoz/signoz -n platform \ + --wait \ + --timeout 10m0s \ + --set frontend.service.type=LoadBalancer \ + --set queryService.image.tag=$DOCKER_TAG \ + --set frontend.image.tag=$DOCKER_TAG + + # get pods, services and the container images + kubectl get pods -n platform + kubectl get svc -n platform + + - name: Kick off a sample-app workload + run: | + # start the locust swarm + kubectl --namespace sample-application run strzal --image=djbingham/curl \ + --restart='OnFailure' -i --tty --rm --command -- curl -X POST -F \ + 'user_count=6' -F 'spawn_rate=2' http://locust-master:8089/swarm + + - name: Get short commit SHA, display tunnel URL and IP Address of the worker node + id: get-subdomain + run: | + subdomain="pr-$(git rev-parse --short HEAD)" + echo "URL for tunnelling: https://$subdomain.loca.lt" + echo "subdomain=$subdomain" >> $GITHUB_OUTPUT + worker_ip="$(curl -4 -s ipconfig.io/ip)" + echo "Worker node IP address: $worker_ip" + + - name: Start tunnel + env: + SUBDOMAIN: ${{ steps.get-subdomain.outputs.subdomain }} + run: | + npm install -g localtunnel + host=$(kubectl get svc -n platform | grep frontend | tr -s ' ' | cut -d" " -f4) + port=$(kubectl get svc -n platform | grep frontend | tr -s ' ' | cut -d" " -f5 | cut -d":" -f1) + lt -p $port -l $host -s $SUBDOMAIN diff --git a/.github/workflows/playwright.yaml b/.github/workflows/playwright.yaml new file mode 100644 index 0000000..9ad3ef4 --- /dev/null +++ b/.github/workflows/playwright.yaml @@ -0,0 +1,24 @@ +name: Playwright Tests +on: [pull_request] + +jobs: + playwright: + defaults: + run: + working-directory: frontend + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "16.x" + - name: Install dependencies + run: CI=1 yarn install + - name: Install Playwright + run: npx playwright install --with-deps + - name: Run Playwright tests + run: yarn playwright + env: + # This might depend on your test-runner/language binding + PLAYWRIGHT_TEST_BASE_URL: ${{ secrets.PLAYWRIGHT_TEST_BASE_URL }} diff --git a/.github/workflows/pr_verify_linked_issue.yml b/.github/workflows/pr_verify_linked_issue.yml new file mode 100644 index 0000000..927b46c --- /dev/null +++ b/.github/workflows/pr_verify_linked_issue.yml @@ -0,0 +1,19 @@ +# This workflow will inspect a pull request to ensure there is a linked issue or a +# valid issue is mentioned in the body. If neither is present it fails the check and adds +# a comment alerting users of this missing requirement. +name: VerifyIssue + +on: + pull_request: + types: [edited, opened] + check_run: + +jobs: + verify_linked_issue: + runs-on: ubuntu-latest + name: Ensure Pull Request has a linked issue. + steps: + - name: Verify Linked Issue + uses: srikanthccv/verify-linked-issue-action@v0.71 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml new file mode 100644 index 0000000..6869cf7 --- /dev/null +++ b/.github/workflows/push.yaml @@ -0,0 +1,198 @@ +name: push + +on: + push: + branches: + - main + - develop + tags: + - v* + +jobs: + + image-build-and-push-query-service: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup golang + uses: actions/setup-go@v4 + with: + go-version: "1.21" + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + version: latest + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - uses: benjlevesque/short-sha@v2.2 + id: short-sha + - name: Get branch name + id: branch-name + uses: tj-actions/branch-names@v7.0.7 + - name: Set docker tag environment + run: | + if [ '${{ steps.branch-name.outputs.is_tag }}' == 'true' ]; then + tag="${{ steps.branch-name.outputs.tag }}" + tag="${tag:1}" + echo "DOCKER_TAG=${tag}-oss" >> $GITHUB_ENV + elif [ '${{ steps.branch-name.outputs.current_branch }}' == 'main' ]; then + echo "DOCKER_TAG=latest-oss" >> $GITHUB_ENV + else + echo "DOCKER_TAG=${{ steps.branch-name.outputs.current_branch }}-oss" >> $GITHUB_ENV + fi + - name: Install cross-compilation tools + run: | + set -ex + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu musl-tools + - name: Build and push docker image + run: make build-push-query-service + + image-build-and-push-ee-query-service: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup golang + uses: actions/setup-go@v4 + with: + go-version: "1.21" + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + version: latest + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - uses: benjlevesque/short-sha@v2.2 + id: short-sha + - name: Get branch name + id: branch-name + uses: tj-actions/branch-names@v7.0.7 + - name: Set docker tag environment + run: | + if [ '${{ steps.branch-name.outputs.is_tag }}' == 'true' ]; then + tag="${{ steps.branch-name.outputs.tag }}" + tag="${tag:1}" + echo "DOCKER_TAG=$tag" >> $GITHUB_ENV + elif [ '${{ steps.branch-name.outputs.current_branch }}' == 'main' ]; then + echo "DOCKER_TAG=latest" >> $GITHUB_ENV + else + echo "DOCKER_TAG=${{ steps.branch-name.outputs.current_branch }}" >> $GITHUB_ENV + fi + - name: Install cross-compilation tools + run: | + set -ex + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu musl-tools + - name: Build and push docker image + run: make build-push-ee-query-service + + image-build-and-push-frontend: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install dependencies + working-directory: frontend + run: yarn install + - name: Run Prettier + working-directory: frontend + run: npm run prettify + continue-on-error: true + - name: Run ESLint + working-directory: frontend + run: npm run lint + continue-on-error: true + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + version: latest + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - uses: benjlevesque/short-sha@v2.2 + id: short-sha + - name: Get branch name + id: branch-name + uses: tj-actions/branch-names@v7.0.7 + - name: Set docker tag environment + run: | + if [ '${{ steps.branch-name.outputs.is_tag }}' == 'true' ]; then + tag="${{ steps.branch-name.outputs.tag }}" + tag="${tag:1}" + echo "DOCKER_TAG=$tag" >> $GITHUB_ENV + elif [ '${{ steps.branch-name.outputs.current_branch }}' == 'main' ]; then + echo "DOCKER_TAG=latest" >> $GITHUB_ENV + else + echo "DOCKER_TAG=${{ steps.branch-name.outputs.current_branch }}" >> $GITHUB_ENV + fi + - name: Build and push docker image + run: make build-push-frontend + + image-build-and-push-frontend-ee: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create .env file + run: | + echo 'INTERCOM_APP_ID="${{ secrets.INTERCOM_APP_ID }}"' > frontend/.env + echo 'SEGMENT_ID="${{ secrets.SEGMENT_ID }}"' >> frontend/.env + echo 'CLARITY_PROJECT_ID="${{ secrets.CLARITY_PROJECT_ID }}"' >> frontend/.env + echo 'SENTRY_AUTH_TOKEN="${{ secrets.SENTRY_AUTH_TOKEN }}"' >> frontend/.env + echo 'SENTRY_ORG="${{ secrets.SENTRY_ORG }}"' >> frontend/.env + echo 'SENTRY_PROJECT_ID="${{ secrets.SENTRY_PROJECT_ID }}"' >> frontend/.env + echo 'SENTRY_DSN="${{ secrets.SENTRY_DSN }}"' >> frontend/.env + echo 'TUNNEL_URL="${{ secrets.TUNNEL_URL }}"' >> frontend/.env + echo 'TUNNEL_DOMAIN="${{ secrets.TUNNEL_DOMAIN }}"' >> frontend/.env + - name: Install dependencies + working-directory: frontend + run: yarn install + - name: Run Prettier + working-directory: frontend + run: npm run prettify + continue-on-error: true + - name: Run ESLint + working-directory: frontend + run: npm run lint + continue-on-error: true + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + version: latest + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - uses: benjlevesque/short-sha@v2.2 + id: short-sha + - name: Get branch name + id: branch-name + uses: tj-actions/branch-names@v7.0.7 + - name: Set docker tag environment + run: | + if [ '${{ steps.branch-name.outputs.is_tag }}' == 'true' ]; then + tag="${{ steps.branch-name.outputs.tag }}" + tag="${tag:1}" + echo "DOCKER_TAG=${tag}-ee" >> $GITHUB_ENV + elif [ '${{ steps.branch-name.outputs.current_branch }}' == 'main' ]; then + echo "DOCKER_TAG=latest-ee" >> $GITHUB_ENV + else + echo "DOCKER_TAG=${{ steps.branch-name.outputs.current_branch }}-ee" >> $GITHUB_ENV + fi + - name: Build and push docker image + run: make build-push-frontend diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..cb8189b --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,35 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - main + # pull_request event is required only for autolabeler + pull_request: + # Only following types are handled by the action, but one can default to all as well + types: [opened, reopened, synchronize] + +jobs: + update_release_draft: + permissions: + # write permission is required to create a github release + contents: write + # write permission is required for autolabeler + # otherwise, read permission is required at least + pull-requests: write + runs-on: ubuntu-latest + steps: + # (Optional) GitHub Enterprise requires GHE_HOST variable set + #- name: Set GHE_HOST + # run: | + # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV + + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + # with: + # config-name: my-config.yml + # disable-autolabeler: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/remove-label.yaml b/.github/workflows/remove-label.yaml new file mode 100644 index 0000000..ef570a6 --- /dev/null +++ b/.github/workflows/remove-label.yaml @@ -0,0 +1,22 @@ +name: remove-label + +on: + pull_request_target: + types: [synchronize] + +jobs: + remove: + runs-on: ubuntu-latest + steps: + - name: Remove label ok-to-test from PR + uses: buildsville/add-remove-label@v2.0.0 + with: + label: ok-to-test + type: remove + token: ${{ secrets.GITHUB_TOKEN }} + - name: Remove label testing-deploy from PR + uses: buildsville/add-remove-label@v2.0.0 + with: + label: testing-deploy + type: remove + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml new file mode 100644 index 0000000..8c62c12 --- /dev/null +++ b/.github/workflows/sonar.yml @@ -0,0 +1,26 @@ +name: sonar +on: + pull_request: + branches: + - main + - develop + paths: + - 'frontend/**' +defaults: + run: + working-directory: frontend +jobs: + sonar-analysis: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Sonar analysis + uses: sonarsource/sonarcloud-github-action@master + with: + projectBaseDir: frontend + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/staging-deployment.yaml b/.github/workflows/staging-deployment.yaml new file mode 100644 index 0000000..9b7a512 --- /dev/null +++ b/.github/workflows/staging-deployment.yaml @@ -0,0 +1,42 @@ +name: staging-deployment +# Trigger deployment only on push to develop branch +on: + push: + branches: + - develop +jobs: + deploy: + name: Deploy latest develop branch to staging + runs-on: ubuntu-latest + environment: staging + steps: + - name: Executing remote ssh commands using ssh key + uses: appleboy/ssh-action@v1.0.3 + env: + GITHUB_BRANCH: develop + GITHUB_SHA: ${{ github.sha }} + with: + host: ${{ secrets.HOST_DNS }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_KEY }} + envs: GITHUB_BRANCH,GITHUB_SHA + command_timeout: 60m + script: | + echo "GITHUB_BRANCH: ${GITHUB_BRANCH}" + echo "GITHUB_SHA: ${GITHUB_SHA}" + export DOCKER_TAG="${GITHUB_SHA:0:7}" # needed for child process to access it + export OTELCOL_TAG="main" + export PATH="/usr/local/go/bin/:$PATH" # needed for Golang to work + docker system prune --force + docker pull signoz/signoz-otel-collector:main + docker pull signoz/signoz-schema-migrator:main + cd ~/signoz + git status + git add . + git stash push -m "stashed on $(date --iso-8601=seconds)" + git fetch origin + git checkout ${GITHUB_BRANCH} + git pull + make build-ee-query-service-amd64 + make build-frontend-amd64 + make run-signoz \ No newline at end of file diff --git a/.github/workflows/testing-deployment.yaml b/.github/workflows/testing-deployment.yaml new file mode 100644 index 0000000..efb3d58 --- /dev/null +++ b/.github/workflows/testing-deployment.yaml @@ -0,0 +1,43 @@ +name: testing-deployment +# Trigger deployment only on testing-deploy label on pull request +on: + pull_request: + types: [labeled] +jobs: + deploy: + name: Deploy PR branch to testing + runs-on: ubuntu-latest + environment: testing + if: ${{ github.event.label.name == 'testing-deploy' }} + steps: + - name: Executing remote ssh commands using ssh key + uses: appleboy/ssh-action@v1.0.3 + env: + GITHUB_BRANCH: ${{ github.head_ref || github.ref_name }} + GITHUB_SHA: ${{ github.sha }} + with: + host: ${{ secrets.HOST_DNS }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_KEY }} + envs: GITHUB_BRANCH,GITHUB_SHA + command_timeout: 60m + script: | + echo "GITHUB_BRANCH: ${GITHUB_BRANCH}" + echo "GITHUB_SHA: ${GITHUB_SHA}" + export DOCKER_TAG="${GITHUB_SHA:0:7}" # needed for child process to access it + export DEV_BUILD="1" + export PATH="/usr/local/go/bin/:$PATH" # needed for Golang to work + docker system prune --force + cd ~/signoz + git status + git add . + git stash push -m "stashed on $(date --iso-8601=seconds)" + git fetch origin + git checkout develop + git pull + # This is added to include the scenerio when new commit in PR is force-pushed + git branch -D ${GITHUB_BRANCH} + git checkout --track origin/${GITHUB_BRANCH} + make build-ee-query-service-amd64 + make build-frontend-amd64 + make run-signoz \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f1834e --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ + +node_modules + +deploy/docker/environment_tiny/common_test +frontend/node_modules +frontend/.pnp +frontend/i18n-translations-hash.json +*.pnp.js + +# testing +frontend/coverage + +# production +frontend/build +frontend/.vscode +frontend/.yarnclean +frontend/.temp_cache +frontend/test-results + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +frontend/npm-debug.log* +frontend/yarn-debug.log* +frontend/yarn-error.log* +frontend/src/constants/env.ts + +.idea + +**/.vscode +**/build +**/storage +**/locust-scripts/__pycache__/ +**/__debug_bin + +.env +pkg/query-service/signoz.db + +pkg/query-service/tests/test-deploy/data/ + +ee/query-service/signoz.db + +ee/query-service/tests/test-deploy/data/ + +# local data +*.db +/deploy/docker/clickhouse-setup/data/ +/deploy/docker-swarm/clickhouse-setup/data/ +bin/ + +*/query-service/queries.active + +# e2e + +e2e/node_modules/ +e2e/test-results/ +e2e/playwright-report/ +e2e/blob-report/ +e2e/playwright/.cache/ +e2e/.auth \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000..1771de8 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,36 @@ +# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) +# and commit this file to your remote git repository to share the goodness with others. + + +tasks: + - name: Run Script to Comment ut required lines + init: | + cd ./.scripts + sh commentLinesForSetup.sh + + - name: Run Docker Images + init: | + cd ./deploy + sudo docker-compose -f docker/clickhouse-setup/docker-compose.yaml up -d + # command: + + - name: Run Frontend + init: | + cd ./frontend + yarn install + command: + yarn dev + +ports: + - port: 3301 + onOpen: open-browser + - port: 8080 + onOpen: ignore + - port: 9000 + onOpen: ignore + - port: 8123 + onOpen: ignore + - port: 8089 + onOpen: ignore + - port: 9093 + onOpen: ignore diff --git a/.scripts/commentLinesForSetup.sh b/.scripts/commentLinesForSetup.sh new file mode 100644 index 0000000..c0dfd40 --- /dev/null +++ b/.scripts/commentLinesForSetup.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# It Comments out the Line Query-Service & Frontend Section of deploy/docker/clickhouse-setup/docker-compose.yaml +# Update the Line Numbers when deploy/docker/clickhouse-setup/docker-compose.yaml chnages. +# Docs Ref.: https://github.com/SigNoz/signoz/blob/main/CONTRIBUTING.md#contribute-to-frontend-with-docker-installation-of-signoz + +sed -i 38,62's/.*/# &/' .././deploy/docker/clickhouse-setup/docker-compose.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..245ad5f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at dev@signoz.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6123bd7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,385 @@ +# Contributing Guidelines + +## Welcome to SigNoz Contributing section 🎉 + +Hi there! We're thrilled that you'd like to contribute to this project, thank you for your interest. Whether it's a bug report, new feature, correction, or additional documentation, we greatly value feedback and contributions from our community. + +Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution. + +- We accept contributions made to the [SigNoz `develop` branch]() +- Find all SigNoz Docker Hub images here + - [signoz/frontend](https://hub.docker.com/r/signoz/frontend) + - [signoz/query-service](https://hub.docker.com/r/signoz/query-service) + - [signoz/otelcontribcol](https://hub.docker.com/r/signoz/otelcontribcol) + +## Finding contributions to work on 💬 + +Looking at the existing issues is a great way to find something to contribute on. +Also, have a look at these [good first issues label](https://github.com/SigNoz/signoz/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) to start with. + + +## Sections: +- [General Instructions](#1-general-instructions-) + - [For Creating Issue(s)](#11-for-creating-issues) + - [For Pull Requests(s)](#12-for-pull-requests) +- [How to Contribute](#2-how-to-contribute-%EF%B8%8F) +- [Develop Frontend](#3-develop-frontend-) + - [Contribute to Frontend with Docker installation of SigNoz](#31-contribute-to-frontend-with-docker-installation-of-signoz) + - [Contribute to Frontend without installing SigNoz backend](#32-contribute-to-frontend-without-installing-signoz-backend) +- [Contribute to Backend (Query-Service)](#4-contribute-to-backend-query-service-) + - [To run ClickHouse setup](#41-to-run-clickhouse-setup-recommended-for-local-development) +- [Contribute to SigNoz Helm Chart](#5-contribute-to-signoz-helm-chart-) + - [To run helm chart for local development](#51-to-run-helm-chart-for-local-development) +- [Other Ways to Contribute](#other-ways-to-contribute) + +# 1. General Instructions 📝 + +## 1.1 For Creating Issue(s) +Before making any significant changes and before filing a new issue, please check [existing open](https://github.com/SigNoz/signoz/issues?q=is%3Aopen+is%3Aissue), or [recently closed](https://github.com/SigNoz/signoz/issues?q=is%3Aissue+is%3Aclosed) issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. + +**Issue Types** - [Bug Report](https://github.com/SigNoz/signoz/issues/new?assignees=&labels=&template=bug_report.md&title=) | [Feature Request](https://github.com/SigNoz/signoz/issues/new?assignees=&labels=&template=feature_request.md&title=) | [Performance Issue Report](https://github.com/SigNoz/signoz/issues/new?assignees=&labels=&template=performance-issue-report.md&title=) | [Report a Security Vulnerability](https://github.com/SigNoz/signoz/security/policy) + +#### Details like these are incredibly useful: + +- **Requirement** - what kind of use case are you trying to solve? +- **Proposal** - what do you suggest to solve the problem or improve the existing + situation? +- Any open questions to address❓ + +#### If you are reporting a bug, details like these are incredibly useful: + +- A reproducible test case or series of steps. +- The version of our code being used. +- Any modifications you've made relevant to the bug🐞. +- Anything unusual about your environment or deployment. + +Discussing your proposed changes ahead of time will make the contribution +process smooth for everyone 🙌. + + **[`^top^`](#)** + +
+ +## 1.2 For Pull Request(s) + +Contributions via pull requests are much appreciated. Once the approach is agreed upon ✅, make your changes and open a Pull Request(s). +Before sending us a pull request, please ensure that, + +- Fork the SigNoz repo on GitHub, clone it on your machine. +- Create a branch with your changes. +- You are working against the latest source on the `develop` branch. +- Modify the source; please focus only on the specific change you are contributing. +- Ensure local tests pass. +- Commit to your fork using clear commit messages. +- Send us a pull request, answering any default questions in the pull request interface. +- Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation +- Once you've pushed your commits to GitHub, make sure that your branch can be auto-merged (there are no merge conflicts). If not, on your computer, merge main into your branch, resolve any merge conflicts, make sure everything still runs correctly and passes all the tests, and then push up those changes. +- Once the change has been approved and merged, we will inform you in a comment. + + +GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and +[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). + +**Note:** Unless your change is small, **please** consider submitting different Pull Request(s): + +* 1️⃣ First PR should include the overall structure of the new component: + * Readme, configuration, interfaces or base classes, etc... + * This PR is usually trivial to review, so the size limit does not apply to + it. +* 2️⃣ Second PR should include the concrete implementation of the component. If the + size of this PR is larger than the recommended size, consider **splitting** ⚔️ it into + multiple PRs. +* If there are multiple sub-component then ideally each one should be implemented as + a **separate** pull request. +* Last PR should include changes to **any user-facing documentation.** And should include + end-to-end tests if applicable. The component must be enabled + only after sufficient testing, and there is enough confidence in the + stability and quality of the component. + + +You can always reach out to `ankit@signoz.io` to understand more about the repo and product. We are very responsive over email and [SLACK](https://signoz.io/slack). + +### Pointers: +- If you find any **bugs** → please create an [**issue.**](https://github.com/SigNoz/signoz/issues/new?assignees=&labels=&template=bug_report.md&title=) +- If you find anything **missing** in documentation → you can create an issue with the label **`documentation`**. +- If you want to build any **new feature** → please create an [issue with the label **`enhancement`**.](https://github.com/SigNoz/signoz/issues/new?assignees=&labels=&template=feature_request.md&title=) +- If you want to **discuss** something about the product, start a new [**discussion**.](https://github.com/SigNoz/signoz/discussions) + +
+ +### Conventions to follow when submitting Commits and Pull Request(s). + +We try to follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), more specifically the commits and PRs **should have type specifiers** prefixed in the name. [This](https://www.conventionalcommits.org/en/v1.0.0/#specification) should give you a better idea. + +e.g. If you are submitting a fix for an issue in frontend, the PR name should be prefixed with **`fix(FE):`** + +- Follow [GitHub Flow](https://guides.github.com/introduction/flow/) guidelines for your contribution flows. + +- Feel free to ping us on [`#contributing`](https://signoz-community.slack.com/archives/C01LWQ8KS7M) or [`#contributing-frontend`](https://signoz-community.slack.com/archives/C027134DM8B) on our slack community if you need any help on this :) + + **[`^top^`](#)** + +
+ +# 2. How to Contribute 🙋🏻‍♂️ + +#### There are primarily 2 areas in which you can contribute to SigNoz + +- [**Frontend**](#3-develop-frontend-) (Written in Typescript, React) +- [**Backend**](#4-contribute-to-backend-query-service-) (Query Service, written in Go) + +Depending upon your area of expertise & interest, you can choose one or more to contribute. Below are detailed instructions to contribute in each area. + +**Please note:** If you want to work on an issue, please ask the maintainers to assign the issue to you before starting work on it. This would help us understand who is working on an issue and prevent duplicate work. 🙏🏻 + +⚠️ If you just raise a PR, without the corresponding issue being assigned to you - it may not be accepted. + + **[`^top^`](#)** + +
+ +# 3. Develop Frontend 🌚 + +**Need to Update: [https://github.com/SigNoz/signoz/tree/develop/frontend](https://github.com/SigNoz/signoz/tree/develop/frontend)** + +Also, have a look at [Frontend README.md](https://github.com/SigNoz/signoz/blob/develop/frontend/README.md) sections for more info on how to setup SigNoz frontend locally (with and without Docker). + +## 3.1 Contribute to Frontend with Docker installation of SigNoz + +- Clone the SigNoz repository and cd into signoz directory, + ``` + git clone https://github.com/SigNoz/signoz.git && cd signoz + ``` +- Comment out `frontend` service section at [`deploy/docker/clickhouse-setup/docker-compose.yaml#L68`](https://github.com/SigNoz/signoz/blob/develop/deploy/docker/clickhouse-setup/docker-compose.yaml#L68) + +![develop-frontend](https://user-images.githubusercontent.com/52788043/179009217-6692616b-17dc-4d27-b587-9d007098d739.jpeg) + + +- run `cd deploy` to move to deploy directory, +- Install signoz locally **without** the frontend, + - Add / Uncomment the below configuration to query-service section at [`deploy/docker/clickhouse-setup/docker-compose.yaml#L47`](https://github.com/SigNoz/signoz/blob/develop/deploy/docker/clickhouse-setup/docker-compose.yaml#L47) + ``` + ports: + - "8080:8080" + ``` +query service + + - Next run, + ``` + sudo docker-compose -f docker/clickhouse-setup/docker-compose.yaml up -d + ``` +- `cd ../frontend` and change baseURL in file [`frontend/src/constants/env.ts#L2`](https://github.com/SigNoz/signoz/blob/develop/frontend/src/constants/env.ts#L2) and for that, you need to create a `.env` file in the `frontend` directory with the following environment variable (`FRONTEND_API_ENDPOINT`) matching your configuration. + + If you have backend api exposed via frontend nginx: + ``` + FRONTEND_API_ENDPOINT=http://localhost:3301 + ``` + If not: + ``` + FRONTEND_API_ENDPOINT=http://localhost:8080 + ``` + +- Next, + ``` + yarn install + yarn dev + ``` + +### Important Notes: +The Maintainers / Contributors who will change Line Numbers of `Frontend` & `Query-Section`, please update line numbers in [`/.scripts/commentLinesForSetup.sh`](https://github.com/SigNoz/signoz/blob/develop/.scripts/commentLinesForSetup.sh) + + **[`^top^`](#)** + +## 3.2 Contribute to Frontend without installing SigNoz backend + +If you don't want to install the SigNoz backend just for doing frontend development, we can provide you with test environments that you can use as the backend. + +- Clone the SigNoz repository and cd into signoz/frontend directory, + ``` + git clone https://github.com/SigNoz/signoz.git && cd signoz/frontend + ```` +- Create a file `.env` in the `frontend` directory with `FRONTEND_API_ENDPOINT=` +- Next, + ``` + yarn install + yarn dev + ``` + +Please ping us in the [`#contributing`](https://signoz-community.slack.com/archives/C01LWQ8KS7M) channel or ask `@Prashant Shahi` in our [Slack Community](https://signoz.io/slack) and we will DM you with ``. + +**Frontend should now be accessible at** [`http://localhost:3301/services`](http://localhost:3301/services) + + **[`^top^`](#)** + +
+ +# 4. Contribute to Backend (Query-Service) 🌑 + +**Need to Update: [https://github.com/SigNoz/signoz/tree/develop/pkg/query-service](https://github.com/SigNoz/signoz/tree/develop/pkg/query-service)** + +## 4.1 Prerequisites + +### 4.1.1 Install SQLite3 + +- Run `sqlite3` command to check if you already have SQLite3 installed on your machine. + +- If not installed already, Install using below command + - on Linux + - on Debian / Ubuntu + ``` + sudo apt install sqlite3 + ``` + - on CentOS / Fedora / RedHat + ``` + sudo yum install sqlite3 + ``` + +## 4.2 To run ClickHouse setup (recommended for local development) + +- Clone the SigNoz repository and cd into signoz directory, + ``` + git clone https://github.com/SigNoz/signoz.git && cd signoz + ``` +- run `sudo make dev-setup` to configure local setup to run query-service, +- Comment out `frontend` service section at [`deploy/docker/clickhouse-setup/docker-compose.yaml#L68`](https://github.com/SigNoz/signoz/blob/develop/deploy/docker/clickhouse-setup/docker-compose.yaml#L68) +develop-frontend + +- Comment out `query-service` section at [`deploy/docker/clickhouse-setup/docker-compose.yaml#L41`,](https://github.com/SigNoz/signoz/blob/develop/deploy/docker/clickhouse-setup/docker-compose.yaml#L41) +Screenshot 2022-07-14 at 22 48 07 + +- add below configuration to `clickhouse` section at [`deploy/docker/clickhouse-setup/docker-compose.yaml`,](https://github.com/SigNoz/signoz/blob/develop/deploy/docker/clickhouse-setup/docker-compose.yaml) + ``` + ports: + - 9001:9000 + ``` +Screenshot 2022-07-14 at 22 50 37 + +- run `cd pkg/query-service/` to move to `query-service` directory, +- Then, you need to create a `.env` file with the following environment variable + ``` + SIGNOZ_LOCAL_DB_PATH="./signoz.db" + ``` +to set your local environment with the right `RELATIONAL_DATASOURCE_PATH` as mentioned in [`./constants/constants.go#L38`,](https://github.com/SigNoz/signoz/blob/develop/pkg/query-service/constants/constants.go#L38) + +- Now, install SigNoz locally **without** the `frontend` and `query-service`, + - If you are using `x86_64` processors (All Intel/AMD processors) run `sudo make run-x86` + - If you are on `arm64` processors (Apple M1 Macs) run `sudo make run-arm` + +#### Run locally, +``` +ClickHouseUrl=tcp://localhost:9001 STORAGE=clickhouse go run main.go +``` + +#### Build and Run locally +``` +cd pkg/query-service +go build -o build/query-service main.go +ClickHouseUrl=tcp://localhost:9001 STORAGE=clickhouse build/query-service +``` + +#### Docker Images +The docker images of query-service is available at https://hub.docker.com/r/signoz/query-service + +``` +docker pull signoz/query-service +``` + +``` +docker pull signoz/query-service:latest +``` + +``` +docker pull signoz/query-service:develop +``` + +### Important Note: +The Maintainers / Contributors who will change Line Numbers of `Frontend` & `Query-Section`, please update line numbers in [`/.scripts/commentLinesForSetup.sh`](https://github.com/SigNoz/signoz/blob/develop/.scripts/commentLinesForSetup.sh) + + + +**Query Service should now be available at** [`http://localhost:8080`](http://localhost:8080) + +If you want to see how the frontend plays with query service, you can run the frontend also in your local env with the baseURL changed to `http://localhost:8080` in file [`frontend/src/constants/env.ts`](https://github.com/SigNoz/signoz/blob/develop/frontend/src/constants/env.ts) as the `query-service` is now running at port `8080`. + + + + **[`^top^`](#)** + +
+ +# 5. Contribute to SigNoz Helm Chart 📊 + +**Need to Update: [https://github.com/SigNoz/charts](https://github.com/SigNoz/charts).** + +## 5.1 To run helm chart for local development + +- Clone the SigNoz repository and cd into charts directory, + ``` + git clone https://github.com/SigNoz/charts.git && cd charts + ``` +- It is recommended to use lightweight kubernetes (k8s) cluster for local development: + - [kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) + - [k3d](https://k3d.io/#installation) + - [minikube](https://minikube.sigs.k8s.io/docs/start/) +- create a k8s cluster and make sure `kubectl` points to the locally created k8s cluster, +- run `make dev-install` to install SigNoz chart with `my-release` release name in `platform` namespace, +- next run, + ``` + kubectl -n platform port-forward svc/my-release-signoz-frontend 3301:3301 + ``` +to make SigNoz UI available at [localhost:3301](http://localhost:3301) + +**5.1.1 To install the HotROD sample app:** + +```bash +curl -sL https://github.com/SigNoz/signoz/raw/develop/sample-apps/hotrod/hotrod-install.sh \ + | HELM_RELEASE=my-release SIGNOZ_NAMESPACE=platform bash +``` + +**5.1.2 To load data with the HotROD sample app:** + +```bash +kubectl -n sample-application run strzal --image=djbingham/curl \ + --restart='OnFailure' -i --tty --rm --command -- curl -X POST -F \ + 'locust_count=6' -F 'hatch_rate=2' http://locust-master:8089/swarm +``` + +**5.1.3 To stop the load generation:** + +```bash +kubectl -n sample-application run strzal --image=djbingham/curl \ + --restart='OnFailure' -i --tty --rm --command -- curl \ + http://locust-master:8089/stop +``` + +**5.1.4 To delete the HotROD sample app:** + +```bash +curl -sL https://github.com/SigNoz/signoz/raw/develop/sample-apps/hotrod/hotrod-delete.sh \ + | HOTROD_NAMESPACE=sample-application bash +``` + + **[`^top^`](#)** + +--- + +## Other Ways to Contribute + +There are many other ways to get involved with the community and to participate in this project: + +- Use the product, submitting GitHub issues when a problem is found. +- Help code review pull requests and participate in issue threads. +- Submit a new feature request as an issue. +- Help answer questions on forums such as Stack Overflow and [SigNoz Community Slack Channel](https://signoz.io/slack). +- Tell others about the project on Twitter, your blog, etc. + + +Again, Feel free to ping us on [`#contributing`](https://signoz-community.slack.com/archives/C01LWQ8KS7M) or [`#contributing-frontend`](https://signoz-community.slack.com/archives/C027134DM8B) on our slack community if you need any help on this :) + +Thank You! diff --git a/LICENSE b/LICENSE index f288702..2fef891 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,25 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. +Copyright (c) 2020-present SigNoz Inc. + +Portions of this software are licensed as follows: + +* All content that resides under the "ee/" directory of this repository, if that directory exists, is licensed under the license defined in "ee/LICENSE". +* All third party components incorporated into the SigNoz Software are licensed under the original license provided by the owner of the applicable component. +* Content outside of the above mentioned directories or restrictions above is available under the "MIT Expat" license as defined below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5213c45 --- /dev/null +++ b/Makefile @@ -0,0 +1,187 @@ +# +# Reference Guide - https://www.gnu.org/software/make/manual/make.html +# + +# Build variables +BUILD_VERSION ?= $(shell git describe --always --tags) +BUILD_HASH ?= $(shell git rev-parse --short HEAD) +BUILD_TIME ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") +BUILD_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) +DEV_LICENSE_SIGNOZ_IO ?= https://staging-license.signoz.io/api/v1 +DEV_BUILD ?= "" # set to any non-empty value to enable dev build + +# Internal variables or constants. +FRONTEND_DIRECTORY ?= frontend +QUERY_SERVICE_DIRECTORY ?= pkg/query-service +EE_QUERY_SERVICE_DIRECTORY ?= ee/query-service +STANDALONE_DIRECTORY ?= deploy/docker/clickhouse-setup +SWARM_DIRECTORY ?= deploy/docker-swarm/clickhouse-setup + +GOOS ?= $(shell go env GOOS) +GOARCH ?= $(shell go env GOARCH) +GOPATH ?= $(shell go env GOPATH) + +REPONAME ?= signoz +DOCKER_TAG ?= $(subst v,,$(BUILD_VERSION)) +FRONTEND_DOCKER_IMAGE ?= frontend +QUERY_SERVICE_DOCKER_IMAGE ?= query-service + +# Build-time Go variables +PACKAGE?=go.signoz.io/signoz +buildVersion=${PACKAGE}/pkg/query-service/version.buildVersion +buildHash=${PACKAGE}/pkg/query-service/version.buildHash +buildTime=${PACKAGE}/pkg/query-service/version.buildTime +gitBranch=${PACKAGE}/pkg/query-service/version.gitBranch +licenseSignozIo=${PACKAGE}/ee/query-service/constants.LicenseSignozIo + +LD_FLAGS=-X ${buildHash}=${BUILD_HASH} -X ${buildTime}=${BUILD_TIME} -X ${buildVersion}=${BUILD_VERSION} -X ${gitBranch}=${BUILD_BRANCH} +DEV_LD_FLAGS=-X ${licenseSignozIo}=${DEV_LICENSE_SIGNOZ_IO} + +all: build-push-frontend build-push-query-service + +# Steps to build static files of frontend +build-frontend-static: + @echo "------------------" + @echo "--> Building frontend static files" + @echo "------------------" + @cd $(FRONTEND_DIRECTORY) && \ + rm -rf build && \ + CI=1 yarn install && \ + yarn build && \ + ls -l build + +# Steps to build and push docker image of frontend +.PHONY: build-frontend-amd64 build-push-frontend +# Step to build docker image of frontend in amd64 (used in build pipeline) +build-frontend-amd64: build-frontend-static + @echo "------------------" + @echo "--> Building frontend docker image for amd64" + @echo "------------------" + @cd $(FRONTEND_DIRECTORY) && \ + docker build --file Dockerfile -t $(REPONAME)/$(FRONTEND_DOCKER_IMAGE):$(DOCKER_TAG) \ + --build-arg TARGETPLATFORM="linux/amd64" . + +# Step to build and push docker image of frontend(used in push pipeline) +build-push-frontend: build-frontend-static + @echo "------------------" + @echo "--> Building and pushing frontend docker image" + @echo "------------------" + @cd $(FRONTEND_DIRECTORY) && \ + docker buildx build --file Dockerfile --progress plain --push --platform linux/arm64,linux/amd64 \ + --tag $(REPONAME)/$(FRONTEND_DOCKER_IMAGE):$(DOCKER_TAG) . + +# Steps to build static binary of query service +.PHONY: build-query-service-static +build-query-service-static: + @echo "------------------" + @echo "--> Building query-service static binary" + @echo "------------------" + @if [ $(DEV_BUILD) != "" ]; then \ + cd $(QUERY_SERVICE_DIRECTORY) && \ + CGO_ENABLED=1 go build -tags timetzdata -a -o ./bin/query-service-${GOOS}-${GOARCH} \ + -ldflags "-linkmode external -extldflags '-static' -s -w ${LD_FLAGS} ${DEV_LD_FLAGS}"; \ + else \ + cd $(QUERY_SERVICE_DIRECTORY) && \ + CGO_ENABLED=1 go build -tags timetzdata -a -o ./bin/query-service-${GOOS}-${GOARCH} \ + -ldflags "-linkmode external -extldflags '-static' -s -w ${LD_FLAGS}"; \ + fi + +.PHONY: build-query-service-static-amd64 +build-query-service-static-amd64: + make GOARCH=amd64 build-query-service-static + +.PHONY: build-query-service-static-arm64 +build-query-service-static-arm64: + make CC=aarch64-linux-gnu-gcc GOARCH=arm64 build-query-service-static + +# Steps to build static binary of query service for all platforms +.PHONY: build-query-service-static-all +build-query-service-static-all: build-query-service-static-amd64 build-query-service-static-arm64 + +# Steps to build and push docker image of query service +.PHONY: build-query-service-amd64 build-push-query-service +# Step to build docker image of query service in amd64 (used in build pipeline) +build-query-service-amd64: build-query-service-static-amd64 + @echo "------------------" + @echo "--> Building query-service docker image for amd64" + @echo "------------------" + @docker build --file $(QUERY_SERVICE_DIRECTORY)/Dockerfile \ + --tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) \ + --build-arg TARGETPLATFORM="linux/amd64" . + +# Step to build and push docker image of query in amd64 and arm64 (used in push pipeline) +build-push-query-service: build-query-service-static-all + @echo "------------------" + @echo "--> Building and pushing query-service docker image" + @echo "------------------" + @docker buildx build --file $(QUERY_SERVICE_DIRECTORY)/Dockerfile --progress plain \ + --push --platform linux/arm64,linux/amd64 \ + --tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) . + +# Step to build EE docker image of query service in amd64 (used in build pipeline) +build-ee-query-service-amd64: + @echo "------------------" + @echo "--> Building query-service docker image for amd64" + @echo "------------------" + make QUERY_SERVICE_DIRECTORY=${EE_QUERY_SERVICE_DIRECTORY} build-query-service-amd64 + +# Step to build and push EE docker image of query in amd64 and arm64 (used in push pipeline) +build-push-ee-query-service: + @echo "------------------" + @echo "--> Building and pushing query-service docker image" + @echo "------------------" + make QUERY_SERVICE_DIRECTORY=${EE_QUERY_SERVICE_DIRECTORY} build-push-query-service + +dev-setup: + mkdir -p /var/lib/signoz + sqlite3 /var/lib/signoz/signoz.db "VACUUM"; + mkdir -p pkg/query-service/config/dashboards + @echo "------------------" + @echo "--> Local Setup completed" + @echo "------------------" + +run-local: + @docker-compose -f \ + $(STANDALONE_DIRECTORY)/docker-compose-core.yaml -f $(STANDALONE_DIRECTORY)/docker-compose-local.yaml \ + up --build -d + +down-local: + @docker-compose -f \ + $(STANDALONE_DIRECTORY)/docker-compose-core.yaml -f $(STANDALONE_DIRECTORY)/docker-compose-local.yaml \ + down -v + +pull-signoz: + @docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.yaml pull + +run-signoz: + @docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.yaml up --build -d + +down-signoz: + @docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.yaml down -v + +clear-standalone-data: + @docker run --rm -v "$(PWD)/$(STANDALONE_DIRECTORY)/data:/pwd" busybox \ + sh -c "cd /pwd && rm -rf alertmanager/* clickhouse*/* signoz/* zookeeper-*/*" + +clear-swarm-data: + @docker run --rm -v "$(PWD)/$(SWARM_DIRECTORY)/data:/pwd" busybox \ + sh -c "cd /pwd && rm -rf alertmanager/* clickhouse*/* signoz/* zookeeper-*/*" + +clear-standalone-ch: + @docker run --rm -v "$(PWD)/$(STANDALONE_DIRECTORY)/data:/pwd" busybox \ + sh -c "cd /pwd && rm -rf clickhouse*/* zookeeper-*/*" + +clear-swarm-ch: + @docker run --rm -v "$(PWD)/$(SWARM_DIRECTORY)/data:/pwd" busybox \ + sh -c "cd /pwd && rm -rf clickhouse*/* zookeeper-*/*" + +test: + go test ./pkg/query-service/app/metrics/... + go test ./pkg/query-service/cache/... + go test ./pkg/query-service/app/... + go test ./pkg/query-service/app/querier/... + go test ./pkg/query-service/converter/... + go test ./pkg/query-service/formatter/... + go test ./pkg/query-service/tests/integration/... + go test ./pkg/query-service/rules/... + go test ./pkg/query-service/collectorsimulator/... diff --git a/README.de-de.md b/README.de-de.md new file mode 100644 index 0000000..1fdbcbd --- /dev/null +++ b/README.de-de.md @@ -0,0 +1,216 @@ +

+ SigNoz-logo + +

Überwache deine Anwendungen und behebe Probleme in deinen bereitgestellten Anwendungen. SigNoz ist eine Open Source Alternative zu DataDog, New Relic, etc.

+

+ +

+ Downloads + GitHub issues + + tweet +

+ +

+ Dokumentation • + Readme auf Englisch • + ReadMe auf Chinesisch • + ReadMe auf Portugiesisch • + Slack Community • + Twitter +

+ +## + +SigNoz hilft Entwicklern, Anwendungen zu überwachen und Probleme in ihren bereitgestellten Anwendungen zu beheben. Mit SigNoz können Sie Folgendes tun: + +👉 Visualisieren Sie Metriken, Traces und Logs in einer einzigen Oberfläche. + +👉 Sie können Metriken wie die p99-Latenz, Fehlerquoten für Ihre Dienste, externe API-Aufrufe und individuelle Endpunkte anzeigen. + +👉 Sie können die Ursache des Problems ermitteln, indem Sie zu den genauen Traces gehen, die das Problem verursachen, und detaillierte Flammenbilder einzelner Anfragetraces anzeigen. + +👉 Führen Sie Aggregationen auf Trace-Daten durch, um geschäftsrelevante Metriken zu erhalten. + +👉 Filtern und Abfragen von Logs, Erstellen von Dashboards und Benachrichtigungen basierend auf Attributen in den Logs. + +👉 Automatische Aufzeichnung von Ausnahmen in Python, Java, Ruby und Javascript. + +👉 Einfache Einrichtung von Benachrichtigungen mit dem selbst erstellbaren Abfrage-Builder. + +## + +### Anwendung Metriken + +![application_metrics](https://user-images.githubusercontent.com/83692067/226637410-900dbc5e-6705-4b11-a10c-bd0faeb2a92f.png) + +### Verteiltes Tracing + +distributed_tracing_2 2 + +distributed_tracing_1 + +### Log Verwaltung + +logs_management + +### Infrastruktur Überwachung + +infrastructure_monitoring + +### Exceptions Monitoring + +![exceptions_light](https://user-images.githubusercontent.com/83692067/226637967-4188d024-3ac9-4799-be95-f5ea9c45436f.png) + +### Alarme + +alerts_management + +

+ +## Werde Teil unserer Slack Community + +Sag Hi zu uns auf [Slack](https://signoz.io/slack) 👋 + +

+ +## Funktionen: + +- Einheitliche Benutzeroberfläche für Metriken, Traces und Logs. Keine Notwendigkeit, zwischen Prometheus und Jaeger zu wechseln, um Probleme zu debuggen oder ein separates Log-Tool wie Elastic neben Ihrer Metriken- und Traces-Stack zu verwenden. +- Überblick über Anwendungsmetriken wie RPS, Latenzzeiten des 50tes/90tes/99tes Perzentils und Fehlerquoten. +- Langsamste Endpunkte in Ihrer Anwendung. +- Zeigen Sie genaue Anfragetraces an, um Probleme in nachgelagerten Diensten, langsamen Datenbankabfragen oder Aufrufen von Drittanbieterdiensten wie Zahlungsgateways zu identifizieren. +- Filtern Sie Traces nach Dienstname, Operation, Latenz, Fehler, Tags/Annotationen. +- Führen Sie Aggregationen auf Trace-Daten (Ereignisse/Spans) durch, um geschäftsrelevante Metriken zu erhalten. Beispielsweise können Sie die Fehlerquote und die 99tes Perzentillatenz für `customer_type: gold` oder `deployment_version: v2` oder `external_call: paypal` erhalten. +- Native Unterstützung für OpenTelemetry-Logs, erweiterten Log-Abfrage-Builder und automatische Log-Sammlung aus dem Kubernetes-Cluster. +- Blitzschnelle Log-Analytik ([Logs Perf. Benchmark](https://signoz.io/blog/logs-performance-benchmark/)) +- End-to-End-Sichtbarkeit der Infrastrukturleistung, Aufnahme von Metriken aus allen Arten von Host-Umgebungen. +- Einfache Einrichtung von Benachrichtigungen mit dem selbst erstellbaren Abfrage-Builder. + +

+ +## Wieso SigNoz? + +Als Entwickler fanden wir es anstrengend, uns für jede kleine Funktion, die wir haben wollten, auf Closed Source SaaS Anbieter verlassen zu müssen. Closed Source Anbieter überraschen ihre Kunden zum Monatsende oft mit hohen Rechnungen, die keine Transparenz bzgl. der Kostenaufteilung bieten. + +Wir wollten eine selbst gehostete, Open Source Variante von Lösungen wie DataDog, NewRelic für Firmen anbieten, die Datenschutz und Sicherheitsbedenken haben, bei der Weitergabe von Kundendaten an Drittanbieter. + +Open Source gibt dir außerdem die totale Kontrolle über deine Konfiguration, Stichprobenentnahme und Betriebszeit. Du kannst des Weiteren neue Module auf Basis von SigNoz bauen, die erweiterte, geschäftsspezifische Funktionen anbieten. + +### Languages supported: + +Wir unterstützen [OpenTelemetry](https://opentelemetry.io) als Bibliothek, mit der Sie Ihre Anwendungen instrumentieren können. Daher wird jedes von OpenTelemetry unterstützte Framework und jede Sprache auch von SignNoz unterstützt. Einige der wichtigsten unterstützten Sprachen sind: + +- Java +- Python +- NodeJS +- Go +- PHP +- .NET +- Ruby +- Elixir +- Rust + +Hier findest du die vollständige Liste von unterstützten Programmiersprachen - https://opentelemetry.io/docs/ + +

+ +## Erste Schritte mit SigNoz + +### Bereitstellung mit Docker + +Bitte folge den [hier](https://signoz.io/docs/install/docker/) aufgelisteten Schritten um deine Anwendung mit Docker bereitzustellen. + +Die [Anleitungen zur Fehlerbehebung](https://signoz.io/docs/install/troubleshooting/) könnten hilfreich sein, falls du auf irgendwelche Schwierigkeiten stößt. + +

 

+ +### Deploy in Kubernetes using Helm + +Bitte folge den [hier](https://signoz.io/docs/deployment/helm_chart) aufgelisteten Schritten, um deine Anwendung mit Helm Charts bereitzustellen. + +

+ +## Vergleiche mit bekannten Tools + +### SigNoz vs Prometheus + +Prometheus ist gut, falls du dich nur für Metriken interessierst. Wenn du eine nahtlose Integration von Metriken und Einzelschritt-Fehlersuchen haben möchtest, ist die Kombination aus Prometheus und Jaeger nicht das Richtige für dich. + +Unser Ziel ist es, eine integrierte Benutzeroberfläche aus Metriken und Einzelschritt-Fehlersuchen anzubieten, ähnlich wie es SaaS Anbieter wie Datadog tun, mit der Möglichkeit von erweitertem filtern und aggregieren von Fehlersuchen. Etwas, was in Jaeger aktuell fehlt. + +

 

+ +### SigNoz vs Jaeger + +Jaeger kümmert sich nur um verteilte Einzelschritt-Fehlersuche. SigNoz erstellt sowohl Metriken als auch Einzelschritt-Fehlersuche, daneben haben wir auch Protokoll Verwaltung auf unserem Plan. + +Außerdem hat SigNoz noch mehr spezielle Funktionen im Vergleich zu Jaeger: + +- Jaeger UI zeigt keine Metriken für Einzelschritt-Fehlersuchen oder für gefilterte Einzelschritt-Fehlersuchen an. +- Jaeger erstellt keine Aggregate für gefilterte Einzelschritt-Fehlersuchen, z. B. die P99 Latenz von Abfragen mit dem Tag `customer_type=premium`, was hingegen mit SigNoz leicht umsetzbar ist. + +

 

+ +### SigNoz vs Elastic + +- Die Verwaltung von SigNoz-Protokollen basiert auf 'ClickHouse', einem spaltenbasierten OLAP-Datenspeicher, der aggregierte Protokollanalyseabfragen wesentlich effizienter macht. +- 50 % geringerer Ressourcenbedarf im Vergleich zu Elastic während der Aufnahme. + +Wir haben Benchmarks veröffentlicht, die Elastic mit SignNoz vergleichen. Schauen Sie es sich [hier](https://signoz.io/blog/logs-performance-benchmark/?utm_source=github-readme&utm_medium=logs-benchmark) + +

 

+ +### SigNoz vs Loki + +- SigNoz unterstützt Aggregationen von Daten mit hoher Kardinalität über ein großes Volumen, Loki hingegen nicht. +- SigNoz unterstützt Indizes über Daten mit hoher Kardinalität und hat keine Beschränkungen hinsichtlich der Anzahl der Indizes, während Loki maximale Streams erreicht, wenn ein paar Indizes hinzugefügt werden. +- Das Durchsuchen großer Datenmengen ist in Loki im Vergleich zu SigNoz schwierig und langsam. + +Wir haben Benchmarks veröffentlicht, die Loki mit SigNoz vergleichen. Schauen Sie es sich [hier](https://signoz.io/blog/logs-performance-benchmark/?utm_source=github-readme&utm_medium=logs-benchmark) + +

+ +## Zum Projekt beitragen + +Wir ❤️ Beiträge zum Projekt, egal ob große oder kleine. Bitte lies dir zuerst die [CONTRIBUTING.md](CONTRIBUTING.md), durch, bevor du anfängst, Beiträge zu SigNoz zu machen. +Du bist dir nicht sicher, wie du anfangen sollst? Schreib uns einfach auf dem #contributing Kanal in unserer [slack community](https://signoz.io/slack) + +### Unsere Projektbetreuer + +#### Backend + +- [Ankit Nayan](https://github.com/ankitnayan) +- [Nityananda Gohain](https://github.com/nityanandagohain) +- [Srikanth Chekuri](https://github.com/srikanthccv) +- [Vishal Sharma](https://github.com/makeavish) + +#### Frontend + +- [Palash Gupta](https://github.com/palashgdev) +- [Yunus M](https://github.com/YounixM) +- [Rajat Dabade](https://github.com/Rajat-Dabade) + +#### DevOps + +- [Prashant Shahi](https://github.com/prashant-shahi) + +

+ +## Dokumentation + +Du findest unsere Dokumentation unter https://signoz.io/docs/. Falls etwas unverständlich ist oder fehlt, öffne gerne ein Github Issue mit dem Label `documentation` oder schreib uns über den Community Slack Channel. + +

+ +## Gemeinschaft + +Werde Teil der [slack community](https://signoz.io/slack) um mehr über verteilte Einzelschritt-Fehlersuche, Messung von Systemzuständen oder SigNoz zu erfahren und sich mit anderen Nutzern und Mitwirkenden in Verbindung zu setzen. + +Falls du irgendwelche Ideen, Fragen oder Feedback hast, kannst du sie gerne über unsere [Github Discussions](https://github.com/SigNoz/signoz/discussions) mit uns teilen. + +Wie immer, Dank an unsere großartigen Mitwirkenden! + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..a17c67e --- /dev/null +++ b/README.md @@ -0,0 +1,230 @@ +

+ SigNoz-logo + +

Monitor your applications and troubleshoot problems in your deployed applications, an open-source alternative to DataDog, New Relic, etc.

+

+ +

+ Downloads + GitHub issues + + tweet +

+ + +

+ Documentation • + ReadMe in Chinese • + ReadMe in German • + ReadMe in Portuguese • + Slack Community • + Twitter +

+ +## + +SigNoz helps developers monitor applications and troubleshoot problems in their deployed applications. With SigNoz, you can: + +👉 Visualise Metrics, Traces and Logs in a single pane of glass + +👉 You can see metrics like p99 latency, error rates for your services, external API calls and individual end points. + +👉 You can find the root cause of the problem by going to the exact traces which are causing the problem and see detailed flamegraphs of individual request traces. + +👉 Run aggregates on trace data to get business relevant metrics + +👉 Filter and query logs, build dashboards and alerts based on attributes in logs + +👉 Record exceptions automatically in Python, Java, Ruby, and Javascript + +👉 Easy to set alerts with DIY query builder + + +### Application Metrics + +![application_metrics](https://user-images.githubusercontent.com/83692067/226637410-900dbc5e-6705-4b11-a10c-bd0faeb2a92f.png) + + +### Distributed Tracing +distributed_tracing_2 2 + +distributed_tracing_1 + +### Logs Management + +logs_management + +### Infrastructure Monitoring + +infrastructure_monitoring + +### Exceptions Monitoring + +![exceptions_light](https://user-images.githubusercontent.com/83692067/226637967-4188d024-3ac9-4799-be95-f5ea9c45436f.png) + + +### Alerts + +alerts_management + + +

+ + +## Join our Slack community + +Come say Hi to us on [Slack](https://signoz.io/slack) 👋 + +

+ + +## Features: + +- Unified UI for metrics, traces and logs. No need to switch from Prometheus to Jaeger to debug issues, or use a logs tool like Elastic separate from your metrics and traces stack. +- Application overview metrics like RPS, 50th/90th/99th Percentile latencies, and Error Rate +- Slowest endpoints in your application +- See exact request trace to figure out issues in downstream services, slow DB queries, call to 3rd party services like payment gateways, etc +- Filter traces by service name, operation, latency, error, tags/annotations. +- Run aggregates on trace data (events/spans) to get business relevant metrics. e.g. You can get error rate and 99th percentile latency of `customer_type: gold` or `deployment_version: v2` or `external_call: paypal` +- Native support for OpenTelemetry Logs, advanced log query builder, and automatic log collection from k8s cluster +- Lightning quick log analytics ([Logs Perf. Benchmark](https://signoz.io/blog/logs-performance-benchmark/)) +- End-to-End visibility into infrastructure performance, ingest metrics from all kinds of host environments +- Easy to set alerts with DIY query builder + +

+ + +## Why SigNoz? + +Being developers, we found it annoying to rely on closed source SaaS vendors for every small feature we wanted. Closed source vendors often surprise you with huge month end bills without any transparency. + +We wanted to make a self-hosted & open source version of tools like DataDog, NewRelic for companies that have privacy and security concerns about having customer data going to third party services. + +Being open source also gives you complete control of your configuration, sampling, uptimes. You can also build modules over SigNoz to extend business specific capabilities + +### Languages supported: + +We support [OpenTelemetry](https://opentelemetry.io) as the library which you can use to instrument your applications. So any framework and language supported by OpenTelemetry is also supported by SigNoz. Some of the main supported languages are: + +- Java +- Python +- Node.js +- Go +- PHP +- .NET +- Ruby +- Elixir +- Rust + + +You can find the complete list of languages here - https://opentelemetry.io/docs/ + +

+ + +## Getting Started + +### Deploy using Docker + +Please follow the steps listed [here](https://signoz.io/docs/install/docker/) to install using docker + +The [troubleshooting instructions](https://signoz.io/docs/install/troubleshooting/) may be helpful if you face any issues. + +

 

+ + +### Deploy in Kubernetes using Helm + +Please follow the steps listed [here](https://signoz.io/docs/deployment/helm_chart) to install using helm charts + +

+ + +## Comparisons to Familiar Tools + +### SigNoz vs Prometheus + +Prometheus is good if you want to do just metrics. But if you want to have a seamless experience between metrics and traces, then current experience of stitching together Prometheus & Jaeger is not great. + +Our goal is to provide an integrated UI between metrics & traces - similar to what SaaS vendors like Datadog provides - and give advanced filtering and aggregation over traces, something which Jaeger currently lack. + +

 

+ +### SigNoz vs Jaeger + +Jaeger only does distributed tracing. SigNoz supports metrics, traces and logs - all the 3 pillars of observability. + +Moreover, SigNoz has few more advanced features wrt Jaeger: + +- Jaegar UI doesn’t show any metrics on traces or on filtered traces +- Jaeger can’t get aggregates on filtered traces. For example, p99 latency of requests which have tag - customer_type='premium'. This can be done easily on SigNoz + +

 

+ +### SigNoz vs Elastic + +- SigNoz Logs management are based on ClickHouse, a columnar OLAP datastore which makes aggregate log analytics queries much more efficient +- 50% lower resource requirement compared to Elastic during ingestion + +We have published benchmarks comparing Elastic with SigNoz. Check it out [here](https://signoz.io/blog/logs-performance-benchmark/?utm_source=github-readme&utm_medium=logs-benchmark) + +

 

+ +### SigNoz vs Loki + +- SigNoz supports aggregations on high-cardinality data over a huge volume while loki doesn’t. +- SigNoz supports indexes over high cardinality data and has no limitations on the number of indexes, while Loki reaches max streams with a few indexes added to it. +- Searching over a huge volume of data is difficult and slow in Loki compared to SigNoz + +We have published benchmarks comparing Loki with SigNoz. Check it out [here](https://signoz.io/blog/logs-performance-benchmark/?utm_source=github-readme&utm_medium=logs-benchmark) + +

+ + +## Contributing + +We ❤️ contributions big or small. Please read [CONTRIBUTING.md](CONTRIBUTING.md) to get started with making contributions to SigNoz. + +Not sure how to get started? Just ping us on `#contributing` in our [slack community](https://signoz.io/slack) + +### Project maintainers + +#### Backend + +- [Ankit Nayan](https://github.com/ankitnayan) +- [Nityananda Gohain](https://github.com/nityanandagohain) +- [Srikanth Chekuri](https://github.com/srikanthccv) +- [Vishal Sharma](https://github.com/makeavish) + +#### Frontend + +- [Palash Gupta](https://github.com/palashgdev) +- [Yunus M](https://github.com/YounixM) +- [Rajat Dabade](https://github.com/Rajat-Dabade) + +#### DevOps + +- [Prashant Shahi](https://github.com/prashant-shahi) +- [Dhawal Sanghvi](https://github.com/dhawal1248) + +

+ + +## Documentation + +You can find docs at https://signoz.io/docs/. If you need any clarification or find something missing, feel free to raise a GitHub issue with the label `documentation` or reach out to us at the community slack channel. + +

+ + +## Community + +Join the [slack community](https://signoz.io/slack) to know more about distributed tracing, observability, or SigNoz and to connect with other users and contributors. + +If you have any ideas, questions, or any feedback, please share on our [Github Discussions](https://github.com/SigNoz/signoz/discussions) + +As always, thanks to our amazing contributors! + + + + diff --git a/README.pt-br.md b/README.pt-br.md new file mode 100644 index 0000000..c817e8a --- /dev/null +++ b/README.pt-br.md @@ -0,0 +1,158 @@ +

+ SigNoz-logo + +

Monitore seus aplicativos e solucione problemas em seus aplicativos implantados, uma alternativa de código aberto para soluções como DataDog, New Relic, entre outras.

+

+ +

+ Downloads + GitHub issues + + tweet +

+ + +

+ Documentação • + Comunidade no Slack • + Twitter +

+ +## + +SigNoz auxilia os desenvolvedores a monitorarem aplicativos e solucionar problemas em seus aplicativos implantados. SigNoz usa rastreamento distribuído para obter visibilidade em sua pilha de software. + +👉 Você pode verificar métricas como latência p99, taxas de erro em seus serviços, requisições às APIs externas e endpoints individuais. + +👉 Você pode encontrar a causa raiz do problema acessando os rastreamentos exatos que estão causando o problema e verificar os quadros detalhados de cada requisição individual. + +👉 Execute agregações em dados de rastreamento para obter métricas de negócios relevantes. + + +![SigNoz Feature](https://signoz-public.s3.us-east-2.amazonaws.com/signoz_hero_github.png) + +

+ + + +## Junte-se à nossa comunidade no Slack + +Venha dizer oi para nós no [Slack](https://signoz.io/slack) 👋 + +

+ + + +## Funções: + +- Métricas de visão geral do aplicativo, como RPS, latências de percentual 50/90/99 e taxa de erro +- Endpoints mais lentos em seu aplicativo +- Visualize o rastreamento preciso de requisições de rede para descobrir problemas em serviços downstream, consultas lentas de banco de dados, chamadas para serviços de terceiros, como gateways de pagamento, etc. +- Filtre os rastreamentos por nome de serviço, operação, latência, erro, tags / anotações. +- Execute agregações em dados de rastreamento (eventos / extensões) para obter métricas de negócios relevantes, como por exemplo, você pode obter a taxa de erro e a latência do 99º percentil de `customer_type: gold` or `deployment_version: v2` or `external_call: paypal` +- Interface de Usuário unificada para métricas e rastreios. Não há necessidade de mudar de Prometheus para Jaeger para depurar problemas. + +

+ + + +## Por que escolher SigNoz? + +Sendo desenvolvedores, achamos irritante contar com fornecedores de SaaS de código fechado para cada pequeno recurso que queríamos. Fornecedores de código fechado costumam surpreendê-lo com enormes contas no final do mês de uso sem qualquer transparência . + +Queríamos fazer uma versão auto-hospedada e de código aberto de ferramentas como DataDog, NewRelic para empresas que têm preocupações com privacidade e segurança em ter dados de clientes indo para serviços de terceiros. + +Ser open source também oferece controle completo de sua configuração, amostragem e tempos de atividade. Você também pode construir módulos sobre o SigNoz para estender recursos específicos do negócio. + +### Linguagens Suportadas: + +Nós apoiamos a biblioteca [OpenTelemetry](https://opentelemetry.io) como a biblioteca que você pode usar para instrumentar seus aplicativos. Em outras palavras, SigNoz oferece suporte a qualquer framework e linguagem que suporte a biblioteca OpenTelemetry. As principais linguagens suportadas incluem: + +- Java +- Python +- NodeJS +- Go + +Você pode encontrar a lista completa de linguagens aqui - https://opentelemetry.io/docs/ + +

+ + + +## Iniciando + + +### Implantar usando Docker + +Siga as etapas listadas [aqui](https://signoz.io/docs/install/docker/) para instalar usando o Docker. + +Esse [guia para solução de problemas](https://signoz.io/docs/install/troubleshooting/) pode ser útil se você enfrentar quaisquer problemas. + +

 

+ + +### Implentar no Kubernetes usando Helm + +Siga as etapas listadas [aqui](https://signoz.io/docs/deployment/helm_chart) para instalar usando helm charts. + + +

+ + + +## Comparações com ferramentas similares + +### SigNoz ou Prometheus + +Prometheus é bom se você quiser apenas fazer métricas. Mas se você quiser ter uma experiência perfeita entre métricas e rastreamentos, a experiência atual de unir Prometheus e Jaeger não é ótima. + +Nosso objetivo é fornecer uma interface do usuário integrada entre métricas e rastreamentos - semelhante ao que fornecedores de SaaS como o Datadog fornecem - e fornecer filtragem e agregação avançada sobre rastreamentos, algo que a Jaeger atualmente carece. + +

 

+ +### SigNoz ou Jaeger + +Jaeger só faz rastreamento distribuído. SigNoz faz métricas e rastreia, e também temos gerenciamento de log em nossos planos. + +Além disso, SigNoz tem alguns recursos mais avançados do que Jaeger: + +- A interface de usuário do Jaegar não mostra nenhuma métrica em traces ou em traces filtrados +- Jaeger não pode obter agregados em rastros filtrados. Por exemplo, latência p99 de solicitações que possuem tag - customer_type='premium'. Isso pode ser feito facilmente com SigNoz. + +

+ + + +## Contribuindo + + +Nós ❤️ contribuições grandes ou pequenas. Leia [CONTRIBUTING.md](CONTRIBUTING.md) para começar a fazer contribuições para o SigNoz. + +Não sabe como começar? Basta enviar um sinal para nós no canal `#contributing` em nossa [comunidade no Slack.](https://signoz.io/slack) + +

+ + + +## Documentação + +Você pode encontrar a documentação em https://signoz.io/docs/. Se você tiver alguma dúvida ou sentir falta de algo, sinta-se à vontade para criar uma issue com a tag `documentation` no GitHub ou entre em contato conosco no canal da comunidade no Slack. + +

+ + + +## Comunidade + +Junte-se a [comunidade no Slack](https://signoz.io/slack) para saber mais sobre rastreamento distribuído, observabilidade ou SigNoz e para se conectar com outros usuários e colaboradores. + +Se você tiver alguma ideia, pergunta ou feedback, compartilhe em nosso [Github Discussões](https://github.com/SigNoz/signoz/discussions) + +Como sempre, obrigado aos nossos incríveis colaboradores! + + + + + + + diff --git a/README.zh-cn.md b/README.zh-cn.md new file mode 100644 index 0000000..445474f --- /dev/null +++ b/README.zh-cn.md @@ -0,0 +1,227 @@ +SigNoz-logo + +

监控你的应用,并且可排查已部署应用的问题,这是一个可替代 DataDog、NewRelic 的开源方案

+

+ +

+ Downloads + GitHub issues + + tweet +

+ +

+ 文档 • + 中文ReadMe • + 德文ReadMe • + 葡萄牙语ReadMe • + Slack 社区 • + Twitter +

+ +## + +SigNoz 帮助开发人员监控应用并排查已部署应用的问题。你可以使用 SigNoz 实现如下能力: + +👉 在同一块面板上,可视化 Metrics, Traces 和 Logs 内容。 + +👉 你可以关注服务的 p99 延迟和错误率, 包括外部 API 调用和个别的端点。 + +👉 你可以找到问题的根因,通过提取相关问题的 traces 日志、单独查看请求 traces 的火焰图详情。 + +👉 执行 trace 数据聚合,以获取业务相关的 metrics + +👉 对日志过滤和查询,通过日志的属性建立看板和告警 + +👉 通过 Python,java,Ruby 和 Javascript 自动记录异常 + +👉 轻松的自定义查询和设置告警 + +### 应用 Metrics 展示 + +![application_metrics](https://user-images.githubusercontent.com/83692067/226637410-900dbc5e-6705-4b11-a10c-bd0faeb2a92f.png) + +### 分布式追踪 + +distributed_tracing_2 2 + +distributed_tracing_1 + +### 日志管理 + +logs_management + +### 基础设施监控 + +infrastructure_monitoring + +### 异常监控 + +![exceptions_light](https://user-images.githubusercontent.com/83692067/226637967-4188d024-3ac9-4799-be95-f5ea9c45436f.png) + +### 告警 + +alerts_management + +

+ +## 加入我们 Slack 社区 + +来 [Slack](https://signoz.io/slack) 和我们打招呼吧 👋 + +

+ +## 特性: + +- 为 metrics, traces and logs 制定统一的 UI。 无需切换 Prometheus 到 Jaeger 去查找问题,也无需使用想 Elastic 这样的日志工具分开你的 metrics 和 traces + +- 默认统计应用的 metrics 数据,像 RPS (每秒请求数), 50th/90th/99th 的分位数延迟数据,还有相关的错误率 + +- 找到应用中最慢的端点 + +- 查看准确的请求跟踪数据,找到下游服务的问题了,比如 DB 慢查询,或者调用第三方的支付网关等 + +- 通过 服务名、操作方式、延迟、错误、标签/注释 过滤 traces 数据 + +- 通过聚合 trace 数据而获得业务相关的 metrics。 比如你可以通过 `customer_type: gold` 或者 `deployment_version: v2` 或者 `external_call: paypal` 获取错误率和 P99 延迟数据 + +- 原生支持 OpenTelemetry 日志,高级日志查询,自动收集 k8s 相关日志 + +- 快如闪电的日志分析 ([Logs Perf. Benchmark](https://signoz.io/blog/logs-performance-benchmark/)) + +- 可视化点到点的基础设施性能,提取有所有类型机器的 metrics 数据 + +- 轻易自定义告警查询 + +

+ +## 为什么使用 SigNoz? + +作为开发者, 我们发现 SaaS 厂商对一些大家想要的小功能都是闭源的,这种行为真的让人有点恼火。 闭源厂商还会在月底给你一张没有明细的巨额账单。 + +我们想做一个自托管并且可开源的工具,像 DataDog 和 NewRelic 那样, 为那些担心数据隐私和安全的公司提供第三方服务。 + +作为开源的项目,你完全可以自己掌控你的配置、样本和更新。你同样可以基于 SigNoz 拓展特定的业务模块。 + +### 支持的编程语言: + +我们支持 [OpenTelemetry](https://opentelemetry.io)。作为一个观测你应用的库文件。所以任何 OpenTelemetry 支持的框架和语言,对于 SigNoz 也同样支持。 一些主要支持的语言如下: + +- Java +- Python +- NodeJS +- Go +- PHP +- .NET +- Ruby +- Elixir +- Rust + +你可以在这里找到全部支持的语言列表 - https://opentelemetry.io/docs/ + +

+ +## 让我们开始吧 + +### 使用 Docker 部署 + +请一步步跟随 [这里](https://signoz.io/docs/install/docker/) 通过 docker 来安装。 + +这个 [排障说明书](https://signoz.io/docs/install/troubleshooting/) 可以帮助你解决碰到的问题。 + +

 

+ +### 使用 Helm 在 Kubernetes 部署 + +请一步步跟随 [这里](https://signoz.io/docs/deployment/helm_chart) 通过 helm 来安装 + +

+ +## 比较相似的工具 + +### SigNoz vs Prometheus + +Prometheus 是一个针对 metrics 监控的强大工具。但是如果你想无缝的切换 metrics 和 traces 查询,你当前大概率需要在 Prometheus 和 Jaeger 之间切换。 + +我们的目标是提供一个客户观测 metrics 和 traces 整合的 UI。就像 SaaS 供应商 DataDog,它提供很多 jaeger 缺失的功能,比如针对 traces 过滤功能和聚合功能。 + +

 

+ +### SigNoz vs Jaeger + +Jaeger 仅仅是一个分布式追踪系统。 但是 SigNoz 可以提供 metrics, traces 和 logs 所有的观测。 + +而且, SigNoz 相较于 Jaeger 拥有更对的高级功能: + +- Jaegar UI 不能提供任何基于 traces 的 metrics 查询和过滤。 + +- Jaeger 不能针对过滤的 traces 做聚合。 比如, p99 延迟的请求有个标签是 customer_type='premium'。 而这些在 SigNoz 可以轻松做到。 + +

 

+ +### SigNoz vs Elastic + +- SigNoz 的日志管理是基于 ClickHouse 实现的,可以使日志的聚合更加高效,因为它是基于 OLAP 的数据仓储。 + +- 与 Elastic 相比,可以节省 50% 的资源成本 + +我们已经公布了 Elastic 和 SigNoz 的性能对比。 请点击 [这里](https://signoz.io/blog/logs-performance-benchmark/?utm_source=github-readme&utm_medium=logs-benchmark) + +

 

+ +### SigNoz vs Loki + +- SigNoz 支持大容量高基数的聚合,但是 loki 是不支持的。 + +- SigNoz 支持索引的高基数查询,并且对索引没有数量限制,而 Loki 会在添加部分索引后到达最大上限。 + +- 相较于 SigNoz,Loki 在搜索大量数据下既困难又缓慢。 + +我们已经发布了基准测试对比 Loki 和 SigNoz 性能。请点击 [这里](https://signoz.io/blog/logs-performance-benchmark/?utm_source=github-readme&utm_medium=logs-benchmark) + +

+ +## 贡献 + +我们 ❤️ 你的贡献,无论大小。 请先阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 再开始给 SigNoz 做贡献。 + +如果你不知道如何开始? 只需要在 [slack 社区](https://signoz.io/slack) 通过 `#contributing` 频道联系我们。 + +### 项目维护人员 + +#### 后端 + +- [Ankit Nayan](https://github.com/ankitnayan) +- [Nityananda Gohain](https://github.com/nityanandagohain) +- [Srikanth Chekuri](https://github.com/srikanthccv) +- [Vishal Sharma](https://github.com/makeavish) + +#### 前端 + +- [Palash Gupta](https://github.com/palashgdev) +- [Yunus M](https://github.com/YounixM) +- [Rajat Dabade](https://github.com/Rajat-Dabade) + +#### 运维开发 + +- [Prashant Shahi](https://github.com/prashant-shahi) + +

+ +## 文档 + +你可以通过 https://signoz.io/docs/ 找到相关文档。如果你需要阐述问题或者发现一些确实的事件, 通过标签为 `documentation` 提交 Github 问题。或者通过 slack 社区频道。 + +

+ +## 社区 + +加入 [slack 社区](https://signoz.io/slack) 去了解更多关于分布式追踪、可观测性系统 。或者与 SigNoz 其他用户和贡献者交流。 + +如果你有任何想法、问题、或者任何反馈, 请通过 [Github Discussions](https://github.com/SigNoz/signoz/discussions) 分享。 + +不管怎么样,感谢这个项目的所有贡献者! + + + + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..000076f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +SigNoz is looking forward to working with security researchers across the world to keep SigNoz and our users safe. If you have found an issue in our systems/applications, please reach out to us. + +## Supported Versions +We always recommend using the latest version of SigNoz to ensure you get all security updates + +## Reporting a Vulnerability + +If you believe you have found a security vulnerability within SigNoz, please let us know right away. We'll try and fix the problem as soon as possible. + +**Do not report vulnerabilities using public GitHub issues**. Instead, email with a detailed account of the issue. Please submit one issue per email, this helps us triage vulnerabilities. + +Once we've received your email we'll keep you updated as we fix the vulnerability. + +## Thanks + +Thank you for keeping SigNoz and our users safe. 🙇 diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..5e6740e --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,82 @@ +# Deploy + +Check that you have cloned [signoz/signoz](https://github.com/signoz/signoz) +and currently are in `signoz/deploy` folder. + +## Docker + +If you don't have docker set up, please follow [this guide](https://docs.docker.com/engine/install/) +to set up docker before proceeding with the next steps. + +### Using Install Script + +Now run the following command to install: + +```sh +./install.sh +``` + +### Using Docker Compose + +If you don't have docker-compose set up, please follow [this guide](https://docs.docker.com/compose/install/) +to set up docker compose before proceeding with the next steps. + +For x86 chip (amd): + +```sh +docker-compose -f docker/clickhouse-setup/docker-compose.yaml up -d +``` + +Open http://localhost:3301 in your favourite browser. In couple of minutes, you should see +the data generated from hotrod in SigNoz UI. + +## Kubernetes + +### Using Helm + +#### Bring up SigNoz cluster + +```sh +helm repo add signoz https://charts.signoz.io + +kubectl create ns platform + +helm -n platform install my-release signoz/signoz +``` + +To access the UI, you can `port-forward` the frontend service: + +```sh +kubectl -n platform port-forward svc/my-release-frontend 3301:3301 +``` + +Open http://localhost:3301 in your favourite browser. Few minutes after you generate load +from the HotROD application, you should see the data generated from hotrod in SigNoz UI. + +#### Test HotROD application with SigNoz + +```sh +kubectl create ns sample-application + +kubectl -n sample-application apply -f https://raw.githubusercontent.com/SigNoz/signoz/develop/sample-apps/hotrod/hotrod.yaml +``` + +To generate load: + +```sh +kubectl -n sample-application run strzal --image=djbingham/curl \ +--restart='OnFailure' -i --tty --rm --command -- curl -X POST -F \ +'user_count=6' -F 'spawn_rate=2' http://locust-master:8089/swarm +``` + +To stop load: + +```sh +kubectl -n sample-application run strzal --image=djbingham/curl \ + --restart='OnFailure' -i --tty --rm --command -- curl \ + http://locust-master:8089/stop +``` + +## Uninstall/Troubleshoot? + +Go to our official documentation site [signoz.io/docs](https://signoz.io/docs) for more. diff --git a/deploy/docker-swarm/clickhouse-setup/alertmanager.yml b/deploy/docker-swarm/clickhouse-setup/alertmanager.yml new file mode 100644 index 0000000..d69357f --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/alertmanager.yml @@ -0,0 +1,35 @@ +global: + resolve_timeout: 1m + slack_api_url: 'https://hooks.slack.com/services/xxx' + +route: + receiver: 'slack-notifications' + +receivers: +- name: 'slack-notifications' + slack_configs: + - channel: '#alerts' + send_resolved: true + icon_url: https://avatars3.githubusercontent.com/u/3380462 + title: |- + [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} + {{- if gt (len .CommonLabels) (len .GroupLabels) -}} + {{" "}}( + {{- with .CommonLabels.Remove .GroupLabels.Names }} + {{- range $index, $label := .SortedPairs -}} + {{ if $index }}, {{ end }} + {{- $label.Name }}="{{ $label.Value -}}" + {{- end }} + {{- end -}} + ) + {{- end }} + text: >- + {{ range .Alerts -}} + *Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - `{{ .Labels.severity }}`{{ end }} + + *Description:* {{ .Annotations.description }} + + *Details:* + {{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}` + {{ end }} + {{ end }} \ No newline at end of file diff --git a/deploy/docker-swarm/clickhouse-setup/alerts.yml b/deploy/docker-swarm/clickhouse-setup/alerts.yml new file mode 100644 index 0000000..810a207 --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/alerts.yml @@ -0,0 +1,11 @@ +groups: +- name: ExampleCPULoadGroup + rules: + - alert: HighCpuLoad + expr: system_cpu_load_average_1m > 0.1 + for: 0m + labels: + severity: warning + annotations: + summary: High CPU load + description: "CPU load is > 0.1\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" diff --git a/deploy/docker-swarm/clickhouse-setup/clickhouse-cluster.xml b/deploy/docker-swarm/clickhouse-setup/clickhouse-cluster.xml new file mode 100644 index 0000000..0e3ddcd --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/clickhouse-cluster.xml @@ -0,0 +1,75 @@ + + + + + + zookeeper-1 + 2181 + + + + + + + + + + + + + + + + clickhouse + 9000 + + + + + + + + \ No newline at end of file diff --git a/deploy/docker-swarm/clickhouse-setup/clickhouse-config.xml b/deploy/docker-swarm/clickhouse-setup/clickhouse-config.xml new file mode 100644 index 0000000..dd2b1bd --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/clickhouse-config.xml @@ -0,0 +1,1139 @@ + + + + + + information + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + + 1000M + 10 + + + + + + + + + + + + + + + + + + 8123 + + + 9000 + + + 9004 + + + 9005 + + + + + + + + + + + + 9009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4096 + + + 3 + + + + + false + + + /path/to/ssl_cert_file + /path/to/ssl_key_file + + + false + + + /path/to/ssl_ca_cert_file + + + none + + + 0 + + + -1 + -1 + + + false + + + + + + + + + + + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 100 + + + 0 + + + + 10000 + + + + + + 0.9 + + + 4194304 + + + 0 + + + + + + 8589934592 + + + 5368709120 + + + + 1000 + + + 134217728 + + + 10000 + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + + ` + + + + + + /var/lib/clickhouse/user_files/ + + + + + + + + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + + + default + + + + + + + + + + + + default + + + + + + + + + true + + + false + + ' | sed -e 's|.*>\(.*\)<.*|\1|') + wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge_$PKG_VER-1_all.deb + apt install --no-install-recommends -f ./clickhouse-jdbc-bridge_$PKG_VER-1_all.deb + clickhouse-jdbc-bridge & + + * [CentOS/RHEL] + export MVN_URL=https://repo1.maven.org/maven2/ru/yandex/clickhouse/clickhouse-jdbc-bridge + export PKG_VER=$(curl -sL $MVN_URL/maven-metadata.xml | grep '' | sed -e 's|.*>\(.*\)<.*|\1|') + wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm + yum localinstall -y clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm + clickhouse-jdbc-bridge & + + Please refer to https://github.com/ClickHouse/clickhouse-jdbc-bridge#usage for more information. + ]]> + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + + + + system +
Variables Description Example
REPONAME Provide the DockerHub user/organisation name of the image. signoz
DOCKERHUB_USERNAME Docker hub username signoz
DOCKERHUB_TOKEN Docker hub password/token with push permission ****
SONAR_TOKEN SonarCloud token ****
query_log
+ + toYYYYMM(event_date) + + + + + + 7500 + + + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + query_views_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + system + metric_log
+ 7500 + 1000 +
+ + + + system + asynchronous_metric_log
+ + 7000 +
+ + + + + + engine MergeTree + partition by toYYYYMM(finish_date) + order by (finish_date, finish_time_us, trace_id) + + system + opentelemetry_span_log
+ 7500 +
+ + + + + system + crash_log
+ + + 1000 +
+ + + + + + + system + processors_profile_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + + + + + + *_dictionary.xml + + + *_function.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + + + hide encrypt/decrypt arguments + ((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\) + + \1(???) + + + + + + + + + + false + + false + + + https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277 + + + + + + + + + + + 268435456 + true + + diff --git a/deploy/docker-swarm/clickhouse-setup/clickhouse-storage.xml b/deploy/docker-swarm/clickhouse-setup/clickhouse-storage.xml new file mode 100644 index 0000000..54ec497 --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/clickhouse-storage.xml @@ -0,0 +1,41 @@ + + + + + + 10485760 + + + s3 + + https://BUCKET-NAME.s3-REGION-NAME.amazonaws.com/data/ + ACCESS-KEY-ID + SECRET-ACCESS-KEY + + + + + + + + + + + default + + + s3 + 0 + + + + + + diff --git a/deploy/docker-swarm/clickhouse-setup/clickhouse-users.xml b/deploy/docker-swarm/clickhouse-setup/clickhouse-users.xml new file mode 100644 index 0000000..f185620 --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/clickhouse-users.xml @@ -0,0 +1,123 @@ + + + + + + + + + + 10000000000 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml new file mode 100644 index 0000000..604c2d3 --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -0,0 +1,287 @@ +version: "3.9" + +x-clickhouse-defaults: &clickhouse-defaults + image: clickhouse/clickhouse-server:24.1.2-alpine + tty: true + deploy: + restart_policy: + condition: on-failure + depends_on: + - zookeeper-1 + # - zookeeper-2 + # - zookeeper-3 + 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" + ] + interval: 30s + timeout: 5s + retries: 3 + ulimits: + nproc: 65535 + nofile: + soft: 262144 + hard: 262144 + +x-db-depend: &db-depend + depends_on: + - clickhouse + - otel-collector-migrator + # - clickhouse-2 + # - clickhouse-3 + + +services: + zookeeper-1: + image: bitnami/zookeeper:3.7.1 + hostname: zookeeper-1 + user: root + ports: + - "2181:2181" + - "2888:2888" + - "3888:3888" + volumes: + - ./data/zookeeper-1:/bitnami/zookeeper + environment: + - ZOO_SERVER_ID=1 + # - ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888 + - ALLOW_ANONYMOUS_LOGIN=yes + - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-2: + # image: bitnami/zookeeper:3.7.0 + # hostname: zookeeper-2 + # user: root + # ports: + # - "2182:2181" + # - "2889:2888" + # - "3889:3888" + # volumes: + # - ./data/zookeeper-2:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=2 + # - ZOO_SERVERS=zookeeper-1:2888:3888,0.0.0.0:2888:3888,zookeeper-3:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-3: + # image: bitnami/zookeeper:3.7.0 + # hostname: zookeeper-3 + # user: root + # ports: + # - "2183:2181" + # - "2890:2888" + # - "3890:3888" + # volumes: + # - ./data/zookeeper-3:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=3 + # - ZOO_SERVERS=zookeeper-1:2888:3888,zookeeper-2:2888:3888,0.0.0.0:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + clickhouse: + <<: *clickhouse-defaults + hostname: clickhouse + # ports: + # - "9000:9000" + # - "8123:8123" + # - "9181:9181" + volumes: + - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + - ./data/clickhouse/:/var/lib/clickhouse/ + + # clickhouse-2: + # <<: *clickhouse-defaults + # hostname: clickhouse-2 + # ports: + # - "9001:9000" + # - "8124:8123" + # - "9182:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-2/:/var/lib/clickhouse/ + + # clickhouse-3: + # <<: *clickhouse-defaults + # hostname: clickhouse-3 + # ports: + # - "9002:9000" + # - "8125:8123" + # - "9183:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-3/:/var/lib/clickhouse/ + + alertmanager: + image: signoz/alertmanager:0.23.5 + volumes: + - ./data/alertmanager:/data + command: + - --queryService.url=http://query-service:8085 + - --storage.path=/data + depends_on: + - query-service + deploy: + restart_policy: + condition: on-failure + + query-service: + image: signoz/query-service:0.41.0 + command: + [ + "-config=/root/config/prometheus.yml", + # "--prefer-delta=true" + ] + # ports: + # - "6060:6060" # pprof port + # - "8080:8080" # query-service port + volumes: + - ./prometheus.yml:/root/config/prometheus.yml + - ../dashboards:/root/config/dashboards + - ./data/signoz/:/var/lib/signoz/ + environment: + - ClickHouseUrl=tcp://clickhouse:9000 + - ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/ + - SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db + - DASHBOARDS_PATH=/root/config/dashboards + - STORAGE=clickhouse + - GODEBUG=netdns=go + - TELEMETRY_ENABLED=true + - DEPLOYMENT_TYPE=docker-swarm + healthcheck: + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "localhost:8080/api/v1/health" + ] + interval: 30s + timeout: 5s + retries: 3 + deploy: + restart_policy: + condition: on-failure + <<: *db-depend + + frontend: + image: signoz/frontend:0.41.0 + deploy: + restart_policy: + condition: on-failure + depends_on: + - alertmanager + - query-service + ports: + - "3301:3301" + volumes: + - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf + + otel-collector: + image: signoz/signoz-otel-collector:0.88.15 + command: + [ + "--config=/etc/otel-collector-config.yaml", + "--manager-config=/etc/manager-config.yaml", + "--feature-gates=-pkg.translator.prometheus.NormalizeName" + ] + user: root # required for reading docker container logs + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml + - /var/lib/docker/containers:/var/lib/docker/containers:ro + environment: + - OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}},dockerswarm.service.name={{.Service.Name}},dockerswarm.task.name={{.Task.Name}} + - DOCKER_MULTI_NODE_CLUSTER=false + - LOW_CARDINAL_EXCEPTION_GROUPING=false + ports: + # - "1777:1777" # pprof extension + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + # - "8888:8888" # OtelCollector internal metrics + # - "8889:8889" # signoz spanmetrics exposed by the agent + # - "9411:9411" # Zipkin port + # - "13133:13133" # Health check extension + # - "14250:14250" # Jaeger gRPC + # - "14268:14268" # Jaeger thrift HTTP + # - "55678:55678" # OpenCensus receiver + # - "55679:55679" # zPages extension + deploy: + mode: global + restart_policy: + condition: on-failure + depends_on: + - clickhouse + - otel-collector-migrator + - query-service + + otel-collector-migrator: + image: signoz/signoz-schema-migrator:0.88.15 + deploy: + restart_policy: + condition: on-failure + delay: 5s + command: + - "--dsn=tcp://clickhouse:9000" + depends_on: + - clickhouse + # - clickhouse-2 + # - clickhouse-3 + + logspout: + image: "gliderlabs/logspout:v3.2.14" + volumes: + - /etc/hostname:/etc/host_hostname:ro + - /var/run/docker.sock:/var/run/docker.sock + command: syslog+tcp://otel-collector:2255 + depends_on: + - otel-collector + deploy: + mode: global + restart_policy: + condition: on-failure + + hotrod: + image: jaegertracing/example-hotrod:1.30 + command: [ "all" ] + environment: + - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces + logging: + options: + max-size: 50m + max-file: "3" + + load-hotrod: + image: "signoz/locust:1.2.3" + hostname: load-hotrod + environment: + ATTACKED_HOST: http://hotrod:8080 + LOCUST_MODE: standalone + NO_PROXY: standalone + TASK_DELAY_FROM: 5 + TASK_DELAY_TO: 30 + QUIET_MODE: "${QUIET_MODE:-false}" + LOCUST_OPTS: "--headless -u 10 -r 1" + volumes: + - ../common/locust-scripts:/locust diff --git a/deploy/docker-swarm/clickhouse-setup/docker-entrypoint-initdb.d/init-db.sql b/deploy/docker-swarm/clickhouse-setup/docker-entrypoint-initdb.d/init-db.sql new file mode 100644 index 0000000..f71983c --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/docker-entrypoint-initdb.d/init-db.sql @@ -0,0 +1,31 @@ +CREATE TABLE IF NOT EXISTS signoz_index ( + timestamp DateTime64(9) CODEC(Delta, ZSTD(1)), + traceID String CODEC(ZSTD(1)), + spanID String CODEC(ZSTD(1)), + parentSpanID String CODEC(ZSTD(1)), + serviceName LowCardinality(String) CODEC(ZSTD(1)), + name LowCardinality(String) CODEC(ZSTD(1)), + kind Int32 CODEC(ZSTD(1)), + durationNano UInt64 CODEC(ZSTD(1)), + tags Array(String) CODEC(ZSTD(1)), + tagsKeys Array(String) CODEC(ZSTD(1)), + tagsValues Array(String) CODEC(ZSTD(1)), + statusCode Int64 CODEC(ZSTD(1)), + references String CODEC(ZSTD(1)), + externalHttpMethod Nullable(String) CODEC(ZSTD(1)), + externalHttpUrl Nullable(String) CODEC(ZSTD(1)), + component Nullable(String) CODEC(ZSTD(1)), + dbSystem Nullable(String) CODEC(ZSTD(1)), + dbName Nullable(String) CODEC(ZSTD(1)), + dbOperation Nullable(String) CODEC(ZSTD(1)), + peerService Nullable(String) CODEC(ZSTD(1)), + INDEX idx_traceID traceID TYPE bloom_filter GRANULARITY 4, + INDEX idx_service serviceName TYPE bloom_filter GRANULARITY 4, + INDEX idx_name name TYPE bloom_filter GRANULARITY 4, + INDEX idx_kind kind TYPE minmax GRANULARITY 4, + INDEX idx_tagsKeys tagsKeys TYPE bloom_filter(0.01) GRANULARITY 64, + INDEX idx_tagsValues tagsValues TYPE bloom_filter(0.01) GRANULARITY 64, + INDEX idx_duration durationNano TYPE minmax GRANULARITY 1 +) ENGINE MergeTree() +PARTITION BY toDate(timestamp) +ORDER BY (serviceName, -toUnixTimestamp(timestamp)) \ No newline at end of file diff --git a/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml b/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml new file mode 100644 index 0000000..f8a710d --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml @@ -0,0 +1,161 @@ +receivers: + tcplog/docker: + listen_address: "0.0.0.0:2255" + operators: + - type: regex_parser + regex: '^<([0-9]+)>[0-9]+ (?P[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?) (?P\S+) (?P\S+) [0-9]+ - -( (?P.*))?' + timestamp: + parse_from: attributes.timestamp + layout: '%Y-%m-%dT%H:%M:%S.%LZ' + - type: move + from: attributes["body"] + to: body + - type: remove + field: attributes.timestamp + # please remove names from below if you want to collect logs from them + - type: filter + id: signoz_logs_filter + expr: 'attributes.container_name matches "^signoz_(logspout|frontend|alertmanager|query-service|otel-collector|clickhouse|zookeeper)"' + opencensus: + endpoint: 0.0.0.0:55678 + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + jaeger: + protocols: + grpc: + endpoint: 0.0.0.0:14250 + thrift_http: + endpoint: 0.0.0.0:14268 + # thrift_compact: + # endpoint: 0.0.0.0:6831 + # thrift_binary: + # endpoint: 0.0.0.0:6832 + hostmetrics: + collection_interval: 30s + scrapers: + cpu: {} + load: {} + memory: {} + disk: {} + filesystem: {} + network: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + # otel-collector internal metrics + - job_name: otel-collector + static_configs: + - targets: + - localhost:8888 + labels: + job_name: otel-collector + +processors: + batch: + send_batch_size: 10000 + send_batch_max_size: 11000 + timeout: 10s + resourcedetection: + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + detectors: [env, system] # include ec2 for AWS, gcp for GCP and azure for Azure. + timeout: 2s + signozspanmetrics/cumulative: + metrics_exporter: clickhousemetricswrite + latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ] + dimensions_cache_size: 100000 + dimensions: + - name: service.namespace + default: default + - name: deployment.environment + default: default + # This is added to ensure the uniqueness of the timeseries + # Otherwise, identical timeseries produced by multiple replicas of + # collectors result in incorrect APM metrics + - name: 'signoz.collector.id' + # memory_limiter: + # # 80% of maximum memory up to 2G + # limit_mib: 1500 + # # 25% of limit up to 2G + # spike_limit_mib: 512 + # check_interval: 5s + # + # # 50% of the maximum memory + # limit_percentage: 50 + # # 20% of max memory usage spike expected + # spike_limit_percentage: 20 + # queued_retry: + # num_workers: 4 + # queue_size: 100 + # retry_on_failure: true + signozspanmetrics/delta: + metrics_exporter: clickhousemetricswrite + latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ] + dimensions_cache_size: 100000 + aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA + enable_exp_histogram: true + dimensions: + - name: service.namespace + default: default + - name: deployment.environment + default: default + # This is added to ensure the uniqueness of the timeseries + # Otherwise, identical timeseries produced by multiple replicas of + # collectors result in incorrect APM metrics + - name: signoz.collector.id + +exporters: + clickhousetraces: + datasource: tcp://clickhouse:9000/?database=signoz_traces + docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER} + low_cardinal_exception_grouping: ${LOW_CARDINAL_EXCEPTION_GROUPING} + clickhousemetricswrite: + endpoint: tcp://clickhouse:9000/?database=signoz_metrics + resource_to_telemetry_conversion: + enabled: true + clickhousemetricswrite/prometheus: + endpoint: tcp://clickhouse:9000/?database=signoz_metrics + # logging: {} + clickhouselogsexporter: + dsn: tcp://clickhouse:9000/ + docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER} + timeout: 10s +extensions: + health_check: + endpoint: 0.0.0.0:13133 + zpages: + endpoint: 0.0.0.0:55679 + pprof: + endpoint: 0.0.0.0:1777 + +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages, pprof] + pipelines: + traces: + receivers: [jaeger, otlp] + processors: [signozspanmetrics/cumulative, signozspanmetrics/delta, batch] + exporters: [clickhousetraces] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [clickhousemetricswrite] + metrics/generic: + receivers: [hostmetrics] + processors: [resourcedetection, batch] + exporters: [clickhousemetricswrite] + metrics/prometheus: + receivers: [prometheus] + processors: [batch] + exporters: [clickhousemetricswrite/prometheus] + logs: + receivers: [otlp, tcplog/docker] + processors: [batch] + exporters: [clickhouselogsexporter] diff --git a/deploy/docker-swarm/clickhouse-setup/otel-collector-opamp-config.yaml b/deploy/docker-swarm/clickhouse-setup/otel-collector-opamp-config.yaml new file mode 100644 index 0000000..e408b55 --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/otel-collector-opamp-config.yaml @@ -0,0 +1 @@ +server_endpoint: ws://query-service:4320/v1/opamp diff --git a/deploy/docker-swarm/clickhouse-setup/prometheus.yml b/deploy/docker-swarm/clickhouse-setup/prometheus.yml new file mode 100644 index 0000000..6a796ea --- /dev/null +++ b/deploy/docker-swarm/clickhouse-setup/prometheus.yml @@ -0,0 +1,25 @@ +# my global config +global: + scrape_interval: 5s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. + # scrape_timeout is set to the global default (10s). + +# Alertmanager configuration +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:9093 + +# Load rules once and periodically evaluate them according to the global 'evaluation_interval'. +rule_files: + # - "first_rules.yml" + # - "second_rules.yml" + - 'alerts.yml' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: [] + +remote_read: + - url: tcp://clickhouse:9000/?database=signoz_metrics diff --git a/deploy/docker-swarm/common/locust-scripts/locustfile.py b/deploy/docker-swarm/common/locust-scripts/locustfile.py new file mode 100644 index 0000000..0b51820 --- /dev/null +++ b/deploy/docker-swarm/common/locust-scripts/locustfile.py @@ -0,0 +1,16 @@ +from locust import HttpUser, task, between +class UserTasks(HttpUser): + wait_time = between(5, 15) + + @task + def rachel(self): + self.client.get("/dispatch?customer=123&nonse=0.6308392664170006") + @task + def trom(self): + self.client.get("/dispatch?customer=392&nonse=0.015296363321630757") + @task + def japanese(self): + self.client.get("/dispatch?customer=731&nonse=0.8022286220408668") + @task + def coffee(self): + self.client.get("/dispatch?customer=567&nonse=0.0022220379420636593") diff --git a/deploy/docker-swarm/common/nginx-config.conf b/deploy/docker-swarm/common/nginx-config.conf new file mode 100644 index 0000000..f7943e2 --- /dev/null +++ b/deploy/docker-swarm/common/nginx-config.conf @@ -0,0 +1,51 @@ +server { + listen 3301; + server_name _; + + gzip on; + gzip_static on; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_proxied any; + gzip_vary on; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + + # to handle uri issue 414 from nginx + client_max_body_size 24M; + large_client_header_buffers 8 128k; + + location / { + if ( $uri = '/index.html' ) { + add_header Cache-Control no-store always; + } + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + location ~ ^/api/(v1|v3)/logs/(tail|livetail){ + proxy_pass http://query-service:8080; + proxy_http_version 1.1; + + # connection will be closed if no data is read for 600s between successive read operations + proxy_read_timeout 600s; + + # dont buffer the data send it directly to client. + proxy_buffering off; + proxy_cache off; + } + + location /api { + proxy_pass http://query-service:8080/api; + # connection will be closed if no data is read for 600s between successive read operations + proxy_read_timeout 600s; + } + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file diff --git a/deploy/docker-swarm/dashboards/.gitkeep b/deploy/docker-swarm/dashboards/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/deploy/docker/clickhouse-setup/alertmanager.yml b/deploy/docker/clickhouse-setup/alertmanager.yml new file mode 100644 index 0000000..d69357f --- /dev/null +++ b/deploy/docker/clickhouse-setup/alertmanager.yml @@ -0,0 +1,35 @@ +global: + resolve_timeout: 1m + slack_api_url: 'https://hooks.slack.com/services/xxx' + +route: + receiver: 'slack-notifications' + +receivers: +- name: 'slack-notifications' + slack_configs: + - channel: '#alerts' + send_resolved: true + icon_url: https://avatars3.githubusercontent.com/u/3380462 + title: |- + [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} + {{- if gt (len .CommonLabels) (len .GroupLabels) -}} + {{" "}}( + {{- with .CommonLabels.Remove .GroupLabels.Names }} + {{- range $index, $label := .SortedPairs -}} + {{ if $index }}, {{ end }} + {{- $label.Name }}="{{ $label.Value -}}" + {{- end }} + {{- end -}} + ) + {{- end }} + text: >- + {{ range .Alerts -}} + *Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - `{{ .Labels.severity }}`{{ end }} + + *Description:* {{ .Annotations.description }} + + *Details:* + {{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}` + {{ end }} + {{ end }} \ No newline at end of file diff --git a/deploy/docker/clickhouse-setup/alerts.yml b/deploy/docker/clickhouse-setup/alerts.yml new file mode 100644 index 0000000..810a207 --- /dev/null +++ b/deploy/docker/clickhouse-setup/alerts.yml @@ -0,0 +1,11 @@ +groups: +- name: ExampleCPULoadGroup + rules: + - alert: HighCpuLoad + expr: system_cpu_load_average_1m > 0.1 + for: 0m + labels: + severity: warning + annotations: + summary: High CPU load + description: "CPU load is > 0.1\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" diff --git a/deploy/docker/clickhouse-setup/clickhouse-cluster.xml b/deploy/docker/clickhouse-setup/clickhouse-cluster.xml new file mode 100644 index 0000000..0e3ddcd --- /dev/null +++ b/deploy/docker/clickhouse-setup/clickhouse-cluster.xml @@ -0,0 +1,75 @@ + + + + + + zookeeper-1 + 2181 + + + + + + + + + + + + + + + + clickhouse + 9000 + + + + + + + + \ No newline at end of file diff --git a/deploy/docker/clickhouse-setup/clickhouse-config.xml b/deploy/docker/clickhouse-setup/clickhouse-config.xml new file mode 100644 index 0000000..f8213b6 --- /dev/null +++ b/deploy/docker/clickhouse-setup/clickhouse-config.xml @@ -0,0 +1,1140 @@ + + + + + + information + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + + 1000M + 10 + + + + + + + + + + + + + + + + + + 8123 + + + 9000 + + + 9004 + + + 9005 + + + + + + + + + + + + 9009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4096 + + + 3 + + + + + false + + + /path/to/ssl_cert_file + /path/to/ssl_key_file + + + false + + + /path/to/ssl_ca_cert_file + + + none + + + 0 + + + -1 + -1 + + + false + + + + + + + + + + + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 100 + + + 0 + + + + 10000 + + + + + + 0.9 + + + 4194304 + + + 0 + + + + + + 8589934592 + + + 5368709120 + + + + 1000 + + + 134217728 + + + 10000 + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + + ` + + + + + + /var/lib/clickhouse/user_files/ + + + + + + + + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + + + default + + + + + + + + + + + + default + + + + + + + + + true + + + false + + ' | sed -e 's|.*>\(.*\)<.*|\1|') + wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge_$PKG_VER-1_all.deb + apt install --no-install-recommends -f ./clickhouse-jdbc-bridge_$PKG_VER-1_all.deb + clickhouse-jdbc-bridge & + + * [CentOS/RHEL] + export MVN_URL=https://repo1.maven.org/maven2/ru/yandex/clickhouse/clickhouse-jdbc-bridge + export PKG_VER=$(curl -sL $MVN_URL/maven-metadata.xml | grep '' | sed -e 's|.*>\(.*\)<.*|\1|') + wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm + yum localinstall -y clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm + clickhouse-jdbc-bridge & + + Please refer to https://github.com/ClickHouse/clickhouse-jdbc-bridge#usage for more information. + ]]> + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + + + + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + query_views_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + system + metric_log
+ 7500 + 1000 +
+ + + + system + asynchronous_metric_log
+ + 7000 +
+ + + + + + engine MergeTree + partition by toYYYYMM(finish_date) + order by (finish_date, finish_time_us, trace_id) + + system + opentelemetry_span_log
+ 7500 +
+ + + + + system + crash_log
+ + + 1000 +
+ + + + + + + system + processors_profile_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + + + + + + *_dictionary.xml + + + *function.xml + /var/lib/clickhouse/user_scripts/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + + + hide encrypt/decrypt arguments + ((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\) + + \1(???) + + + + + + + + + + false + + false + + + https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277 + + + + + + + + + + + 268435456 + true + +
diff --git a/deploy/docker/clickhouse-setup/clickhouse-storage.xml b/deploy/docker/clickhouse-setup/clickhouse-storage.xml new file mode 100644 index 0000000..54ec497 --- /dev/null +++ b/deploy/docker/clickhouse-setup/clickhouse-storage.xml @@ -0,0 +1,41 @@ + + + + + + 10485760 + + + s3 + + https://BUCKET-NAME.s3-REGION-NAME.amazonaws.com/data/ + ACCESS-KEY-ID + SECRET-ACCESS-KEY + + + + + + + + + + + default + + + s3 + 0 + + + + + + diff --git a/deploy/docker/clickhouse-setup/clickhouse-users.xml b/deploy/docker/clickhouse-setup/clickhouse-users.xml new file mode 100644 index 0000000..f185620 --- /dev/null +++ b/deploy/docker/clickhouse-setup/clickhouse-users.xml @@ -0,0 +1,123 @@ + + + + + + + + + + 10000000000 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/deploy/docker/clickhouse-setup/custom-function.xml b/deploy/docker/clickhouse-setup/custom-function.xml new file mode 100644 index 0000000..b2b3f91 --- /dev/null +++ b/deploy/docker/clickhouse-setup/custom-function.xml @@ -0,0 +1,21 @@ + + + executable + histogramQuantile + Float64 + + Array(Float64) + buckets + + + Array(Float64) + counts + + + Float64 + quantile + + CSV + ./histogramQuantile + + diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml new file mode 100644 index 0000000..f595b86 --- /dev/null +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -0,0 +1,154 @@ +version: "2.4" + +services: + zookeeper-1: + image: bitnami/zookeeper:3.7.1 + container_name: signoz-zookeeper-1 + hostname: zookeeper-1 + user: root + ports: + - "2181:2181" + - "2888:2888" + - "3888:3888" + volumes: + - ./data/zookeeper-1:/bitnami/zookeeper + environment: + - ZOO_SERVER_ID=1 + # - ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888 + - ALLOW_ANONYMOUS_LOGIN=yes + - ZOO_AUTOPURGE_INTERVAL=1 + + clickhouse: + image: clickhouse/clickhouse-server:24.1.2-alpine + container_name: signoz-clickhouse + # ports: + # - "9000:9000" + # - "8123:8123" + tty: true + volumes: + - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + - ./data/clickhouse/:/var/lib/clickhouse/ + - ./user_scripts:/var/lib/clickhouse/user_scripts/ + 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" + ] + interval: 30s + timeout: 5s + retries: 3 + + alertmanager: + container_name: signoz-alertmanager + image: signoz/alertmanager:0.23.5 + volumes: + - ./data/alertmanager:/data + depends_on: + query-service: + condition: service_healthy + restart: on-failure + command: + - --queryService.url=http://query-service:8085 + - --storage.path=/data + + otel-collector-migrator: + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.15} + container_name: otel-migrator + command: + - "--dsn=tcp://clickhouse:9000" + depends_on: + clickhouse: + condition: service_healthy + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + + # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` + otel-collector: + container_name: signoz-otel-collector + image: signoz/signoz-otel-collector:0.88.15 + command: + [ + "--config=/etc/otel-collector-config.yaml", + "--manager-config=/etc/manager-config.yaml", + "--copy-path=/var/tmp/collector-config.yaml", + "--feature-gates=-pkg.translator.prometheus.NormalizeName" + ] + # user: root # required for reading docker container logs + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml + environment: + - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux + ports: + # - "1777:1777" # pprof extension + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + # - "8888:8888" # OtelCollector internal metrics + # - "8889:8889" # signoz spanmetrics exposed by the agent + # - "9411:9411" # Zipkin port + # - "13133:13133" # health check extension + # - "14250:14250" # Jaeger gRPC + # - "14268:14268" # Jaeger thrift HTTP + # - "55678:55678" # OpenCensus receiver + # - "55679:55679" # zPages extension + restart: on-failure + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + query-service: + condition: service_healthy + + logspout: + image: "gliderlabs/logspout:v3.2.14" + container_name: signoz-logspout + volumes: + - /etc/hostname:/etc/host_hostname:ro + - /var/run/docker.sock:/var/run/docker.sock + command: syslog+tcp://otel-collector:2255 + depends_on: + - otel-collector + restart: on-failure + + hotrod: + image: jaegertracing/example-hotrod:1.30 + container_name: hotrod + logging: + options: + max-size: 50m + max-file: "3" + command: [ "all" ] + environment: + - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces + + load-hotrod: + image: "signoz/locust:1.2.3" + container_name: load-hotrod + hostname: load-hotrod + environment: + ATTACKED_HOST: http://hotrod:8080 + LOCUST_MODE: standalone + NO_PROXY: standalone + TASK_DELAY_FROM: 5 + TASK_DELAY_TO: 30 + QUIET_MODE: "${QUIET_MODE:-false}" + LOCUST_OPTS: "--headless -u 10 -r 1" + volumes: + - ../common/locust-scripts:/locust diff --git a/deploy/docker/clickhouse-setup/docker-compose-local.yaml b/deploy/docker/clickhouse-setup/docker-compose-local.yaml new file mode 100644 index 0000000..248c7bf --- /dev/null +++ b/deploy/docker/clickhouse-setup/docker-compose-local.yaml @@ -0,0 +1,67 @@ +version: "2.4" + +services: + query-service: + hostname: query-service + build: + context: "../../../" + dockerfile: "./pkg/query-service/Dockerfile" + args: + LDFLAGS: "" + TARGETPLATFORM: "${GOOS}/${GOARCH}" + container_name: signoz-query-service + environment: + - ClickHouseUrl=tcp://clickhouse:9000 + - ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/ + - SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db + - DASHBOARDS_PATH=/root/config/dashboards + - STORAGE=clickhouse + - GODEBUG=netdns=go + - TELEMETRY_ENABLED=true + volumes: + - ./prometheus.yml:/root/config/prometheus.yml + - ../dashboards:/root/config/dashboards + - ./data/signoz/:/var/lib/signoz/ + command: + [ + "-config=/root/config/prometheus.yml", + # "--prefer-delta=true" + ] + ports: + - "6060:6060" + - "8080:8080" + restart: on-failure + healthcheck: + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "localhost:8080/api/v1/health" + ] + interval: 30s + timeout: 5s + retries: 3 + depends_on: + clickhouse: + condition: service_healthy + + frontend: + build: + context: "../../../frontend" + dockerfile: "./Dockerfile" + args: + TARGETOS: "${GOOS}" + TARGETPLATFORM: "${GOARCH}" + container_name: signoz-frontend + environment: + - FRONTEND_API_ENDPOINT=http://query-service:8080 + restart: on-failure + depends_on: + - alertmanager + - query-service + ports: + - "3301:3301" + volumes: + - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml new file mode 100644 index 0000000..217135c --- /dev/null +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -0,0 +1,306 @@ +version: "2.4" + +x-clickhouse-defaults: &clickhouse-defaults + restart: on-failure + # addding non LTS version due to this fix https://github.com/ClickHouse/ClickHouse/commit/32caf8716352f45c1b617274c7508c86b7d1afab + image: clickhouse/clickhouse-server:24.1.2-alpine + tty: true + depends_on: + - zookeeper-1 + # - zookeeper-2 + # - zookeeper-3 + 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" + ] + interval: 30s + timeout: 5s + retries: 3 + ulimits: + nproc: 65535 + nofile: + soft: 262144 + hard: 262144 + +x-db-depend: &db-depend + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + +services: + + zookeeper-1: + image: bitnami/zookeeper:3.7.1 + container_name: signoz-zookeeper-1 + hostname: zookeeper-1 + user: root + ports: + - "2181:2181" + - "2888:2888" + - "3888:3888" + volumes: + - ./data/zookeeper-1:/bitnami/zookeeper + environment: + - ZOO_SERVER_ID=1 + # - ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888 + - ALLOW_ANONYMOUS_LOGIN=yes + - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-2: + # image: bitnami/zookeeper:3.7.0 + # container_name: signoz-zookeeper-2 + # hostname: zookeeper-2 + # user: root + # ports: + # - "2182:2181" + # - "2889:2888" + # - "3889:3888" + # volumes: + # - ./data/zookeeper-2:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=2 + # - ZOO_SERVERS=zookeeper-1:2888:3888,0.0.0.0:2888:3888,zookeeper-3:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-3: + # image: bitnami/zookeeper:3.7.0 + # container_name: signoz-zookeeper-3 + # hostname: zookeeper-3 + # user: root + # ports: + # - "2183:2181" + # - "2890:2888" + # - "3890:3888" + # volumes: + # - ./data/zookeeper-3:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=3 + # - ZOO_SERVERS=zookeeper-1:2888:3888,zookeeper-2:2888:3888,0.0.0.0:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + clickhouse: + <<: *clickhouse-defaults + container_name: signoz-clickhouse + hostname: clickhouse + ports: + - "9000:9000" + - "8123:8123" + - "9181:9181" + volumes: + - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + - ./data/clickhouse/:/var/lib/clickhouse/ + - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + # clickhouse-2: + # <<: *clickhouse-defaults + # container_name: signoz-clickhouse-2 + # hostname: clickhouse-2 + # ports: + # - "9001:9000" + # - "8124:8123" + # - "9182:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-2/:/var/lib/clickhouse/ + # - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + + # clickhouse-3: + # <<: *clickhouse-defaults + # container_name: signoz-clickhouse-3 + # hostname: clickhouse-3 + # ports: + # - "9002:9000" + # - "8125:8123" + # - "9183:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-3/:/var/lib/clickhouse/ + # - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + alertmanager: + image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.5} + container_name: signoz-alertmanager + volumes: + - ./data/alertmanager:/data + depends_on: + query-service: + condition: service_healthy + restart: on-failure + command: + - --queryService.url=http://query-service:8085 + - --storage.path=/data + + # 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:${DOCKER_TAG:-0.41.0} + container_name: signoz-query-service + command: + [ + "-config=/root/config/prometheus.yml", + # "--prefer-delta=true" + ] + # ports: + # - "6060:6060" # pprof port + # - "8080:8080" # query-service port + volumes: + - ./prometheus.yml:/root/config/prometheus.yml + - ../dashboards:/root/config/dashboards + - ./data/signoz/:/var/lib/signoz/ + environment: + - ClickHouseUrl=tcp://clickhouse:9000 + - ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/ + - SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db + - DASHBOARDS_PATH=/root/config/dashboards + - STORAGE=clickhouse + - GODEBUG=netdns=go + - TELEMETRY_ENABLED=true + - DEPLOYMENT_TYPE=docker-standalone-amd + restart: on-failure + healthcheck: + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "localhost:8080/api/v1/health" + ] + interval: 30s + timeout: 5s + retries: 3 + <<: *db-depend + + frontend: + image: signoz/frontend:${DOCKER_TAG:-0.41.0} + container_name: signoz-frontend + restart: on-failure + depends_on: + - alertmanager + - query-service + ports: + - "3301:3301" + volumes: + - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf + + otel-collector-migrator: + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.15} + container_name: otel-migrator + command: + - "--dsn=tcp://clickhouse:9000" + depends_on: + clickhouse: + condition: service_healthy + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + + + otel-collector: + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.15} + container_name: signoz-otel-collector + command: + [ + "--config=/etc/otel-collector-config.yaml", + "--manager-config=/etc/manager-config.yaml", + "--copy-path=/var/tmp/collector-config.yaml", + "--feature-gates=-pkg.translator.prometheus.NormalizeName" + ] + user: root # required for reading docker container logs + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml + - /var/lib/docker/containers:/var/lib/docker/containers:ro + environment: + - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux + - DOCKER_MULTI_NODE_CLUSTER=false + - LOW_CARDINAL_EXCEPTION_GROUPING=false + ports: + # - "1777:1777" # pprof extension + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + # - "8888:8888" # OtelCollector internal metrics + # - "8889:8889" # signoz spanmetrics exposed by the agent + # - "9411:9411" # Zipkin port + # - "13133:13133" # health check extension + # - "14250:14250" # Jaeger gRPC + # - "14268:14268" # Jaeger thrift HTTP + # - "55678:55678" # OpenCensus receiver + # - "55679:55679" # zPages extension + restart: on-failure + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + query-service: + condition: service_healthy + + logspout: + image: "gliderlabs/logspout:v3.2.14" + container_name: signoz-logspout + volumes: + - /etc/hostname:/etc/host_hostname:ro + - /var/run/docker.sock:/var/run/docker.sock + command: syslog+tcp://otel-collector:2255 + depends_on: + - otel-collector + restart: on-failure + + hotrod: + image: jaegertracing/example-hotrod:1.30 + container_name: hotrod + logging: + options: + max-size: 50m + max-file: "3" + command: [ "all" ] + environment: + - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces + + load-hotrod: + image: "signoz/locust:1.2.3" + container_name: load-hotrod + hostname: load-hotrod + environment: + ATTACKED_HOST: http://hotrod:8080 + LOCUST_MODE: standalone + NO_PROXY: standalone + TASK_DELAY_FROM: 5 + TASK_DELAY_TO: 30 + QUIET_MODE: "${QUIET_MODE:-false}" + LOCUST_OPTS: "--headless -u 10 -r 1" + volumes: + - ../common/locust-scripts:/locust diff --git a/deploy/docker/clickhouse-setup/otel-collector-config.yaml b/deploy/docker/clickhouse-setup/otel-collector-config.yaml new file mode 100644 index 0000000..d382a25 --- /dev/null +++ b/deploy/docker/clickhouse-setup/otel-collector-config.yaml @@ -0,0 +1,169 @@ +receivers: + tcplog/docker: + listen_address: "0.0.0.0:2255" + operators: + - type: regex_parser + regex: '^<([0-9]+)>[0-9]+ (?P[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?) (?P\S+) (?P\S+) [0-9]+ - -( (?P.*))?' + timestamp: + parse_from: attributes.timestamp + layout: '%Y-%m-%dT%H:%M:%S.%LZ' + - type: move + from: attributes["body"] + to: body + - type: remove + field: attributes.timestamp + # please remove names from below if you want to collect logs from them + - type: filter + id: signoz_logs_filter + expr: 'attributes.container_name matches "^signoz-(logspout|frontend|alertmanager|query-service|otel-collector|clickhouse|zookeeper)"' + opencensus: + endpoint: 0.0.0.0:55678 + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + jaeger: + protocols: + grpc: + endpoint: 0.0.0.0:14250 + thrift_http: + endpoint: 0.0.0.0:14268 + # thrift_compact: + # endpoint: 0.0.0.0:6831 + # thrift_binary: + # endpoint: 0.0.0.0:6832 + hostmetrics: + collection_interval: 30s + scrapers: + cpu: {} + load: {} + memory: {} + disk: {} + filesystem: {} + network: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + # otel-collector internal metrics + - job_name: otel-collector + static_configs: + - targets: + - localhost:8888 + labels: + job_name: otel-collector + + +processors: + batch: + send_batch_size: 10000 + send_batch_max_size: 11000 + timeout: 10s + signozspanmetrics/cumulative: + metrics_exporter: clickhousemetricswrite + metrics_flush_interval: 60s + latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ] + dimensions_cache_size: 100000 + dimensions: + - name: service.namespace + default: default + - name: deployment.environment + default: default + # This is added to ensure the uniqueness of the timeseries + # Otherwise, identical timeseries produced by multiple replicas of + # collectors result in incorrect APM metrics + - name: 'signoz.collector.id' + # memory_limiter: + # # 80% of maximum memory up to 2G + # limit_mib: 1500 + # # 25% of limit up to 2G + # spike_limit_mib: 512 + # check_interval: 5s + # + # # 50% of the maximum memory + # limit_percentage: 50 + # # 20% of max memory usage spike expected + # spike_limit_percentage: 20 + # queued_retry: + # num_workers: 4 + # queue_size: 100 + # retry_on_failure: true + resourcedetection: + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + detectors: [env, system] # include ec2 for AWS, gcp for GCP and azure for Azure. + timeout: 2s + signozspanmetrics/delta: + metrics_exporter: clickhousemetricswrite + metrics_flush_interval: 60s + latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ] + dimensions_cache_size: 100000 + aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA + enable_exp_histogram: true + dimensions: + - name: service.namespace + default: default + - name: deployment.environment + default: default + # This is added to ensure the uniqueness of the timeseries + # Otherwise, identical timeseries produced by multiple replicas of + # collectors result in incorrect APM metrics + - name: signoz.collector.id + +extensions: + health_check: + endpoint: 0.0.0.0:13133 + zpages: + endpoint: 0.0.0.0:55679 + pprof: + endpoint: 0.0.0.0:1777 + +exporters: + clickhousetraces: + datasource: tcp://clickhouse:9000/?database=signoz_traces + docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER} + low_cardinal_exception_grouping: ${LOW_CARDINAL_EXCEPTION_GROUPING} + clickhousemetricswrite: + endpoint: tcp://clickhouse:9000/?database=signoz_metrics + resource_to_telemetry_conversion: + enabled: true + clickhousemetricswrite/prometheus: + endpoint: tcp://clickhouse:9000/?database=signoz_metrics + # logging: {} + + clickhouselogsexporter: + dsn: tcp://clickhouse:9000/ + docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER} + timeout: 10s + +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: + - health_check + - zpages + - pprof + pipelines: + traces: + receivers: [jaeger, otlp] + processors: [signozspanmetrics/cumulative, signozspanmetrics/delta, batch] + exporters: [clickhousetraces] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [clickhousemetricswrite] + metrics/generic: + receivers: [hostmetrics] + processors: [resourcedetection, batch] + exporters: [clickhousemetricswrite] + metrics/prometheus: + receivers: [prometheus] + processors: [batch] + exporters: [clickhousemetricswrite/prometheus] + logs: + receivers: [otlp, tcplog/docker] + processors: [batch] + exporters: [clickhouselogsexporter] \ No newline at end of file diff --git a/deploy/docker/clickhouse-setup/otel-collector-opamp-config.yaml b/deploy/docker/clickhouse-setup/otel-collector-opamp-config.yaml new file mode 100644 index 0000000..e408b55 --- /dev/null +++ b/deploy/docker/clickhouse-setup/otel-collector-opamp-config.yaml @@ -0,0 +1 @@ +server_endpoint: ws://query-service:4320/v1/opamp diff --git a/deploy/docker/clickhouse-setup/prometheus.yml b/deploy/docker/clickhouse-setup/prometheus.yml new file mode 100644 index 0000000..6a796ea --- /dev/null +++ b/deploy/docker/clickhouse-setup/prometheus.yml @@ -0,0 +1,25 @@ +# my global config +global: + scrape_interval: 5s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. + # scrape_timeout is set to the global default (10s). + +# Alertmanager configuration +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:9093 + +# Load rules once and periodically evaluate them according to the global 'evaluation_interval'. +rule_files: + # - "first_rules.yml" + # - "second_rules.yml" + - 'alerts.yml' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: [] + +remote_read: + - url: tcp://clickhouse:9000/?database=signoz_metrics diff --git a/deploy/docker/clickhouse-setup/user_scripts/histogramQuantile b/deploy/docker/clickhouse-setup/user_scripts/histogramQuantile new file mode 100755 index 0000000000000000000000000000000000000000..3b77a7b26dcd984e1b770659ad8f1c44f296729e GIT binary patch literal 1849156 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

bdLkrR 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®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


pX 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

    X8!>RxkTlr?8ej_Gp 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

    &vFqTG|p_z;94qB4#d(cv%_&jg*JUbr zz0oc&Ur8)$&O#}jFFbpEa^qsR(jM(F#^PCJjdiqlxZA=$cARr6phG;0&#r`G#n+dk zJRVkjZ8<9J_D~<3P=@OANFXWio-SV~#2imAueQW+ll5ih!gxA!_I!?}$`EIZA$DF1 zpB|U7735657<0w;@v%_Y7hw0jctt_;JeEzVPla|xU znhc4{kCv$t3Y#-b87}NzV*2*la;!10{JPA2!;$b}9xW%8;UYeX(PctL7D*^Ou^eSY zIVA%Yn;?pV+zDlEMSB7PFUNt=GMcSa8f*)7#CRgKrc;8o zaZtA$mY^QCj@7{g?d~Cevn)+earrHFVs=e?V;&!;J}qV(MFyNVT5}%4e10DIcAh?I zYLgwjkQFNddimp=b_o_rHfon(VJCOF-3SY1{;}pelx(5#C8!c1cre*z=&oeNZuuys zHP0a1ZZGGG%EE%cGP#f28T;J}gJEO&MSWuD=65gk1tJ0N+EKe&elMH0l%?0dBxvMk zcBA=yfp2fk7vX8n%1uyqzsNpTB3lnTyj`k*goYCRgB)*5Y#o+V)EX4nURc`uz;3Gu z_pN;>M53JOSy2i(M0zTDZB$ZgJKN||_PP>*mr!rmNx3P@!P~oo0dK6m%h&CRwRd~n z;iwGd(NTq)N)_=&6`E2`9&gm$-sNRyqgRrQB|G!LC6Ty7Y1IyaTSJ-Sw z33!(n+4CrVHMOL%fOmF8oU*! zXKctF#+RRP9f>b(*)7CKyO|ZA6Asa^yPc=EawPUpx6!!e+FUc@>+CYZtMgpOhG--Sa*P9qqUCq;2T1m3RvFdiN+%MvJUo;wbJ)Q zV?BXIf#4DjbC$PxqOm1iJhZL#o;MWi?)HXRUW+w@nHW81#1xM(T|JzcSv^BwA2(^~ zLQsE%1y{V6;K6`5(iODYunfcK9!8abLt)OoE!z@x*kfjrikl0{5Lb0VK?QKg$yH@2; zPsq)RuP#ILcqki`zR+S`q`Z7asRqVw=^x65q`9uO)|V_qw={oJncZ>65@QDifcMJ$ zn%BI>=C`%^CTxR27GFtw8RHMO`J!IV(X4$*!G*dbF+Y3BV`VK8wuiBgmSIC~jt0Lj z8=oO}kL-A!TwcrCQ2n^H^FpDJsb6Wd2!%p1kCBJJR?apQipg?DnHDL|ASxpH;p^oU z4QDBhoQk8AqQXW@yIq}7sIf>pkDYa8y&ExJ{yn8khDbnmcdV5dbBlU8U;q2#rKU1B zcpopbel+a%bxRugJu49)@uFdzM`W%j4TDSE0W=o5pS9BAic8w%4+-2?CcP1E*H~TF zfqB0*vs%J;crcNS_>N%LQF4rqn5QSy!E>gS1PRnOPp`E)xFhCW+AZ5WRws4fxty?4 znkFa@E-OY6wAymxsA}GxfWcO!T@p5+R$uzs{h`HkaXA~6@vGoE z+_8?B$L;CB(aqz;5>v3=P}tkp06986OCGWF=+^%9vm% z>ht>|QQ7UUHXVY|A#d344w$RYQlTT5IC$LQWn{2YY}D%y1;g9N2BPk$PyR-WlJ#TD z#+jhyD83FnIqvrNbVqsJp@en9^+bbyugAwhT3s|Ewk;fVd)nR7->MNhVjgc>4-ZIH z`;M4rS-|ZVyNoK+0rJB++1!R!JF6q+cSnpqRz?w4BH2wLl%+Nz13_czv01qTg8+wp z?pn3$5gC(1kP>#dO>=mUw@0F}&Ac74P|z2M2I;@lbE>}}HXMz}0;sHJIB4#fmDnWQ z6A1VMY{`=LBHiZS;;}bn9Ddk)B-&HDenksKdoUarAHgqaT;_V!1v_Gk1Kwz?%foAG zyGsPz{)msmvm#L-=5B8{29d%m5Oa5T$GnTZ0okS5X5AG)WHcJ~@qDhhP$1Uc?T$p; zv96%_-L`Gp6o)qPhO?_3Zz7c)sn4t^sNf@Az7Fn1D|I)}ZED+Al(W!g)9PrFw%e4p zzuH#bc3Vto+h#=4yIpD9W=g@DihZ-K0`qp2xiY{|HY@#57xs*j*}lXS!H72&2rdcx zWZ_vNEut-?y*29(F{mCFz#RgwaI@Ra<3&U);_c>pB5Du_#5}=3G}fVfFGk%r^Ra{D zyyBCcC;B5_#@3lH5c72gf?=5jY-1o0^F@rEE!-%wIieA+44W(WdVB$lSc}Zv5Ui_= z#sF@27)eM8+z8#a-lebS#5#h+Sd@3#?Cy^B6rGPJ3JO#>(a6d^$=1H-H%GVD_(gFnjrUMgp39vB}|Rx9M=t<1N|YZ;5uiDi|-ZcdujU?~EI zgFR7WX5Fv6Vq`H3*<6N-w2S;FmD@fl%RVc2H=2_=yO*)+ksh&@mqwKyPpI1nt|zN) zEuI^jHEaGX)sxi@5Oh~C)a~=j>9y+57T$C)Wg0Wkp+!+&K=$udmyp__ML3LnK{>rq z6&8wc)W{O7bsurZq`Je{nJ&@G?eXA=WSch}=@Q?v_MNzPPqfQmY%NC!)!yZ8Uli$K zKT(nbl*khfhDteG+dE96Ll3~+9F#X+#2kMjAwGOwdP{%8+v z9bwR>MAq7pV?3@FP-3(S=g-zH(h>@}KBoHi;<;ANPID-=Vckh14gv4dXqW8JS=$4I zQvFg5w~Ob-LjGuv#}{djd3Z7`aa#o~^Nkux)cT=tu#J=al7V5eSmG8ICH@=swl7xw zNR7aY=Mvg0=TX*PE$M|dZ9`U_@uaFcCmdvV6|AnBW@b6s8j$nKkx|MZsV^QZF$+dY zZ#d@e_R1c(I(eXE=0Klc!WR*$H4d(TM<0*`v$S2T9*f}RBTf%WZB6*-vXD2%U!y3k zzf^6zWJHITs>UoCw~8?yDq-3aDDizIsX}8co*Oj&R+)9QLztxxTPY5)R)$Ma9=3q@pia-x}%lGpK96)bkQj9ZQ02r7gux6 zP{lxN-LEzXD(f;s@sC(X*z0Zc$rT@Er4+)|r!OFDR&{`u$6K{?a9yU{TUPH$`3R|4 zmIs0wZ)1MZR!zP`F~2Wh*hY=6F@L1PXKqWWDNxKW+ew?$DI0?>_17fT0%eg{o$r)h zlqjnt-6|H>Da2cye3aUT{A#Kk3wCt)-4Wgr-S$&eDBRpKW__Cq!LYB>6N%#NPR-e@ z7joktK<2X%b+)jDb2UjpgxZuk3z%V;+OZkM4A2#(02L|GTOHgMx^nop@D+nUMPMVg zPf#s}q1nt3)I^Cf-)1(f8up?g*dx`XhLBOYgl%2t_X$EqteO+!MJZDdJ1cQ+b(I<_ z5BzE(7c-i+#e`_s9f3&a;H3!7Fh!S5f1y=}o@ zG}i9-#M<2+bX?+L^=xG_-VA&HlAHc)d0iQ0m%E$QxblR1-nT4SMz_ek74gui|N5OZ zD#{2$_zJrz<4a$!yXaep!`?{LEmQ7Wr=DSrfHJm1$#)u_UH8Np)|e=xjVy^pdLrf@ z$9(nk6GtuQu~GZooxXOymVLv$>przYa5)vXy!DpHHd%d3Q;{6xlt2Te`+7tx_w_7) zB4`iyRGm4Z{BFSZ3`CDmzkG5l2x^)HJ2C`;J(=wn%ELrV7P6d`qQ6t;rtAJHx>x5sz^S zrUW)bVy8sA*$dUpLd2oh2}gCZN0fcfOxX@(WY`<=hGhd!&595k3Wj>R-Nxo-i8!IK zZ?S<>yDb!pbh(Y4N!1TRu}IXou&-X6#Fj82COaw4*4`a!?+KeX9u-#Y-La0IXpg*b zpfnzc%0*J*Wk-0IhI~>(8AY@S87bwV;Kq6lt@yMp6JI%8hBN33M71P+HQAMYTt!;RYmvcGBH&?U574^*8Z z(i!sy#ZI;^k95WY!HBOjAVbyWERoJwFvzZHH)6=^<%CS>j7TSDD@%EIK^Xv%&RAFu zNYr5z>BPTqOx99%ZV_ymSQ)U9&R8_OtjirPi@u znBN`nV1OVKs^Ksa^LMt#{E{+{+75RFiGP#<1N!LbZR?b2B0}5rjInbJ1l>`eUr?&& zrVBawx0KT4-BWYMQfgj3?F?XtQ*5{vk{ zjeYu(PFGG^?f%Dm6E+!brZt_@)=LQO3J1}?B%`gDdIIR{)*PrX47mMXT*Oi(Wk);U zrMGu0?IBoJHi>#X5)-ZL!N8dZ`Y~@fEFsd8+@NX?1_IvpsNk*a%fNdA;iz#d+RCF$ zY`A8M&mstWml(mx%4Q67r#Ij=-o>=6 zZ(#9N@0fXywA=u`(A-!DpDS2Nlvz2_9`R8RS)O3v76scDdfTJ19y;_UB5RH|hyvcI zvUOm^2@~JZ9pw7})@sbe1!LYou&qZ7S5Bbr5SE+8no6w{%V3Ra@!~EhOA>1tZ_;>a zWk8D6n)!M_+_NXp6Op{C+$}NGnAiQw{Q>vRh^@7J4s^R??rvXaAQ~i$(ofxZ_b1?u zM5Q@(I2*`ltZiA;8w`8o>W~^;kfzHMHr~NfIpgfMy;c$DG3GqQx`ePsl%gGBl$I1< zB$VG5u+q6wWWcz>s#ue1;l_Qqhi7@4?6HoRku;XUL*|N5nKc64R3{qr`*^QKjYfo1 zkMb3VC!D88P85}Q85!ek?+Q|WmU(b5g@f zCJ@9t$18i_s-GH&pxhNx=el6bgYyjWQYNjSVq#0_V64L;9UW=OWjh&;@js&;nCraC~F=o9tzsL7iAemFt`tFUazyJ z2EqEnF}HdX&@!JzKm!d={z&|?2K-@Mi?`kH%gHtB=1#~RSz$Xg^k)}%11DV!~2??kSRtD`+~7hr@WJ+XhiUE(7e~C7z0r_ zosp3gEyKYm?QjrWQ2^m3n-%I9u<(-Y*lsrw*1R>LN)g1D>g0l|DtIueT+33$!!J3t zMZ8gRLlV+D7!Fxj^Zi5XSU41pc>^AwI}mG=TiB`~2Cmx|jmjpODvyBLoNnQF{khjezhb+65wu+1J zaHQMFove=pNqxNSEq8RS1%ZK+cj2wmenIpED32s)QPViUB?_ARj*U}H^@rN?hMlNtgE z%Q1w6w$2VijI*4{TI^XUaVBfABq*CRS(%w&O=q$eVS=$cQ<)n4<~Mi1zy7i230wD} zo~pN5mnDRGiMLsIb_8GIZPtx33vYOvwY)ZP;%(N|13{?XR-^iN)E!xbZzOccO>83) zQa4x^vP0o=N+qgT8!$-dLjkQxRW>%(MY@6LK!aN>x_6iIi%Wr zTvXlubYT!ge4TA_NTD#GSLpE#-L6xV6Af% z-)AH93inbD7V3c{HlEa|Hs+8 zz{yoq`Qzs%nK4n*MurJL_UG=ht6~y$NWw#KSC^iiJCimu-A#ATOrpCEce?NNw9~Kj z?MY?`V1xw}K^`h^B&f(s0bk(y1QZ{D2!bLY7+$W3Ao#+!|Ihc-satisJHh{NKFR0y zt#j(ssZ*y;Rh>Fj*Q-=?_0rfxPq=&=1h;f&2?`@rg$@l25kwcP6ya}sJpM`H_# z+}7MkI<4IQ9U3DztP#vji6`C-AMgg!aN(i-{2E zj;RTvHO|8l44bO5GbLP7-tfYc?O>G8G@1(7|KIBrqf~DHC|BAmkW! z5QdGWJI?)bx;~iIhOXnF+t|-4@35Y0S$YJ%v-$MYnC$tQ4k{pJ ztJ-Rs&F3fL%1{|o#2IoqT1H6{vvWZO2KhDDrvbOEf*al=pBiOYl0zT@tnFvB(jmppp@L~5ZQob$V7ZJjH0mC ztJ(Z8ylh#HR36jOiMVvi7~l{mozg36yRv-THJ!3fg4rrR+~%Oe?zFA*!&3=dti@d= z;-f*S5?m`lyX~W=UZd<-r=r>z9f{}NN%61RnmKG-e9=OZsl1yV4&B{tVjZ4JW<@V8 z-zyl0Fl0+7;=W*5TF>4=FDk3C^iXm*DJ~OZnvqSLDQn)r$#USJJsJM{(5;AqqV=^D zr7b5M$Jt38o?yATQ5w8r<`h9>`&fZ>tf(|uw56>>8*Lo|+X~54F0W$?qEgAa*y2+p zqwRwno!cm2CBG>v9l5=N;_{2KW_A2Frnh9>TsrJKwx*3u4eCo9qRDXLCoolImd&Yw zY<`SkLvqsB%wa9aXnatPG=Ik$GgVHg71J~;9f73W~7*GM3 z%}FyC*PwCnE1C(4K=N!Wm<l-7C469|Bn)OCn5qm@KG2`*kE?07ZEDn!{ z*Gojy7&$XpcUZ2JZfl$oT-g`4f;rX#ntdNW1UAl>xFQ6&(o z>DRSa+-`bvzz-Sq2>HDze<^p;>Lup3P-Fp2w~z5#SrF4c-|nCq3Od>=aV_EyHWAWt zL-;p3avDh+50VthAT8;L$s7Y^OAXCYHpyyQ!JOF;kZA=omDeEM3idF-Wm&CY4tGc{ zyA{mISO#XNhT~(&(WFdW+k}&y8q19!%u)61wi@P)Vz;$oPDLWHIWTK76qs|B=4zo? zr+%{e)TX^``7;Q2U36@-2*jFc*keIz)70=(#?4N|bNZ?dd&I-xaTP>i&v|*LXD}(D zcrUk9T0!)VwLKSP$j~M^lxj;+K~mhN%Ffx7ScYN(qCoZt#~{SWiQ%^yXV{p`CG5F8 z15$YsYNVV3A3L4&(U|9|Y z!D6FBV{?fyL>~;P0CLr_OxpsQO^$4m9THOs0HW)=e+(Nc0@KSVGpsRU-xC;fxK!r>$G{1Sp#P{&q-yEH3w` zp3(*y()Z2W+YWF?LpI*i#>S9q-N5si&Wysw86A)bJJj;IcB;wYa9FyrJ!^O++nzSa zh_1kW?Es71hdW_|YGHeGH^@-P$Az8XaG`c?H<-Q{;tQR?A?v8w`JG^MUBeZfz~Rxj zIG;b*3EVUp@_S_`G?^NbGcBL(1dhoK5zlo2xhjC|vTh(8ab+dEv}l%&CJOb;wyZg=f20dkwE3f*pirAX z)&*v1^Cg{NOPfE}2{qb$LnqME=C8LyLv22(9TIBuV>Tq6*o>89xG!VJV=yqhmc}^w zRQgCZuFQiqK!Muc!rvIELZ8@fn8_AH^tC5uE~t`+R3T=*#K}4L7>ktgO)6kM>k4ny zz?vSNip#d{`)rm+r}RgLtWO#Nd&mvHSaeD>Lo$I|{J0ioHHj>8{f9O9r$^1@vSvBY z$@;S>r$>of^*SvI(IT=YT_A)y%idwMx}7(>09{|^d8$o(EWRa?PAP|q<)F!N0I8eF zhd{GL28?%CL6B zOZx1($}Y9diHV3ou5>t?HFpniIxczEU+J>d$O?Y8Rgc3n;-h*AWaj_5G@Ozn5@BH$ zCOPbz%FN3wPjd)4Rd>Iz7MaO=E{&5^Nw|BJ)7I`Ld?Y=Ql_ic@8|4%v=c*Ce_!R(f z-E5e}__YKQ8ZfgU7lA?bWNkyf4(;|G@j(xKG&1GpC-vtEjDuk>$kCGsCK3_osUa;Qvl|ws8xa{- zg#sE88P|Y<7!esKXfD0?Wse2wvg)y*GPAK3J!uo}Z`p2qhauc&wEU5|sbo0TSWb}K zRB~8E=c^%M?r{!!I~kzI7z)Z#e9-&U+($Obti7_SoDfhqLl^ z2m2rf&|z&^!FKJ6WC&wSEf;ETDw)-RxK`UuCA0cdla_6nN@n%meH)3%Eo!UtNV80x zN@gdws6Jq645zC7;F}}Ng1vkSRHOE zX`PpjG|psL#YplNP1>>$Q^_rwbQD*Uu+%4?NDhyV%Wk9@ee;QA+N2u>^9d{25YHzv z2!tAObLidN$i|Q>pjdJvTqF;NhqcJ z#hGn@^mtx1_Bm~U%w|=>?#^@*w`7@&WExqfhSIpsfSWHW+RWzs6x;c55RQs0gqvY0 zDrz7k(>ijttw8}ynWCf80x%a|c@q_xg2|PDQQ7CT^|`W51iA3I*$3JoZWfM5IW2H+ zClntukQRNQE7UlSAM66p<+I~*VDCp=z>~>QrSA820&}iK`=KuIF!K*Ofw&%CsYA5U zuqVcC{jv5uB7z&QI>#PBlIcV)YB=w2&lnocvpXPpeOLddJE5 zg5T`|Or;b0g4J(#0jGzDN7J&mbayxSWJ*@`cXokh+-xo>2J?i84OyiUTPXM`V%PUNeACf(hL!+Phu&}&WgT+F+3oM;_2U@IC{q+pigtO6TJf@#H82A2$Py8y}kBG%Iw$jSr{Nx$z9&A%LPEwed%=Ns3f6 zw|C>C7>2fT@~|a zH**H#rckNrM3;26DVKGR;Vci>l+ub{2=a5p)clgiy^ zW2Q3M^ay_{1_k^&0?I3;GRDA7rf?S!FpXQxO{O`;tV3OVM?aO~)SufF`I`jDZTwMg zGM9=gW`A#?Zg#h-ySkK8S)V8XNX04&>^;UTDcA%KPjE5T9M~P*xJeY>+)wO|ZcJh6 z$jBy?&K*5e(N0Wny zr9krcqsh6165~B~a!PkA6VK)1kUSGGj!hR|z!@Jyyb(kGY|$9w5PuYCT$b(_kDy32 z{$6)1H^N36$q(8T5d4a(t^u&(!(4ci^Xc?xIyJ&D^BS>zAZ>jI!A+%e8PyNWCIS+b z2{9M(B2Day`G^G)x2oaW>>9YKbROPdlw?A$keblO<>?4-dH8~XZ8ije?T%&gO3G%} z2kF}E{4Pp_&%3)>J~|F!CtKzvmCm#07wJEWX2T^1Nd8GQSxPp0UZ63aN~&SzP8)fP z*iqvKbkiA>IG#~G+a`^xaySGZW!_<__6D7@QL-y9Tz-MAWqerq9PDJu0bq4(H&%PJ|}Z{bd+$47_MVLb3-O_H~0 z`?;Sns#}J~N7bC%+-;vrj>HGEdAjz6UaNZ`bu>H1sb&@>hogaNT*0~INMh6I5T}}1 z7*e|?Vm1r{hAp|H1Bu3f<8$ca8BR2P3g;I`M>)|(b$1p za)fmjrJE%}E;+&qfmE~TL26pckC4q|)5DBwCYQjS%yOd9`do74nDL}y!KkV}w2s0| z10prQd3aP|n{@)Gr~Lnz1#P9GS?6+GzSu zE(teyViTjAkujGX*@8ox;KEG6kUE@uJExivBeyj-g|#HR!A!S8@?`i8GfV%r=BC2W zSy{@qHHQ$Nj>}k(j)-KYZYk{6+|+QGZZ*Ny+|+28ZmH;2+^spPcBHI+$LRX{SF7i2 z%}tHzFfB`^k&MwqUm9kK9iZ`)&SUA|*4&i#qgw3&q2+N?Lfjlrv3f}e#$8BZyrE@9 zLWHcFAJ3`{6-z(2=BA9R*iw9m0gWY}HnnUGAlzO8vTO^=a^ZEr#cfu9*_u-=tnP}i zG=3}Yq7AQbvXmZ$B;_t4OY67h5LK;(YH2tCiB0a%)Yhaco?S~FA@Z!NMIP12F8JS(g_{vWd)O=f2#{lIg6R$0|NJ$9LO;)0RT`; zKFv9dj|KqTOE#>(+h>->03pvF^E6`C;RSQvkUS!z`JQ3^(gx%&S?HBAq7- zsDhaiB6&O%idl{!J?Dnq&sqWsL^iIhRiFS8a>I-@8E43*u_2{_);bk{vD}E>k+mGF z0?ElW0M;~EAlZbnewMdTAesSdB?8EBA|;$zP6-CE;}2r7e0B^7P1)_y;dL0-Cxk$2 zm4#e<)1p2~Yc1pe8;$@i2QGk!25?id8EdU@1eTaoy}@!P0g~8+)h)QcyNyW$v7AK6 zl4Vy6=drv>fQ)U{#4Ilq0NF7Ou$+Vd@O7z7%34bSz-|e!Wvxk&K91l;6(eP>1pvuy zRemO9vKG+*VH6ArT0U675}U^4Iv2|e1;DU@SZhl_5}Wi_6f92#0K*1iEmQ#+H4tk> z2grChOR(1A0F1bKj0oxBA&FYC7SRI74H?pdt&IZ>76KoaqcHv86okwA2~ zjphCXL`~drud@zAwfz4E0exTyV<2n0{=XWlYMc;^U zT@(RVTG`<1qhR^Y{a8B8e?t@jciF1b_m}{xvGCJTlw-!->{gq^XWHTEd|YeYXQQyx zFru&~H1u;(C@jvHtXMxE1!czbA?U^^C_bFy4xszoO;K=ud`k%WLKMU|rD{|DKT#M@ z7u6L1i%|$>zwu4_g!`AGz?_@k63QQ&X{_|+(I#HRYSC~PzWw7%;P z-5rhqZ)wY!%vsFd+6Ih5zaE7SN1@+{Le(eaj2v!@f=Aoo-;BbC+u`4e!t>$6`P*%P zq;$mZMAL`I^J2! z*pQ2R_Z0`<>wscTJ`pZQ@9u!-#s~G%<(>|p3I}3)^8F|f-Cae9nX5?ejiQXE@g1D0 zp+q{pIjQDR_eC*W>|}^(;zEAV6`mi()uoK^!)^$eB8C|EM={vFi4%kF6uzOT?9z{- zI3sSJ7wqD=eIN?u21*X`1#hl=BKN^4iflzjjLAb$l$<*TB^wfV%a5bbVdXj%1|E(A zkuPgY>5*=58TD97Kk0_Rc1hHH&t4F)>#RS}@@O|4H4Jbek451L%xxlEJl+k#=HiLH zAn04;pX>&YI&Xg(ML~okK3O1r<$v3tne^x=YYSK4XWgKBfO#qk%&QGL46HwoLU@s& z)avOdWP-oBs2q{Mh=RsKpXf7D0F-Ua9kcFudp3%IeiudfWfWoT7_&R{TnEVV+C1L@ zgrW_>`OEeERd;ySO}P4C?5{geQt9xLvEOt6F`swy;UM?hC<1?~b}9_g{aqA_J}#r` z3sFdXXlM#yHfWpQM?qQjA)OHXhbVZnyHz*$AES_D8Y>F429y0$SEye0{<#gRWWWwq3J=bb)|fLfKwH;9)*P<(;?=4^?KA0HbE1<1vk$}bwi%|t0<%x< zW8X2E(z)!T{@6ZJ9!SY%pS)+1arQw9^!;u1U}vMi&S-;SmnEm`cxDus$+_5oQ}+dY zpbZAY!xB9!3K&oEXa5*cdf#i7|`nc~RI{dcs!7^P^DKk(3tT1yP`~&KY!cF7?7F zSbdaqSlOnFqA(b)?C_91MqV7n;3<$~z9b47j*pI}Lr4FmQJ|)5kFFo-j$)5&mqk&a zHyPQ<*&PMxYN>6W%cDTyTCGSw+6IG8NsZN~us;?>7#hzU%7XlOG#&fLisBVffHq8+ z&nk+ah$3XLfKj_OSGGZT<6D8Qih{EFsr(4XoVer0Q+IU~DdXzTJh0JXwy%kz@YbVV z<6Rqt4aeJU?kD$x5Z;gVsVG85IVNNRcAWtR<>UPNOs3Ujo=kv)O3%&Kn=OX4=SbF6 zx{0_@IY#TX5@%>91pxlH0W)4}cFbGgRQsqqvz4nC=DcFDnXLIOr9f7sLI;L2P1eqQ z1S4Ks%&ea`MV^}-IWM=yjVa9fr#)8spQE_YS>oO;as)4l&mBpQzYn5v=_3nk% z{MHdRLVLl?Ren6F30UC_Tsp3v*?z=uxzZA8v?Gicntris+4)2!Bx`NyB!}Y|Z8CCs zh}mKU0N0A?rHz@)00PUXMlPe^oF3k0_$KHG zFb+cx?_f5ON6F#Qz19dH;2aw2TBXr=8;^FqM&naSbK8?$v(b2X8M|Gn(YVntkOgI%obexb0qT=L9T$-$gY!g4lkTfHzDGGz*~6=nqMRw9>Gs|D(vtk!-~RSD)8x3ue=i)zr-nqejdcOKzS!UN zL8K;=>TIg%e@IPBs_A}6#Z|ESE_wj1KvKWc0Rf5h8q)<8$Q{;2n?4B07+(>m)W`Hf zhTt?zCZ6BKt!a88U>v89iL&WtNRC=H)6D>5F>@YUfT_I+5CCHYZ6T(twz9>TYBO`T zFjEmzYzs8iW^Qefrltn*p}9?Ixv#*MDfr`)YS3G*mMzy549=$HU{ZAjTd*lO5#@?` zTd1iiz0NddEt^k8?~%8~of;jO@m<&A_BD#^ma}}sv3TT%|t}L2E$?^B5Q+T znCO&vGQ)+0*4kEci-dKvx3TA08zZcUvFI9}8p6hUR)^*pyRIfnsfsm^Goy?J^qXpu z*Pb)m8u4q@K+U<0Ug(_hmQ4Fi11nIrwO3Gb2D2|svz?pdOuSfZYN%;4O?FIpF;di! zl)uoLtv6T^+wg*j`k*cIGp)>Sn-SgO@q<(WlyyDuvigD@4(PYn<0c(qH+Ma}@I ztri_kW*tliP#iG)8H~|U>ED)j3OMRWx3aZX_K75prNWw;K0Lt(7LaBd2}ihO#5SFF zBHZ9Hopr)ZtMSCNBHt~?^i0Q`&;bFaCr;!hrH7l&H<9ad4afuzxHCP|$0iVW!fB>g zO}J?_nKu1sBI}xO$C_rJ7)s_6`tu=X9yT%QW_7L2G;Crt<;qvc%xHqL)H2^RXmTW_ zBk7GG2eX@TbOq8if=rI2GIH&Y5nCpn9h2*njE*H!8TqD>RZcFSl}j?s!X=p+O(CTXi{7KR7SYME z_!j+{5c}G>@8NsDfX{ovqTsk$H zl}kR2;PACBB@Rn)>ER()C0Z;Fu4k@I#~D0ar5KI}-=waGGW-s1*r1S1ml@n}sB$Nm z8Vqg_Ql`fYZd|W2njSNFs3e&VGkAm~nVJu7&^b+a8QgHB;F<0+xbZMaGyP@oFpXzA z%ivME4AWBvHyou4F&$;_&?7ZP(@_$9`WfnP!qyWxB_$)tL{7O+Oj{*#Z{l30p%OWz zH8!1mI-}yjthPfehVo-tZI_8>-IPMLv_2EhriTRAG{&a!OhOvpNEI7!vR1bgfAH`( zY2m!ba5}hQ!{J&yCt1lG4_&VXbh4Fv*b(cs)c@N`K72j?SO}-s$tY1JpK2u^wQ+;y zBrH4o5dPGaJ z^5#}6OS3>h?|xaTh2)If>u)I+k~b^HEal?cxKtG^v#R1cSuJe)nRK`4^B(qXn|a)x zr)B}~vErxlxEV?%pLCV;(qJ(t;7g=_ZF@i~mhO5gOZa>4HN9kjQ^)zuI;~i`{0HLz z(s_^eSkPRnSgz5Er6f@Fc#}8RD%R&)v|{O&>0>x^2N;vc6-zT+!G5#ps#{`KEX@Rp zw}JQDya7X9uCZe2v3tc(cMGgo+SnULy@~1{bh|3zkN@@(a_DCc(K6m5yE^Q|^P_C6*A+erw6;bDsB}C*b|K%w!p?6C`=gSk2mBgUiBP0bA^_E6n|Dwo=NE4JA_&RJrtWQ(`n9AKkuPfrB3RX$x#l5%1yt}O3ru% zpB$&JMfAJJdn}AK>w;EuzR;Q}`<3F_f%?o$;J1J|LsaVNwJ^OR3hrwVo%(1G^(`o~ zVwF`Zx8jvj+|7~WnB+C&#WD2%(sTUfN4(?J>Anwef3IFQR%DsJJhdOgU)eI z=#3QIs#nW}pjB`B4RF?IHH8Px1#V1nUZGXq?vK@r^A$gUpc?d>rAPJI^a`bVvX=E~ zC6rQ}$tLGL>xT69Q6>S2h~Ja}HJXbO`!GhZvffi$4TE60IB8u}Nz*+Ue+)@m|; zGt`EnYdZj&;W%8`rwk#4bl#(C$j)*_D|Pk5kYBTI zs8$4nv!@78{*|y--IP z-KcMmPzMhK<-lEN)SE3neu{% z5q#~yOw)(ef!)|O&`VllV%w>F;~};(1dBn|5B%nKM&GWdAF5qr4f?c$Sx5{kqfa|g z-SrCF=F3gLs%^}DOQSusiWO*=bH-b3UYdTz_W~wp z@L|0kYNL!1e7NcRde#*XUDe-9vDU0VSZ>kxd%6 z4qUUYikZ~ffoU&bAxS+<_EI6=ESE}tbD-s`31*9E6w<4BL90}t8YH9{`GL}CJHvDpxb^LRa|UHQ4$Ro8 zZ|fxp`K(b}GuH@m;J5aH-&fy0LrlRgW|oU7*9y&=op+Y#t($u(R`r58de(`Gb&cdI zG<~n-r!5vGn<)S^N9$g3*ekT^JYp5<^R*VSccX7mX{SM-o-fS#t=w#RhV@{&9o|G= zm~TM4ArXGI*(*h2417B*Gw8o5LGvDXRUcbhBhC5_Aa-!>Z}$rGo|xZhCAiou-RVT> zwd##oPfh>$$=t+R*eGkmT(hp~HDIpS4ghnkT$*iVV8u~0K(D)TCB8mm`|gY@9?Xv0~&H2s;1 zzfj)p?^VF6eAiY9k*hAGPoYTrPt&iKqp#X}r#keUzsS6aMo|w>9pEtYrpp6s)+KAL zc(auCOM~SWcg+#y7MLbFI@Qao5DXtS;Zgx6ZS4RI36H_kM0Xy58METX#;szM$hEwd zKUiN_i$M?qohGWJY>NWJpXN!BUTQbX^oM1u+^mUu)>T7ul!?55F8|tHxf`y+HKwXY4^~&AMs57@s?Vl`gK_)T<4q8rJoY?soLQSC1i4g8mtV<7OWvhF|u z>}sQnUTR}-i;N_4?De$$wSixi6~>EY%}?sweRE}aOrrw={2YnQtW4S$+C@?-43?mO znWm5K7~x?=&CergYu3TXzo{Iw>ZPVvT|3b93-u;&U`OBaU%k{l-Y8t?hTcR<{64BzpHi*ZQDrnudlHsuqJA}o4&XZ#dwFTc2QW^>mv?Z8|f}jAKOd1 zyI^heV)@x?Y4^7SJwu?qzw_Y5}jbOG8D-yEIzNHdIuX&zUb$H^s$Rt@~k;K)$N}1>-1t_s!X?aS64RzyZoeyTE zoy6CQ_G)h?-Lj$2RoXNp*oB*6wRAK?>I~6e3cVC-(p^jbp5{UGj@CWyk@Li@80z&46D9s5A(=xLTSv)+Jf8huWx7GqZuJvH$5SJ@35$zr3I8uai$)Du$~3RC=bk=m0_k_E2cv)LqMDX-3VG7 zgP-s2C76B0>eKI{dzQSR*oBZWUM9~aA#H7I3SHF-EJ7~fytWd;wMjXEdyWfGuDQN4@BgX1FhC7@%)us`@_#(m zv62s$T;&#PciW1KjaY-2M4wuW)^mM~Qd}TjaPgaRsGvM-qJBEMiQ2`iiIEM54Q*y+ zng*X+sFHinuI-xS-dB#$j)p~wA=O2~c^48GSYu}G4Yh}6=In47(CcA)W9%D0O*6B!Q0LH_iBlj!p;uS1-KX_xDERpsY2VgnFmjlvECQ z>DI+IM;3PFKrmp{u(As&bQdz#a-qOvI;#a1?fhP=URL`q0r93(tVS0s<#|vrJ%RL<19I9k48Q+x3PiIN z+oE9cpY>Z_89QQ*CCfGIg2iCWuVNTkJ5Z6aGa!2TdgRA^cOYoh8~Jk8=UHEiXwP4< zhG!7ZzuIAaJ7%$BkG3acB9Bd_D<+l-92&vi1Bk~X>VFg1(NQS%nVER8*c_<%L6D#I zc!5~iK=il6d#SIoQT;G-(&)vq+RfSrDu+=F%7UR#Cfff9yWlnJU=CO%*DBo5aJ*2( zrcp-@CYUTP3{*CtD*J%5YrX@nd52U$pweDBRR9}phnM8A`ljv;-e=0qR|l#d?*ul8 zo`^VMm_SAw!mdPS%9To7?OB@MyP&p8p==RLV0u@u7z~X1Ua?#&z1?rtgClr9Q}P)W z*Fn zf^?-wZ^KWUzldF7KCtua{9Tp>%MM;P$RvB7)S^Kq+0oW&Ti#T?=G;(I_geG=*fGpA zTRwd~>Y?K4|Kf1HnJakPeLlKvb#&7x5&sn?%!V&s4GCY--dpo`{Ws!jzMEW z(!eGfMrT`6frYi!1WuHRdbUM7QWTR%6vugS#Q7QytjB9b9Kx}3#0v9G`dw`E|FKJf z{MgSF&H_^PTAX9KkhMx}9w*dklNY4wy0XBMiIECyLO-F8u0*S0@c-CK_5;s)^zoIL zE!d*yoYrW&A{4tIb7uv|-6laAs5(1qj?B=`oM|i%DTX7xM0GtV|zKKdQ>3M<45r7z0oiXCHR^wtF=d1Xv|n z8&QZtr9M6FH?jGPk{4uu+iYva%5*aq72z5_d@RG(4z#>E7-nRyAPSWJ1AhN{Yu0b7 z-?4yx^YV6mwFp?dHHJW>FgV88SB)VV1%+@fe z`^wYRW6aKnE&+@oMH#PnZ{PAO6^IUK+5KeAx+-P}Yq3b-#vg-RQNvlH`ubiD26*3c zID2COoxQw6is(4Tn}|1AP4I>}t7=~jLuZFOV~uJ8%swJ@eiP=*Q6@AxJIp#Vc+m_pIm*{ZQF&z-9Va3RNUMFy zr1;02NJ&`Do1(KKgM$X?hQR{oGV}GcnvepIlcgOH#?t1J7M9V9jIDU%RdPq)(72f9 zhVgLwn7rynR4oIwM-kF07+T?hh8q^osMyi=CL2#x+ww*6Bh4hGpiy-xQS2yn)2dNz zF(RL@=?JTi*(v}TCYYN?KvSP;LatIDrnu@C8jXJ$My9bd#Ix9ab)8CGwHW#cH(ku9 zYkBQ&C_&C8RHI&4i}7`&S>M6)VxGd=3J}a!lf?xd_nDX^jH*rvWsbo@rd7y$z`dS( ztfaI!1t>Is>Z4e8=_L?W)`R61cYignF35bmrYat5&>2h7(6Ko>lYc}jVA_Q3vZC>K zwK)qi#Q%F*oeRU%se3VMW~11TGO5)p@LPjuzOJQWTGKx1K%JC6vmyC^&1?pvzo(jL zlStPIe6LxUh4+YcF;=F(Iou=*v};Kx%ce>AC%3Kit=o5~fCuE(Eo_TQg~9MyWi<<1P|>S>i|QFG(|eb&QZC>Zbk8oM>Q|LpHL+Nz z_?XVI3G;|vy9>NWUAc)Bmn1{pX&%#@`Kvaf@a z_hF1RZZOZm68kjt@VDi?vKmdy>K4HnZ|NoFj^ou{U&||3)K9}zG&kNLK*?;-)~o|4 zEXH_yW{Dm+$<~k#%!0?F5kxiRWX?E90l!Q!h`-$Ad!`^}hJygGpP*{MKTV!B>oD8O zNhjyoor#QX5dHNe=obtC(QEe2D*KtZ$j(~HDWGCGD0phyc3PbRGMv=>R-<02%-2+D z825`rKmPAt>Z@{WFg$^Mn#tcCB`aBtanOs%1e73wX175SVeM;@Lz>Cx@Z6}JoSDAQdn8_TJOGUpT;}93j)FDe) z>Ifp-bP>!;-fL;lUZj0wbVvM?r<)CJY;l+ZX1yTO^k>w00&#rzHB<_F!40a4$S-d% z7cqXBbcELEy|o#2PzDu2Ickz~=W;Y~tU=#ZKd=vZ*K*hm<=nVOB{#ht^h5q>Xro7N z_YlD_UJGRBbf&1>2(czz-NRG@j;Fn*2eOMz(R_O9}L2Ql6J#Y zsz-(&HT84Qf|%1NXMUd$g-$fePy8CnHzoL8{YnVT$BA0Oi3RkHUdYmr3d*GbCt5HVsorm3r8=L& zw{1%qF&#LOgA8RgtL_VY9EFNjTE;pQ%o8+2b~{%{c18B2F7?9E;_)Kw9wd?MNVPwFGtN@TdpZMdPDz z4i0@7`qMYnm?9QcjAuXRy#d`l#uK>n)$itfjs4yiyrHdO>eF7cS@xTg-kgu!HPjUi z0Ku!x*Pr&lLC?SA#dp#F@~f%EnsrdAdNtiF`e;K5vFk&$8l^5!0}c4;%;MJOt7}7l zykDEI5`F6eTfCaM!Yry_w7&zhQ4h)-8|QS#Zj5=s9NTeVG>Cq5J{T>kwu|im^H;wf zT&uz08vbo+P_E(7ObO0g9%*gT;7r9a!MU1RFy*4#e~A#z-oW5?cAea zxiwHR;_GHvA1lTVqbssmbPnyY@szec-mxi&GBOJh9`dV0+jo1;sY z^j-#Mu~+ixvbOc3YF(M0*o}+PQZ?=kujk*~r9a))xGFm@=DAQyJ2(?-$4wT2FSHS` zq7%74T5C*OT9JMeE+(LQs^6%&9QU1AnSON$)Y0TG6`&1RLn0kxE1m`}jal^;FpuO# zmHNSzFnBm1p7jyEJdkTOWgfpnMKT+#HS6@@;cbR5*`L0aS5m*)nbNHwYiwx*)qaq( zYCrrkIZO3`wW3xp)Ehk51h|lb!=ZwJezdfVYdHdXU}^NoG(=TYal-nPG+tTx`fxy$ z#$JYSOFy75E#+=CU2f5rIkd%;Uhryr3+|3>r=M`E7;FS{*XuB&;!=qQftTt$5J&9V znsq_BRA?=4#vpxr048+er3H_D+eUJ%ER?3>ErFG{8S;)0ve9eH16Df7KA zj2Sit`@(o}%|*1e4E&l>U(0Kj{8lC*8>=`zUG?>AENDV^BCt;MlHUr-Jkc)8xEOf!+|t8eX7|^iTuRo07J}F7o4i`FqT;}ikq3%ci>`GB zuIR{8ZY4{#dQ(l+0>LfQNlVn|QV$N{1$qSq)V#`6xf}NBg01&Xc6hZ`JXow1%oL9Y z&gd-17DKs&0Hd%4y349uiU`&q@% zN!Kr9mjS;_sRPDz1E(3%Q6`yAH#TL>G@^tRPCO!i7m^zBF2WoSo@{{<=?S zCzr}de_Vllv7i++=shouDqi5Xa%HqjfXM7z2D}BNXlM_8*SgH;h?RYHo}gUf-Q8{# zE_gL|LIH?55%Y*?{MQw`)dqcI1=;|c>^Ly-y%kEMw2pA@s$WH%A&78EZ3}3P6B)cX zF`PZv3v-6>@%Tuxs)kIAdG5Ox58pKWB#5NVsqSVvkFr-kUI7I%^5pX1j_3WJieHNq z8-*757^2CGL!vwUW_l*SgFVe1S8bIFh{I~MnuwQ&fEq;C9T4fiXmfq#fE?e8@%vZW zd?jA-UGstMa`KArVjno|gtl$R3JJTP2-En&vge=dfrs#a*!N>NRz!mNVqFM0?Sc|QB8(O2!#)~4@Inv87<7Iw6gw7_=mO3iYe)c8d-<5PKpNdkZ^z~~ z;6dWW@G;}_nk{UQfjsYnz3wBu)HhR8F3vtKS&1oV&?~{ODUREs>fwYrdsc!L-iTg* zCH&lK0%&7n$uZ=$yfgxH)P+6b>|j824%OwPTnen%?xX|KXH)yv-Qt6g}A z^)`g|5Ub%%FEjgQ$`zj+Olo9fteXOnwH$X11k5c#>zRMHwR6onTv&r+$Lf+N zNOgv&9J$0Nqy>uDRuL?aVnJNQi(EdYa`6&43dL)9iF)CU``wIVWCVRP{zjgR=(Ou1v z@{IzYMBpf~&FWr-sK^a!0fNx}aA}_|(BWpin)0eX-1p|%tUhE!60bK*wg&GBggz=E z+t-4rQzO2?h4)x_`9ZiL_ckF_R)e^bhgTr6?etr0`3rs;*YYY#lF&0>vuK+m$$8F@)wo^#~Uq-(hcpv0CpO*H&TIF$=>k(PaP2oCnsS5(j` zY|YntPx};yjolKaH41bKo96E2As#}^7k2fKy#tYVn+GwpW?Dvc4WcF2_wsCa>vgu| zSk9Sdy^w=Fj*O@!ANN(jObp8uat}aWE0n&HL(&9ZxH;VKaBWcc;hDCECpx|}%{1#6Te65uQNhw+ znR=w!1Gp5RSoN#by1Iiayqc-6h>0|2D1AY*pax;X<{E30x-mapDF?IA5fq^}snw4E znaXZ0(1rZNP>_w9dc(&Qj(0U#al-8Wc8b6G)ZWf4hyviI#!e{m0CiJW)RM$B*s~y3 zb`yYOID*qMQwBFYsos4E&#$Wuy8IAKs-uYGwT3~xno+~MZ3n7;6;}Xl*mz`kHSMck zuWrkAF4=_3Z1-yr{S$=_R2H%s(SsK^J;aOjmMHrJEGuNK9i)da<1uTtT#preJ{ z>GITMJ)*-v_M1ZXFp&L2NS0q|mnc!D2u^FcUU-*D9akns$ zVJ6m{B~1Jmet?Oy&x$Zn&`e0-f0d^ea)W3BOnm(;#l!@dSaG&yf?pxz$;oBC^zUce zLN09BcqCVl22{n~DNl>`J3#xSvlZ=kfcCdT+WZRI|9S4RUiwpnb{SW5Gx1}EsDFqm z#3Ftyh{w-S#E%8>);XGSex;QjdEZ^uOJ6+4p_LfTrU!a@=^1{b8QgMw(76sRzXpvf z1>kAI$&W%X4R<0QCb_rLftnc8C<3Ue7S8+EF?*8Xo%^kmjyX|l5>7~D& z3+`wK_}TbD;pdY=`E&9FKksIK7Cso^2Ud?IwOe>N_0T9UEuym>(EjQN9YTYj%{LJ) z`;cPoY#!vAAJV+3cUrw_ifCn-u5xfwWc`>;c+Z$?U)u4fwG z1rn`3=vbna0e#N7wx^f+&vR&X11U4~dFQtO?V)#^2lkiWx!}+P8x^OI$`f_`ABUFz zZovuDo{7Nvdlnr!3lvrW>F#4ykGw$v{&Jo}{VNw8nmZq5ggvq9SA9eTuBY81n@`Bo zF(&NWER~O(@6dkRNZ#%_UrXiN93Ens7)|p?x>;y_N1hhVJDBFO3lvTD_NEIo%{%r+ zbHfp|M~MAgo)*!&nCQENsCv6JBzo80h#tC;=rASk4lopd-g{3 zh{Nb0A+}DQ7Sa2d=n)sH>Z`YMNc6tF5ye<@v`~AyJT0pCGu2Cks(SlgNcH}9sxyaf zCz`EWbQ zf+o3JNZl__i{u_Ad6$q>Z~I@Yx!lu1a>K?W6`Om6)`5qs_P1z0&NN3ZRy9{|XNEK% zZ>Jfwnu_E>LTZOREs{TFk~a%U_4ezKfH7 zVGiHB1RQ?lBf{a^q@1ig!QmN8n8W8j65-IRlsJ-zj#R?BLY~6m^3{tD9WH4Zd4k$k zmRR=IYvG3f9n*BPV7%%mMa{Y7ZE6;Z9oGKu_Vm)LFLP+Wn!kgnAN8EL%%PP*rQV{| z&p+8i@4L*Q6>nU0Xq88VtOxWTv*^&5FLS8>9g7bA@-l~3`HK$y>u%Jdx4!7mK~mO# z$#r{fW|Fz&g`B8^f*66jMb3A@C^RET(*W*?3 z&hL-$dr`ggf6Sqk;QX~?g|;WCVNiYQeq%*rrz%O7#Q|6mU@fpb8wMnUgJPrUJZ3=T;tG+-z++`Ynll8HuS&pIEQY$#-UXQ9_P@XuZiePHwHv~ZkFQ5 z;}JGiqTC};?BKNytprm?fT#Ce+tyxOu`zME94kbgmZu|iVn56vhLSH|>yWDAAR4g$ zle&uhTE{Ca0}Z0+@8DIPFX#7Hf2v#Ixk|avDObF$%m$`P22X z78$&uYh&PabdlYfbhBjsiK{9QVY~vy=Wo!0s(|q?;gtndfuNqns}dByzvI)eCiGqh zN2iy3#-Ww7eB1Yh`~jH5KjYBqRRyAza*(J^w zoQN`cZ|UcZc~FsPL!Plk00mI_a*le@;s1;&hP1;JI+!!H=g_-EIUDjs5jQQhSH#-P7|j+Vvq!T0!?5B~ z^m`@j!nr(K{=yfbvCRUV>p;Js#w*n7TnB3TPhSEQTB_cU`BFDSfYYRO(6B1vr}C7d zR4>1liU0SP9NMo`;2uHO^2Ho?APMEkN#eiaoePwNcYhhypw+D6s`l$R&o`08L_Wjb z;KRP+(92KEuBYp{6k2`53>%EkI;g_uzv9qpDseCPDkHr5tK2RJ>~ttOte75^C)(vY zr~h?39hv|ZTN(}AVj*3x6z_eE1WFA72J!@@8x^H51F3UR8pk0tv>4Hd;^MVQ_0*z6 zE74utOuzi9HbctmaQN38QgXY?K`ocu3^*D}625w~L#vNPwQXB-KO-M^3yjTf0KFDH z?!c$?sas$yW?67gFxIb;(m)E_+zr1-PjULc-RjUvbxyBH&v4Rtw?d+{dJki_=(p^E z`p&Hmt+2xEj-^%aQeE$lO2U@{!hnnbex?8~0AQqw zU=DnaiH;T__HNd~L|1XJKHRkG|Mt>bzY$7m6NZ~@-*7NzUr){^{?gS-z7szQ+keM5 z99miRtCWL?Uwa$arvC(1t^{Xj~4K%OY8#1`y<+Z@^-7K~{5kvkoldYj^>AWvXU z_mA&%=;hx;<-?@_Y7zLg;s!cMsxTr?N2*dFI?DkY|M5+S_G5nOELQC%NOe%6V}-!k z$0!~wLYFb2E0|D&=rShr%5OQeAIH=n1#8lGBL#C(zlk0B%ed8^`IZ*lWsF2CTkGi? z4uXUa{Wj)I4SL4Gtm$KTt1bp5de(XUpL^)(Z#%U5>~}#kN_00TZTb#$@@}MUYtRGC z#!jTIc;`-s4w@qt*v(FVZKp%m0kC|>PKRDOs%rEP@`O~sH@7neg244tW_xxGGN& ze!%HJ@py+GzQdvZQ;&Bjd8a5`S&(exojg(=e-V4T!7x-&J^fEycJ>}6x>NasNEhBCgSP?_t-e{X&shpi?-c9{8N2WMf~|nq zE2&~F-=HfPKKy<3Rv3m!Vbzx>^y;Z4Y#0{5FNR@@z%&l@N*RWmL5G%~b-Y9G7o9?n3&x_ z;j#{$P;zzUiLx$JW!<%zN` zU&{UMEJ@NJ$fZuS{C{E21oj1aBHPE8((;#{;Lw*O?Ou5TcE!?F>rQa!vasAo1o)IZ zf%NneF83#rq(M@y>E2L6X#EB{S~PH4p1NZdh;CYnKK}jt!lCh|rI^|F{2-#!vmR5T zT|&T}QsS@(eTxYl`2!GY5Pgfud>pSbG=7V(Gu`ip4!tt)D}I5`imF6Man}!35BTIV7?0=h#)IRhmc9PZJ#^&#=!^4(f*%BQC%acZe7~qYJAG(1 z)l`Q%oijXqKZeeuk+u!jEuglmeuRb4;U_qBLrrPjkCyfipWx7z5b&G;exU&00D!yB zSzDDTE|4c$=r2pT>#To3y3W}G(>Q1<)pgE%07}Rz_9;R7f;>U!-a$;^W?N`Co4N6i)Q76dgi{t_7#OXt5s z)$Iy-f~AitmM)Ma4HA~PCf^a*eey)MD^<4JB}s!M8;|`5HB?Q$w2a68e|^ZIRT%r< z{SagnnWsUmW$ZjwRPj7{ib}35&{@l%+MjvIQCyuU2&c#sT%EP7|0Tye^dxXFcD`F+ z8i(2AvGf1@7)%&0zya+R^lhG!la!5qwTBDe{9}wzC89m7d*|R4y<-on-H-4Zj-7j! zVeEX_!^kmLZXgfF&g{cV{de)%{0kqB>h_)n5%l<3Cb;Ke-RA4JDOo!R+%)oluBY1~RYq{s)l!Rv=cA(y* zSHQV_)K45*iCdFfer2#(E|&aZuUf7w(ksDD;ml$D!3XY$IAZTWr!y-#_=# zDSOaB=@T4U)g%Hv%y;#+Mh6e6dTpdmHwy#bEOsz*92ohZJy4m}9MN$o_7^B)?=)RL z6u(Dk9a8F``5^~H6OY0jS8URG4j6ymqgb6bdBJ-=N8NwrQHSV&M|_q4?KA4hRlSU} z^gQO!%1S+G0C0w4OAmqg`V@n$F%zKE3Lpx=jL}iQq^n3~7mh6HinpJ}OzC4{vRxs08C;kH#&aZl0bNvqp zO;?*>YwO|9ky4JTs8Xf0l?OKpv<{B)lOK0TZLF-j zWWQdz9k25Z%H2r4^vWk3T3N%LB^yxu&=U?VFP`Ageq}|hU!JJI8)*4yCph$lIR*NT zJORpBtrs$hFjVGoBONWg{Z^jBBO8~C9gO*pFeCns5?#vguXz$%yC*m_C0UB{L~)lo z{kLM|eiA+HPV}^Z81M;a)x#$^^nPT1>I8>AE17PTC#Hs19=Lz+iI^0>@)Z+F33qO*FPfaxn)I=4VND3vvHyd7oq=ONAjmlOxJl;dU zu#!;QD)tx;{TWmmn#9D9!>iXU1r#J+-AlLp42~9&#R(9_Z+@mlaYs$@_XBxC6sIdu zyzVI=K@>j};KT9+()*PtYCu{6p;Alf^Mi z^`D1875(RFq4umiq5F~k^9uoJ1n7QS|M{Ju{6U_e5$QiK2tXr%hSh&~2JVPdV)8`w z9&;@FzC-{T0XEv$_tQ^w=oNx-j66Z&ai{a z$^!>&`v!p>B2VCaLoIDDdKy{+q@x6wkSCD7sgU-BNJ9d&OZ&D$I`|hnS>u5zD`->l z1mjOACsa<7GzhisJ~F`=Z=jn6|1H}hN`{_z8|zc$7Y?c3@=XWbNA!fd(Bvj5%9D;SK@lU7o0EtzK1@HLQEY?^^)K6Yujm;pxzef+D-D50AEg}mtp9A;#oZ> z;8$9SY%hQAHN8~$rCJXRRQwvLuduDYxrOyEtwPQ}{1W~9p%WciKTia@uk8QTi4Ohf zIfwfHc%nni=Ocn&vo62b@Z&+?HxWVNHkza>Rs~~fP#{m==A(1!jlgsaxEMs z6i#*v2QSFeF`SGpJGhtL`y0*4D2lLz&a`k8*AIdSNm@{qnwF_)q)4PmC-mM# zktY0J@7Z}S7xnx5@CTEfdGGA(>}+}V*=LXDDO~YEcn`?mf=N1|6z$-Z?`oY_vEJ-N zX>_{RX2Tt&k;%-s}S=uCv>U#DDEyD6e;id+wLQ&jbaqS&XpDe81Xk>jOqib}c= zBj>63*MM}r-%ZgRz%sij+J7Uga$dexxkjse@NZFh|1DA3@s_A8?5dIFR4xDvDzDvw z%Ce5epEJ3A`BW@UysKAvXL^aYa}P}-6II^YQ%lbYb#|iPSUSLBBC7mEH!b!Eb#|g% zJcewuLp)h>trzO-M0@yo=`IJv6AE_kWTqTkspep_-4 z5bEqiXZco{i!9#QUFW_c)Y*y7^QioE1(f}pjQAzq>a6NcTXu_kf?s;|1C9wzj< z3$c-hHu?MM&H=ZdiU(>nHHT^{>SFw<^o`tnb&3CVAC{@B&fSWixaRzcTD_9wCA}3bUzbNb%{E1|G0MB__0PH{$ zzg36oHfrHAk^RGNioO+Vs){wRWvg-x=%(mXmx*G>c2l&)Rjz^LE3Br*H&ZlBSLD`k zP1VGAxk7%TDt!)G$(mrIIDMxepC0SQY0`;=$|e~oS%&XW_!0Tt$|j6!{P(oVCQ9&s zMC8mSK9#r~jKYsG1$sPbb*Ua2t@a%zaw1)i9!I+G9k80uE3^{}-xCV^YP<0HMkzq{ zsofO4FR~P&z&z^ux|^cgtC+~Xq?@7-L=*{m$F;SaqNY_$6uYOJqWmgcC=?o~bjbyU zwOQd@Lt(0jA|YDg0YhQjoneJuLFWjC!kVnG$(>?hM-fFrw8AcTiiLBuLL3+i2>KJD zP*|PavL7J(Q3Qd=x(fy7Vb`T@iYiq#k^M$DMcsz!c8`R-;i_Dq=%uPAioLf$QRk{$ z3n(;Dy(AYDR%eB?4TU{K6baD^_ZbRf?+Pm%Ea*a^Q1~D#YQ>$j9f(8c zz3>k!iTzZY`aH+5RXV9dDCZRm+Zsr&RMNdtso=e0;pg}R7Vd&}_SOBO<{_a__X7JW zCecJ1Q(t7y3k4>LF)xNOhO9{hrXgbjjDa@{S&Ilv5_@JwqIjmMh$11{GdmL{{-f`M zXIP)xwirGt5TMR5Ru|PLcy-VOue|IS*Gk)l7Q0GIX0n zH_Up&g#opwnhaN4t07+OsP{vHswWia7VHDtL*j#{9}*vAKV+hc@PR~7VR!Ab7V#Ly z^cXh90B^w`09zkMVc`XuIHSQ6VP_;u(Vq*Aa7HUQ7YJeONk=r8?}h!x?=AH9{!RZP*!4Ru^Yu@v~#XdM@7VkKS#>E~m*|A~`U^42g>Tqxg9 zbUzN$eT`(Q+n0`?X`uVDd+VraIOcwQBSp>}s)IjLYhs;A4Wet>sKO&Ay(jkMNL^GD zp(yaVh%K?Fgm1{PIaIbKhF2b0g6JX?8eZU69@)Y%B*=>c56ER3rOUltC~~#oTv5U? zBu1`x3Kab&vQnYQRm;__K+*G$l=Eoyn%V1BK84%>QaoB0Y8#`e;olIE8(#50 zVj{!YZa91TBZjlxaQ2Qz49~jZP;&C|S>9oH)H0DXlhP5H&($(fLfanv1dstnE*OPi z_$hNmO&hHPI)}2M<$5hxZXZyfXpPvnQ7EdH?wVGhsP>~Ka(z{x$o;4>?i>-&8KD4Y zMhrJ_K*O{&N|egJJ+^$MbBMa4{E?4xSoBozz@A4L zNz*yuf6PP)i9OlHpD|}TILYAnX@-K6Zy-ZXJ^Gy0Jys{|<{U8~C=~UWuGr5hb;Qr3 z0O!?7p`gYJ1sb0)g?q2AYz=KHLptRKW=_iKehz};3CjgjxC zm3%(=btJpcPinv;HkyQl&r3h^!`T(2I?_f?w6A9(XL_Em4(W5ur;+ZGlbIHv4XQ>h z8|6F>XL>@JxoJV#q`tVvM(Z9o5%zURf5lA+MQ~mgm9WMqo-k2DVJ|oskHoZv?b-N* z3HIUMLJ19VgIn1B5rB9wqv5Vn}^ijoCiZL(2qSGDenIzD3} zSDo&P=HZWLyDRz&MTPywTQzC9A%t%WN|MY~rWi_4yo$@j?;OxT{+jrOXH68>+3gE~ zf${!BFudvRRFA<~@+|r%4Px=FXHDdQg(c#(LK9oo^f|F?F#dpL+n$4m!=|O?`Z~Qv ziDJqqq0*d~jaIP!s?VD!t`pG;K0WGw-WWJ^8W)XhV9BiKBX@{cxi1%A4;P8%xT5lv z4iRnU63$Brn^{l&7mN~g8YSq1tRKJduM%)kf~XxL)TjX8d34Q$3%Y)S$}Xg?aRCuC z1Y0Q^ZAFs*sj217WIesi*oAYvXd>w8#is@Tr3atPQIcdoDYTq9L(KSzivgl$$UoJ% z`9(&PdM${iX(L7pHMGbYtk{fUIxY$80-|Cw!sN{)!*9iA%v=%Xh4t2>B2idtx-Q@! zir2B?fl%xrT4%m>#Y#Uwae&l1b5*O2@B#bJTwsTWh(dP?ZQ}C!H;il@Z^(n6I{Sdf z4N8*I77H~>P4^L%nrlC5qvfoO4x2Imn;My@a)w|h3Uzwvh#Bjc7L81l5Ze!<4ISm8 zr#C`imqm7-FW_;fX5!Yh);!x#u_!q!ROyaamZf^l>~y{@vIbv0O!S%dPm>v%s)=#D z2IhuD@RN9XSrBro&&|T3<=I3=+l-OVM?{W?4uIR3pP}y)V&Q6E^4(@Dk z8ZPik7JIFqm$w@rqUU=&YNJ{tj4*>FlJ zQ3~rI6yNcGr04_?WrpUaX4Ru)f!v&F*oRS8#bp!KyH?dkRhyUy?{FW-X$m(Io#tYYI&+v378C{XLoZY$`TNe+`XcmSr8+Is)>BS_}#CFhB z`IE0Te6>)gmkRknaqre9s@y}k%Y{1qG|YmzU}I}M@OphA`odzXZKGRI77+>6YsxQt z2lIn`<0Tb~U2T@=0+qHeE|chnCAW@^>a{^zA%z0Ev@uboPNZ(|iDjz9pJA#SEYlAz zxAfl$MF?d-4YVTL);1V^k_O^5EvzfH!n&@nfLjQzpEfYC_!Ybya;MRHD^?ixwm{Sw zgoBh`I%s{UijAhXHBmw_SlRXHkagqJHmde2-igt9YvU?5%6b(K`h)Qb3EzlbW4*h8 z@Oe5n39*UN=Gm$H{hoZCXOcZ-Z4@LFcn<)0a9*Q#Cn~1oqQ4Zy?gu>lB2(-a$;I^i`F z#ksS+89sya-fKpm(^>FVur4VLN9@kvjGw(`qJ*L$;25RrOI=UDX2O}7H2R5g^ zRvsU%Vvgr^{ct6CA_v@fLEJFI#vU8{x_ImW{(#5gI$(4~>;EdXJR%g1Sz+TyXwtz% zE=0m0pv$Ji>NRsx-Km)#{To?L10D|*iH{b1SKe2tL}zS+Ua-;D4)7Ju3ZJokcGgN& z-q4-tjE%2MuM{M8#x~1kL%+(*&C5>9^kn-pQo+R*B019o9uNN#51r?V2jBSjsFRUO z#bUt4RpkxQ%``X)&9vzaES9ljIXDW-<$iCXn|nNIT|I$ZqQOxp(eakia)YDL*yPL$ zdIIgy+M`&@fVWKKbO(dpV5k}W5QS;}FjzT$+7dP9yp^WBjde+6W%jUU=R24I-64G~ z#9mgm2)tC<7X@Ecd>0x0ZXX?tf=fO@x(gj*JHC7mOUv9~s@q40S>M$6q0f`;&iACf z?Fr=KcHCnuxd{o7Z1x^ID&PA92^ei8RkkkIo=O$!)B#+?c3t{_2ce;)FBIy-DxOZ} zWzIA^nr=oi5V3_a=A>r3gF*V1@0Gp@9NK)T9Zmj{o3nk#**d$CtrXeta6m&GSeTW7 zTiGFPcbCwZ5G`=DO0prkAMK&2?J(WRw?xG@?4ihlAEAIQ zvXlMJo{AO=Z-r3EmfBs9L*y}$t8q_7r#vRIzuHsLG6DS{6xVKywGeS7l7{Pc`%YJ) zS-MoBEB-(wzQiA>L}@xI;p?fW^9o&wkA$N1jdoYxo{DbI00_RU{F4SfFBG6L4)&!l zLwvcH(S=6Ntn!1U1p z=J>sswe2f;K?&xU!kJ?`x6+*B$R z{a>vOn~1ex9DW$4e+3MmxgE9t`d5JerXj^1qyGF8D3|A68g$N*(O@?Iz-X{93*GBRPeq9%bYu>x5aAR2EUBl9A3ujj#iB3j=YN>% zar=nAtboDm!ED15Usk|ZDCGQ*iYwr$tz)){oF8WAX3&ZXjV(5slx-r%?Y$HwOS#1r z99_C7DjGrb5)n;QeD(0*Y?HqKI3dTR?>}ylg8*>U?WO2~WL;6g)uflAj{#}jOVQW( ztb4&jfqUW!~k6UBylDQb>Cdi7HD5&js`OVL+86WPc2Qq*9buGh0d-y*%w5Pk&q zdjgp0Jid@_zeI7w&q9qj(M@!bo9LjgY&oPk@wr$9Nfsgz%Ry{>@R;bc4hU^BkT#YD}kGR-&$hGE4YY(%`@M9zo& zbg9CfEp61y|NkktE+t7>w>Fk5Q|f+$3mxJwtGni01IH+pZg8<5eQ4DEMg`QEZ*m(E zjqtzmp^2Pjb*7Qg&$YDC8v$_X>u8`?2V^SOIjHgjVo+0|&Qy$7L7Ewj0VyVkaK{8% zXQU4I5z#z>z6gOGyEg}x2H<_%v0;30=D=9&7v0}P^Y|MG z%J{nph4Ax6i6ACK^K?rgS!F&Rf_=++;l6Ec-`CNu9laC{0b+kIMJs^dt^4&GwCjHp z3Pa~d$7Ab;cmscf2n+xW&5g%h5=43hU^wXwaTZj386A&DAie7SBV1acKqd(Vv?#K) zVInX9xo#YFMVdh;?0dWH#P zlu$r#Dzalvo-e%BLLuvG;(7VWd^iwg)fr7J7-Dig*;`S^d=qg&-{21vbS2-UUrCk< za=U&d*{&;34DEU=@{iFv`kP$A-ipR`HBszGy%inkY9gLxN(69G&oXfz$t=?Zf8c`q zE+4V7e!UeXjn&Eqnq0GcE1JWS@T7ptm~!0y4}#eAA^KIfVMwU7Wu;GX>Cy}nS`#M{ zKIgwKek9h+lM-f{c+Pw9W3lE9`~hnQd<<*;Stb=oDOI-WQl&J!N}6XvtbG5miQ;(2 zh31(sKVM)5QZVJcPmJK1$7#K8(I=%BePSYKBl^LFU*$OYiHQi5WMSCG~Il7YMl1ek0#fq-ilH`HIZvqZ$;mKY9jlg-im5%(S|kICY``k9~#Xn23!YR*LH&T?QRpht2+dC z2wvZ+EBd}r`1Ftltp&(o>Z7Poavw3ds`OEGt-Fc%p{KuO8YVQIh+_HiD5(dYEbw5o zQe>Ni0u!g_kJxRx>_nju-N7?jA7s~OH8zQ`L?{d_F!N!&jXg}n7dqVnoV!0w)b;Fc z=I&pwr-``NdwQC*Iz!FQ2z4?7jO~fg;9=;1NDm7|p|?z2=mw~C<}13%@Q7aJck@jo z^{7CC=d0gz{UU;Bof#dgZZC8!yb?q|nuHX8@U@QLVcyf*=vO*Tl|IFe*E+sKg`Ao0 zU?%P7mv>Wo;|K^H;KUXrQmB+${*FF)otW#V)BL+QbNiSmA!#Ci&U26pxdcuTIJwzr zDRhi;J>3@`NGJu{OXpZ^c3*6WP9kz<(Vv`kJ~+kTc<35)j&VM+SL>tbFR8qBhps$a zcfjm=wvVDJ{iGus7!`r}QMu5OTlIqp+z5Az^gf}`RBC45cLU1)QXfSRiSRL@039;B z()uX+mO+?Ro)zJXLIFCaW9aez5<~g@5wSUaR2bDN0)5=<>d{Bh-2Ntt9neS70Y;DP zqv%yZy(<*DPn%sc`zVSVV4~P>`zUHM0J?wZqbNmCUZFtmFuM--Q8Wl32PRyatb_Qh z*%j4W(Uk!v;@P(SKndbP$vsvmwEU{4u^$J9{dZiX=Y<0Gr>^y#gJ96vJ}NA{909s) zc2(@FD33w#-%Aq6uL^~xYg*Gm236^+!ooKrK-bN#=lUvoZZM4ccUO6tiUmA#myVW* zuF}VXt}<(|?kasO=qhT6iF8-#!>hZtQ&r=_b%LU)w`{7PZp5bi1iIT1S)?+Iv- z1%qSi+buL7e5=z1L(s))o8^E?xxT{R% ztQWy42FF96Gv}#c$fvtX;!a)Z+Ct&hffnv6S;M5O7?_$780jjLSrs=>Q<1h53QdD7 z+*N)Bio41yB79RQKtn9tRV>314`}K%5oQVnXt;$(zQAzlDoYrRu416_BG4l&+*M8w zm#%Wp2;FE|NM`CbsjkKN)D+*qm53W(-QaF zIVPVQgHY$C2$eFY7jx(chw(-v5d23u_@5bzvwyyzClK<|aZ8iKRvJ^r%Aylo;3MPk z4kg=@Lnk?XY#h2wkvu*0v7%RX9gi2%Q~3KfEn(a6ara!o|N0O9Ap*^>Wzq0rOE%AN5V(0Bl>Jw-a> z*8*B56kzF$p(!ZTKBF%#+tQ|;6bjTii|(k=d$lYoJQy%*C|Jh8cVLbeM-{?ZnS@Os}=^v-WuwU|Tp@1eWx+HlDvTXGmu>Fe@W4GMG$&qJO&&kBO8l z)$|qA0@{8Q|~h0LMTHjK=%Gcm@~8TQ>i zBvLaysag6dn9g#BmuHzM;lk$_(X-P;)eV;XF$-gJ@=PAaNAvsChrhsdK;t>#`$Bfc zbdnP4d|^yTpY!|BXCMFA)|B1*m@%Kf*0y5QckoSbM`N6rf>It|fgHon#Pfx?6-b zgaR}=iigj;zJlQx^lFK)zEFV1MDYOFnL*IhPK3!q0UED0{Q!{Tr9O)0NfSucgZynCBueKkBF3vrt41 z6i0J(h%M1YB+i2!7-QER)kSO;iXwi9;?X(-ptA86I|0oCxt*y%Mv2!?=&Z(Q5h5CD z$6))_JU!SBwPUb7I1g(=Z$^eEKqL5^LG^ENP%c}#dXUE2vEQ8wF4kqdd<@b!<}T9Q zE}`&7-Z(qPm6PD7XS*|kG=Y^i{?^o`a z-d+MedV7iRrMFjJAie$l1=8EQNZGxF!fxs9!xo@D(Nji=aEefXq_^(`i1d1Lu80>2 z1?-HS$EE0n@Im@N=3*xZozcpxr|4f}8Auu}Fm*ndl~a;lM%! z1Kr{_j9Y|nu;$`3fqd%YSlQY}J-{mbN{{#aetPjD9`DC+qS|7-cb~`W{IUExdjvQI z-{Ne0uAjy^Fh!qRY$9h?J(|jSdM&|*X3+B%&0y()CD`*^V9XO=@SE?amZH)`3G)`> z2VCe7XC1K=Z|_0@I?Bm4Nc!j)=eWu_xaDY(?py^99;a(9la>oEla_l@T=$Ytcw1U- z<1#D-&~nM5MOy3j?_d#+x^GIhG@;1e-NB=7_IEPsP63?9rD1{^B^2m>4jy&4ghAs) zI9Vt_{T*7tEaA-)3fTaMo^h54Z0jet4`1z0BO`|(?P8s!yf zj!>XRJ9tj$1{7Dy5O$6LjnSaJVbD|&&JYSshjh&={$RMr5PlH>I;u4_0*If~iXF<#rQ8s3_nA6&OE};NTbMVX-1c)cCwo=6B zx?z_>z>`+4{p6teXW{m7BHMEhrpgD0^&SD+h(M>BU?C$}%v(?kzy-dta6 zcOZ+3WJnu$UYBVMZJ9(D9GJtBSLnBz7aW-1Mj(Zm3hUzwJQ}Q7fg^wzg8tz2)fFbP zckQR>sHnDxYM6S(;TqLX(Tgih^F zDlCjteaX(lLiA5EEWGd&s$W+B9OJz~-4AeF=J9$|B!<}!exr-4-B8&Nf6}A6 z6Mk5X1aAg8aZaYvDq}Cmi5VPM<=lbE`K49Zj#*+1v3KJ%!%wStOY|Q6eHro+$Wtj1 zIiFi?qJ%n2aQ3f#l1q^fIV z&Nm>n1OkW3DyhvHl*2LCT%@gpLeahK_3i-izUHeUds8Sd_wfL@fH8>OG?8Tr1*RI0 zC%3JYUCoATjb=1J!3gj}+=`*K(uy+y=55N7rNr7E;Q?!)PQUh2;44l`q)O4 z!!A7`(lbJ#s2Rs;3P2pE7e#hOC@?KJPDe2Y&1Vt6+Jyqsnw#(ZMv2q=HyLqifZ`*- zZ8%O}-z0I`4`7bdg70*kw&ytgZj(&R=Qc^4))ytu3q?I8PM`RN>xnpRCek)SVc%;U zry0M9qUItpAW$T6I$kS6Mbib-$R%_zFCLqdkE8fBsfgpLzr&eY=r512o~8*&qNT}O0ZVury&Y}(V7|52~jr^ zf9t*T6zT#F{feCy*-g&}{$d#?i@HMn7hABH#$LMqO}4#T@O=sb7bkIs@kyMDTd~Go z&cXaC)HDL4?f1MU@6TVgP1p*D=$*VbFX>8qgu-n;7mly~0u%@SB9W~W3QT|lUu~1X zHy}R<1nnq+|HL+w#@i{YMY>+-2SgO)rrw>ccVU){DXwTnqi9j(^{Z0?qAy>-M|3%7cdHy{*?3^ z3gso@e332|3YFb>g%|{gyTLY*9S{mke?9Pj9tJg*NN*z)pjjHUD-22(p-(74^80)- zJ7H6}M;VXV;SMD^@-OQKi5T}!o6w_P+^KuiX+8?n2Px?&bed17Y}zSL+w`Mu>vJX- z_SR^ZNk6l-{ZTIyl^HXSNl)Fec)e%_YKng#skcZ}#1& zB5`2V5^)2e9}v++69?9_3=4b0c-l5RQ6M=UzxFRrTxL)7*sVQrnLV)%DNIdz)S%1k zjEZ|OkkDTy3=@s^;QjKC`l0)#2@l=hf|a;}JGlO^{Wx-XXeghzczrKs$(1}H6q;9^ zw9-%DTmT0v-eG1ublHK||Dt|IYv`KutW9~k73MC}1;#qhl7jOVs@tD1u zlX3g;#SE?C98LBk2R~*PuF_3;#l(-_x?l~A;XG}#>o(D~717gp(p zmuC6^0{xi1L$VzaidMR6;u&tq0h2z7aT;*$C%65jTO>{>&;@25QSK>4d|;%hEW)Zn z0qSnn3KE6)piszqnDyjcLwNOsLe@*4GkG``ti}YuqP&q`O z>(~GkgA8Fw1Zb!RRX=37^)C@#7Ya=)baz(d$d!ZA79}G@)0nK!N4e_7s!>5Ap|Ia*76X-3*?r^$`;6< zKyJr=_e!ZwiBMEVFOYvaCbO#n*(eZ%nyeEpLyvaz36Y)=3PpN>T;n(z5?k{ZMRr9f zFnWRfE@RMtE#gB z^*AA2YT*g#QuRg2^FmQi=~CCBg!BSgq-})4KD|J$eNq%P7m)#hB3U52PoiPCqUnO_ zC{!!L0(m4rj2R-b(L#aI3*@!HlucqeZhyoAxxo_{+Dj$V)tmo{)Wv*M{Mtz!s*Cv; zdD1D99KBl1KRG)7lo6~tE$if!d~?^PQ<&fMl(&&N@u$I|5?;{0gM?lfr_dH=tu14D zX)9;ha3;Kb&Z2ExXwS38YPmljv|V%-QzxPrtL4+I=_Gi`>o~G^vZA}rVY>X8XVb&1 zC+{5mEX(B^y8a%aaN;_}>*ZO1aX(lj!j(b++NgNJyyu*B1OxMfV9>785pFw=@_6O3 zTBPfRLenO_q-_Wkub7K|*0ES%;uZ6R^Ri+-#0d0*3xX(7Ji)mxpcCQ!X7t~>IcUHAf0-IAnV-EVOJ*M+ zykzbv`G*OGHK+BGc`DFYGH<&m9m4<*5ikms9(L#ND3pi$r6T=aC{*rJJlwYiihIUx z5gru^&|y9Q6@)>}CGy(~1*nHUGEX zAh0fMENfYD<(*3PB33=K_{qkylfjIC%z1AN|5P?!Af}++=lr)m`tJUNg75Dor-- zsx;Ye;;bV=(HhcZze5eLnG1i_4KK~~^fl<`Iq!;Ovyk>RTIsr8GY`Keliq5;xu4V) zR6U_U_t5LMD`C)+B79aTKs{xNlq9^DghJL!*32!0*G?#8ef0Y4k?ROP3}kK*dV~Tr zKwA(9gF1@PClsK8VnHY2brlNPAXzj2EWBMpAsedK%rmab1aG<_!_O}Q+94EvyqU_w|ByF~|^2rfgwgJ`a(3kpTqBlVj3)tgumVLjGXa!=H4HWvb}-diSZ z`FlclKuz^=1Pv64V zd_)`g4xSOWjCF`ydKrTS^$vC0d8^TD^>6tfx`pvW4=D$ET~LF}*mypwFrxU8z{B$~ z{<)L=NP2niE`?4o;#p`B&eq>PnCr+8^G&}1ejZ|LbC$^OA)ri7v`c#+a6 zo%-?UbrHO_TX`(fzh3m9*-Qx+z{#SkZ2KE#v%V7NDyzjmKBxDdMLVgZP`GNJUam|w zo8@GAu?RN`1?Zq&uKa2?>o0!{%sRm!+-139vEYX}c{#CJq}zl-(;?+@_f_;HPnT-BSN9_g8UF`kr;GUuS{~0i#ymQoA%00 zp~y5vFFK}&LC;8cX(AM$85*<$ptAY6+{%P|oMg$j>vAG`Twne|$zeBBTqaRp{z9oO zQqpy(FVARW>}Gv%qs;q|OdKL&?&llEbWz%Dw53+2M-VU=bVOKAGnR zIuIUMnz%%GuTX$w&f5YI=_suw;s!$Dios@%Qfmb?#eXkN;EDpt(mUo~t{Bd)XjMVG zVmP~EI8qU7li}=*T@`pF8Npvs#YCGapXw&ws!nVrT8<|tC%2P_ieyB1)~PkBi79$nlJ6&)q;QeT;oHp=mlg~mjkkHv#hWQE z7)UkJqvIpB2R!Nc?R)%0A{US(iksf{9|aue0xm!y7jT>lc)(>8pwk^W%{Lny=M~8+ zFan;8AklGkDx&r^Mm2oJIqvhw^(;ZsMFt>-BQGm19za0geL*6Sq|S$ zL`hPe2|~-Y@-n_sIj)MC;;>~#%Xru6%_@@+!8Y)@EHdgU1 zRQiavv3Xa@DXd;IC+K(kY9L;`+1Y&6MPqkrriV&J;RT^;;vF23L84I#_nf_Wrx!mCR-F;)o8|5be$kHK3lyjRlG4OYxOW=e?Nk4lAbAoq}|_gO2ozeiT? zCs-Gey&1d!g~HW`Ol0@cT~?1ahHx2K}M_mpdfbgwV%m6VR2)DRU( zRrbc~%!cdzMBRB}-*k_e$||9Gbg$-dB{b{td(BiS1r=4A!B+`(H^VDbDL18~cDW(s z-xuhir3wDBx(_#_?&eMJGQ@~jrW>~?~`0A_7@Aq zMQ2wnuB@(#Zb6H7n?q{n6bjc2H*2@uF1*S@wT#_XFA#wLd(Mz8NFg1r?GiXTg6?Tav6o47ovmp3ptj;V>s|4nVTG1rV6RI`3nN^al9!#;$Q=lHJ%P3X&l>@6$U~)4 zgrjQt3iG76)PnKNUd@;kb^2LO^Q5R5%d44jZ^|AEe4p@;nG)y#IxQ7j5akW3;}IPk z+$rg&pDg&%1#fjTQRQiO=wi6@I9J5<|4xwEA3sL}SrWU9a9&j>hfvB^vDwBpN?yAYCFL#`YA7niiXQj+xg0`Z#1uMEaXh zpn6$3WDf(y&G)AW{}u{RUn@6X!jsZ`2Igu6Mw-w56pG_OD;4wmTjLREI?Ap+B~kY1 z)8XbjrmH#F%2Ag5v^3uc@%%WUu;l-2zDC|qj@wUkw{ESW_h?$K)yA>ct2|@0nofVf zX>K*m_#6!UdebZE=|NA3gnbt56mKX925t;UeAY}(;Ao;1=aA+LeX&{Pe293XC{#1q>6bFDlcq(5J-myHYK3Iz9!#%+9F#*JIgON-Wf z0d)wk7ee`d57rBf$|gH1l4kiWv3&FJqvi%H9yL8L=m#kxeb@OMr1?=(r+?=(Hk z;;!?D7=BeKJh?}=N$rM)yC1z**Q$OHOKcAJ=Xi*-+fLwxPK38lYuv z46ls5K5wW!u6iyN=RJ16F3yPYOrmQ%(mdJ-5snmQeSc#!Iene!23LG~V~jbc@I%AC z&eWT~b*}aj)&=14nyH@+(LEB(3t)PwKf~6(1b65e;HUd^*=vNNssnAV+dfisjbY(1 zFzj#d3B8u@_fV2pU9Gm(U|8RUsF;g+vr(17?BY=Xg5H zj2B)ule05j;KWfRe4Xhcrxvurkk5YnowXg;47$YP{MHcDAsc3dx^2un4V_Z8+ zghMaIug%m z9g8clj_fx?$CNij$LDWi2Ep$J6bp2BlpY*^dO%n97omvGBX%AfcfDyQ*JB?kYVwxR zZ7V;h(N%>4{dZJ6KsY+nRnb0?925$~Pf6`9dMwP=QUm`5ycO(~=F%S}^K;(Y#tRZ4ipg zYz`)mBFVI=0z2-Z56oov1mZ6q)?WKwDDv*r(1H)(S`_T8u0aii0(49pvF8IZ;vSJb zAQYIe5v5|pQLVW!xy%UYe^v5i2}NGfe>xdPV6{3`gr5lo=#-tusv2$s)S-s9G+8J> zlN@@mn=HI(LLvK3WS50^O(Oe$uYn4bcBN z%r|S?HjtY^NixX)TvzKc2K!8+U!yUSI8(7570AudWBaes7?1j;nJEt2WRc86V3y4S z8tL+w@iQ9Pxv7!R=HNmpO2ObY&0{8KC^zKJ#)5=)a4FZp^!U=)(6KSt)^3=tM}FRl zq;bp(rDLqSfP<#FdA<D|jPk@d^4{Kwiof%jd zLWPe`Feot-!yNacrytX ztz@lTv-P%VkXAEb1=8383ep;8tj^)Zz&b`3`pU&a@olJk6~CDhN^~n74fVxBZ(XM2Xp3Q0W&4|FX38#+RB-$1Tm@31ItV2nDs#rq4WoXE;DC1 zK$PGI56T8OSD&-ePH+=S4d!TOMu(s;=}+9h><)!I0Uym_!(Ip>`V8J&<~j1rl+gAv zuN-zU=UN_6DYTnYuXRHFC0qa^J>bruVO)zZ!A_|27cZ2@F{_kW1z<677ISa!3}cGH z_0l|MH3f?jVy|%ZHZd<|o;TqA`d_Yw^n(*>hm=K+cqh;XG)fC_JO4H}^6xqN^y7hDwKpF#l|aU0ulkU`KC z_k=b!Q7Aw&wWj#4hNgBR>>w1N*;>=mFz6Q%ZV?JliLUmUFo^2w;w(Y|+7+YymLojB zP{_tQ+1_Cv8Kx`};ZmUheXC7b6$Tl?*VbD+!jt~maB@J2`2CWp~8leE) zkaj&Lypuv9>!D5gGpry%hS4fQ0otq`UHwy|QZIq6QIh2h^S*YW|14)nlW9d5%iJ2~!KxdRyRhr8PZ`UcH8^Ku(|Eo#6@6dd`v93pJwcEbGb%uHG)L-ilsk za@^TdQCsOR+Z6xa(;qk_{c2A|wimT&aYFId2EX@YUo$|_XTmHNis~O$uJZ#Fwdsw< z#T%XVBHStzpd*Uk?sNypfj#p=DgLP9U4wPKW!K<(Z`m~%+{din%bpcABtF9cBfAjA zec*B~^n{?!2t}bsb)lCS^&brr@ML@3K~KZ%Y$_Jz!y4(@M>NbyZkT8K!mFunpMK^z zt;SumQUNnF=$yhn&|0L@@Z(agyq!FBo*@^?B$ug!hyIsH)%XYm}Fk;R#VX#`$Z^ zq)g=acpv~7lnslX9AqXZhCOswJ|6KcJFFx4!EEwUI~z?Jgp-NaappOT@=?gIET;UX z?QCQjj74&hhzD~YdulMspuq}&tid=84LJ(Z#}9^aDN~SDH^Ky|_9UUG^s78DYyrrA zYk;ETBD1}uT?0&x>w$ratV7J?dSsxYmxo|8=-Gjal5P+KK8$~7pdyM*9jNHjA!f3F zI8af4(J)kKO(Ghv$o|lsey`)pcr!eIZ#odl#}PGI!ICF zp=NSDFi6pRLt)6{gA|ntpuci;7^J8Wi1!C6+5rR)3wKIW*Aj|)ja9DW0~9$5&BWuw zD}@Nya=p-qojhMz^iVVHnxjHhS@f(_2%jE>7$J#Pa!*-Uh<3`LwcG2>P|5j) zP-iem2e}=q4nvjD5J`V)-yT!!{a1#G_j@n`BVC~&PU&ha1Omg{2_XKv8aUNV)Lh%& zu7#!%_FrK8s}489{{q(lYqCH1uQtQYI+Uu4j(df|ofq{aGio?SSsV=si11UP0A1Hd zL)HPrqil)P;%`05RvaOtY@-n}$}S!uqwFM6^OewuQTEgbv>cDJV?{bqC<^^skFs}< z1S;IVK|J&~#zn3MaAjHmq56r;V(m%vAY#(bHb2(@XU+P`o?}W<<1*$CnmiMuEUD3RT(+yKj)9BH=9& z3Imhuqa!5KMRFez{a7Kgl%S`qvP3WII-Y5zsT2~Io6?b}1vV)@9c89CKI5d*tB}UV zf=)({Hd9;^PVJw!;h&YG&60j2H;_i}K>wfM;e?%^-fME$ zO4Y}h$^OG2MTuhC142>9mSo4~{NuuUNhm~b@vG@~#=xg#O>6wxGPTzAyDU^qOhcRE zv`;*&m^ubs)%b0=_?S92Ix)tmNIW+3@C#Nh{PEaW?C1po-M- zYNHFxYpbbIU?-x7q2>Fr`jpSZDBU~`eo5=(4SL9nsiNgLyj?_eWqRCc`u?V20EOJ% zY@Iwk4pTw$EjFw(RK7W04C@TT298JfJ2FVo3t~z$p{RKaazIpci#XZZb#;)UUy-Gp z^Ii{n(x^bHQ2*6`RpnEZYnh;{!uRX6oM5KPizG*7$>H=we8786mJFO4R0^5cnc2!1K7 zqv-4+6b>1$T=?;|uNef_7{Y=G&_u<4Is{NTKjBm-l?w8ncK`CzOg7myNjpj(=X@8b zaLCP6h`H&L{t->B-F`|GeV+FJ(z}H9?tmsA(GsrbU6Uh2c?s)Hoy=R6-!XhN5(wb$ zxDobEjx6)TJWrk{-Rt9L`T!M3nMYqY%D$KTji0Cu8uZC2W{OMq5w*GUyp29V$``-^ zhc<}o-;hqn?~YTO%ZII0x5!L!xgnx9m0z&Yz#_aiCPmJa?A+8W%H7r8Mn{XxRQXvc ztx%}bOC1r@cT7dR*zse5#bVedp)l*Mh^N36!Z+l2I4gS!#Dm?PqWoc@(C{%o*i{vd zAwk}94|bj1S@2N?Q3vgokAy1i9=$ivU!A^$59tD)APwNZU!H0v2dqyf#pMlD5hmXy zN~#G(vE@zfAQF#Im^@6e&2I?H5Fv9pn^OZGcSu`ZAlMy3#p<$%4&?dLy}l2rQ~-6} z(bW_nwrz7f^chFfjH$e;b?10e-F}+EA?TfEri3E2Yu5zG83e zp3Wl}cs@^;5M5`W^D|7WCWS^{#!IEw!D(w&SUkIPJi)H=ygA`BGdTmamCZQJ2^`-4 zUVU=fN|`edDx`xY@eOVNqe9`g*^#clPXq=4-Yau`g~5tG6vPOjQ1Ok5e{`@SihqXx zZ;t=4Lo5`6QqHQL0`LRgxVq!NR@c#32GsUFS-;ysK`EGK)>N62mAsS%5 z>kS*t;S>gvVP*^@wP)egL%G?CLsL(P)4iJqIhgV9`B!nAtUH zu%arn%@jLhu%d~xv4H<}u%bFvjhJnAZ5pg-GZ5PcD{42#OtA+CE9yPRtiQh*`<^av zf=~+)E!JNpE}3H{mos0{v-lvGcp7`*eGP9V6!y(A^EBoIib1nPxKJoSUuw`IfXX_3 zWF+vGTsm`DV2A&ADhKN!0z)Xp{NeCTU1>o6Tp(jzKy%MDxvvg!NxW z&N}}pyDm|Ru3ZD6Wo7r^vi~lppnA=8Zx?rVc5Z47KGj4?lC6EJR-+~+^2C)+RKK9M zjp}@bS(10J>SL-(MLOLZ2!@F24|>c-qrq?oGKly_p`Ve&<_J-J%+xo&!VqUw6qDoi z(bG^7xyX*Rhs~m7F{F3ezYJ;tgC6_ZSZ=n!#7fRi#Fr@MeI4njj8vT2q9Rf9uE$V@ zb-uFyK3LKBB3mOAl1VDQ+7SMw(0W4@Z4k+mk{gJrD*g>1TptWkv>auYMMg%d$LCJT z_E559Xp&y8SZ6BDQ(fP+(Rt*v1oC_#Z;mINO@b*|GR+mLCaV7fTbGIj6O&;S6fryb zbF;IX2HdH*@z|Y>8|^)TCSANCN)+_3LY4N4o{a~X^y7#xw=)ekjk{*02J_71O!H)i z+%$w4UNHPL+?1MRqlriaJ=y86d2>88f&rW7naT0WCyGj~ioJ#(XA3`UWTf)zW3Fyp zDitH_nMM`h=7?$toDu~v&sPo@7Yca!X9nm;ORFRsnZCi%P(Q7*j5%+mp`6N1^Ux`a z{j8O4AdRXGwW4ZoeT&)Frf1vLmeZH4G~`6!&q+y+z$IO!R5-GT>?sQH*S z`FEOQqjVitS@?IFUx9>wWVx*fezx$BEcakRVNXScBG_Sx{kEr~b@R<+_YYB2-K%R> zTPPgyrG?0jpKP8b)!!r(!Zlj+b!fIv8=`2LU-M8^-WwX^=*$oP~UMl>-Jyur9{zwH1zLM)^aI#EWs@9=@NQP4?peP zA#X1Aj%sYNQ4q3#hxf~9uMOkr=S$>+J)K*3nir*%mO|0i18mY;InFwYq^nRM&f19g zw#Er>iBQN!*zDNcGDOoOMA+QouDVf}`-LLk_;RII4C-1Z3I*avn;y}N#i?s;x(kjJ z%xa;)ZnPO)5PhYS_^^*qAa2-n{eBeQW}%RcwEbtBa17QX%G0(j5^A*nLx#Wi+3Fs) z(zzvON}vh~TPpiGF6%Ev=iM??QD@0IRwxQB{db|Sc>+0JADY!CWfx0^ahMqSWuBWF-1$5dO35$EirNZ5AqJ|F4Ws)UHdV zlI4!h<;>Qdw#bf7`xDIkN5}uC4Np*_DCi*cf7?LG0-e&ceA)l%IW(BN&gG@p{KrN! zFDHgap+~>4Oi$?}qcElSLF$8y4`_50#*0lzX836=r_^`ombsm2C+BRAWYCjJyEs{h zq)(?8k&S*rI?w0r(k8bv?Pkbju!4AzhWa=5=h0E-*H{i4+<`2xPB803gJmF3GW#>I zQ}a`^J%FBK-Wl-t6eM`(n0N2@sA@L#v*UeMyYFRy(3K?q23A*J|E()T2}ua*ypT7W z=Gw8>vmI*u0Z%8IZ-=w&KR|6tUOIhculKx-QhzX0Wm7lpertiIPA@Is++PCi3(^A4 zTj57DQRQj^3Uv>ImoqryM>sb~%Ne{1U~(X~iz?`uWvCI)dhsXR;Zb@vO^nv4kx@LG z-dBtcd3mU!2Kl<6HbT)gR!7P7FT6j5LbfwXPiV=aa9l`09#5Fv9e5x?^*5hMHA zBVPJFy+U64JtR463B`*&GU`gs1 z$z-8G?6C8y=fjn<>REyk9j_NEDir%R+hel}6`fvb)_Xv;WoSqe`ZN)>jgR_R;hU_F z6+I?=L(a~PuL(K+{f|+rE)_}sUxdo2a_HrY;)~J>7lv02jImHEG@_hF)eY7|G-9ze=<|$kSP09s58e;%N!UxPoa+7c82UT!NA=04sN@< zS4rEoU4`I^aByS=s8n)V`?;(No#Jrq|5#BMT+YJJaYatuf2KNkc$y1=z0#+O#z?LSLXmr_!&U!NMF#-l+!rMG zZk;=BEjk)A8lYIp#bf1@aLgj0OIj9={9ZPF^3)+iv+w# zC^C(zpcjX=`)Y-)g+eyB0xu2?(Nhs3UL2xJe<;i`LXmG?1rCcjYb7l9!y>NRSV2t| z3iLM>;^CRdO`eQ$fy0qsj}j$p<_*x6O5`Ja{^Ibdw9ZUUzn`vfq`bKfpYYzM$mye= z+|*MbPYbxczE`Pdh5H}0(Xn-KI)?izqTDLVVOXyU5ySn}e*iG3lnwVI3l-TUYX_lF z(Yu0cd7+{^e>RhSb)lkIku??i3=tJPVxRqqBA2l45DL++wGoh*OWqK!M<|r{slao| zo5C_gu&mrpbOt|CQL*6a4K3$C?7ao_;)ynJH*x=rZU@i#>3jwBx@F)bR>ZDHYG?Yp z0v4(@)}yait~f|*Yb4a^rwP%>^X_^xRjw`Eur!q>M&n4)P|nS(>oWtjhG@Z}YBwvm z&JI z?3F@A-32jNC{!$p<~_k4!ZJjda`zj1f@HsMn4%#987~yM7e~jxHjMWifY>XN6GDMl z8b-K>DLN^VKZOFZEINLa)`0-IEE22u1c>jVIY9pUhX@tKi;-!=6xjq`Nhq>?A059! zD@M<_TO>7w0q2p$PFvKx)Bc<8F-2lP6;?^G&Lsfs+G)}FoS1%eObcju}Qk` z%cA*Bp)hbnOhor>B?1EgGt1X;fgn~3g^KYp+%*;o%Mc;65sGU>wpA#yO^z&itq2SN zGM6v8wz%V2p-?eXm;9Kp3=uMy^KN=hs7?EZ!Pf05S)c`@$~iX zNDWsoG{>{Y0b^F2pM1onuqHa;&}$JMMKVg}JT4eFt2=q-C-=>KmcZ(v&Z98N!(L z!xa4{vcp1wY34dROwo#MX8l>jP7y^yUgWPL{(u@?)}I238Le%Gp4ME}!`rdAKv{J} zmLwFIHeA-*+oi0!B8r6QvIav9*{=;#G)_P>g+h(n+W3_Ec`_t*A zUJ9*W4AuY99-Nz3U3Dp0@^%|nP6rQty%mPOKen66`H+`-t0Qh3J+s5Cw;u-a5d*Tv z4_7o_lzcA~h7VG%M}{lP*-ohFb!11H5}p zGZ?q*H+&~p7G<*)!tDHSx`MTFbKY!qtd>phx75z|_-Ho!l_;U4EZaQJcK^>P8<$nq|ib>vGMv&ObB45`a-mbPk_wZ34bLNgNwJOc|qFD^*_AR%tt^raHAyb zLhwz@SF~Lmc}6IFKU;Bxwg`h<&w#>v&_aT_ei>oh1xJ)QOKbUyk`<1W^5z587H!xh$ zFCsfD6xn_>xke9H^xb|l*{4KceiaNJ^}0g=mapf8d0i+J6zlq)7nUJHx#iXjMwsFo zj36aSt7U&yuJv@xt>KtEzaNJ+bj+>cm}_x>7iv_(-+FwEl-o;NO<0ZohE%4TwwbUH ztx$@ywK${Q7UxR0oA5bw8kj-9C)MlD4)WpUohE#RKBg3>IuIFN+RX)ZIEW9d;}wPB zwAX}tQzk=D3PDFIWu?c!DR2Qwr}$>fDu>LJkdy!pUvhblFS&Fd!n&Y<*?ziVdif3; zeF0=CSbplo$7d{uQA2+^^)R=4-bPOzMmNAeZua77+F5Ia!u{7xE`0RY?XX#Y_qRf1 z1_tf$m}}NYiq^0)2imc1WgYHA%p9xRCC>q&$b(qL$5IbY*JV~0iZXlYJ0qP(FplG{ z&Ywm2i%@{@Q$XP{DkHUFFz5}28se}H6-c93nyGb5*X4P8j2S)i^&@7|-`%9qR5K>Z z3E<#&5Q4mc+13-l2Q@U)jCYiEj-t7_1aO0GJppV?(kzyyLYn4D(K7nNjQzIJUNEsmj8Lb;el7c=MMW4MW-vf|yyNoMRut~`dpcGB@`h@O zl(E4Qv5v_|eZ!rF4+wQ52B|JLzs2=jyjv_^yGhe4f0Xp{!fIt@Aw zkX|v&5Y$Yeu<0*LJl+S$QQ@yevRo(-SGBX}3Cj?n${6}_`uS&(ofe90e_Q^q)6drh zWtWBk`h+gz#y><-NaXa;wA{l{lIU3dWw}O;E9?IQ`NUh*)9C-HZXb{OG@6-xPfL&d z#f1F^N+_(N7k%Tcm|99t8{_l^iGfj8?v+dS7L#fC(PbLQM<9$h(gx*g?JFL93VY!H)`fY@wcSYD+C_opiu0KX7YRMoxU9bOIYuY9hpq*M% zA%kv?Q1t#c8uW=!fWEPjx9}vAI4Nsc6SVGsOl*Dsub|J3bw$Xo=+dQ7Cd3 z+g#H|DthI2GsVvxiB}Sig(DT!xl1csZ*%P(sc81^W{N#BQqeyAaekzt2QHZ@_S#5A zUi=X?O3}niDChQ3il%<6t(+wkSE)ZB2nt6m-bunleg}`V%I@`%^^t zg;0R@Y45iPgC3EtQC}!PTQq1WKxKn1VxGHI?esifYF(m*+J;w!$_yH(`vncOsVp09 z|IU{y5|E#%s2~bu90!NDDGK<2TJ68ghDv-!5rgE!DmW{pxb?_OP{*>=~uQHomsZKl|gQHnoWZ zKI4TVc7L=J%Q`R9vYtXA+gko43OlC_6^mDX{$A%O=baOF1mT*$5kcXHh!b3yn43l% z>$Kj2Xw-jioroZQ$IfX5xm9C3Ln(D zPy67C_QB`3#0O^p;d-_dt2+oqJ;yjAf&gagPbNzRlJR5til)>dn(M$vDfg0v;;_Z1 zlO80E9XXooz+^HPJPw?>4!GtL(!o#`K;a}Czn7w9vC53}XJaOq^R)uPt~IwvL21B#rvDcPP7UFH1S zZ5E=00?qD7qghXfC<|3Kt zYrlhUs=428(Z4nDy4|9GYaqs9A^w7XzBp!;(1<$}UUaa&hr23D5`708u0~xI1t8(A zpccj2bD%`ae@)gH_fyZ5IA)iKh z!D%6!WWf&%Ev$gf)!S*IggVu@3vFYq8^I|Ar;|IIPBAa;HX{zMa~!;dG&)M(Xms*H zx1k?Cgi#0~`CA2SQ2uxusz-MGlFO-|bQP?tG=-PDNAoYa{C=B7|B_3!iWc3t2Kbz0 zf>*C7ZrtpOXt!VcE4nN~L!*E$_g6HfqJ{Y9UbX;D_7_L1Fzk^C1A0We-i9dFLb1Nl zik^zKkfYydMHe1YtnZL=eLGrF7Wl6Iit0S9_>QOP745h#I97DU3&r(MlPY2;h-lPI zcbbP1r3HHljcCb2cHrb#3srtXq+X%y;9)HPHP-O|uxRu{OC<}7<$@i?JXy&?m77T3 zunsi-@MsLH9V>BrgyStE;K`R^sz`E8TBn=Gun#RvlUe(uN*3c94$U|W2G$jlem)MN zg`t8bvyFZ*P&`fM@)sc;9y=u01GihqnZr>tJsQ_{bhzC@3I2zb+|xnJ_%ugUz|f9_>0mTG9F2E#&%Zw4(a)7K*ivQIr`k4VzqDH*61+ zYuac_N*tr;DG@#= z6rjb?t{28A$_B{MY>c8J@yoJk*ZX4>O-ZnjD}9WjE%+laMv=9$jK-}*O|nqb_8Y4U zzvuK+Wz>WR-DVo33U^(#XS{J19+oSo%fNra5A^vr(*Q-iv?2`%}5sKE3 zU)Sna6@KB?==HO%{$QbS^d8+Bn}KrRZAem0?dZMH#BWE|t=E}02t}qN(GlZSMk-#8 z`8=IvWGEIVFW#gx8qU@oyZW{x6B_O1B~}X2LyCjs7wv`Nw%-= zMmNL!aYTfMMgd{|I0TLOxiHr~@HUSz1>(q|F+9dJzek3akM5D7rMPBzXu&}8)jcwh zM2X{KgrYHJ{;&%xQl#e(JNmf6jujZ{JTyjkZF)qv>+l%v+6V8Et{s1`g}7@syH|oK zQF7N4it?pvcfA+YW0zGG>AgZx*6*z}t)*Yb;FRmbiRg@&I_7gg`H&9J6!SS0rhy0d9AzYUxZIX*dR5(wl5sEC=z?vs&5_KYiU+pJBwA=K%o#b!9N%KaE+is4E9 z!cl)Kxq{WSg3vyGjG~oNr42$+p_``ojbpHj#fXg}*)9}_TLyyP8pYO9%w3`14Z2FS zNe>A%_F4ajp93DgVX8oU`^y%sMH&e{E;Yl)-R`>ferR0Pek`gJyhF_AQv%*swfq z#%B6aq%o+_2~OYffJHwp(@B2G_cGG>r51MhS>}BT9`axVp-C6NCpCoUYL=~vl(qBZ zxV=8FFN2ozn>O!*c;523{bhpnESLy^_C9nP?}B$(8e43%xlGt7KUDTEcsEN;)hv|I zwiaI8y1Rt8I9=@CxaCM%{3YXc>?zjQ>R}6Ucb1+`XBpk?VGAYD zqjF&!E!knCH;s{kLjw6bgmsr&x8U7`!q6Av1WB|3miOHm!4`Q0QDCIt|{=|BxBH zBjsiKE4D}V2(#82i?7%ou3^z%v5l41oFVj|U$Ko7z9D~~i2mynHRIUQywvT!eVxUe22E$HvR(}9eYIk=NgNWYRNpd+J-H9N+@{@Z>BuB z?&Lgw>L03wCpa85h^?cmzTD;-JV?>ZS{D7y%RsSjwosfhSYCnn1j2mbeJ>O;K7r6` ztfID}aJ4NyeJpN-{cx>a|$nBC}abp$bN9FqH4l>Qz&FZBVP*qXWz=s%|Mj#3aLnPKE3x}wvS=k`@nYn zUDBAS!)L6t6pZk37aGG~lAfs@d7$J!OU?7AVRI`<3OXt@!dWx8z!&GN(!fH_fV=Z6c_EZj#LUOR%+;Lf%t>!xA;+9?ijwQ{q;I7LNu6}>}5cer+pQ*@w#h2r;*LvYzo047ykEL0oGh#$KkPuA2p~8hMPH0S|~2t9V~k+(fs{S$_S>%wO$JC zS?MWw-xG3seIEV1(py2vOObqB5;C6I1D>*wSiEt* zFn7G7M3Gbz3cEHcSNHLXE<(3`=*Nnl6Jcwi0PRz*c^@l!@M#P2F{tJuGcY&?b)z;P z)O+J;1Q4HKdQ(syq0m&S@7a7yScV9;{__MAw{!b!kH=310)O{NIS<9*+ql#GRnLH@ zjUJ}cgi}j_5M5$v$DSJd(u3)#OJujn@c8Q>>~=$LY?9k1vUfH#j*$@5hMfiu$i}gt#ItQBVzp0)0L5$0AZPg3^8D086qU`w3OL50!PgvK&+-n35Ca z+aQ&wrwO+i9)89`G2S$fJAB(9jWnTGkux>m4RK5A&h{0vYd(vI#_%ZII5#&tEw{6e zzUA$y085R0WrH&6*&X?r#{kXfeE|0=Z$={kV|>*Ic(Ntd;GW? zb=);8Rec`C`tzE~m54NjH=x>pots6|_{@Cw=kXl$1gqDh0ex>edCE!)0ON&^P9H~_ zZ?2%tZ1T%2<3bd;=vghEB3>AzwJ1#Uj33)(9b=(%S8E~Z3ShKP%j^RY)nWQU%_c1?72ai`kZmzVOia~l;w?Z!e4kMRhbeg6qEhK5 zFQ3w$k*yq@@Ld z^qtxDq>bt)A<&HA|A7^BOtO%D&v-?nPU=$U35C(cX8X+u(QFanGK!jHe{_PPFNI~~ zf(X8c#?2x-B@`N0XpM&Gh=`C)G#(e0kqaW^3@;M@(|N||B(7U4R9xuv(;D{R6nNkv z?UU8!JA5|U0*?4}4SS?YLkr=#2KKm=K3{p`F|?YZ_W8F5C{ZlZ%aMWTCip*_GSLLrVKb7P4cpaK~w_tASPq=Z@3M!Z+mTQ2)K-^dDi&Zw;Dx zd{iV_R-e_z8;r)Cl7&J}7CJmKLDD&9~~?==R1I{r%Mj$-Ye}>U`PE-(NKa z23K}(6q$j6p^v$+ei_`D2S!A14JrS5p-^r_a;5kvr(21s2R|Bf$#LOwPFHC7DLFOnW$5ij5bFdQG>mhwpD<(OveNhnLn@+-s#nw-Xg_3Y8AcuFVx*SD`Rru6C_oScV8C zV=_20NYMz9O%{r53oKgYT;VMi3fU5^@=IYEB2oE2hL+cT*rGyv(RtlY)(e_OM07E? z*Ssd^*hIzbbVpO1eDnB_#(3PfsSLIEi=O&IVd4r42dg2fB_cRW{P>u#j9d_5sKxA< zE3z&^p>eHl%7Z8soy9LQ1B2S@rmWr!P00;XB%nD$p=g8l-c(^3BIN(yvHptBLy6+A zoxlIfZ+qEqxy>vT$A>v-FS~9X7&-1PNF$gBHa9xLUQSC#&|v&5eiwXt8lMb zi@ZL~`-^$CTUaOtTf~jszBF$dw$S=nyXV@75)vC=(MEqWZeR<%ax4HV)j&4R%=2a8 zT+VE3d;=T(+5$(DvS_vy&*JS{VsQurEwuiYYok73q@W`%vf@kFrAYfQQU--{rX_YH z&~qAzXF3RlyAS9{Fhs3Igyxn=Xe%ru7et5zjO~wH)IP2&6dIRXUAPqQiI*+JAA{5q znStS!bk+P=QO3(032#nNR8K(7g+kGfR#(mhMdN|uo}c)nUNFtHy8axm=&n{cHaK3< z+mb0)C@d7?1H$Sev^EhvMr7~&k)r>{*n7uERdoO3XOg{}4X_aMu)%J8RO|?f6-z9D z1vFBukCJ6`lPn~=%kG8%B8DoR&_M!(-XU}WfuPchfb=3!iqboRjo<5i=GuKw>{?*BhK*FT zprMUiV@4{f+{i|bsUsDw7A@NZK7>_OjH_g%qJl=KopmD>of6^&fx=WeQc>bt$csJx zxjy@QN4@0nyoHKH=Z5_Mv33w zvKmVdj#p_cJq&~^w#^0_7+hAp9ob3-@_Ra!Qf-t}^b{WEP%jp40!-@Dik#VxQK2!p zj-9%va@miKRCGnm`AZ9g^K7M)CDT6vuNol5ZVe2uCT|M2^BU=hzkUQX_VnYg=K*0|Lx}b?ogQ0#JLY& z)+Ml<93WcHt{d@AS%0;jU49UVvL1)lvomWq=7SW{H3_n9W2KhBmN< z6^(J~^ckEwMSrr5`;+sHZDk+%)R=plV3!B=aaP>7!O%neNGZugfpExX-Jr6Y$PiyB zG>ZqZsTsei48+C~QT1lhs%Hw#;we}3Ml)_A?~hb8PngyTgrXlKuVugS6w&R= zG#mOoc*>P>S$ph-D;h?G<0%fu&zjjN(c>jL#ZC5GBspN{6h};jcd>5NBaoopdDjZV zQygjJ#G;7KabozpHcFzpIP&Llx1r07Joql2mz)(_{ub!;bGTkJu03I=#P`5*({I|$ zkU;EpghSNdjt`!G02cO3P&Bj=2)~@u<4YfA!f3WYh)V^6=?`5CKfGtted|J@Sv=*n zV1J)$0TZN+!n92w6#cCm!ZYtnL)auViwBByL&(>PFtS-h%Mnm?&2Zf^QqdZ4vDw!| zQw4DWm^#}z_()I|37KG83nM=lS~r0Z8)oO>ukr_CWWLZW9>{AWU-|&0<+hhnSGU%Y zb{_u1AIR{x1X|FWjutJ`1nP#yy{Y;TT`qdlUpfI5~eGNG;o zrxXd(BUg1CSmD~1XtEs}V*Nivzlap3M}K^1MWUvX!&j&%Ta@${2v?NYUA+qx)%?gt z)dpd3(5$0{Wu-u{exq5#|6tuOEF`9Z^*hbF3`7%JS!|83k2(0 z&07B-teL{nT_9N3Yt}yHtkJ_%cDCR1sypm{pk|gkWIX~XpT*Me+Rab=p`qpO9HN7E z%tyEV5AOE*kj7m4rjIR$AG9YQGKi|z6BWG#I=ys^&vrEb*hWeD=m^@A z%|OhrueUHoEbI99br$xEgN5{ojS@qqJLt*6ESvhpAu#F#%XatU7x&t68fyZbF8RbN zhvt`&R1!`vjg7S%ew^D2iT1T=9_O$OsbH>;=5z8alGu9H$v_&mCvcv3 zJ(JgS8rv1LnGsdpc%e#rXCkYA&yB@H!gKr{)^g6i7nlOTw51iybOlV5R192}e2np> z7XCIS9bmN&wXjiA%JYiKs4tt@{yaLw)K6NV0vsbcDe5hLEfk1g8Xd=*7Xw<@$Th!{ zqFrE*FDz74@VxF5=Eb?bEmU-e2Lm+-r9${BjvPo=`Ab*RT>_!DL<_wGA9kE<_1o!Hq;X5)PGx4!j@ANXi@)BXqIku*PX``wEQs>wjsv2dAI>)9^Qt}I^N*HsVjY&1}pa4i%bb&1z zW};Rjo7F!(cIlx&0O6@!9UPg3sZv zbzfurBcl}6dP(aEx|)qrRKJyt64FK~YS+p}Y{+z*F6SJ9FyswQx-6(a1%lL&$N{xq z3GSFc(DJGw3$1^frGK$7ln4Z)=>Jww>jZ+-Fmg_D|ATH1u5aM0o*%#|Sjv}bc_>bs zt)zO~M3YqW#&&ur8zV_JlGNBvpJZeG^b%?X=iq5NUywL6+u97}NAjdUl)g=L9}oz8 zX6p~7muB0@g%73wCkJf<_e@+!j9Bwx1-N^8`ZopvcFN zlZ9Y0e1e0BWoHiX%xk|YPPERqo+#Rst1aF<}85(u4BZ)iByHqQ!I1A*Y4sPFfyD=3Qu!~LW$ zA4?Tlnm~w+jL|fo;MxlWZFKa5N3Ks^SOy=c84k94$G7X$O3`qDaDZU~I%g z>kh)$O&~l`9xZU=K%tBgh%yh>&yxoU$|6BHYRB5Lj!J|x!5LNhsvU#1NNwanlrOg0 z#8+8X@!QIFVD>Zd6~_1*`GY2ogwg@_!yI5k*|Z}DozWFPn)u7;jOZXc@!7?_jI*_k;#>DtRO?ku zwLS~;mz2_3QI`^~lF!1Vw3aR|)EXTCzgia%@xw9X7(PnT0ufs$5aO3&TGpMU2GcycIwwg zkKG~aVtex@JiRHdSKfYN@oMU~ZQ%Zd`lz24^8R>teztQ3+5+?iCmzbf_BX$m-NT0W zvRmh2-PHh-b$6(~*GGN$)tCw3GkxiAXJmxTF!g2Oli;P-a4I+_K>gU1I&G0}Dv$vh z$!zU`!ixbw>bw?nUNhU`Xa;A#gb|n8;+4VIdEtZ zUOLFgQXrE`fYO~vz5yrHZ4YAtZ;Ee1RA`)k+fD&S7nh?qFnT=D{QB$>iHhR_5nhL5 z$x(Z>qBpDPNO?~nXs2RbEk-Na$qM|V6?ui2D-cXwY_49T6~%vwB`4lrrG`5Ezp(KO znCVOy)k{&T5C$8ruX`z40fJ-bXhmO&x`_gzwyRCQxmqehJ!~$#9Us?0-i~k4L0(}U z(m`HfO>R{73M*cU-v>^-%Uby}n|>+&n5aK35bu(X*LNWt7gQu^6yJrwFST3fi^2v2 zd)oM=_NJfNh#V!O75y#5stMYYV47tk$Fb3hstfKBfuK#bk)y^KMFGKmDiE{*HdmuD zioSsI_;<%BN^7K@I?v_`jZw7ZGrS8jMv<$djS_~AQS@3zxqf1fs97Kor5xwvs`#`c zY;D$Fl@V7*FpYN-ImV7rRHLdCz=AlaYjdp{qi82<*gQs2eIdRf5KK#Ku8U(7)&AT@ z)&3lV)5Y-*7AYzg+t(Pb7m5`1`P@dX`bCOXe2$QLyGT*OTe@_=*j&CMMREByN@!E0 zs0Dr$fE7PR6e+r;lZ_k~Ix9LY`Nk${WAOs?2s_`~->4Is5=Z=bq0)ae7p%-TD1J4< zQ8JxrxX$!aln*J#wO)!|uBMsO1VU)G;d-dIqP5I~C|@DOZv}#BjlphC=nOX_i+V1t z$t@5}Kj`dQF%wF1F-bE~bq&FE(8eX12d4O`MT!c<*zavRB0m@A?gAn7wa}&uZmvL~ z#cwTAv_F}1wzecn{KhY1$Rgw zXv1v8?LYZ#U6~_w`yb2-(LfwGYlXK8gwS|tAa@9^jzG{x{bN~=@%Dk5K{MbFhClS@ zw7_d>DKfx4d%M;hG1wn9Fxa2zVzX}OIA(m?#7<9iE$f|+Gs4>y6ScRwcRs;SLq-8p z3Jh<_U$?p7^Wm$x%iSZb~gmfJ?D(2gCy3Egd!=yCI#?U-8V6COfL_yUuKJTnk}*&XtRRX_*anP#Xd-qs1zbUTjvUim`LFGQH)vDhyVuT8cSFaGZoGiwM$31E)#aSZ@ZYNZ4xM8C`Pgy(U`E+wL#+z^j+3n46exE`~1RPNoKX=~g`Vh#@#0eQJ34|k z+j*@#S?r%F5XGGwOWIH0337u#5KF~R8wK}+K+t}&>knns5NFg92-*p|3vXPvh9QwN z+e{A@To3T|2$UiMLj|hB`+5I3kH|Ca6FW^nA@f?~W;{k_K?6G-hqj2Nf~Ab#qY)jG zlC7F4)8&V*TD`10LHvl!Hoa`*%*f6SWtr&|K;vHq3^JqE$SbyYbYB&b*URp(CyPoX zqe=h0F2hOup^MJFtsEz*(|;Irx_3mD(|Nb~d>>p9fPLi|3Yj+Vi;n9Z{`TDxg^kwp zebKwzpv>W5y9Q>MN+kC$@4n88^=!p6{j3GRdcEzf%eaB_`@G+c3z_Z>1^gkCibQL- zd#=;|2W!994~t|N%%%8~wZ7jUUELD{6*UpL<^oYA!=g7hEWfsJXLD0164npyy)Mf! z{ONc>Mzah&=H{pOyi`7_AA9YF&5%HVl{&V0>m?3+ft+5*!?Wh#`LKIHjp#pO?lY;CxoNEZj;{u`Ln$Gz? zL0KdSwbn=Men%UL5tmt*#{wo~Gu$nXl}N@U~< z|3-l#hlm=?mHNRywMh2A%Xa5JUE9q>uvEmS`o6~7>wx~^WnDovNZ2!^RSoEjEheTCCkUwBn zKWOqkV2j+05EV%lLm!IDiuXKABp6B}_dH8PV3j~bPg&^28)FqwGu;f=##Xx*$%N;| zD%vo_MvnSp75yl3zY4_i?o1_3bBPgm3k2 z+EB&bO%s2P`JOJhWYXs0sFEiJ^lic37TG4zX5Lo%yUg7>P0k#D(4^A5GC3w-;h2axZ?sr+ontVqo6Dj6r&Ym*EwG-`AM_B zEG2wPpv8*nC=u4u$UB*%93_oCUxXijB+6a5Pk1NqujLKJme9N0VejJTTMkSrFd4_0&EoL5FibZnd2pCt^-+6tQ zm%kn4?@SRrs?7=sRAOEWqJ1{ZvJQ=~TID_)=2)<2zYUAHq%Upy^0_VV>MJbw+xYUi z*T0m@=Q@08(-*T=6y=Em;Q_gPZuXZjj=SmP_q3V=dWrfoD7;_rzKDG!5aOkgeE%*mi*spAvmOQJ$9L1~Q^rx{B)6*3c?a7SUn@ zQPQB$MoDMi;~@RsKo{AU^9qht6eB|M0+H928|D=>J=tz=4y6fapU1DuZa-&NQiy{C zSmY4xXP-Vi3d{Kb9XIf$pdXOJ$J)$vI>$@!nxnBw$LFd-bk-RCtewK6WjxX%lvL|| z%$PI$xapxkFBfCS*eEIC16=8*m#h~#uP1>{13K((Wzr=^ddI-)#7o8YVt*%rDDfeK z_`~1DALtM}Zt!Qrk3lRRpVB@ngfVvheD-ZcHcA-TSy5gQR$*huDoXoMi();#W2~a5 zAJgu6O(3%Q-Ebw2Q#7r}My}h&DLTN?*vKsr=}z`)*u0h3fqIMV0D(6XQ7=26x$h$= zi-hhC4^r_|Xq(V}76`Gyc77_9Fcv|E4;xpN>bprGn7Z1nPamU0y+bJX3IxXpePr@Z zL0Kf^9yRLRZ$+p?4Zp8JMItuqDeaqxNS$Iwq`op1=VWvs&=fl&p$$+7CY&sxDO~lx z0Ozw(w~Vt|tfrFFSBr|7(LD}59mf82n!}hWz=X1Ct{u;fUKo$=`y*WZ>ZL{Y8aLZ1 zG#>q{o4&H+e#l8ku|ZgS(^5VknKS|6><%J;m)Vc}ZqVz%czNyqH6s!xA~^rYO>UJP zO>Q_a$sYp)zGl80tzfnN%byLp0BXu7d`fKtXY$r0ECrh5X#s7rqd)Bg45i^AN-NsT z2LI9tIwLq+9bJ8(jn)qWhfS6TUF$hFj-UTMq1iG8B7TnO`1x2+7D?iV*8$yyHb5Z6 ze$_$$g`g}F#1Qm|)-F%#-1-TG*cP2zS3y}Mh*_(SF+!Ur5MtYPZbgE!ND#xpxw4V6 zEXef+V~Ip{*3;UOi0JMXhv@!mvehw$h`R9!%34$K2tVNVd9vLh#^NcQXNnck-QrO5 z7=wE9uc1>S$F*n!tT~Hh|F=Lf!*oyU2&?eBLLZpvvR-K9{Dl?*hZFnEZ8X@Wx zhegd8Jomt6(^S#AP$2x(H;y-+rU}X-Aw1-ehD9nC7LfRm#=kkUk=vK+&L(_&f zj>vE}XZSg^>y!MmIlH+)uscaGM}P?-g}S?Gy6#Wr#-TrXb2^@Ax-pi~JeD5-OaU-n zn$MW;rlY~~6QA~CP9K5r_MAArBe}v18)>F}Li|}Em=jQa7#V;@>vY zM$YzT&~K*md$Ai?SqosflV#*4Ms6-cHuU0}t5U|QSyrjD#O8c~DD^sB>IPu?w^BDk zMeFD8lrQDYeD1n(k8z;Iwx4Ci&KL(OlbnG7jdOTX>{K!fjfRRDf6HteRZkEt^#wY; zG|hpoztLEq{Od<6sb{xhp{%|W3(?lzJh zigaespBK)eL(K8;TpK&-6g#O9DLe!ZQSSd3Y>-7ASu! z8W4{)#OsVp@nBre&h~paEGF<5B|6N*R1wHjJShqvnBC(yP?8- zY}JEk-3mlYneaRa3F!(K=BE3pgmVZ0nF^%8jTv-jnzWMfU(H9eBuXj(jl-3uRzRaZ zF&|}iXPTIzOyzGE+_3;ve&0AnKS-_Z7Kkc8?{L){r|2DKLR{<>;z5C6I;G>HD>Gq( z>?a}qA`na$96THRSx^>9hAJKyyk|+>jmmItu=Ma=df*6+5Z;exQHeF8>{&2Vg z;}k7gV55Y#;}rdcADzc3s1UIRYUzPm6g3Wsxi~T-eY>XoCepY-v1S*kF-*2?>KdE*0rBPSJh~ z_hPw`8{%CP$0_nGw2`ZLoTBLqZRA)tPSIV$6v;fUQnhpA@Kl}Re}~sh?ba>vu3N?{ zI?qmo>=2P1D-iZ=j~7`7PGZaz$^wDl*b~pEGM-t4x`3KiDca6Ffnd5~so`bx7O`M# z1%B?G57GDr!xSZFXw#-vaN+9ywTt9_??3PZSNGTaO4hgAU(%Tz6^KmaesA|zXeIox zeZ5eB5D2cRI@7`6;u9?QNoV%7Krqd$aHEb8-3*lq+u(ZIf{5{XX$ADy`@gdKK%%AG z6#u)}>ce$fUP@Ui*S3ZIVRv=|-G!}Ufo6+sp>dblo2unUucuB#1gu7KUth2U|AaH?uE3mpSbDR>yUpOLRDwNdqHujq2c zy{e&fo`<;e?6nlW)o*)T6Gu2j0Dk+n0>5dp3~c;L$lXG$Ef7rWG}GCo@;b`hUn6wz z@=ATlv#CIEU)0?1el6TR!F_{KiZ{=&-&G_Pb8~%NfQSkjP!SdM_1Bp366tS>oPoaU zC)AcYnbIbUvJL`8sWXcPvCjW4LnY8q{;+~~nVfrmO}O6@i1G}o$lcQGg0e{HD6DQN zO=ztILTqeB?v~twvPclS@v;{MkU4C=2wf8>#$yg!%+?n#(;g{i^DiUC)7RpP7_pyU zF2$K5^0NfOuDQB6Qw3#_V3$>#)k51U5MuLnan=aRB0=omi-YQwc@s6zl^c9R!-yg+ z;v#iju8XvYi?kjoF47_{Qq>ixE^ae>xXrw}0?+$0(Ppsrr!^HZtmc8u8dld-#Af5P z6|ya~l9NR7caK+8oT)pm6BS(z#w$9o!bYyQ$1A$K#6}74jaSsN#72%~ofKVr?PaWk z>A}~v;c&(VhwG=lWy}jsMFnMrchf61lJn$0EV_x0`U!;eN}a`5$ReuQa&UM;Dil~`NkZq#UB9P14tn%#@mFEGAP|byIJx&bCMb&pc}&W%KYCGUcfO@P39+?K*TBw- zj;)rx(pqa!j27b+C3|&~+2M43HeQiujg1nzjaM`cKZ?+V*T}iZ*F;}KfzbPtQw9pq}4@G9l-`edCBuU|R5CL$%_ zb%Mj|BrriUl$#x&QRR(t zKK7-J5(gE;ivMNE!h;`6bHgA75o76=WUDsy0G3d$nERx7ZBLK`Iz zVx22 z8AL-WUwPh6pCaW8nqeT&ZF~bnhS$f4%Xm()8ApydS^Wh&rEa!@W=Lh}lW17w#?RYn z*kq@e2j zJUxBaLYX@X$5eq(QKHM7xmC)%aw{B*GEWn(MFPRSQkQwU zpez!yiYhbi*be$LyxC@pTu&=AOr;{xqp7ajh(d4TLf^U#`;J7LD&L)Jrxx4T1>aX* zl53}_+vEhoS&5j%aCiz+DY&%jmRAekKy=nMs@ZXN(~jgbcVlM8K&&8g2v){Jc7G-74CcQ z6O=_lR_J@th~E*~Cjya4mqeDMltqGYR4x0=JbcvR zWqe(X?m`%Wm3*4DRI`?V1FRSD1FSW6qk2(K zJ>Jz$86pr)-XB>{J%wN~z}02-#JL3|x8pju4|a=TeeeUhtw3(fS}LrUH0ybAz_5Gw z*oax*-vh%?`wvKMJt+`|{jF>NK|xt0n1r~4*vCR^DG*|3wAd$tvPh9)p9`&zK#2XM z#qtGZkwmO~&fqiiUdqN!H-e2m(Eh9=(CQYgVC#{9VC%id3bq~z9Bh%=+J+@`U&4aA zUcm3kq7+egTA-+{{+*-`3v}Y5eHxp9pj{4M^0~NFzmA8I2@xGdnuwSJQLeIzWnaz| z$`XNa%Jc+o`m+ROks$of2FT_&a<^;d!+{Z^54C5C1xgY0nZ{za7vuo&2!YPf>YI*t(#j?V-_#a9X3W40EQMS?K4k;^89 zQCEubv7HhTsrf%$CRK>rocM0(uVZi<;G27KOAxjGs_2K)2@86HkYYR{w8WM~9Is^N0@;RW$8KjYX z$Zb5B^sc2>tj!k)x15jB7ui_2J`uDFF|Mn96zzltzRIvTpjhay7{1Cd{s8&|+;O;1 zq)rKhW?_1VnQ)z0?T@vS>I(!@pIE+5Yy_D2Im#n@QB&g-WGa-G+GKiViBH+9u& zt!+3O<0>7g=))heM2N>lUjd;EjpfU|`ZDFlagwV)EL0@bW~OV)A|hpOEFz`k$Fl01 z%csWe2d!Q35DBUwP#(W8?9BuUYlY8&;%8|VX4OF(IXzi!Unbu7Wu}}`yjtCww^RF+p#UmnJ~DOch7?PaF_224IM zq1+t0!sX69!k+SGrw2_R{l->*c?32Rajfl?O8Z+N!e~(}2W0Y546+Ew(p()Si(++H zCAZP(bvmpvj>-*E;qpC%B>j>-*d?4#fWm!4(M<8pZ!HUQwzjyb$ZUWrCuof3cCP#soz} z@Z7|vUM=(161en`_nF?*_ zZQnkDl5xNlw9{4D&&HAXJ19)p5>0sNB>K!Z3l;q-#jR-S;=*J7ZG6XP7)9-2$N%v53$~72JCQg{A|#L=+Cz3hSH_g{y^my+9FS zg@*)pT%gdj!h}z?>7%s5_NOGSN-WI_GIYU91ZruHtM^aCOR)KX5Dy6yCdAcQFj0K! z1Vw3**BD#;rxO$veX8gYBKkVkHEM#Q9%pP6KWT!ZQo$dIaeX&I(b6+EO4v9-(djc> zk2@wPO75UVm&GRhH9^r`XPLqt@1V;wR+p#gS#i+gnYtJ$0=0$OK}`ksp+KQU%qXL# zFSt{%ksBW_=bJ`Tq#^t!Frq=s<_7WQS)A%jPY)8!w&7ONO=qp>*J-LgR%AB_bhcsD z3%UK!IeokLLOx*r_&GSeS$jnRVeTXlX3w|r>8K1a{YzVh`9xmrjzwV&BaHrmjUI6> zVhH_#?KpVOGFzwRN%9X|CJYy6Z8+{z<2>#)$4TY-xOlCfacg@l1u?5`lAA+g4Gf}R zpT`K~4grN@Bjfm7cIE|)Dqb37N4wpHR7)Dn=Rqr6M4!O-xq)9JO(5B`W9(QH3`bfs zvH!+1b_1{(-e4%q23@nYZ)c~!FJfHppvaj`6FKw8F4-ulq@yCI`H!)5GSkgVFgao@ zox)eyP6xVWEn2`#_RH3Zq3@XLMw%y*I=vNX(ND7t%oH+zlbK{EE~uVK-r#oQ(8UUi@9!_3oBBfJ+6U5y-Xa!QatR&^`Sr#F$o^S(wm0O#~D1 z{l5}LLitXXr_vP!0P8GDoPWpN7!BOPgL{*6WkLO6_*p* zPgK+z1P5*ze=bK`_PRi*9btF9T&QRQnBu!kRCHFPw-~N*6BX_H!$z)I6BSkX)5#*t3}cixPThiIWCf^cxe9S*j2&>&#yKOEL@oWo1?@9G!G2 ze+GdIQzM`YQ%4{Q(@7V`{2S89n0o4rJJ~toUqNszo~UR~P)i>d2RAfiK!j5sXNEwt=tC!+S>FHbUSbkTA z?lE8*osv7H>4$Ssm`tQ3f{m)P~a^7+F1 ztq!Zr(85!W5+Qu2`zRHQYbQo3s+6alksuJttVv!?!POBcG~LHE5#;{_g1FwUYpzsq zR|JA~*zUR;i|kl~bTNju)j3ZP2&OfWS3X3~XE01BOU>QeL5oJz;5n|rv9Sip$?tPq ze@>f0WmT$C{eVIxn!JLhbNrUZN}FNL@2{|AoMBK>LLc5Exx_Ww$S{bKQi1YkWatym ze=ub;Qwl)wQ2#j03#!>sPV7~@Bt|zAh;m-C^IpY=VA3D|>7(oT5ACIWOhDx)_tk{% zaV~uF;jTDCJ}r2a39h3+q3P`V2y(DM5PR$FPg&U~SFo}VNcLTH_Dw2C_Fszh zSb@-DWxr5x%LEEd?=Z|2q{RecXI+&mI9D|2lJZ>fS%#E~FsI=pMbuw=qf4Bt)g(nL zD;gBvVUnWM{(5C`JjOL*lA?LT0fC*`R85;Z2UU^vbZJ zYzHC73}%{5cvp;XJ(8Yf)MwgFRu2=(#S^5xahMAat87qW#2h=-3}Vedd*iSV@N^Xe zYmQo^_Qqj;N;n=G4AViTwyI+2fk7unBZ*_II=QuqK}kggyscFlhjq$>2?iyd1t#RB zpIJrg1gNl56Ob~k)UE^+E`IwYMS%g@#RubDXD2DDP!&N1La`8j)`aJoaCMTRvqI?P za3xMw6sT&D>yF8a#^cBRlNId*Yq{&9`sYx)(Ca>Ln2N-4j?XOz*5bQl0z|K3jEzsk z@HkLmNz(u&d^-`NUzRr`Oby__6<{a;gS7&A4Q%JZ)`H#~`jbI#|Hr_lbVEF=?Tp`{ zY-&8dy`2{P2gT=uvJZFE`9CWVR=mk~yB`Nr{P|9ba7hvH4XZgiDSSGvVi$#sc|e>CSl1^<`n{!ls(&QV`dYzL^;+~Gbk}g)YA2oo&Kz5 z#R}69H?>KAV;}SN^tODaxfLW^xD4X22h|q!PY6U2TIo%@ltD@-dCzW$%g}pvYw@G@ z1Vw+~2R83&CLtUipRA}zM6zAYCM$9$8RT+LRx~)tpoGw5MQf4_y{$h?vMUmZCN@Ly zw*K#sQhgOYQXO3g4k}w*lcTwDxqcd$bWyT|+Df1<%Kzwi5Ua(${_IW?SmZR|sX^WKdG+AddV3#>@t$cracxGtG3XvIjD)&dmls zO~`0H)zJ7%3s0jILTKjAdTNOWLv9+SP++Rmv2iUfa2bXu{xeke5FH<*lxzHCMN@7u z$TfSiqDO8u$W<~~QQEBrC2X6lXcB%LnyhHgty2GwbkgpAQXuLoq#K)sdMSbw=DMzQ z8v^2TTSadO?Ja>2&vQB3DSEMt$D%ck;AzV|LGTcNJngKMVVZK`<7pFbGxQWHZD`c@ z6Vp0tb29|i#7#P)V#`qNhS^$$^LD&iK2lM_Fsb>_`M+bBJB-JvL?ZafuG&^BlBL7=9L@f~?YhJG9JPJV9ae|IN&WdDj>@LdQJk+3 z#{atmeJc-cH;m*?3ZdLzxU2xo=K$PUj@9v}(eZa0M0UhdiWoglpep2;dhSk%DL;N7 zrpDcA8689!bbRf&(;$woTayi{a%r-{dwZ0N_WM?{L|mF^_6mgiL)_1FN=8_rpRs7+ z2%a!;#PtzWfk24sh?~#ih{^#%8zK-qI^uo@&p&JN6}KnLd_dNdB_b20{Tz-| zDxzQa)(%tBJ_1Bx)Y!QDte^{zcojpYesI4c&)^Z!5XDtFg!!-xDH6g^#a>(cfOxHKPi=9& zK=F(NYopPEn#o3v{W+dpC}KMfR?kpeZVc14Ncz7q&qPwk4thYjLf>phIl z0L$-xQi6U7fn%=KU&w-SQUS0ilQxIc_-Y}dB;>mmqg|-fsh@iT#ctHYKsj1 z*-mhF>;W9IV-|H-W4VKn5La-WsXmxG@={U>K)bebc#hh-1XrJ+ZJE~z+lCJAv)=CgPdU+$m(84BAW(r zqQm1TLDCj*ducYy&wd;l$12>MU(E8CAQGSzEb?U?1MyYDiPAc#NAR|!-HiFq6VOQU z_!QcfvATT^QvA)d`XKQRL-IIHn5KA#q1_X*!*B#Yu)}cEljtO|8IdNehZOHYbOj;0 z>dRfO9~KYPUY;y4qA8tWZ!dZ>Vmfw)M~uIKDt}l>XSgV@JY|hbIxVvyNmS2}3_cgA zhoEyT+U695KcXwjws+DKIOL=sE7g%ib4EVd&VlDPD=qL!TV8z znX$n$ya>%}QuaJo`|z*25k6ePKCJbeL6JUO!uETfvwW!2;zLQup5Ibt9r~O>`tiC> zuS41w=~a#Y=y`*Z0u$J)U$bOCV9o;5mX@(ow?7YEw2{eiFR%~ckAjKXA8WNgo&|*o zr9#-I2{|BK@2FS#Lzx_RdSId<+CgmuDjAq^a0+pk-%dp@=uUJlpVd2xln*q-*CQ_) z(@;3g{96=ov|u>m={z0O7eIWbK0Qri>RAWxE0ZO z?y;7*Qzyd?WDWIZYojOeq5Pi=*4Dp5ziC|>r0f&Xs75RGLQ7Rm3xZ-6$$$_ff2c{ z;@t0h$;y3|8uU5J$hoiM+&_Fta$hgv+XN!_bvpOHFCj?E2F*~omCl}MnD+F=5gJ-q zv&T2H$4g#9=wmD+{pm>i%Rrz8HY?nv-3Vz88*t#f>V%gqdpEQFl>37Z<<8AX4f->hzh$-!ztYZ=Z4!N{McW7z7suy)s;E@* z9T5|8R{u6(yjLI}QIC3MuZd7hfv{$bzCO9Bpez!Eqt5DU^XlWab;=j!Jpv<5OP-?r zJ1x?*LSdXJ5Za?nTPl>b0%6*0ZQ9p@vPcmAH`5yWJ-D>5R79qa)IJmgogSJOgHQiu zHoz2Q3Pv27AA{yU3z%YHassp<25r^x8Y(D0h@hFO3$-Nj-gE7dMmmB^BqwuJRE}7$ z?ubESulpLJpPR!D9vV8mhVc>YVHbaVa4pg;Xb+o{@H#JH_p-I`BasuJeQZ_{QV`o2 z(?Z$lhu01L(pRlQo%8bo;lbUJl~7v<76bekT?xyDV}n4bIH)UOg`g}Fgl|v@+8Z_X zxsrVG#X^BrEd=N&yTtJZx8tK+;<|4{_OjZye44F9q;p2=@`)Dou_sv4_ePm#Pq1ef zK(B{RvGe{!A}2tn*?F(LX~^3yQllp$J#<{MIwKH;jP}rzVnjWG@X&efp{E38ksuu9 zp%yr{ZniOfVagZY_r^q*$%B?3BI*~54`Z}>69sGK4FG|CvZ-(4*_AtN(jaEokJ3z- zrl>$lGdR}u^)y9Q8XDwUH%-yY4GoHK+g1@x(*axTGMyAGA`)eAmWOAUh zyj>^{3IxaW$lI*%5Q4>knv1UXYr;`UJOC97^)=KaD2oK)sA>-f-5yhS5^2IaX1uo5 z3grONB|AEaQ*Xg#+(}&GgCup{Hsr3uOLn{*-r;TSu;1(_E*SLf+XhwtWt>))B1$U- zJe1FO=~1OnfI8`)I_lFT3@+Ov#*Kku)i8l5L3!~jOjoo?vN$3T#s4C%+MUxCQMLQ| ze{KACJv&{|mAA1fzx1i1G_fd0AjEpdITAW3atq2LAbBC$`P_|6&dP!}%Z zL#deE1|;Q3Fy9;*zWs>Gc9(QkOUvO7dN*K`s>Lw^V~7%JbkmXdGp8Ag)e6 zP*f^9>P*xXAQquE`^BL)KYpjIHuXK66W-CaSHMP{e8=F*8qDT%Wew3kd5kZTy6PVn zQCAfv=)9^3gx|{R3Qqq+C=Rg-B^;@p{^vhP5We2&D4X~^MRa^BP)Z1&4`WM{8*88I zR1>5!12l|_*S)d$+%3{A1;V0X+UJWKi_cFsj`I1PO|JKOrib!H!!wh#7c6@+160I$ zHf|!_d#N~YOr-OQM0}z^lt0>eNuu~(fiQK7c3yQsStJNYIWNcE$}E47n!ph{a8;>2I>@Da_V`bQgY{gg43p5S!M#&Z-&8ZGmVEQ;B3U zda~sgotc}cE)>*liig^gxYpFlg=zZOW%6|W@bWow;1uQ=QTV$+sC!QD?f*yexLY7d zFKE)ef_hXSNGY0BTTpcbMvxi^>J5P)y&N$Zm6umvsn8&3y2EC$UTfSRTO>j?rd(I@ zH&F7YniVqw&tzTN<)UPrKv?jmD`t|SEzQuE!6O@lwpAc_-f~r(q$u{? zs93%K!7_tG57gurK~u!IBLb~ha1%{Z&-m>0;F)r`@U2(MsoUEAd0dghAZ15&5Iz3;}!}kQVNGp#!Xk0FC|_X<61dg(Obys z-x=Wg)!{dQ@8b-Ka6T6_(|H>M}CNATe{~6>Ac-#2Hw3(x&^2b<%@J(3hq*t<; zA4^M^BI>sYM0xkba7*|DJZK3P&$kggx+T2)3H*kZuuf=^dUQ+Z2_A_BamC&k2V&tb zk*P3O7Y%vCYp4gBt>a|B1ga#(-w%kyqXHu(^M&=G4!9CYB95VWoroM02+>`-f-Taa z2$E!YR8Wyp|3?UWvi&~ubzj&|DN=3K=Ib&>RNpbKzAc~VfnKK`XdbzwzW>2c!0l^d zh99UI$PHytvM8Cqz*2S_Z<@9sdc){!r$BQINoXggK1v0uRvLJ)$fbAg>1?MtX$B=xu_Db!O))2~=(d;w)C`Ch+!#Yrz{aux`W)!i zZiA{X6%&)iM5oWpd&M0f6WL9GCZCzt1V)e-LMNb0-v;3m(Xc?2tvN3}{DQG4GLh4B z?qbE9AI2Aas;IPBF%`9q^FGgt8LtS0&d-UzO_V%G3wBYQM-$=rNTB4wT3QLtFA!Rq zxr}y-9&7QBnM8So`2$oU{AU(kS6_VP#-D>8G&cHM|AJcDFZM(i@PL z3Z2Uwe*?ocN{e`dxVERfs=i`OdKre6DOB+=AVRd9^IPx1rrI2(^X0q-sD$V3^hP?b z5+qheDAeBgbj%YrPghhbt{bRar=}~~nQl_7`n%FW$ zxw_3zG|n{0)qjSf9bj>MIYZIYqUU9S$ajizEt;XokztVQn;DAgW}ve}RJn!A6bSCG z^cJy?nKsW*G*5_Y1cGUwa{Vzw(O57=jsay+6Es6+m}rrXmwUg`P?fjyUpQ{IXW$X_ zTx^D-@doIkYS+b1w`5w?PnWo|8)aHmM)P!a{HmRqpBe2;uCf&25eLqLuxEQU&bKhDXC;0x-=Te$%a{Q3O=Ss^R-h(>hCmuo@G$N zr^6M^#SeTs??{$Gj@(X)Ovx)Cu>8|`H+d1*k(HTcy3?Dwz1e&i80!fSQdBCU{TD}9 zY8KIsm_42C^rqJ`dq)h~AUU(bw3B~!^cp(e9~I%J1;TH8bPHR>hLkNou#Bl$hl+%8 ztUy(mgdwAx@BRlv?Lv*r9Nb;=08dOygf+0lGL%Jdd)hPu>zqn0G3g?jW{mjVpch*j z4rdb9a1J7(3J%V}szK}N*HhtmOOm_gegdV8r_O#CR22IMBEZ$ifomCFeIQ!@> zcDmTo^2s(xzqVt*Fl~@RrnYp1(S=^6Na7Aaxf(D^Zj=)91McG zvT$Qn{HRGbqVh$}LV=?0MiNvaJn74{3BsG0j(drxsB=dQTAhQbP0&L$g$JXnIR-f% zpQ*?uGKm6V^h%{^4F&h1K+qON_A=#G*QR`7s$CLoMDsv%qFoB%^{~&d9`)~GE4l%J z%Q~;9X`k5XD&J7cmB~o%jfD(WKkjTH#r^@?$w8LG(L+920&LlrG) zZBT-Jn4(jy4RZ8|kQyaYHxW_i7}vC+is}aqa?Ks8s9n$?hpVTeLSeF)ad+4FDn#U1 z-ba;XJXyG4K<^mWzCMb!1u+sQ4^y;Si0cJ{sb7rig<*<}5W4vW!xWuesF*My#)Xe4 zJRLH~l`%|FEBwH1;G;u^{%p@)QFBrtERx&6w?ZB9E!;bVxJ6BbA_17XQfp9!nP`YrgQVgYlAWk( zUfC}*81(tARD?=19PnpnH!yu>(CgtBwbDe}T7jzYacJd!@pHlT5eUjk6|sk%F9eGL zol5jJ>{Q`cC=e=E>zVa5L0KdSM{UF2{~(OzL&Oi%#9lU)ipU$^ST2A*mFOYr5tGx^ zP6c5c1y4tEe^)#049gQ$5&^qFi1m!&j$H}LBEbwS6VS2VCbWA6LadK|MtHlREE2?` ztQTJgO?P@bN)zrkzl}CL>~EawBU%!hbDN!R&lTgB#KQPgBy;@fCg`Z(p}APEXVOxB z#I_%)JS+7?8-wECpQ$KqkzzBK#kzts6@A*qpoB3q6|HPzP?c#jaf9Aid`nUcu?s}W z@JY-aV--~q+&uz8n__b%j#G58jX~9JlW(LTyJE?%B$jizFE1*WrBdt?fzV{-vQBV^ z1cD~He3%!JOaBgv=qttaZ(|)xIw%?=BI5);OGJOxByxaviXayXgx=M$E?l3|H_sqe zFi&U}k5=INv4f(LJal!}H5qP+9)Aul)k_nCf_VY4TL0}RaBc@ z(}v$tpg6%@3dOy+2MAi+U5giYZ;E?yx8UyXR@~jKxVyuje*Z}hvI6T#_TDqIN3MHO zF`E@NEx+iga8i}yo08M#0%X{kD9wc$t+4g~upCb8Ue$?QK;%soeJ%76DmP1o@M>a| zRoed{rvv^hBGmg?fb|v-vv&!44gev6m|vt?Y( z2p5CS{5L7cZKWkwWL)6Y>!a_nNP^|PukG)dA7ar$D{Jyue!l)(FOHMWjSyueK^q%Y zTxs(y5i(9Oq3i5YIL_BAbAR99lH;;`OpH!9llas=XZDq~sG{AF4b+kpJ2jgTbh}zC z-YN9I0fWH;tB*W6Ad*Wjdf)4<1b~ah>YBVTOpYXE9jWHtt!HHETl4ZW?V$H6qBW+W zJCJYyie{MU_(QYg2*aW5a%Njdn8BvO1oQQ`{0#Q56Uxny?bBMEsjq?OE>q6Co`wyO z>I_szK`xysmJC!Gch?4pWDEXZpr3!wT&o+~ zpi5gLq+d_*a<^l25H-z4Y^i`C8`~k*Cr&*VDGS&?GE?7Tpk6gI#0*c>;9!n5*PnVd zUZJ@R)u!&5i)=Oi#0tbp(Dkc?($)&*M)jrs4dmpkNTS5TWQe-g!1u!=Ig_z;+Ju>&K0tj}sZy&n0mR2M@yCBGGX*$qg(?gcE78Q`-wRngxdp{*qus+{Q zUdZ2QL&R5dc4$=C7&9(as3LnBf!(&kL|qx2F@N91KsZv}D}uMku+U`)@9zxONWS%Q zbb61}2s`y81V!Q0;=nanT`wbpJbYCvNNRCNjxB$eIyXR8JPD>-U6)ImTM=RJmDZBCk#L){8ibVuTk_d7i^RaB;z33TO7*MPX) zMTMowvcG8C7KBbnb9GOZU=AHTL=~a9RLPs8!m^AL9D+5Q%;$8E(V#1a+u zz5_Vvig5a|yB>hBJf4i8VV>&VO1GBfpZgTg^26NKQl&4Tk9-Q8OxZ~^3nh6=6^iNZ zVfmnxbb!9UVISN~iMaNd_~d$LU?HToq_@^z-NBU0PJ$}?vO^pEziVkE^(sI1w5w-J zCDaCv+7G5l=Zm_XPc{QxpC17`&aOFCD>GqNfrr1lLIltqGCw{a%Az?@dp(KI3!%BWv#(`VEu6=p zCEZ!AkYUh1Q4B9Lt5S>6_0;QT+tc(A%?<^9<$pG5V!Qs$Twz#9E%jVGQ|^5KriAOk z?sIk`p-O>td^)qI8K$IU@}sDzf3o*6qS}b&NV|@deU$_Gcuqq(Z40)Qdj&-~m6jb{ zf?>=`p>89{Os%tvoOndAODlqo)&6blMo}SGS%F^ynO*iHsy!*3`C^(0eEZ>Sk`m{q zt{Jnm|8Ci}@9I=mKEPAqa+&YsZx+?(L?e{(nnMo{Au+m>X$BQtyMuu&%exiN(CRq?sxoAC_V+<>^{5XC?JAV zZ@H4`KFaQL5|7^Ir;40*P@jsDy18=ghL4c)=G9C2Y(}j3@{fOw!Vjn}<9Z$a1p3B% zo1Aw>7C}I5w`1Y>9|Eb3Z_WLYX<;Qi(cdDKvV%0`a^GAazQ?dWQ!bBZGr<12EmNaL zR-tq`o&q-mrwFOm0|xuzw@|066{~t;jx6Nw=>Uohsj=aj)P48SCcvLK$5?!cVojXf zd3EGfsY$_N z0n99Y8ez=xeUT5H&YaLt;;N@o+cKbr82@e_!A28}1N}|Iltm?s_Cd)o00Q`aA4(y= zdb``Hb?eAZkAq4Qt!J)GDm(n7oEHD7zV=ORW=A*t5%M=`XMye+>fu>==skmhMS$^uv5|O~b7S8)n z5vMnyVbZLWfukZ&UUYiHSC9e4brW1!aQb20Z@>PC>76l(Os|LENU#T`_yVCz!o%4z zBt#3C6jW46o3eO5Bkbwi5+<`W$ND;KKrs zmBq9~G5w8VgGU+=x&mvQ3;Dq!&|zXOj>zCrU~Te{<{T_R)VGo6WsWl10?i;p1LWfS zWQ`DhE(EAFwV>ILFl+Q_0-O7ECqjwYUf(r^rp*+q<$Y(Krr7gzRJ#>!Ztr>ufnD5U&NhDo2xt`%GE;f_&4>H4Stzs=V zYjpML591x$R-fbt{|Qa{wZlrk!}R+(KSBW^AXS*6bo&Eztm8t*L#=BnKXzzidFm9D zod!f$U7T7CC;~mmBVyYnHOAJ2b@$!2Rm4id>JN-^;&b#ii;(3bsH7olOC__J<9F+f zU=0wc3hFi01dgQ0Q<_XI-$m>!;YDsWf8}NORwaHOQ1d5@7-&t{pm!{I_Mw*BY~S+T zQ8M3E|Mbg@{NOgQ`S<6P)xB?|X|Q49@6_V?J@)A)Q%vM$g z;BhXFS@qh+3}UP*a6J6Cl}Y=RPwl6(s6~S>!?JnULw}n`v@`I7NB=mQ(U9vja5xke zcAm0tpe1)`OhwM2w>ZhlU@V!yFp(^dzpn@Bm)?N{k}m;16JFVeQb90M#&OPzp!8Cd zR2xkio9Y}2G}(v~cf0t?rX=5)L^l9!^G<(+Ub;@tm(kxJ>%!T+;EHAQ3^j@JhHKZy zv=lOK-dT=Q>*)->t~sa?eNLeV{!A&xeWPYCL_CQ2)hcZ43Bo%K_^rsh)1QYh`{$nzpUt!@8UkK zHXo|sFkTkj(_I+zyC?WS2{K>Tt-B!%DNxmII8$JDXPN>2bernNwif6x z>zEx#0#^Y^P88NH-OtzzDDG~!%Bysn^f8UMNue5qn)qC4;BrK?I)G1ak!bdC)1l7C zXPb=)K&K^}Ovx?D`Ci3W)?TTMP;p$=%pZQV+!ivV)|y-MDLf1nh;>uIbEC9C83+^n z^0`Xwq;((7xKezL48KqQRtc}n#Y6#a`-O720_52}IA?+KoaQ4HRv68MPV_18lC^Dv2SSJg!ifn0pzjgg~ zGOw`%-pkKz_JN^D$%AZN1<&7-(mVG4;3-b=eNyJX$5 zd||!+&a#(+n4MaNMPbo-SbcIk90nnVpZTdEU(6OsMo|=t<2Q?dmy&87XJ#KdJ#guE z(NjtNh}fmRAH)jNEB?jQqD!dSJPTBhqYw30aSv??+8`}$LXenQ=7Bvy4yWMnvsa(Y zlB9#&^Me!L08`^UWuiJ_VrD<@YCB?&)ui6_=#dB)>$@R?1d#T!ebM+l>`Ib+L@yED zczic9Jl>J$&ad{XsmxAV$iI=?54hz|pXh5q1ZP$JI(Vg0vs65g*=_OZH#B05o^=$Q zgDl$h|30;pAcfnNDGVnwE=wTQ4Ts>tYP~)7+cyRWRc}{F1;WHG+qc`IInKZ06Kme+DO3>$xNydCnJ)OU+5)t%Q-(qZ8EGT7EyhP~yLnye1w_5{Q&h@;KhBPx>Y? zzLzp)Nw%MU;gVP2ytcwIk@Fp+Ggl^_FzQdoWyo)hPA*yumF$j7gj{_oW2C$|sCJOH z@rJv5T>8_|^w#C#U8^NQrpXZ)VI-+ zX>BdC(N>%eC>4I*?PBnkR+ZGQ`cDHU7L;N4<4US^>TcJsJ9Ka1ykRm_f&%NDo>7pX z5mZMxF`J!%7v8WvW^US??@(Bn4rr~9VS~!X?1p|`aQI2g;Jd(Z; z$S2CN-u{IgK)?4#GCfb55}LfBA5LU!J6d8UmV^#-wYmG&b%-hAh6YkT&nYGfbMp&w za>vj0r43b!4Jpr+!ph7#CcF>U|85X1d6BHR5|J|dBpq9-p{e#pxj@f5EvzyNs%vKe zNMuGX)Xo1whSGzSy+I>aLHbr(=s_w5Im$*3R2CCDe?I@$ssa3Op02v)TpvssRohAK zm;^@=PKrZOgRmO}Q^3;xfhv6O12aQ#p~ole8EzIBp<2d|=W{SWOrA#@w$R(mT=@bo zP~6QBQ*9Qb6|9wrk`;}pdIV<2_bovli#8LCVjQeKv$)rdB^!osW(}x?@N(=l09(*$ zcG+Jdl11kzb3@`;XZ?418J%Euf)7@R8I8W-yGn?P`c}~)e#bigv0?MM)suyTCiDO> zYFPSm@_+@AuvFyZcughv&?~}sQ$t(`e%d_skfqyxxk3@@LyfANlwxM~Cvqu~sF&3V z1aKTwiHZ(5FV}TowptUh(WjRp!{V}9NC}G$=%o5PUsE7;a)f>$r5AK3Y@BaT8oN)X zQILtQga1tS{G21bsR1o4baKaZO^7fe&vF*|zgigqb;8)Z<6}yyovHJWIkQ|AYY8K4 z8dGO#@KmZ9r3X=SLCmXZJ*%?hXu%omO+1krxe7qGPQ2}Cm~Ll%cGOQQpi#InMcb&Q zpJ89F4R19T<77mcv0a1Zh=aw`ZFI|NTm{`*e_W)#Q!9FdKo6)?$T-MNiC1sxX{v{LW^;fQiJP``day6ep@?G^f zej1z_C)x(sZI836yy_byhk>Bge2A(&p8YQ;+iHDJpbxutoy742OV4hdePd6LVl@KAA-1SAx2+tiZ zV~*7bl4fVZKl_OLKSWs+Vbd3qGS1LiEs3$v>|%dTNy|XaalkYY=X(*+9z=r&w~ zhc@4^7JqJ<)g3>q?9T^$xuvPFm;mE-Tw$8zGTcfvshRVOtCAzQak&E^kV^y6A zRcph4p`Jd>Odbv>x^*XZEo9Nl$8Uy;%FF6aX`~_p0tg`#MG#pwPtV9ABH6u}1 zFr;7*rZ&U(7a1KuD^Eh!^$B?$LK$8&B=52H_4?O(l{8h4&+T|oY`67!kF~*%h1WGn ze5dnO@L(YdPw6F^KQYUw8i)25X#HA{NjGwzp0N7k8)GSpD^03-&G?ZtSXt@a@Wxan zjI|EK-Q*9huOHVg@`yaRCv`E1HUy*gZhWvJh-ql>uC-T{9wvIDJTeSQKnoj_Kjgnm zPV&60O%s#aoRvm*c?R+eRv}wo+PEXfg<&`8oPRBPB6*)HA;mnnnbyS3szQE~LIU=D zW5;hq<_iNOPRagJT|}+$bMM`mK>VI&Lnl7EN;mm+LF^{-$RjHwGuXLq#AzxeF|2Fe ztIi?Okm#go^R?2qb?SIhyTOPbllFkHzBnjj`%ZUXaGtIuv*n&IL-->7Z}ahPjUvL9 zU*R7Y$4`ripS>0nl^aaSFspXSSCq|JgnnF1=|Wj>LYmq1&VhUMSHHGA(G8`Qzof6g zY0D!3GWK&d0~bbudPFK&;$+109(|^`f2EX)$XJV%gQ6?wf5dKQxU#pWM0?%b3d9H@Gg!9v zkld|3VEh#nCIro8VF$7xm(3=^|KTXH7`Spd(-o&%!BNP@EqXw8!rA>+GvkL9HL5my zBVl=3!%4Q!4h2nDF+J z-6W@EGP3gNveiVLU=G}u+)Bp@6*mlySX*>jXK z1)N4{m7mJw(2d|0>}Bd;ejV*JhG7yGNvPplku2M*wS4IcNqC<81F| z1e3Tm^5h%7EJr4Yo*nqf{q0yBX(VnH@H#jW(WC55QCJJXL>zi$d-?D6S=pMzUK&Y6 z8KG%cvi&9G5mDA1^9;k71#I|46_j-^1ZFn=ZZ591$z1971GRN#1!Hc0+>%DoD&72a zf9WVjm>_SYV#FhJq?zDo*b_8G&F}sQv5o;wAxi3gPdG9sehA{%+7+MI+}|I3NYF@1 z`&;nLUJSw>E6n3 zdavqC&qW(g*T+q$vzSB~NqT6C%Yus|k40>$8223H1Sd)bB~*i2_W z9~Zfw%^7U+5?k;*=c!*O0gJS-v;_A9ps)cy({>Cp3#s*dCY?$WeFNPEO>$TW@p?~2 zW^XX|p=HKBB@C1K&Sua2qObpH{3req?I@}T{QMA5RF~8c2_x@mR;&m+C3OGt?s#KB z5tep0SO_?Lc z4^LXwmQ2C08hZRhcVsb3bJQX3B{4U{xO-iArA13{Xpabr+8ih+29vnRD(n z%Ca$<#f>+@28~HDM817sq+`H!!%=_^PQoZf*txJ}Vnfja7pUP=i>GXK22CUrdIs^g zy74^*Q)B`Db=_PK%>1tr-y04`P|T;`g+zZlqe!N>(v{_MeceMfv^rJS-Imkc{xC54 ziD!IFP4$b`I2OVR_af%@9$ilhXE&$96i(H^2B(73o9!Ai_mb|!n99wCgTL{be3ATj1#7XqK&5QEI&PZIitz2>_P5&?r)XgO2_8`=j&Wvu;mJ92d2?3_Xw&3z z>sJ~T;EJaH#r~Ub#3VIh-QQ}GT50=zSkbqLNNy*t^aw|d!8p@`+TN>vr8Q*;Nl>tK z+~d~61`Wo4BAqlKB2@SNY8Ce>jP=VY>OXb-rU3o-#qWkp!aLYp3(EIbfQwu(+atMR zRO3EfD%GP60D)*9AF+IoPDUW|t!+&}JS{+T2hIjNd5;kBb&u|8vrYuDcaQGKX_6!rCbYKWY{-x12$&p(i;pO>>lX_dR&-N&Fl$A-~%B9uf&E ztgo@lfr;+iRuCyh%m-o&66dB1^;r+>U&K+5VGYPG(ZGWNQr_iPrEWcF`rVTfV*M-* zZLm#s@F4r?;~S3oZrmKlg^&HdgdncdcOe&4`i>xep9!9kphen|oI((fo9J3dcF7BM zj{<65`FGc%H9`w+KSGXV1b9%hayPsfV*m3zqOys)IYw*y)SzoY1V<5F8WY!<{>N+( zrGd@BFF_gPTL+xVE%enpagWDjJ0I$1*!3j~c8rm_@u3=q@%72=iVEFcXMb3f;8Xf= zEG=XW`1JH2zj|(pl$nQRnc%*;e}@1=WLt=W$ARgD!_YQ26xnhyh@7W@0A-$x440nH zKZa%cXzAPs0gGsBTCK)3>JH09Dhl@*l=l_+H}8-&_`#c`rOn6bSZ9oTe=OWYn@U1I zb-gC6ab{;gVnzdy+z>DI!G_vD_K_4Yp-13W2`4H^XIUwxYOJ&{oIR8sj9<8AsQu?>Jf}&H&p&FhD)TeF)slLc0B?(S`_fO-$Y1B2^+18? zX}PpIP0Qbwy4%;r(S7w>Y0-V3^f+$fs^)uMih-;n{#kaKl$ik;7~2~#-CxjsiIXO+ zlcF7Ya8}k)%u_tx3|;vWLQva}jxnVKRo~Sv6cE=B^VxMjOjLI)8djjQe44vy5+e}t zlfQ=l9KAv_r}*V-u^s5}Z_8=_zEzOkSA=tAqm2;H{+x2+|Ax)?#Iw(uBTB|@-A5He zJO_+)EnKup&f(Smn2+22G|&1(XrZ&*S@2nagD=WQ=&9!6<-+vRqk`292f^&}QLZxw zck6m15T7hZ$@>j4*}20H;THE2P8D_X5$#3pYV9@c@3%_poe~7t+P^x7VF`AApQCUQ zsD~Qap#6NyJ7|HgDHxn_)atXID&}A6$2i$TtqYzLLQ+%uIBW4~OU*ct1S`+vXdLPl zZ$Cpk5`pHw&At|Y;5#gG??(PpXa z-zO(v3(Wu@dx9x?K69j_^Yp*xTSr;FbP)GJ%4 zJhjTmIZQil1hZd-OZccZikRL@Cgp_2r+004%yt3$$uklGZ?jHnhVfeP>}C2(FyA?7 z|NH3n+2*;rF2JTkIj*BA~

    &3XC;cMOBd5F=_yte5Tw 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�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 hxDzhDyKQ#i6_ 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

    vlHlu0t_OsIW&&c^G2 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 VyhggKap;zRjlm5`g9C{d{|_jHuM7YH literal 0 HcmV?d00001 diff --git a/deploy/docker/clickhouse-setup/user_scripts/histogramQuantile.go b/deploy/docker/clickhouse-setup/user_scripts/histogramQuantile.go new file mode 100644 index 0000000..9540a77 --- /dev/null +++ b/deploy/docker/clickhouse-setup/user_scripts/histogramQuantile.go @@ -0,0 +1,237 @@ +package main + +import ( + "bufio" + "fmt" + "math" + "os" + "sort" + "strconv" + "strings" +) + +// NOTE: executable must be built with target OS and architecture set to linux/amd64 +// env GOOS=linux GOARCH=amd64 go build -o histogramQuantile histogramQuantile.go + +// The following code is adapted from the following source: +// https://github.com/prometheus/prometheus/blob/main/promql/quantile.go + +type bucket struct { + upperBound float64 + count float64 +} + +// buckets implements sort.Interface. +type buckets []bucket + +func (b buckets) Len() int { return len(b) } +func (b buckets) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b buckets) Less(i, j int) bool { return b[i].upperBound < b[j].upperBound } + +// bucketQuantile calculates the quantile 'q' based on the given buckets. The +// buckets will be sorted by upperBound by this function (i.e. no sorting +// needed before calling this function). The quantile value is interpolated +// assuming a linear distribution within a bucket. However, if the quantile +// falls into the highest bucket, the upper bound of the 2nd highest bucket is +// returned. A natural lower bound of 0 is assumed if the upper bound of the +// lowest bucket is greater 0. In that case, interpolation in the lowest bucket +// happens linearly between 0 and the upper bound of the lowest bucket. +// However, if the lowest bucket has an upper bound less or equal 0, this upper +// bound is returned if the quantile falls into the lowest bucket. +// +// There are a number of special cases (once we have a way to report errors +// happening during evaluations of AST functions, we should report those +// explicitly): +// +// If 'buckets' has 0 observations, NaN is returned. +// +// If 'buckets' has fewer than 2 elements, NaN is returned. +// +// If the highest bucket is not +Inf, NaN is returned. +// +// If q==NaN, NaN is returned. +// +// If q<0, -Inf is returned. +// +// If q>1, +Inf is returned. +func bucketQuantile(q float64, buckets buckets) float64 { + if math.IsNaN(q) { + return math.NaN() + } + if q < 0 { + return math.Inf(-1) + } + if q > 1 { + return math.Inf(+1) + } + sort.Sort(buckets) + if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) { + return math.NaN() + } + + buckets = coalesceBuckets(buckets) + ensureMonotonic(buckets) + + if len(buckets) < 2 { + return math.NaN() + } + observations := buckets[len(buckets)-1].count + if observations == 0 { + return math.NaN() + } + rank := q * observations + b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank }) + + if b == len(buckets)-1 { + return buckets[len(buckets)-2].upperBound + } + if b == 0 && buckets[0].upperBound <= 0 { + return buckets[0].upperBound + } + var ( + bucketStart float64 + bucketEnd = buckets[b].upperBound + count = buckets[b].count + ) + if b > 0 { + bucketStart = buckets[b-1].upperBound + count -= buckets[b-1].count + rank -= buckets[b-1].count + } + return bucketStart + (bucketEnd-bucketStart)*(rank/count) +} + +// coalesceBuckets merges buckets with the same upper bound. +// +// The input buckets must be sorted. +func coalesceBuckets(buckets buckets) buckets { + last := buckets[0] + i := 0 + for _, b := range buckets[1:] { + if b.upperBound == last.upperBound { + last.count += b.count + } else { + buckets[i] = last + last = b + i++ + } + } + buckets[i] = last + return buckets[:i+1] +} + +// The assumption that bucket counts increase monotonically with increasing +// upperBound may be violated during: +// +// * Recording rule evaluation of histogram_quantile, especially when rate() +// has been applied to the underlying bucket timeseries. +// * Evaluation of histogram_quantile computed over federated bucket +// timeseries, especially when rate() has been applied. +// +// This is because scraped data is not made available to rule evaluation or +// federation atomically, so some buckets are computed with data from the +// most recent scrapes, but the other buckets are missing data from the most +// recent scrape. +// +// Monotonicity is usually guaranteed because if a bucket with upper bound +// u1 has count c1, then any bucket with a higher upper bound u > u1 must +// have counted all c1 observations and perhaps more, so that c >= c1. +// +// Randomly interspersed partial sampling breaks that guarantee, and rate() +// exacerbates it. Specifically, suppose bucket le=1000 has a count of 10 from +// 4 samples but the bucket with le=2000 has a count of 7 from 3 samples. The +// monotonicity is broken. It is exacerbated by rate() because under normal +// operation, cumulative counting of buckets will cause the bucket counts to +// diverge such that small differences from missing samples are not a problem. +// rate() removes this divergence.) +// +// bucketQuantile depends on that monotonicity to do a binary search for the +// bucket with the φ-quantile count, so breaking the monotonicity +// guarantee causes bucketQuantile() to return undefined (nonsense) results. +// +// As a somewhat hacky solution until ingestion is atomic per scrape, we +// calculate the "envelope" of the histogram buckets, essentially removing +// any decreases in the count between successive buckets. + +func ensureMonotonic(buckets buckets) { + max := buckets[0].count + for i := 1; i < len(buckets); i++ { + switch { + case buckets[i].count > max: + max = buckets[i].count + case buckets[i].count < max: + buckets[i].count = max + } + } +} + +// End of copied code. + +func readLines() []string { + r := bufio.NewReader(os.Stdin) + bytes := []byte{} + lines := []string{} + for { + line, isPrefix, err := r.ReadLine() + if err != nil { + break + } + bytes = append(bytes, line...) + if !isPrefix { + str := strings.TrimSpace(string(bytes)) + if len(str) > 0 { + lines = append(lines, str) + bytes = []byte{} + } + } + } + if len(bytes) > 0 { + lines = append(lines, string(bytes)) + } + return lines +} + +func main() { + lines := readLines() + for _, text := range lines { + // Example input + // "[1, 2, 4, 8, 16]", "[1, 5, 8, 10, 14]", 0.9" + // bounds - counts - quantile + parts := strings.Split(text, "\",") + + var bucketNumbers []float64 + // Strip the ends with square brackets + text = parts[0][2 : len(parts[0])-1] + // Parse the bucket bounds + for _, num := range strings.Split(text, ",") { + num = strings.TrimSpace(num) + number, err := strconv.ParseFloat(num, 64) + if err == nil { + bucketNumbers = append(bucketNumbers, number) + } + } + + var bucketCounts []float64 + // Strip the ends with square brackets + text = parts[1][2 : len(parts[1])-1] + // Parse the bucket counts + for _, num := range strings.Split(text, ",") { + num = strings.TrimSpace(num) + number, err := strconv.ParseFloat(num, 64) + if err == nil { + bucketCounts = append(bucketCounts, number) + } + } + + // Parse the quantile + q, err := strconv.ParseFloat(parts[2], 64) + var b buckets + + if err == nil { + for i := 0; i < len(bucketNumbers); i++ { + b = append(b, bucket{upperBound: bucketNumbers[i], count: bucketCounts[i]}) + } + } + fmt.Println(bucketQuantile(q, b)) + } +} diff --git a/deploy/docker/common/locust-scripts/locustfile.py b/deploy/docker/common/locust-scripts/locustfile.py new file mode 100644 index 0000000..0b51820 --- /dev/null +++ b/deploy/docker/common/locust-scripts/locustfile.py @@ -0,0 +1,16 @@ +from locust import HttpUser, task, between +class UserTasks(HttpUser): + wait_time = between(5, 15) + + @task + def rachel(self): + self.client.get("/dispatch?customer=123&nonse=0.6308392664170006") + @task + def trom(self): + self.client.get("/dispatch?customer=392&nonse=0.015296363321630757") + @task + def japanese(self): + self.client.get("/dispatch?customer=731&nonse=0.8022286220408668") + @task + def coffee(self): + self.client.get("/dispatch?customer=567&nonse=0.0022220379420636593") diff --git a/deploy/docker/common/nginx-config.conf b/deploy/docker/common/nginx-config.conf new file mode 100644 index 0000000..f7943e2 --- /dev/null +++ b/deploy/docker/common/nginx-config.conf @@ -0,0 +1,51 @@ +server { + listen 3301; + server_name _; + + gzip on; + gzip_static on; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_proxied any; + gzip_vary on; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + + # to handle uri issue 414 from nginx + client_max_body_size 24M; + large_client_header_buffers 8 128k; + + location / { + if ( $uri = '/index.html' ) { + add_header Cache-Control no-store always; + } + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + location ~ ^/api/(v1|v3)/logs/(tail|livetail){ + proxy_pass http://query-service:8080; + proxy_http_version 1.1; + + # connection will be closed if no data is read for 600s between successive read operations + proxy_read_timeout 600s; + + # dont buffer the data send it directly to client. + proxy_buffering off; + proxy_cache off; + } + + location /api { + proxy_pass http://query-service:8080/api; + # connection will be closed if no data is read for 600s between successive read operations + proxy_read_timeout 600s; + } + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file diff --git a/deploy/install.sh b/deploy/install.sh new file mode 100755 index 0000000..1d4905b --- /dev/null +++ b/deploy/install.sh @@ -0,0 +1,559 @@ +#!/bin/bash + +set -o errexit + +# Regular Colors +Black='\033[0;30m' # Black +Red='\[\e[0;31m\]' # Red +Green='\033[0;32m' # Green +Yellow='\033[0;33m' # Yellow +Blue='\033[0;34m' # Blue +Purple='\033[0;35m' # Purple +Cyan='\033[0;36m' # Cyan +White='\033[0;37m' # White +NC='\033[0m' # No Color + +is_command_present() { + type "$1" >/dev/null 2>&1 +} + +# Check whether 'wget' command exists. +has_wget() { + has_cmd wget +} + +# Check whether 'curl' command exists. +has_curl() { + has_cmd curl +} + +# Check whether the given command exists. +has_cmd() { + command -v "$1" > /dev/null 2>&1 +} + +is_mac() { + [[ $OSTYPE == darwin* ]] +} + +is_arm64(){ + [[ `uname -m` == 'arm64' || `uname -m` == 'aarch64' ]] +} + +check_os() { + if is_mac; then + package_manager="brew" + desired_os=1 + os="Mac" + return + fi + + if is_arm64; then + arch="arm64" + arch_official="aarch64" + else + arch="amd64" + arch_official="x86_64" + fi + + platform=$(uname -s | tr '[:upper:]' '[:lower:]') + + os_name="$(cat /etc/*-release | awk -F= '$1 == "NAME" { gsub(/"/, ""); print $2; exit }')" + + case "$os_name" in + Ubuntu*|Pop!_OS) + desired_os=1 + os="ubuntu" + package_manager="apt-get" + ;; + Amazon\ Linux*) + desired_os=1 + os="amazon linux" + package_manager="yum" + ;; + Debian*) + desired_os=1 + os="debian" + package_manager="apt-get" + ;; + Linux\ Mint*) + desired_os=1 + os="linux mint" + package_manager="apt-get" + ;; + Red\ Hat*) + desired_os=1 + os="red hat" + package_manager="yum" + ;; + CentOS*) + desired_os=1 + os="centos" + package_manager="yum" + ;; + Rocky*) + desired_os=1 + os="centos" + package_manager="yum" + ;; + SLES*) + desired_os=1 + os="sles" + package_manager="zypper" + ;; + openSUSE*) + desired_os=1 + os="opensuse" + package_manager="zypper" + ;; + *) + desired_os=0 + os="Not Found: $os_name" + esac +} + + +# This function checks if the relevant ports required by SigNoz are available or not +# The script should error out in case they aren't available +check_ports_occupied() { + local port_check_output + local ports_pattern="3301|4317" + + if is_mac; then + port_check_output="$(netstat -anp tcp | awk '$6 == "LISTEN" && $4 ~ /^.*\.('"$ports_pattern"')$/')" + elif is_command_present ss; then + # The `ss` command seems to be a better/faster version of `netstat`, but is not available on all Linux + # distributions by default. Other distributions have `ss` but no `netstat`. So, we try for `ss` first, then + # fallback to `netstat`. + port_check_output="$(ss --all --numeric --tcp | awk '$1 == "LISTEN" && $4 ~ /^.*:('"$ports_pattern"')$/')" + elif is_command_present netstat; then + port_check_output="$(netstat --all --numeric --tcp | awk '$6 == "LISTEN" && $4 ~ /^.*:('"$ports_pattern"')$/')" + fi + + if [[ -n $port_check_output ]]; then + send_event "port_not_available" + + echo "+++++++++++ ERROR ++++++++++++++++++++++" + echo "SigNoz requires ports 3301 & 4317 to be open. Please shut down any other service(s) that may be running on these ports." + echo "You can run SigNoz on another port following this guide https://signoz.io/docs/install/troubleshooting/" + echo "++++++++++++++++++++++++++++++++++++++++" + echo "" + exit 1 + fi +} + +install_docker() { + echo "++++++++++++++++++++++++" + echo "Setting up docker repos" + + + if [[ $package_manager == apt-get ]]; then + apt_cmd="$sudo_cmd apt-get --yes --quiet" + $apt_cmd update + $apt_cmd install software-properties-common gnupg-agent + curl -fsSL "https://download.docker.com/linux/$os/gpg" | $sudo_cmd apt-key add - + $sudo_cmd add-apt-repository \ + "deb [arch=$arch] https://download.docker.com/linux/$os $(lsb_release -cs) stable" + $apt_cmd update + echo "Installing docker" + $apt_cmd install docker-ce docker-ce-cli containerd.io + elif [[ $package_manager == zypper ]]; then + zypper_cmd="$sudo_cmd zypper --quiet --no-gpg-checks --non-interactive" + echo "Installing docker" + if [[ $os == sles ]]; then + os_sp="$(cat /etc/*-release | awk -F= '$1 == "VERSION_ID" { gsub(/"/, ""); print $2; exit }')" + os_arch="$(uname -i)" + SUSEConnect -p sle-module-containers/$os_sp/$os_arch -r '' + fi + $zypper_cmd install docker docker-runc containerd + $sudo_cmd systemctl enable docker.service + elif [[ $package_manager == yum && $os == 'amazon linux' ]]; then + echo + echo "Amazon Linux detected ... " + echo + # yum install docker + # service docker start + $sudo_cmd yum install -y amazon-linux-extras + $sudo_cmd amazon-linux-extras enable docker + $sudo_cmd yum install -y docker + else + + yum_cmd="$sudo_cmd yum --assumeyes --quiet" + $yum_cmd install yum-utils + $sudo_cmd yum-config-manager --add-repo https://download.docker.com/linux/$os/docker-ce.repo + echo "Installing docker" + $yum_cmd install docker-ce docker-ce-cli containerd.io + + fi + +} + +compose_version () { + local compose_version + compose_version="$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)" + echo "${compose_version:-v2.18.1}" +} + +install_docker_compose() { + if [[ $package_manager == "apt-get" || $package_manager == "zypper" || $package_manager == "yum" ]]; then + if [[ ! -f /usr/bin/docker-compose ]];then + echo "++++++++++++++++++++++++" + echo "Installing docker-compose" + compose_url="https://github.com/docker/compose/releases/download/$(compose_version)/docker-compose-$platform-$arch_official" + echo "Downloading docker-compose from $compose_url" + $sudo_cmd curl -L "$compose_url" -o /usr/local/bin/docker-compose + $sudo_cmd chmod +x /usr/local/bin/docker-compose + $sudo_cmd ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose + echo "docker-compose installed!" + echo "" + fi + else + send_event "docker_compose_not_found" + + echo "+++++++++++ IMPORTANT READ ++++++++++++++++++++++" + echo "docker-compose not found! Please install docker-compose first and then continue with this installation." + echo "Refer https://docs.docker.com/compose/install/ for installing docker-compose." + echo "+++++++++++++++++++++++++++++++++++++++++++++++++" + exit 1 + fi +} + +start_docker() { + echo -e "🐳 Starting Docker ...\n" + if [[ $os == "Mac" ]]; then + open --background -a Docker && while ! docker system info > /dev/null 2>&1; do sleep 1; done + else + if ! $sudo_cmd systemctl is-active docker.service > /dev/null; then + echo "Starting docker service" + $sudo_cmd systemctl start docker.service + fi + # if [[ -z $sudo_cmd ]]; then + # docker ps > /dev/null && true + # if [[ $? -ne 0 ]]; then + # request_sudo + # fi + # fi + if [[ -z $sudo_cmd ]]; then + if ! docker ps > /dev/null && true; then + request_sudo + fi + fi + fi +} + +wait_for_containers_start() { + local timeout=$1 + + # The while loop is important because for-loops don't work for dynamic values + while [[ $timeout -gt 0 ]]; do + status_code="$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:3301/api/v1/health?live=1" || true)" + if [[ status_code -eq 200 ]]; then + break + else + echo -ne "Waiting for all containers to start. This check will timeout in $timeout seconds ...\r\c" + fi + ((timeout--)) + sleep 1 + done + + echo "" +} + +bye() { # Prints a friendly good bye message and exits the script. + if [[ "$?" -ne 0 ]]; then + set +o errexit + + echo "🔴 The containers didn't seem to start correctly. Please run the following command to check containers that may have errored out:" + echo "" + echo -e "$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml ps -a" + + echo "Please read our troubleshooting guide https://signoz.io/docs/install/troubleshooting/" + echo "or reach us for support in #help channel in our Slack Community https://signoz.io/slack" + echo "++++++++++++++++++++++++++++++++++++++++" + + if [[ $email == "" ]]; then + echo -e "\n📨 Please share your email to receive support with the installation" + read -rp 'Email: ' email + + while [[ $email == "" ]] + do + read -rp 'Email: ' email + done + fi + + send_event "installation_support" + + + echo "" + echo -e "\nWe will reach out to you at the email provided shortly, Exiting for now. Bye! 👋 \n" + exit 0 + fi +} + +request_sudo() { + if hash sudo 2>/dev/null; then + echo -e "\n\n🙇 We will need sudo access to complete the installation." + if (( $EUID != 0 )); then + sudo_cmd="sudo" + echo -e "Please enter your sudo password, if prompted." + # $sudo_cmd -l | grep -e "NOPASSWD: ALL" > /dev/null + # if [[ $? -ne 0 ]] && ! $sudo_cmd -v; then + # echo "Need sudo privileges to proceed with the installation." + # exit 1; + # fi + if ! $sudo_cmd -l | grep -e "NOPASSWD: ALL" > /dev/null && ! $sudo_cmd -v; then + echo "Need sudo privileges to proceed with the installation." + exit 1; + fi + + echo -e "Got it! Thanks!! 🙏\n" + echo -e "Okay! We will bring up the SigNoz cluster from here 🚀\n" + fi + fi +} + +echo "" +echo -e "👋 Thank you for trying out SigNoz! " +echo "" + +sudo_cmd="" + +# Check sudo permissions +if (( $EUID != 0 )); then + echo "🟡 Running installer with non-sudo permissions." + echo " In case of any failure or prompt, please consider running the script with sudo privileges." + echo "" +else + sudo_cmd="sudo" +fi + +# Checking OS and assigning package manager +desired_os=0 +os="" +email="" +echo -e "🌏 Detecting your OS ...\n" +check_os + +# Obtain unique installation id +# sysinfo="$(uname -a)" +# if [[ $? -ne 0 ]]; then +# uuid="$(uuidgen)" +# uuid="${uuid:-$(cat /proc/sys/kernel/random/uuid)}" +# sysinfo="${uuid:-$(cat /proc/sys/kernel/random/uuid)}" +# fi +if ! sysinfo="$(uname -a)"; then + uuid="$(uuidgen)" + uuid="${uuid:-$(cat /proc/sys/kernel/random/uuid)}" + sysinfo="${uuid:-$(cat /proc/sys/kernel/random/uuid)}" +fi + +digest_cmd="" +if hash shasum 2>/dev/null; then + digest_cmd="shasum -a 256" +elif hash sha256sum 2>/dev/null; then + digest_cmd="sha256sum" +elif hash openssl 2>/dev/null; then + digest_cmd="openssl dgst -sha256" +fi + +if [[ -z $digest_cmd ]]; then + SIGNOZ_INSTALLATION_ID="$sysinfo" +else + SIGNOZ_INSTALLATION_ID=$(echo "$sysinfo" | $digest_cmd | grep -E -o '[a-zA-Z0-9]{64}') +fi + +# echo "" + +# echo -e "👉 ${RED}Two ways to go forward\n" +# echo -e "${RED}1) ClickHouse as database (default)\n" +# read -p "⚙️ Enter your preference (1/2):" choice_setup + +# while [[ $choice_setup != "1" && $choice_setup != "2" && $choice_setup != "" ]] +# do +# # echo $choice_setup +# echo -e "\n❌ ${CYAN}Please enter either 1 or 2" +# read -p "⚙️ Enter your preference (1/2): " choice_setup +# # echo $choice_setup +# done + +# if [[ $choice_setup == "1" || $choice_setup == "" ]];then +# setup_type='clickhouse' +# fi + +setup_type='clickhouse' + +# echo -e "\n✅ ${CYAN}You have chosen: ${setup_type} setup\n" + +# Run bye if failure happens +trap bye EXIT + +URL="https://api.segment.io/v1/track" +HEADER_1="Content-Type: application/json" +HEADER_2="Authorization: Basic NEdtb2E0aXhKQVVIeDJCcEp4c2p3QTFiRWZud0VlUno6" + +send_event() { + error="" + + case "$1" in + 'install_started') + event="Installation Started" + ;; + 'os_not_supported') + event="Installation Error" + error="OS Not Supported" + ;; + 'docker_not_installed') + event="Installation Error" + error="Docker not installed" + ;; + 'docker_compose_not_found') + event="Installation Error" + event="Docker Compose not found" + ;; + 'port_not_available') + event="Installation Error" + error="port not available" + ;; + 'installation_error_checks') + event="Installation Error - Checks" + error="Containers not started" + others='"data": "some_checks",' + ;; + 'installation_support') + event="Installation Support" + others='"email": "'"$email"'",' + ;; + 'installation_success') + event="Installation Success" + ;; + 'identify_successful_installation') + event="Identify Successful Installation" + others='"email": "'"$email"'",' + ;; + *) + print_error "unknown event type: $1" + exit 1 + ;; + esac + + if [[ "$error" != "" ]]; then + error='"error": "'"$error"'", ' + fi + + DATA='{ "anonymousId": "'"$SIGNOZ_INSTALLATION_ID"'", "event": "'"$event"'", "properties": { "os": "'"$os"'", '"$error $others"' "setup_type": "'"$setup_type"'" } }' + + if has_curl; then + curl -sfL -d "$DATA" --header "$HEADER_1" --header "$HEADER_2" "$URL" > /dev/null 2>&1 + elif has_wget; then + wget -q --post-data="$DATA" --header "$HEADER_1" --header "$HEADER_2" "$URL" > /dev/null 2>&1 + fi +} + +send_event "install_started" + +if [[ $desired_os -eq 0 ]]; then + send_event "os_not_supported" +fi + +# check_ports_occupied + +# Check is Docker daemon is installed and available. If not, the install & start Docker for Linux machines. We cannot automatically install Docker Desktop on Mac OS +if ! is_command_present docker; then + + if [[ $package_manager == "apt-get" || $package_manager == "zypper" || $package_manager == "yum" ]]; then + request_sudo + install_docker + # enable docker without sudo from next reboot + sudo usermod -aG docker "${USER}" + elif is_mac; then + echo "" + echo "+++++++++++ IMPORTANT READ ++++++++++++++++++++++" + echo "Docker Desktop must be installed manually on Mac OS to proceed. Docker can only be installed automatically on Ubuntu / openSUSE / SLES / Redhat / Cent OS" + echo "https://docs.docker.com/docker-for-mac/install/" + echo "++++++++++++++++++++++++++++++++++++++++++++++++" + + send_event "docker_not_installed" + exit 1 + else + echo "" + echo "+++++++++++ IMPORTANT READ ++++++++++++++++++++++" + echo "Docker must be installed manually on your machine to proceed. Docker can only be installed automatically on Ubuntu / openSUSE / SLES / Redhat / Cent OS" + echo "https://docs.docker.com/get-docker/" + echo "++++++++++++++++++++++++++++++++++++++++++++++++" + + send_event "docker_not_installed" + exit 1 + fi +fi + +# Install docker-compose +if ! is_command_present docker-compose; then + request_sudo + install_docker_compose +fi + +start_docker + +# $sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml up -d --remove-orphans || true + + +echo "" +echo -e "\n🟡 Pulling the latest container images for SigNoz.\n" +$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml pull + +echo "" +echo "🟡 Starting the SigNoz containers. It may take a few minutes ..." +echo +# The docker-compose command does some nasty stuff for the `--detach` functionality. So we add a `|| true` so that the +# script doesn't exit because this command looks like it failed to do it's thing. +$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml up --detach --remove-orphans || true + +wait_for_containers_start 60 +echo "" + +if [[ $status_code -ne 200 ]]; then + echo "+++++++++++ ERROR ++++++++++++++++++++++" + echo "🔴 The containers didn't seem to start correctly. Please run the following command to check containers that may have errored out:" + echo "" + + echo -e "$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml ps -a" + + echo "Please read our troubleshooting guide https://signoz.io/docs/install/troubleshooting/" + echo "or reach us on SigNoz for support https://signoz.io/slack" + echo "++++++++++++++++++++++++++++++++++++++++" + + send_event "installation_error_checks" + exit 1 + +else + send_event "installation_success" + + echo "++++++++++++++++++ SUCCESS ++++++++++++++++++++++" + echo "" + echo "🟢 Your installation is complete!" + echo "" + echo -e "🟢 Your frontend is running on http://localhost:3301" + echo "" + echo "ℹ️ By default, retention period is set to 15 days for logs and traces, and 30 days for metrics." + echo -e "To change this, navigate to the General tab on the Settings page of SigNoz UI. For more details, refer to https://signoz.io/docs/userguide/retention-period \n" + + echo "ℹ️ To bring down SigNoz and clean volumes : $sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml down -v" + + echo "" + echo "+++++++++++++++++++++++++++++++++++++++++++++++++" + echo "" + echo "👉 Need help in Getting Started?" + echo -e "Join us on Slack https://signoz.io/slack" + echo "" + echo -e "\n📨 Please share your email to receive support & updates about SigNoz!" + read -rp 'Email: ' email + + while [[ $email == "" ]] + do + read -rp 'Email: ' email + done + + send_event "identify_successful_installation" +fi + +echo -e "\n🙏 Thank you!\n" diff --git a/e2e/package.json b/e2e/package.json new file mode 100644 index 0000000..b0d1f6f --- /dev/null +++ b/e2e/package.json @@ -0,0 +1,14 @@ +{ + "name": "e2e", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "devDependencies": { + "@playwright/test": "^1.22.0", + "@types/node": "^20.9.2" + }, + "scripts": {}, + "dependencies": { + "dotenv": "8.2.0" + } +} diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts new file mode 100644 index 0000000..b1a0f30 --- /dev/null +++ b/e2e/playwright.config.ts @@ -0,0 +1,46 @@ +import { defineConfig, devices } from "@playwright/test"; +import dotenv from "dotenv"; + +dotenv.config(); + +export default defineConfig({ + testDir: "./tests", + + fullyParallel: true, + + forbidOnly: !!process.env.CI, + + name: "Signoz E2E", + + retries: process.env.CI ? 2 : 0, + + reporter: process.env.CI ? "github" : "list", + + preserveOutput: "always", + + updateSnapshots: "all", + + quiet: false, + + testMatch: ["**/*.spec.ts"], + + use: { + trace: "on-first-retry", + + baseURL: + process.env.PLAYWRIGHT_TEST_BASE_URL || "https://stagingapp.signoz.io/", + }, + + projects: [ + { name: "setup", testMatch: /.*\.setup\.ts/ }, + { + name: "chromium", + use: { + ...devices["Desktop Chrome"], + // Use prepared auth state. + storageState: ".auth/user.json", + }, + dependencies: ["setup"], + }, + ], +}); diff --git a/e2e/tests/auth.setup.ts b/e2e/tests/auth.setup.ts new file mode 100644 index 0000000..9ca8173 --- /dev/null +++ b/e2e/tests/auth.setup.ts @@ -0,0 +1,37 @@ +import { test, expect } from "@playwright/test"; +import ROUTES from "../../frontend/src/constants/routes"; +import dotenv from "dotenv"; + +dotenv.config(); + +const authFile = ".auth/user.json"; + +test("E2E Login Test", async ({ page }) => { + await Promise.all([page.goto("/"), page.waitForRequest("**/version")]); + + const signup = "Monitor your applications. Find what is causing issues."; + + const el = await page.locator(`text=${signup}`); + + expect(el).toBeVisible(); + + await page + .locator("id=loginEmail") + .type( + process.env.PLAYWRIGHT_USERNAME ? process.env.PLAYWRIGHT_USERNAME : "" + ); + + await page.getByText("Next").click(); + + await page + .locator('input[id="currentPassword"]') + .fill( + process.env.PLAYWRIGHT_PASSWORD ? process.env.PLAYWRIGHT_PASSWORD : "" + ); + + await page.locator('button[data-attr="signup"]').click(); + + await expect(page).toHaveURL(ROUTES.APPLICATION); + + await page.context().storageState({ path: authFile }); +}); diff --git a/e2e/tests/contants.ts b/e2e/tests/contants.ts new file mode 100644 index 0000000..e44f761 --- /dev/null +++ b/e2e/tests/contants.ts @@ -0,0 +1,10 @@ +export const SERVICE_TABLE_HEADERS = { + APPLICATION: "Applicaton", + P99LATENCY: "P99 latency (in ms)", + ERROR_RATE: "Error Rate (% of total)", + OPS_PER_SECOND: "Operations Per Second", +}; + +export const DATA_TEST_IDS = { + NEW_DASHBOARD_BTN: "create-new-dashboard", +}; diff --git a/e2e/tests/navigation.spec.ts b/e2e/tests/navigation.spec.ts new file mode 100644 index 0000000..6f30b2f --- /dev/null +++ b/e2e/tests/navigation.spec.ts @@ -0,0 +1,40 @@ +import { test, expect } from "@playwright/test"; +import ROUTES from "../../frontend/src/constants/routes"; +import { DATA_TEST_IDS, SERVICE_TABLE_HEADERS } from "./contants"; + +test("Basic Navigation Check across different resources", async ({ page }) => { + // route to services page and check if the page renders fine with BE contract + await Promise.all([ + page.goto(ROUTES.APPLICATION), + page.waitForRequest("**/v1/services"), + ]); + + const p99Latency = page.locator( + `th:has-text("${SERVICE_TABLE_HEADERS.P99LATENCY}")` + ); + + await expect(p99Latency).toBeVisible(); + + // route to the new trace explorer page and check if the page renders fine + await page.goto(ROUTES.TRACES_EXPLORER); + + await page.waitForLoadState("networkidle"); + + const listViewTable = await page + .locator('div[role="presentation"]') + .isVisible(); + + expect(listViewTable).toBeTruthy(); + + // route to the dashboards page and check if the page renders fine + await Promise.all([ + page.goto(ROUTES.ALL_DASHBOARD), + page.waitForRequest("**/v1/dashboards"), + ]); + + const newDashboardBtn = await page + .locator(`data-testid=${DATA_TEST_IDS.NEW_DASHBOARD_BTN}`) + .isVisible(); + + expect(newDashboardBtn).toBeTruthy(); +}); diff --git a/e2e/yarn.lock b/e2e/yarn.lock new file mode 100644 index 0000000..1fd28ee --- /dev/null +++ b/e2e/yarn.lock @@ -0,0 +1,46 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@playwright/test@^1.22.0": + version "1.40.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.40.0.tgz#d06c506977dd7863aa16e07f2136351ecc1be6ed" + integrity sha512-PdW+kn4eV99iP5gxWNSDQCbhMaDVej+RXL5xr6t04nbKLCBwYtA046t7ofoczHOm8u6c+45hpDKQVZqtqwkeQg== + dependencies: + playwright "1.40.0" + +"@types/node@^20.9.2": + version "20.9.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.2.tgz#002815c8e87fe0c9369121c78b52e800fadc0ac6" + integrity sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg== + dependencies: + undici-types "~5.26.4" + +dotenv@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + +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== + +playwright-core@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.40.0.tgz#82f61e5504cb3097803b6f8bbd98190dd34bdf14" + integrity sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q== + +playwright@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.40.0.tgz#2a1824b9fe5c4fe52ed53db9ea68003543a99df0" + integrity sha512-gyHAgQjiDf1m34Xpwzaqb76KgfzYrhK7iih+2IzcOCoZWr/8ZqmdBw+t0RU85ZmfJMgtgAiNtBQ/KS2325INXw== + dependencies: + playwright-core "1.40.0" + optionalDependencies: + fsevents "2.3.2" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== diff --git a/ee/LICENSE b/ee/LICENSE new file mode 100644 index 0000000..c024dbd --- /dev/null +++ b/ee/LICENSE @@ -0,0 +1,37 @@ + +The SigNoz Enterprise license (the "Enterprise License") +Copyright (c) 2020 - present SigNoz Inc. + +With regard to the SigNoz Software: + +This software and associated documentation files (the "Software") may only be +used in production, if you (and any entity that you represent) have agreed to, +and are in compliance with, the SigNoz Subscription Terms of Service, available +via email (hello@signoz.io) (the "Enterprise Terms"), or other +agreement governing the use of the Software, as agreed by you and SigNoz, +and otherwise have a valid SigNoz Enterprise license for the +correct number of user seats. Subject to the foregoing sentence, you are free to +modify this Software and publish patches to the Software. You agree that SigNoz +and/or its licensors (as applicable) retain all right, title and interest in and +to all such modifications and/or patches, and all such modifications and/or +patches may only be used, copied, modified, displayed, distributed, or otherwise +exploited with a valid SigNoz Enterprise license for the correct +number of user seats. Notwithstanding the foregoing, you may copy and modify +the Software for development and testing purposes, without requiring a +subscription. You agree that SigNoz and/or its licensors (as applicable) retain +all right, title and interest in and to all such modifications. You are not +granted any other rights beyond what is expressly stated herein. Subject to the +foregoing, it is forbidden to copy, merge, publish, distribute, sublicense, +and/or sell the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +For all third party components incorporated into the SigNoz Software, those +components are licensed under the original license provided by the owner of the +applicable component. \ No newline at end of file diff --git a/ee/query-service/.dockerignore b/ee/query-service/.dockerignore new file mode 100644 index 0000000..9521c50 --- /dev/null +++ b/ee/query-service/.dockerignore @@ -0,0 +1,4 @@ +.vscode +README.md +signoz.db +bin \ No newline at end of file diff --git a/ee/query-service/Dockerfile b/ee/query-service/Dockerfile new file mode 100644 index 0000000..55ed33a --- /dev/null +++ b/ee/query-service/Dockerfile @@ -0,0 +1,31 @@ +# use a minimal alpine image +FROM alpine:3.18.6 + +# Add Maintainer Info +LABEL maintainer="signoz" + +# define arguments that can be passed during build time +ARG TARGETOS TARGETARCH + +# add ca-certificates in case you need them +RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/* + +# set working directory +WORKDIR /root + +# copy the query-service binary +COPY ee/query-service/bin/query-service-${TARGETOS}-${TARGETARCH} /root/query-service + +# copy prometheus YAML config +COPY pkg/query-service/config/prometheus.yml /root/config/prometheus.yml +COPY pkg/query-service/templates /root/templates + +# Make query-service executable for non-root users +RUN chmod 755 /root /root/query-service + +# run the binary +ENTRYPOINT ["./query-service"] + +CMD ["-config", "/root/config/prometheus.yml"] + +EXPOSE 8080 diff --git a/ee/query-service/app/api/api.go b/ee/query-service/app/api/api.go new file mode 100644 index 0000000..6defd85 --- /dev/null +++ b/ee/query-service/app/api/api.go @@ -0,0 +1,187 @@ +package api + +import ( + "net/http" + "time" + + "github.com/gorilla/mux" + "go.signoz.io/signoz/ee/query-service/dao" + "go.signoz.io/signoz/ee/query-service/interfaces" + "go.signoz.io/signoz/ee/query-service/license" + "go.signoz.io/signoz/ee/query-service/usage" + baseapp "go.signoz.io/signoz/pkg/query-service/app" + "go.signoz.io/signoz/pkg/query-service/app/integrations" + "go.signoz.io/signoz/pkg/query-service/app/logparsingpipeline" + "go.signoz.io/signoz/pkg/query-service/cache" + baseint "go.signoz.io/signoz/pkg/query-service/interfaces" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + rules "go.signoz.io/signoz/pkg/query-service/rules" + "go.signoz.io/signoz/pkg/query-service/version" +) + +type APIHandlerOptions struct { + DataConnector interfaces.DataConnector + SkipConfig *basemodel.SkipConfig + PreferDelta bool + PreferSpanMetrics bool + MaxIdleConns int + MaxOpenConns int + DialTimeout time.Duration + AppDao dao.ModelDao + RulesManager *rules.Manager + UsageManager *usage.Manager + FeatureFlags baseint.FeatureLookup + LicenseManager *license.Manager + IntegrationsController *integrations.Controller + LogsParsingPipelineController *logparsingpipeline.LogParsingPipelineController + Cache cache.Cache + // Querier Influx Interval + FluxInterval time.Duration +} + +type APIHandler struct { + opts APIHandlerOptions + baseapp.APIHandler +} + +// NewAPIHandler returns an APIHandler +func NewAPIHandler(opts APIHandlerOptions) (*APIHandler, error) { + + baseHandler, err := baseapp.NewAPIHandler(baseapp.APIHandlerOpts{ + Reader: opts.DataConnector, + SkipConfig: opts.SkipConfig, + PerferDelta: opts.PreferDelta, + PreferSpanMetrics: opts.PreferSpanMetrics, + MaxIdleConns: opts.MaxIdleConns, + MaxOpenConns: opts.MaxOpenConns, + DialTimeout: opts.DialTimeout, + AppDao: opts.AppDao, + RuleManager: opts.RulesManager, + FeatureFlags: opts.FeatureFlags, + IntegrationsController: opts.IntegrationsController, + LogsParsingPipelineController: opts.LogsParsingPipelineController, + Cache: opts.Cache, + FluxInterval: opts.FluxInterval, + }) + + if err != nil { + return nil, err + } + + ah := &APIHandler{ + opts: opts, + APIHandler: *baseHandler, + } + return ah, nil +} + +func (ah *APIHandler) FF() baseint.FeatureLookup { + return ah.opts.FeatureFlags +} + +func (ah *APIHandler) RM() *rules.Manager { + return ah.opts.RulesManager +} + +func (ah *APIHandler) LM() *license.Manager { + return ah.opts.LicenseManager +} + +func (ah *APIHandler) UM() *usage.Manager { + return ah.opts.UsageManager +} + +func (ah *APIHandler) AppDao() dao.ModelDao { + return ah.opts.AppDao +} + +func (ah *APIHandler) CheckFeature(f string) bool { + err := ah.FF().CheckFeature(f) + return err == nil +} + +// RegisterRoutes registers routes for this handler on the given router +func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *baseapp.AuthMiddleware) { + // note: add ee override methods first + + // routes available only in ee version + router.HandleFunc("/api/v1/licenses", + am.AdminAccess(ah.listLicenses)). + Methods(http.MethodGet) + + router.HandleFunc("/api/v1/licenses", + am.AdminAccess(ah.applyLicense)). + Methods(http.MethodPost) + + router.HandleFunc("/api/v1/featureFlags", + am.OpenAccess(ah.getFeatureFlags)). + Methods(http.MethodGet) + + router.HandleFunc("/api/v1/loginPrecheck", + am.OpenAccess(ah.precheckLogin)). + Methods(http.MethodGet) + + // paid plans specific routes + router.HandleFunc("/api/v1/complete/saml", + am.OpenAccess(ah.receiveSAML)). + Methods(http.MethodPost) + + router.HandleFunc("/api/v1/complete/google", + am.OpenAccess(ah.receiveGoogleAuth)). + Methods(http.MethodGet) + + router.HandleFunc("/api/v1/orgs/{orgId}/domains", + am.AdminAccess(ah.listDomainsByOrg)). + Methods(http.MethodGet) + + router.HandleFunc("/api/v1/domains", + am.AdminAccess(ah.postDomain)). + Methods(http.MethodPost) + + router.HandleFunc("/api/v1/domains/{id}", + am.AdminAccess(ah.putDomain)). + Methods(http.MethodPut) + + router.HandleFunc("/api/v1/domains/{id}", + am.AdminAccess(ah.deleteDomain)). + Methods(http.MethodDelete) + + // base overrides + router.HandleFunc("/api/v1/version", am.OpenAccess(ah.getVersion)).Methods(http.MethodGet) + router.HandleFunc("/api/v1/invite/{token}", am.OpenAccess(ah.getInvite)).Methods(http.MethodGet) + router.HandleFunc("/api/v1/register", am.OpenAccess(ah.registerUser)).Methods(http.MethodPost) + router.HandleFunc("/api/v1/login", am.OpenAccess(ah.loginUser)).Methods(http.MethodPost) + router.HandleFunc("/api/v1/traces/{traceId}", am.ViewAccess(ah.searchTraces)).Methods(http.MethodGet) + router.HandleFunc("/api/v2/metrics/query_range", am.ViewAccess(ah.queryRangeMetricsV2)).Methods(http.MethodPost) + + // PAT APIs + router.HandleFunc("/api/v1/pats", am.AdminAccess(ah.createPAT)).Methods(http.MethodPost) + router.HandleFunc("/api/v1/pats", am.AdminAccess(ah.getPATs)).Methods(http.MethodGet) + router.HandleFunc("/api/v1/pats/{id}", am.AdminAccess(ah.updatePAT)).Methods(http.MethodPut) + router.HandleFunc("/api/v1/pats/{id}", am.AdminAccess(ah.revokePAT)).Methods(http.MethodDelete) + + router.HandleFunc("/api/v1/checkout", am.AdminAccess(ah.checkout)).Methods(http.MethodPost) + router.HandleFunc("/api/v1/billing", am.AdminAccess(ah.getBilling)).Methods(http.MethodGet) + router.HandleFunc("/api/v1/portal", am.AdminAccess(ah.portalSession)).Methods(http.MethodPost) + + router.HandleFunc("/api/v1/dashboards/{uuid}/lock", am.EditAccess(ah.lockDashboard)).Methods(http.MethodPut) + router.HandleFunc("/api/v1/dashboards/{uuid}/unlock", am.EditAccess(ah.unlockDashboard)).Methods(http.MethodPut) + + router.HandleFunc("/api/v2/licenses", + am.ViewAccess(ah.listLicensesV2)). + Methods(http.MethodGet) + + ah.APIHandler.RegisterRoutes(router, am) + +} + +func (ah *APIHandler) getVersion(w http.ResponseWriter, r *http.Request) { + version := version.GetVersion() + versionResponse := basemodel.GetVersionResponse{ + Version: version, + EE: "Y", + SetupCompleted: ah.SetupCompleted, + } + + ah.WriteJSON(w, r, versionResponse) +} diff --git a/ee/query-service/app/api/auth.go b/ee/query-service/app/api/auth.go new file mode 100644 index 0000000..a469b99 --- /dev/null +++ b/ee/query-service/app/api/auth.go @@ -0,0 +1,338 @@ +package api + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + + "github.com/gorilla/mux" + "go.uber.org/zap" + + "go.signoz.io/signoz/ee/query-service/constants" + "go.signoz.io/signoz/ee/query-service/model" + "go.signoz.io/signoz/pkg/query-service/auth" + baseauth "go.signoz.io/signoz/pkg/query-service/auth" + basemodel "go.signoz.io/signoz/pkg/query-service/model" +) + +func parseRequest(r *http.Request, req interface{}) error { + defer r.Body.Close() + requestBody, err := io.ReadAll(r.Body) + if err != nil { + return err + } + + err = json.Unmarshal(requestBody, &req) + return err +} + +// loginUser overrides base handler and considers SSO case. +func (ah *APIHandler) loginUser(w http.ResponseWriter, r *http.Request) { + + req := basemodel.LoginRequest{} + err := parseRequest(r, &req) + if err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + + ctx := context.Background() + + if req.Email != "" && ah.CheckFeature(model.SSO) { + var apierr basemodel.BaseApiError + _, apierr = ah.AppDao().CanUsePassword(ctx, req.Email) + if apierr != nil && !apierr.IsNil() { + RespondError(w, apierr, nil) + } + } + + // if all looks good, call auth + resp, err := auth.Login(ctx, &req) + if ah.HandleError(w, err, http.StatusUnauthorized) { + return + } + + ah.WriteJSON(w, r, resp) +} + +// registerUser registers a user and responds with a precheck +// so the front-end can decide the login method +func (ah *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) { + + if !ah.CheckFeature(model.SSO) { + ah.APIHandler.Register(w, r) + return + } + + ctx := context.Background() + var req *baseauth.RegisterRequest + + defer r.Body.Close() + requestBody, err := io.ReadAll(r.Body) + if err != nil { + zap.S().Errorf("received no input in api\n", err) + RespondError(w, model.BadRequest(err), nil) + return + } + + err = json.Unmarshal(requestBody, &req) + + if err != nil { + zap.S().Errorf("received invalid user registration request", zap.Error(err)) + RespondError(w, model.BadRequest(fmt.Errorf("failed to register user")), nil) + return + } + + // get invite object + invite, err := baseauth.ValidateInvite(ctx, req) + if err != nil { + zap.S().Errorf("failed to validate invite token", err) + RespondError(w, model.BadRequest(err), nil) + return + } + + if invite == nil { + zap.S().Errorf("failed to validate invite token: it is either empty or invalid", err) + RespondError(w, model.BadRequest(basemodel.ErrSignupFailed{}), nil) + return + } + + // get auth domain from email domain + domain, apierr := ah.AppDao().GetDomainByEmail(ctx, invite.Email) + if apierr != nil { + zap.S().Errorf("failed to get domain from email", apierr) + RespondError(w, model.InternalError(basemodel.ErrSignupFailed{}), nil) + } + + precheckResp := &basemodel.PrecheckResponse{ + SSO: false, + IsUser: false, + } + + if domain != nil && domain.SsoEnabled { + // sso is enabled, create user and respond precheck data + user, apierr := baseauth.RegisterInvitedUser(ctx, req, true) + if apierr != nil { + RespondError(w, apierr, nil) + return + } + + var precheckError basemodel.BaseApiError + + precheckResp, precheckError = ah.AppDao().PrecheckLogin(ctx, user.Email, req.SourceUrl) + if precheckError != nil { + RespondError(w, precheckError, precheckResp) + } + + } else { + // no-sso, validate password + if err := auth.ValidatePassword(req.Password); err != nil { + RespondError(w, model.InternalError(fmt.Errorf("password is not in a valid format")), nil) + return + } + + _, registerError := baseauth.Register(ctx, req) + if !registerError.IsNil() { + RespondError(w, apierr, nil) + return + } + + precheckResp.IsUser = true + } + + ah.Respond(w, precheckResp) +} + +// getInvite returns the invite object details for the given invite token. We do not need to +// protect this API because invite token itself is meant to be private. +func (ah *APIHandler) getInvite(w http.ResponseWriter, r *http.Request) { + token := mux.Vars(r)["token"] + sourceUrl := r.URL.Query().Get("ref") + ctx := context.Background() + + inviteObject, err := baseauth.GetInvite(context.Background(), token) + if err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + + resp := model.GettableInvitation{ + InvitationResponseObject: inviteObject, + } + + precheck, apierr := ah.AppDao().PrecheckLogin(ctx, inviteObject.Email, sourceUrl) + resp.Precheck = precheck + + if apierr != nil { + RespondError(w, apierr, resp) + } + + ah.WriteJSON(w, r, resp) +} + +// PrecheckLogin enables browser login page to display appropriate +// login methods +func (ah *APIHandler) precheckLogin(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + + email := r.URL.Query().Get("email") + sourceUrl := r.URL.Query().Get("ref") + + resp, apierr := ah.AppDao().PrecheckLogin(ctx, email, sourceUrl) + if apierr != nil { + RespondError(w, apierr, resp) + } + + ah.Respond(w, resp) +} + +func handleSsoError(w http.ResponseWriter, r *http.Request, redirectURL string) { + ssoError := []byte("Login failed. Please contact your system administrator") + dst := make([]byte, base64.StdEncoding.EncodedLen(len(ssoError))) + base64.StdEncoding.Encode(dst, ssoError) + + http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectURL, string(dst)), http.StatusSeeOther) +} + +// receiveGoogleAuth completes google OAuth response and forwards a request +// to front-end to sign user in +func (ah *APIHandler) receiveGoogleAuth(w http.ResponseWriter, r *http.Request) { + redirectUri := constants.GetDefaultSiteURL() + ctx := context.Background() + + if !ah.CheckFeature(model.SSO) { + zap.S().Errorf("[receiveGoogleAuth] sso requested but feature unavailable %s in org domain %s", model.SSO) + http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently) + return + } + + q := r.URL.Query() + if errType := q.Get("error"); errType != "" { + zap.S().Errorf("[receiveGoogleAuth] failed to login with google auth", q.Get("error_description")) + http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "failed to login through SSO "), http.StatusMovedPermanently) + return + } + + relayState := q.Get("state") + zap.S().Debug("[receiveGoogleAuth] relay state received", zap.String("state", relayState)) + + parsedState, err := url.Parse(relayState) + if err != nil || relayState == "" { + zap.S().Errorf("[receiveGoogleAuth] failed to process response - invalid response from IDP", err, r) + handleSsoError(w, r, redirectUri) + return + } + + // upgrade redirect url from the relay state for better accuracy + redirectUri = fmt.Sprintf("%s://%s%s", parsedState.Scheme, parsedState.Host, "/login") + + // fetch domain by parsing relay state. + domain, err := ah.AppDao().GetDomainFromSsoResponse(ctx, parsedState) + if err != nil { + handleSsoError(w, r, redirectUri) + return + } + + // now that we have domain, use domain to fetch sso settings. + // prepare google callback handler using parsedState - + // which contains redirect URL (front-end endpoint) + callbackHandler, err := domain.PrepareGoogleOAuthProvider(parsedState) + + identity, err := callbackHandler.HandleCallback(r) + if err != nil { + zap.S().Errorf("[receiveGoogleAuth] failed to process HandleCallback ", domain.String(), zap.Error(err)) + handleSsoError(w, r, redirectUri) + return + } + + nextPage, err := ah.AppDao().PrepareSsoRedirect(ctx, redirectUri, identity.Email) + if err != nil { + zap.S().Errorf("[receiveGoogleAuth] failed to generate redirect URI after successful login ", domain.String(), zap.Error(err)) + handleSsoError(w, r, redirectUri) + return + } + + http.Redirect(w, r, nextPage, http.StatusSeeOther) +} + +// receiveSAML completes a SAML request and gets user logged in +func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) { + // this is the source url that initiated the login request + redirectUri := constants.GetDefaultSiteURL() + ctx := context.Background() + + if !ah.CheckFeature(model.SSO) { + zap.S().Errorf("[receiveSAML] sso requested but feature unavailable %s in org domain %s", model.SSO) + http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently) + return + } + + err := r.ParseForm() + if err != nil { + zap.S().Errorf("[receiveSAML] failed to process response - invalid response from IDP", err, r) + handleSsoError(w, r, redirectUri) + return + } + + // the relay state is sent when a login request is submitted to + // Idp. + relayState := r.FormValue("RelayState") + zap.S().Debug("[receiveML] relay state", zap.String("relayState", relayState)) + + parsedState, err := url.Parse(relayState) + if err != nil || relayState == "" { + zap.S().Errorf("[receiveSAML] failed to process response - invalid response from IDP", err, r) + handleSsoError(w, r, redirectUri) + return + } + + // upgrade redirect url from the relay state for better accuracy + redirectUri = fmt.Sprintf("%s://%s%s", parsedState.Scheme, parsedState.Host, "/login") + + // fetch domain by parsing relay state. + domain, err := ah.AppDao().GetDomainFromSsoResponse(ctx, parsedState) + if err != nil { + handleSsoError(w, r, redirectUri) + return + } + + sp, err := domain.PrepareSamlRequest(parsedState) + if err != nil { + zap.S().Errorf("[receiveSAML] failed to prepare saml request for domain (%s): %v", domain.String(), err) + handleSsoError(w, r, redirectUri) + return + } + + assertionInfo, err := sp.RetrieveAssertionInfo(r.FormValue("SAMLResponse")) + if err != nil { + zap.S().Errorf("[receiveSAML] failed to retrieve assertion info from saml response for organization (%s): %v", domain.String(), err) + handleSsoError(w, r, redirectUri) + return + } + + if assertionInfo.WarningInfo.InvalidTime { + zap.S().Errorf("[receiveSAML] expired saml response for organization (%s): %v", domain.String(), err) + handleSsoError(w, r, redirectUri) + return + } + + email := assertionInfo.NameID + if email == "" { + zap.S().Errorf("[receiveSAML] invalid email in the SSO response (%s)", domain.String()) + handleSsoError(w, r, redirectUri) + return + } + + nextPage, err := ah.AppDao().PrepareSsoRedirect(ctx, redirectUri, email) + if err != nil { + zap.S().Errorf("[receiveSAML] failed to generate redirect URI after successful login ", domain.String(), zap.Error(err)) + handleSsoError(w, r, redirectUri) + return + } + + http.Redirect(w, r, nextPage, http.StatusSeeOther) +} diff --git a/ee/query-service/app/api/dashboard.go b/ee/query-service/app/api/dashboard.go new file mode 100644 index 0000000..83c82a1 --- /dev/null +++ b/ee/query-service/app/api/dashboard.go @@ -0,0 +1,51 @@ +package api + +import ( + "github.com/gorilla/mux" + "go.signoz.io/signoz/pkg/query-service/app/dashboards" + "go.signoz.io/signoz/pkg/query-service/auth" + "go.signoz.io/signoz/pkg/query-service/common" + "go.signoz.io/signoz/pkg/query-service/model" + "net/http" +) + +func (ah *APIHandler) lockDashboard(w http.ResponseWriter, r *http.Request) { + ah.lockUnlockDashboard(w, r, true) +} + +func (ah *APIHandler) unlockDashboard(w http.ResponseWriter, r *http.Request) { + ah.lockUnlockDashboard(w, r, false) +} + +func (ah *APIHandler) lockUnlockDashboard(w http.ResponseWriter, r *http.Request, lock bool) { + // Locking can only be done by the owner of the dashboard + // or an admin + + // - Fetch the dashboard + // - Check if the user is the owner or an admin + // - If yes, lock/unlock the dashboard + // - If no, return 403 + + // Get the dashboard UUID from the request + uuid := mux.Vars(r)["uuid"] + dashboard, err := dashboards.GetDashboard(r.Context(), uuid) + if err != nil { + RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, err.Error()) + return + } + + user := common.GetUserFromContext(r.Context()) + if !auth.IsAdmin(user) && (dashboard.CreateBy != nil && *dashboard.CreateBy != user.Email) { + RespondError(w, &model.ApiError{Typ: model.ErrorForbidden, Err: err}, "You are not authorized to lock/unlock this dashboard") + return + } + + // Lock/Unlock the dashboard + err = dashboards.LockUnlockDashboard(r.Context(), uuid, lock) + if err != nil { + RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, err.Error()) + return + } + + ah.Respond(w, "Dashboard updated successfully") +} diff --git a/ee/query-service/app/api/domains.go b/ee/query-service/app/api/domains.go new file mode 100644 index 0000000..6456928 --- /dev/null +++ b/ee/query-service/app/api/domains.go @@ -0,0 +1,90 @@ +package api + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "go.signoz.io/signoz/ee/query-service/model" +) + +func (ah *APIHandler) listDomainsByOrg(w http.ResponseWriter, r *http.Request) { + orgId := mux.Vars(r)["orgId"] + domains, apierr := ah.AppDao().ListDomains(context.Background(), orgId) + if apierr != nil { + RespondError(w, apierr, domains) + return + } + ah.Respond(w, domains) +} + +func (ah *APIHandler) postDomain(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + + req := model.OrgDomain{} + + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + + if err := req.ValidNew(); err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + + if apierr := ah.AppDao().CreateDomain(ctx, &req); apierr != nil { + RespondError(w, apierr, nil) + return + } + + ah.Respond(w, &req) +} + +func (ah *APIHandler) putDomain(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + + domainIdStr := mux.Vars(r)["id"] + domainId, err := uuid.Parse(domainIdStr) + if err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + + req := model.OrgDomain{Id: domainId} + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + req.Id = domainId + if err := req.Valid(nil); err != nil { + RespondError(w, model.BadRequest(err), nil) + } + + if apierr := ah.AppDao().UpdateDomain(ctx, &req); apierr != nil { + RespondError(w, apierr, nil) + return + } + + ah.Respond(w, &req) +} + +func (ah *APIHandler) deleteDomain(w http.ResponseWriter, r *http.Request) { + domainIdStr := mux.Vars(r)["id"] + + domainId, err := uuid.Parse(domainIdStr) + if err != nil { + RespondError(w, model.BadRequest(fmt.Errorf("invalid domain id")), nil) + return + } + + apierr := ah.AppDao().DeleteDomain(context.Background(), domainId) + if apierr != nil { + RespondError(w, apierr, nil) + return + } + ah.Respond(w, nil) +} diff --git a/ee/query-service/app/api/featureFlags.go b/ee/query-service/app/api/featureFlags.go new file mode 100644 index 0000000..22ee798 --- /dev/null +++ b/ee/query-service/app/api/featureFlags.go @@ -0,0 +1,24 @@ +package api + +import ( + "net/http" + + basemodel "go.signoz.io/signoz/pkg/query-service/model" +) + +func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) { + featureSet, err := ah.FF().GetFeatureFlags() + if err != nil { + ah.HandleError(w, err, http.StatusInternalServerError) + return + } + if ah.opts.PreferSpanMetrics { + for idx := range featureSet { + feature := &featureSet[idx] + if feature.Name == basemodel.UseSpanMetrics { + featureSet[idx].Active = true + } + } + } + ah.Respond(w, featureSet) +} diff --git a/ee/query-service/app/api/license.go b/ee/query-service/app/api/license.go new file mode 100644 index 0000000..5c39702 --- /dev/null +++ b/ee/query-service/app/api/license.go @@ -0,0 +1,272 @@ +package api + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + + "go.signoz.io/signoz/ee/query-service/constants" + "go.signoz.io/signoz/ee/query-service/model" + "go.uber.org/zap" +) + +type DayWiseBreakdown struct { + Type string `json:"type"` + Breakdown []DayWiseData `json:"breakdown"` +} + +type DayWiseData struct { + Timestamp int64 `json:"timestamp"` + Count float64 `json:"count"` + Size float64 `json:"size"` + UnitPrice float64 `json:"unitPrice"` + Quantity float64 `json:"quantity"` + Total float64 `json:"total"` +} + +type tierBreakdown struct { + UnitPrice float64 `json:"unitPrice"` + Quantity float64 `json:"quantity"` + TierStart int64 `json:"tierStart"` + TierEnd int64 `json:"tierEnd"` + TierCost float64 `json:"tierCost"` +} + +type usageResponse struct { + Type string `json:"type"` + Unit string `json:"unit"` + Tiers []tierBreakdown `json:"tiers"` + DayWiseBreakdown DayWiseBreakdown `json:"dayWiseBreakdown"` +} + +type details struct { + Total float64 `json:"total"` + Breakdown []usageResponse `json:"breakdown"` + BaseFee float64 `json:"baseFee"` + BillTotal float64 `json:"billTotal"` +} + +type billingDetails struct { + Status string `json:"status"` + Data struct { + BillingPeriodStart int64 `json:"billingPeriodStart"` + BillingPeriodEnd int64 `json:"billingPeriodEnd"` + Details details `json:"details"` + Discount float64 `json:"discount"` + SubscriptionStatus string `json:"subscriptionStatus"` + } `json:"data"` +} + +func (ah *APIHandler) listLicenses(w http.ResponseWriter, r *http.Request) { + licenses, apiError := ah.LM().GetLicenses(context.Background()) + if apiError != nil { + RespondError(w, apiError, nil) + } + ah.Respond(w, licenses) +} + +func (ah *APIHandler) applyLicense(w http.ResponseWriter, r *http.Request) { + var l model.License + + if err := json.NewDecoder(r.Body).Decode(&l); err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + + if l.Key == "" { + RespondError(w, model.BadRequest(fmt.Errorf("license key is required")), nil) + return + } + license, apiError := ah.LM().Activate(r.Context(), l.Key) + if apiError != nil { + RespondError(w, apiError, nil) + return + } + + ah.Respond(w, license) +} + +func (ah *APIHandler) checkout(w http.ResponseWriter, r *http.Request) { + + type checkoutResponse struct { + Status string `json:"status"` + Data struct { + RedirectURL string `json:"redirectURL"` + } `json:"data"` + } + + hClient := &http.Client{} + req, err := http.NewRequest("POST", constants.LicenseSignozIo+"/checkout", r.Body) + if err != nil { + RespondError(w, model.InternalError(err), nil) + return + } + req.Header.Add("X-SigNoz-SecretKey", constants.LicenseAPIKey) + licenseResp, err := hClient.Do(req) + if err != nil { + RespondError(w, model.InternalError(err), nil) + return + } + + // decode response body + var resp checkoutResponse + if err := json.NewDecoder(licenseResp.Body).Decode(&resp); err != nil { + RespondError(w, model.InternalError(err), nil) + return + } + + ah.Respond(w, resp.Data) +} + +func (ah *APIHandler) getBilling(w http.ResponseWriter, r *http.Request) { + licenseKey := r.URL.Query().Get("licenseKey") + + if licenseKey == "" { + RespondError(w, model.BadRequest(fmt.Errorf("license key is required")), nil) + return + } + + billingURL := fmt.Sprintf("%s/usage?licenseKey=%s", constants.LicenseSignozIo, licenseKey) + + hClient := &http.Client{} + req, err := http.NewRequest("GET", billingURL, nil) + if err != nil { + RespondError(w, model.InternalError(err), nil) + return + } + req.Header.Add("X-SigNoz-SecretKey", constants.LicenseAPIKey) + billingResp, err := hClient.Do(req) + if err != nil { + RespondError(w, model.InternalError(err), nil) + return + } + + // decode response body + var billingResponse billingDetails + if err := json.NewDecoder(billingResp.Body).Decode(&billingResponse); err != nil { + RespondError(w, model.InternalError(err), nil) + return + } + + // TODO(srikanthccv):Fetch the current day usage and add it to the response + ah.Respond(w, billingResponse.Data) +} + +func (ah *APIHandler) listLicensesV2(w http.ResponseWriter, r *http.Request) { + + licenses, apiError := ah.LM().GetLicenses(context.Background()) + if apiError != nil { + RespondError(w, apiError, nil) + } + + resp := model.Licenses{ + TrialStart: -1, + TrialEnd: -1, + OnTrial: false, + WorkSpaceBlock: false, + TrialConvertedToSubscription: false, + GracePeriodEnd: -1, + Licenses: licenses, + } + + var currentActiveLicenseKey string + + for _, license := range licenses { + if license.IsCurrent { + currentActiveLicenseKey = license.Key + } + } + + // For the case when no license is applied i.e community edition + // There will be no trial details or license details + if currentActiveLicenseKey == "" { + ah.Respond(w, resp) + return + } + + // Fetch trial details + hClient := &http.Client{} + url := fmt.Sprintf("%s/trial?licenseKey=%s", constants.LicenseSignozIo, currentActiveLicenseKey) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + zap.S().Error("Error while creating request for trial details", err) + // If there is an error in fetching trial details, we will still return the license details + // to avoid blocking the UI + ah.Respond(w, resp) + return + } + req.Header.Add("X-SigNoz-SecretKey", constants.LicenseAPIKey) + trialResp, err := hClient.Do(req) + if err != nil { + zap.S().Error("Error while fetching trial details", err) + // If there is an error in fetching trial details, we will still return the license details + // to avoid incorrectly blocking the UI + ah.Respond(w, resp) + return + } + defer trialResp.Body.Close() + + trialRespBody, err := io.ReadAll(trialResp.Body) + + if err != nil || trialResp.StatusCode != http.StatusOK { + zap.S().Error("Error while fetching trial details", err) + // If there is an error in fetching trial details, we will still return the license details + // to avoid incorrectly blocking the UI + ah.Respond(w, resp) + return + } + + // decode response body + var trialRespData model.SubscriptionServerResp + + if err := json.Unmarshal(trialRespBody, &trialRespData); err != nil { + zap.S().Error("Error while decoding trial details", err) + // If there is an error in fetching trial details, we will still return the license details + // to avoid incorrectly blocking the UI + ah.Respond(w, resp) + return + } + + resp.TrialStart = trialRespData.Data.TrialStart + resp.TrialEnd = trialRespData.Data.TrialEnd + resp.OnTrial = trialRespData.Data.OnTrial + resp.WorkSpaceBlock = trialRespData.Data.WorkSpaceBlock + resp.TrialConvertedToSubscription = trialRespData.Data.TrialConvertedToSubscription + resp.GracePeriodEnd = trialRespData.Data.GracePeriodEnd + + ah.Respond(w, resp) +} + +func (ah *APIHandler) portalSession(w http.ResponseWriter, r *http.Request) { + + type checkoutResponse struct { + Status string `json:"status"` + Data struct { + RedirectURL string `json:"redirectURL"` + } `json:"data"` + } + + hClient := &http.Client{} + req, err := http.NewRequest("POST", constants.LicenseSignozIo+"/portal", r.Body) + if err != nil { + RespondError(w, model.InternalError(err), nil) + return + } + req.Header.Add("X-SigNoz-SecretKey", constants.LicenseAPIKey) + licenseResp, err := hClient.Do(req) + if err != nil { + RespondError(w, model.InternalError(err), nil) + return + } + + // decode response body + var resp checkoutResponse + if err := json.NewDecoder(licenseResp.Body).Decode(&resp); err != nil { + RespondError(w, model.InternalError(err), nil) + return + } + + ah.Respond(w, resp.Data) +} diff --git a/ee/query-service/app/api/metrics.go b/ee/query-service/app/api/metrics.go new file mode 100644 index 0000000..81af703 --- /dev/null +++ b/ee/query-service/app/api/metrics.go @@ -0,0 +1,236 @@ +package api + +import ( + "bytes" + "fmt" + "net/http" + "sync" + "text/template" + "time" + + "go.signoz.io/signoz/pkg/query-service/app/metrics" + "go.signoz.io/signoz/pkg/query-service/app/parser" + "go.signoz.io/signoz/pkg/query-service/constants" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + querytemplate "go.signoz.io/signoz/pkg/query-service/utils/queryTemplate" + "go.uber.org/zap" +) + +func (ah *APIHandler) queryRangeMetricsV2(w http.ResponseWriter, r *http.Request) { + if !ah.CheckFeature(basemodel.CustomMetricsFunction) { + zap.S().Info("CustomMetricsFunction feature is not enabled in this plan") + ah.APIHandler.QueryRangeMetricsV2(w, r) + return + } + metricsQueryRangeParams, apiErrorObj := parser.ParseMetricQueryRangeParams(r) + + if apiErrorObj != nil { + zap.S().Errorf(apiErrorObj.Err.Error()) + RespondError(w, apiErrorObj, nil) + return + } + + // prometheus instant query needs same timestamp + if metricsQueryRangeParams.CompositeMetricQuery.PanelType == basemodel.QUERY_VALUE && + metricsQueryRangeParams.CompositeMetricQuery.QueryType == basemodel.PROM { + metricsQueryRangeParams.Start = metricsQueryRangeParams.End + } + + // round up the end to nearest multiple + if metricsQueryRangeParams.CompositeMetricQuery.QueryType == basemodel.QUERY_BUILDER { + end := (metricsQueryRangeParams.End) / 1000 + step := metricsQueryRangeParams.Step + metricsQueryRangeParams.End = (end / step * step) * 1000 + } + + type channelResult struct { + Series []*basemodel.Series + TableName string + Err error + Name string + Query string + } + + execClickHouseQueries := func(queries map[string]string) ([]*basemodel.Series, []string, error, map[string]string) { + var seriesList []*basemodel.Series + var tableName []string + ch := make(chan channelResult, len(queries)) + var wg sync.WaitGroup + + for name, query := range queries { + wg.Add(1) + go func(name, query string) { + defer wg.Done() + seriesList, tableName, err := ah.opts.DataConnector.GetMetricResultEE(r.Context(), query) + for _, series := range seriesList { + series.QueryName = name + } + + if err != nil { + ch <- channelResult{Err: fmt.Errorf("error in query-%s: %v", name, err), Name: name, Query: query} + return + } + ch <- channelResult{Series: seriesList, TableName: tableName} + }(name, query) + } + + wg.Wait() + close(ch) + + var errs []error + errQuriesByName := make(map[string]string) + // read values from the channel + for r := range ch { + if r.Err != nil { + errs = append(errs, r.Err) + errQuriesByName[r.Name] = r.Query + continue + } + seriesList = append(seriesList, r.Series...) + tableName = append(tableName, r.TableName) + } + if len(errs) != 0 { + return nil, nil, fmt.Errorf("encountered multiple errors: %s", metrics.FormatErrs(errs, "\n")), errQuriesByName + } + return seriesList, tableName, nil, nil + } + + execPromQueries := func(metricsQueryRangeParams *basemodel.QueryRangeParamsV2) ([]*basemodel.Series, error, map[string]string) { + var seriesList []*basemodel.Series + ch := make(chan channelResult, len(metricsQueryRangeParams.CompositeMetricQuery.PromQueries)) + var wg sync.WaitGroup + + for name, query := range metricsQueryRangeParams.CompositeMetricQuery.PromQueries { + if query.Disabled { + continue + } + wg.Add(1) + go func(name string, query *basemodel.PromQuery) { + var seriesList []*basemodel.Series + defer wg.Done() + tmpl := template.New("promql-query") + tmpl, tmplErr := tmpl.Parse(query.Query) + if tmplErr != nil { + ch <- channelResult{Err: fmt.Errorf("error in parsing query-%s: %v", name, tmplErr), Name: name, Query: query.Query} + return + } + var queryBuf bytes.Buffer + tmplErr = tmpl.Execute(&queryBuf, metricsQueryRangeParams.Variables) + if tmplErr != nil { + ch <- channelResult{Err: fmt.Errorf("error in parsing query-%s: %v", name, tmplErr), Name: name, Query: query.Query} + return + } + query.Query = queryBuf.String() + queryModel := basemodel.QueryRangeParams{ + Start: time.UnixMilli(metricsQueryRangeParams.Start), + End: time.UnixMilli(metricsQueryRangeParams.End), + Step: time.Duration(metricsQueryRangeParams.Step * int64(time.Second)), + Query: query.Query, + } + promResult, _, err := ah.opts.DataConnector.GetQueryRangeResult(r.Context(), &queryModel) + if err != nil { + ch <- channelResult{Err: fmt.Errorf("error in query-%s: %v", name, err), Name: name, Query: query.Query} + return + } + matrix, _ := promResult.Matrix() + for _, v := range matrix { + var s basemodel.Series + s.QueryName = name + s.Labels = v.Metric.Copy().Map() + for _, p := range v.Floats { + s.Points = append(s.Points, basemodel.MetricPoint{Timestamp: p.T, Value: p.F}) + } + seriesList = append(seriesList, &s) + } + ch <- channelResult{Series: seriesList} + }(name, query) + } + + wg.Wait() + close(ch) + + var errs []error + errQuriesByName := make(map[string]string) + // read values from the channel + for r := range ch { + if r.Err != nil { + errs = append(errs, r.Err) + errQuriesByName[r.Name] = r.Query + continue + } + seriesList = append(seriesList, r.Series...) + } + if len(errs) != 0 { + return nil, fmt.Errorf("encountered multiple errors: %s", metrics.FormatErrs(errs, "\n")), errQuriesByName + } + return seriesList, nil, nil + } + + var seriesList []*basemodel.Series + var tableName []string + var err error + var errQuriesByName map[string]string + switch metricsQueryRangeParams.CompositeMetricQuery.QueryType { + case basemodel.QUERY_BUILDER: + runQueries := metrics.PrepareBuilderMetricQueries(metricsQueryRangeParams, constants.SIGNOZ_TIMESERIES_TABLENAME) + if runQueries.Err != nil { + RespondError(w, &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: runQueries.Err}, nil) + return + } + seriesList, tableName, err, errQuriesByName = execClickHouseQueries(runQueries.Queries) + + case basemodel.CLICKHOUSE: + queries := make(map[string]string) + + for name, chQuery := range metricsQueryRangeParams.CompositeMetricQuery.ClickHouseQueries { + if chQuery.Disabled { + continue + } + tmpl := template.New("clickhouse-query") + tmpl, err := tmpl.Parse(chQuery.Query) + if err != nil { + RespondError(w, &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: err}, nil) + return + } + var query bytes.Buffer + + // replace go template variables + querytemplate.AssignReservedVars(metricsQueryRangeParams) + + err = tmpl.Execute(&query, metricsQueryRangeParams.Variables) + if err != nil { + RespondError(w, &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: err}, nil) + return + } + queries[name] = query.String() + } + seriesList, tableName, err, errQuriesByName = execClickHouseQueries(queries) + case basemodel.PROM: + seriesList, err, errQuriesByName = execPromQueries(metricsQueryRangeParams) + default: + err = fmt.Errorf("invalid query type") + RespondError(w, &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: err}, errQuriesByName) + return + } + + if err != nil { + apiErrObj := &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: err} + RespondError(w, apiErrObj, errQuriesByName) + return + } + if metricsQueryRangeParams.CompositeMetricQuery.PanelType == basemodel.QUERY_VALUE && + len(seriesList) > 1 && + (metricsQueryRangeParams.CompositeMetricQuery.QueryType == basemodel.QUERY_BUILDER || + metricsQueryRangeParams.CompositeMetricQuery.QueryType == basemodel.CLICKHOUSE) { + RespondError(w, &basemodel.ApiError{Typ: basemodel.ErrorBadData, Err: fmt.Errorf("invalid: query resulted in more than one series for value type")}, nil) + return + } + + type ResponseFormat struct { + ResultType string `json:"resultType"` + Result []*basemodel.Series `json:"result"` + TableName []string `json:"tableName"` + } + resp := ResponseFormat{ResultType: "matrix", Result: seriesList, TableName: tableName} + ah.Respond(w, resp) +} diff --git a/ee/query-service/app/api/pat.go b/ee/query-service/app/api/pat.go new file mode 100644 index 0000000..ea43f47 --- /dev/null +++ b/ee/query-service/app/api/pat.go @@ -0,0 +1,165 @@ +package api + +import ( + "context" + "crypto/rand" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/gorilla/mux" + "go.signoz.io/signoz/ee/query-service/model" + "go.signoz.io/signoz/pkg/query-service/auth" + baseconstants "go.signoz.io/signoz/pkg/query-service/constants" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.uber.org/zap" +) + +func generatePATToken() string { + // Generate a 32-byte random token. + token := make([]byte, 32) + rand.Read(token) + // Encode the token in base64. + encodedToken := base64.StdEncoding.EncodeToString(token) + return encodedToken +} + +func (ah *APIHandler) createPAT(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + + req := model.CreatePATRequestBody{} + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + user, err := auth.GetUserFromRequest(r) + if err != nil { + RespondError(w, &model.ApiError{ + Typ: model.ErrorUnauthorized, + Err: err, + }, nil) + return + } + pat := model.PAT{ + Name: req.Name, + Role: req.Role, + ExpiresAt: req.ExpiresInDays, + } + err = validatePATRequest(pat) + if err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + + // All the PATs are associated with the user creating the PAT. + pat.UserID = user.Id + pat.CreatedAt = time.Now().Unix() + pat.UpdatedAt = time.Now().Unix() + pat.LastUsed = 0 + pat.Token = generatePATToken() + + if pat.ExpiresAt != 0 { + // convert expiresAt to unix timestamp from days + pat.ExpiresAt = time.Now().Unix() + (pat.ExpiresAt * 24 * 60 * 60) + } + + zap.S().Debugf("Got Create PAT request: %+v", pat) + var apierr basemodel.BaseApiError + if pat, apierr = ah.AppDao().CreatePAT(ctx, pat); apierr != nil { + RespondError(w, apierr, nil) + return + } + + ah.Respond(w, &pat) +} + +func validatePATRequest(req model.PAT) error { + if req.Role == "" || (req.Role != baseconstants.ViewerGroup && req.Role != baseconstants.EditorGroup && req.Role != baseconstants.AdminGroup) { + return fmt.Errorf("valid role is required") + } + if req.ExpiresAt < 0 { + return fmt.Errorf("valid expiresAt is required") + } + if req.Name == "" { + return fmt.Errorf("valid name is required") + } + return nil +} + +func (ah *APIHandler) updatePAT(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + + req := model.PAT{} + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + + user, err := auth.GetUserFromRequest(r) + if err != nil { + RespondError(w, &model.ApiError{ + Typ: model.ErrorUnauthorized, + Err: err, + }, nil) + return + } + + err = validatePATRequest(req) + if err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + + req.UpdatedByUserID = user.Id + id := mux.Vars(r)["id"] + req.UpdatedAt = time.Now().Unix() + zap.S().Debugf("Got Update PAT request: %+v", req) + var apierr basemodel.BaseApiError + if apierr = ah.AppDao().UpdatePAT(ctx, req, id); apierr != nil { + RespondError(w, apierr, nil) + return + } + + ah.Respond(w, map[string]string{"data": "pat updated successfully"}) +} + +func (ah *APIHandler) getPATs(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + user, err := auth.GetUserFromRequest(r) + if err != nil { + RespondError(w, &model.ApiError{ + Typ: model.ErrorUnauthorized, + Err: err, + }, nil) + return + } + zap.S().Infof("Get PATs for user: %+v", user.Id) + pats, apierr := ah.AppDao().ListPATs(ctx) + if apierr != nil { + RespondError(w, apierr, nil) + return + } + ah.Respond(w, pats) +} + +func (ah *APIHandler) revokePAT(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + id := mux.Vars(r)["id"] + user, err := auth.GetUserFromRequest(r) + if err != nil { + RespondError(w, &model.ApiError{ + Typ: model.ErrorUnauthorized, + Err: err, + }, nil) + return + } + + zap.S().Debugf("Revoke PAT with id: %+v", id) + if apierr := ah.AppDao().RevokePAT(ctx, id, user.Id); apierr != nil { + RespondError(w, apierr, nil) + return + } + ah.Respond(w, map[string]string{"data": "pat revoked successfully"}) +} diff --git a/ee/query-service/app/api/response.go b/ee/query-service/app/api/response.go new file mode 100644 index 0000000..fef5f89 --- /dev/null +++ b/ee/query-service/app/api/response.go @@ -0,0 +1,12 @@ +package api + +import ( + "net/http" + + baseapp "go.signoz.io/signoz/pkg/query-service/app" + basemodel "go.signoz.io/signoz/pkg/query-service/model" +) + +func RespondError(w http.ResponseWriter, apiErr basemodel.BaseApiError, data interface{}) { + baseapp.RespondError(w, apiErr, data) +} diff --git a/ee/query-service/app/api/traces.go b/ee/query-service/app/api/traces.go new file mode 100644 index 0000000..22d66f7 --- /dev/null +++ b/ee/query-service/app/api/traces.go @@ -0,0 +1,39 @@ +package api + +import ( + "net/http" + "strconv" + + "go.signoz.io/signoz/ee/query-service/app/db" + "go.signoz.io/signoz/ee/query-service/constants" + "go.signoz.io/signoz/ee/query-service/model" + baseapp "go.signoz.io/signoz/pkg/query-service/app" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.uber.org/zap" +) + +func (ah *APIHandler) searchTraces(w http.ResponseWriter, r *http.Request) { + + if !ah.CheckFeature(basemodel.SmartTraceDetail) { + zap.S().Info("SmartTraceDetail feature is not enabled in this plan") + ah.APIHandler.SearchTraces(w, r) + return + } + traceId, spanId, levelUpInt, levelDownInt, err := baseapp.ParseSearchTracesParams(r) + if err != nil { + RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, "Error reading params") + return + } + spanLimit, err := strconv.Atoi(constants.SpanLimitStr) + if err != nil { + zap.S().Error("Error during strconv.Atoi() on SPAN_LIMIT env variable: ", err) + return + } + result, err := ah.opts.DataConnector.SearchTraces(r.Context(), traceId, spanId, levelUpInt, levelDownInt, spanLimit, db.SmartTraceAlgorithm) + if ah.HandleError(w, err, http.StatusBadRequest) { + return + } + + ah.WriteJSON(w, r, result) + +} diff --git a/ee/query-service/app/db/metrics.go b/ee/query-service/app/db/metrics.go new file mode 100644 index 0000000..3bafc6a --- /dev/null +++ b/ee/query-service/app/db/metrics.go @@ -0,0 +1,401 @@ +package db + +import ( + "context" + "crypto/md5" + "encoding/json" + "fmt" + "reflect" + "regexp" + "sort" + "strings" + "time" + + "go.signoz.io/signoz/ee/query-service/model" + baseconst "go.signoz.io/signoz/pkg/query-service/constants" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.signoz.io/signoz/pkg/query-service/utils" + "go.uber.org/zap" +) + +// GetMetricResultEE runs the query and returns list of time series +func (r *ClickhouseReader) GetMetricResultEE(ctx context.Context, query string) ([]*basemodel.Series, string, error) { + + defer utils.Elapsed("GetMetricResult")() + zap.S().Infof("Executing metric result query: %s", query) + + var hash string + // If getSubTreeSpans function is used in the clickhouse query + if strings.Index(query, "getSubTreeSpans(") != -1 { + var err error + query, hash, err = r.getSubTreeSpansCustomFunction(ctx, query, hash) + if err == fmt.Errorf("No spans found for the given query") { + return nil, "", nil + } + if err != nil { + return nil, "", err + } + } + + rows, err := r.conn.Query(ctx, query) + zap.S().Debug(query) + if err != nil { + zap.S().Debug("Error in processing query: ", err) + return nil, "", fmt.Errorf("error in processing query") + } + + var ( + columnTypes = rows.ColumnTypes() + columnNames = rows.Columns() + vars = make([]interface{}, len(columnTypes)) + ) + for i := range columnTypes { + vars[i] = reflect.New(columnTypes[i].ScanType()).Interface() + } + // when group by is applied, each combination of cartesian product + // of attributes is separate series. each item in metricPointsMap + // represent a unique series. + metricPointsMap := make(map[string][]basemodel.MetricPoint) + // attribute key-value pairs for each group selection + attributesMap := make(map[string]map[string]string) + + defer rows.Close() + for rows.Next() { + if err := rows.Scan(vars...); err != nil { + return nil, "", err + } + var groupBy []string + var metricPoint basemodel.MetricPoint + groupAttributes := make(map[string]string) + // Assuming that the end result row contains a timestamp, value and option labels + // Label key and value are both strings. + for idx, v := range vars { + colName := columnNames[idx] + switch v := v.(type) { + case *string: + // special case for returning all labels + if colName == "fullLabels" { + var metric map[string]string + err := json.Unmarshal([]byte(*v), &metric) + if err != nil { + return nil, "", err + } + for key, val := range metric { + groupBy = append(groupBy, val) + groupAttributes[key] = val + } + } else { + groupBy = append(groupBy, *v) + groupAttributes[colName] = *v + } + case *time.Time: + metricPoint.Timestamp = v.UnixMilli() + case *float64: + metricPoint.Value = *v + case **float64: + // ch seems to return this type when column is derived from + // SELECT count(*)/ SELECT count(*) + floatVal := *v + if floatVal != nil { + metricPoint.Value = *floatVal + } + case *float32: + float32Val := float32(*v) + metricPoint.Value = float64(float32Val) + case *uint8, *uint64, *uint16, *uint32: + if _, ok := baseconst.ReservedColumnTargetAliases[colName]; ok { + metricPoint.Value = float64(reflect.ValueOf(v).Elem().Uint()) + } else { + groupBy = append(groupBy, fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Uint())) + groupAttributes[colName] = fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Uint()) + } + case *int8, *int16, *int32, *int64: + if _, ok := baseconst.ReservedColumnTargetAliases[colName]; ok { + metricPoint.Value = float64(reflect.ValueOf(v).Elem().Int()) + } else { + groupBy = append(groupBy, fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Int())) + groupAttributes[colName] = fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Int()) + } + default: + zap.S().Errorf("invalid var found in metric builder query result", v, colName) + } + } + sort.Strings(groupBy) + key := strings.Join(groupBy, "") + attributesMap[key] = groupAttributes + metricPointsMap[key] = append(metricPointsMap[key], metricPoint) + } + + var seriesList []*basemodel.Series + for key := range metricPointsMap { + points := metricPointsMap[key] + // first point in each series could be invalid since the + // aggregations are applied with point from prev series + if len(points) != 0 && len(points) > 1 { + points = points[1:] + } + attributes := attributesMap[key] + series := basemodel.Series{Labels: attributes, Points: points} + seriesList = append(seriesList, &series) + } + // err = r.conn.Exec(ctx, "DROP TEMPORARY TABLE IF EXISTS getSubTreeSpans"+hash) + // if err != nil { + // zap.S().Error("Error in dropping temporary table: ", err) + // return nil, err + // } + if hash == "" { + return seriesList, hash, nil + } else { + return seriesList, "getSubTreeSpans" + hash, nil + } +} + +func (r *ClickhouseReader) getSubTreeSpansCustomFunction(ctx context.Context, query string, hash string) (string, string, error) { + + zap.S().Debugf("Executing getSubTreeSpans function") + + // str1 := `select fromUnixTimestamp64Milli(intDiv( toUnixTimestamp64Milli ( timestamp ), 100) * 100) AS interval, toFloat64(count()) as count from (select timestamp, spanId, parentSpanId, durationNano from getSubTreeSpans(select * from signoz_traces.signoz_index_v2 where serviceName='frontend' and name='/driver.DriverService/FindNearest' and traceID='00000000000000004b0a863cb5ed7681') where name='FindDriverIDs' group by interval order by interval asc;` + + // process the query to fetch subTree query + var subtreeInput string + query, subtreeInput, hash = processQuery(query, hash) + + err := r.conn.Exec(ctx, "DROP TABLE IF EXISTS getSubTreeSpans"+hash) + if err != nil { + zap.S().Error("Error in dropping temporary table: ", err) + return query, hash, err + } + + // Create temporary table to store the getSubTreeSpans() results + zap.S().Debugf("Creating temporary table getSubTreeSpans%s", hash) + err = r.conn.Exec(ctx, "CREATE TABLE IF NOT EXISTS "+"getSubTreeSpans"+hash+" (timestamp DateTime64(9) CODEC(DoubleDelta, LZ4), traceID FixedString(32) CODEC(ZSTD(1)), spanID String CODEC(ZSTD(1)), parentSpanID String CODEC(ZSTD(1)), rootSpanID String CODEC(ZSTD(1)), serviceName LowCardinality(String) CODEC(ZSTD(1)), name LowCardinality(String) CODEC(ZSTD(1)), rootName LowCardinality(String) CODEC(ZSTD(1)), durationNano UInt64 CODEC(T64, ZSTD(1)), kind Int8 CODEC(T64, ZSTD(1)), tagMap Map(LowCardinality(String), String) CODEC(ZSTD(1)), events Array(String) CODEC(ZSTD(2))) ENGINE = MergeTree() ORDER BY (timestamp)") + if err != nil { + zap.S().Error("Error in creating temporary table: ", err) + return query, hash, err + } + + var getSpansSubQueryDBResponses []model.GetSpansSubQueryDBResponse + getSpansSubQuery := subtreeInput + // Execute the subTree query + zap.S().Debugf("Executing subTree query: %s", getSpansSubQuery) + err = r.conn.Select(ctx, &getSpansSubQueryDBResponses, getSpansSubQuery) + + // zap.S().Info(getSpansSubQuery) + + if err != nil { + zap.S().Debug("Error in processing sql query: ", err) + return query, hash, fmt.Errorf("Error in processing sql query") + } + + var searchScanResponses []basemodel.SearchSpanDBResponseItem + + // TODO : @ankit: I think the algorithm does not need to assume that subtrees are from the same TraceID. We can take this as an improvement later. + // Fetch all the spans from of same TraceID so that we can build subtree + modelQuery := fmt.Sprintf("SELECT timestamp, traceID, model FROM %s.%s WHERE traceID=$1", r.TraceDB, r.SpansTable) + + if len(getSpansSubQueryDBResponses) == 0 { + return query, hash, fmt.Errorf("No spans found for the given query") + } + zap.S().Debugf("Executing query to fetch all the spans from the same TraceID: %s", modelQuery) + err = r.conn.Select(ctx, &searchScanResponses, modelQuery, getSpansSubQueryDBResponses[0].TraceID) + + if err != nil { + zap.S().Debug("Error in processing sql query: ", err) + return query, hash, fmt.Errorf("Error in processing sql query") + } + + // Process model to fetch the spans + zap.S().Debugf("Processing model to fetch the spans") + searchSpanResponses := []basemodel.SearchSpanResponseItem{} + for _, item := range searchScanResponses { + var jsonItem basemodel.SearchSpanResponseItem + json.Unmarshal([]byte(item.Model), &jsonItem) + jsonItem.TimeUnixNano = uint64(item.Timestamp.UnixNano()) + if jsonItem.Events == nil { + jsonItem.Events = []string{} + } + searchSpanResponses = append(searchSpanResponses, jsonItem) + } + // Build the subtree and store all the subtree spans in temporary table getSubTreeSpans+hash + // Use map to store pointer to the spans to avoid duplicates and save memory + zap.S().Debugf("Building the subtree to store all the subtree spans in temporary table getSubTreeSpans%s", hash) + + treeSearchResponse, err := getSubTreeAlgorithm(searchSpanResponses, getSpansSubQueryDBResponses) + if err != nil { + zap.S().Error("Error in getSubTreeAlgorithm function: ", err) + return query, hash, err + } + zap.S().Debugf("Preparing batch to store subtree spans in temporary table getSubTreeSpans%s", hash) + statement, err := r.conn.PrepareBatch(context.Background(), fmt.Sprintf("INSERT INTO getSubTreeSpans"+hash)) + if err != nil { + zap.S().Error("Error in preparing batch statement: ", err) + return query, hash, err + } + for _, span := range treeSearchResponse { + var parentID string + if len(span.References) > 0 && span.References[0].RefType == "CHILD_OF" { + parentID = span.References[0].SpanId + } + err = statement.Append( + time.Unix(0, int64(span.TimeUnixNano)), + span.TraceID, + span.SpanID, + parentID, + span.RootSpanID, + span.ServiceName, + span.Name, + span.RootName, + uint64(span.DurationNano), + int8(span.Kind), + span.TagMap, + span.Events, + ) + if err != nil { + zap.S().Debug("Error in processing sql query: ", err) + return query, hash, err + } + } + zap.S().Debugf("Inserting the subtree spans in temporary table getSubTreeSpans%s", hash) + err = statement.Send() + if err != nil { + zap.S().Error("Error in sending statement: ", err) + return query, hash, err + } + return query, hash, nil +} + +func processQuery(query string, hash string) (string, string, string) { + re3 := regexp.MustCompile(`getSubTreeSpans`) + + submatchall3 := re3.FindAllStringIndex(query, -1) + getSubtreeSpansMatchIndex := submatchall3[0][1] + + query2countParenthesis := query[getSubtreeSpansMatchIndex:] + + sqlCompleteIndex := 0 + countParenthesisImbalance := 0 + for i, char := range query2countParenthesis { + + if string(char) == "(" { + countParenthesisImbalance += 1 + } + if string(char) == ")" { + countParenthesisImbalance -= 1 + } + if countParenthesisImbalance == 0 { + sqlCompleteIndex = i + break + } + } + subtreeInput := query2countParenthesis[1:sqlCompleteIndex] + + // hash the subtreeInput + hmd5 := md5.Sum([]byte(subtreeInput)) + hash = fmt.Sprintf("%x", hmd5) + + // Reformat the query to use the getSubTreeSpans function + query = query[:getSubtreeSpansMatchIndex] + hash + " " + query2countParenthesis[sqlCompleteIndex+1:] + return query, subtreeInput, hash +} + +// getSubTreeAlgorithm is an algorithm to build the subtrees of the spans and return the list of spans +func getSubTreeAlgorithm(payload []basemodel.SearchSpanResponseItem, getSpansSubQueryDBResponses []model.GetSpansSubQueryDBResponse) (map[string]*basemodel.SearchSpanResponseItem, error) { + + var spans []*model.SpanForTraceDetails + for _, spanItem := range payload { + var parentID string + if len(spanItem.References) > 0 && spanItem.References[0].RefType == "CHILD_OF" { + parentID = spanItem.References[0].SpanId + } + span := &model.SpanForTraceDetails{ + TimeUnixNano: spanItem.TimeUnixNano, + SpanID: spanItem.SpanID, + TraceID: spanItem.TraceID, + ServiceName: spanItem.ServiceName, + Name: spanItem.Name, + Kind: spanItem.Kind, + DurationNano: spanItem.DurationNano, + TagMap: spanItem.TagMap, + ParentID: parentID, + Events: spanItem.Events, + HasError: spanItem.HasError, + } + spans = append(spans, span) + } + + zap.S().Debug("Building Tree") + roots, err := buildSpanTrees(&spans) + if err != nil { + return nil, err + } + searchSpansResult := make(map[string]*basemodel.SearchSpanResponseItem) + // Every span which was fetched from getSubTree Input SQL query is considered root + // For each root, get the subtree spans + for _, getSpansSubQueryDBResponse := range getSpansSubQueryDBResponses { + targetSpan := &model.SpanForTraceDetails{} + // zap.S().Debug("Building tree for span id: " + getSpansSubQueryDBResponse.SpanID + " " + strconv.Itoa(i+1) + " of " + strconv.Itoa(len(getSpansSubQueryDBResponses))) + // Search target span object in the tree + for _, root := range roots { + targetSpan, err = breadthFirstSearch(root, getSpansSubQueryDBResponse.SpanID) + if targetSpan != nil { + break + } + if err != nil { + zap.S().Error("Error during BreadthFirstSearch(): ", err) + return nil, err + } + } + if targetSpan == nil { + return nil, nil + } + // Build subtree for the target span + // Mark the target span as root by setting parent ID as empty string + targetSpan.ParentID = "" + preParents := []*model.SpanForTraceDetails{targetSpan} + children := []*model.SpanForTraceDetails{} + + // Get the subtree child spans + for i := 0; len(preParents) != 0; i++ { + parents := []*model.SpanForTraceDetails{} + for _, parent := range preParents { + children = append(children, parent.Children...) + parents = append(parents, parent.Children...) + } + preParents = parents + } + + resultSpans := children + // Add the target span to the result spans + resultSpans = append(resultSpans, targetSpan) + + for _, item := range resultSpans { + references := []basemodel.OtelSpanRef{ + { + TraceId: item.TraceID, + SpanId: item.ParentID, + RefType: "CHILD_OF", + }, + } + + if item.Events == nil { + item.Events = []string{} + } + searchSpansResult[item.SpanID] = &basemodel.SearchSpanResponseItem{ + TimeUnixNano: item.TimeUnixNano, + SpanID: item.SpanID, + TraceID: item.TraceID, + ServiceName: item.ServiceName, + Name: item.Name, + Kind: item.Kind, + References: references, + DurationNano: item.DurationNano, + TagMap: item.TagMap, + Events: item.Events, + HasError: item.HasError, + RootSpanID: getSpansSubQueryDBResponse.SpanID, + RootName: targetSpan.Name, + } + } + } + return searchSpansResult, nil +} diff --git a/ee/query-service/app/db/reader.go b/ee/query-service/app/db/reader.go new file mode 100644 index 0000000..b832605 --- /dev/null +++ b/ee/query-service/app/db/reader.go @@ -0,0 +1,39 @@ +package db + +import ( + "time" + + "github.com/ClickHouse/clickhouse-go/v2" + + "github.com/jmoiron/sqlx" + + basechr "go.signoz.io/signoz/pkg/query-service/app/clickhouseReader" + "go.signoz.io/signoz/pkg/query-service/interfaces" +) + +type ClickhouseReader struct { + conn clickhouse.Conn + appdb *sqlx.DB + *basechr.ClickHouseReader +} + +func NewDataConnector( + localDB *sqlx.DB, + promConfigPath string, + lm interfaces.FeatureLookup, + maxIdleConns int, + maxOpenConns int, + dialTimeout time.Duration, + cluster string, +) *ClickhouseReader { + ch := basechr.NewReader(localDB, promConfigPath, lm, maxIdleConns, maxOpenConns, dialTimeout, cluster) + return &ClickhouseReader{ + conn: ch.GetConn(), + appdb: localDB, + ClickHouseReader: ch, + } +} + +func (r *ClickhouseReader) Start(readerReady chan bool) { + r.ClickHouseReader.Start(readerReady) +} diff --git a/ee/query-service/app/db/trace.go b/ee/query-service/app/db/trace.go new file mode 100644 index 0000000..529a9a9 --- /dev/null +++ b/ee/query-service/app/db/trace.go @@ -0,0 +1,222 @@ +package db + +import ( + "errors" + "strconv" + + "go.signoz.io/signoz/ee/query-service/model" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.uber.org/zap" +) + +// SmartTraceAlgorithm is an algorithm to find the target span and build a tree of spans around it with the given levelUp and levelDown parameters and the given spanLimit +func SmartTraceAlgorithm(payload []basemodel.SearchSpanResponseItem, targetSpanId string, levelUp int, levelDown int, spanLimit int) ([]basemodel.SearchSpansResult, error) { + var spans []*model.SpanForTraceDetails + + // Build a slice of spans from the payload + for _, spanItem := range payload { + var parentID string + if len(spanItem.References) > 0 && spanItem.References[0].RefType == "CHILD_OF" { + parentID = spanItem.References[0].SpanId + } + span := &model.SpanForTraceDetails{ + TimeUnixNano: spanItem.TimeUnixNano, + SpanID: spanItem.SpanID, + TraceID: spanItem.TraceID, + ServiceName: spanItem.ServiceName, + Name: spanItem.Name, + Kind: spanItem.Kind, + DurationNano: spanItem.DurationNano, + TagMap: spanItem.TagMap, + ParentID: parentID, + Events: spanItem.Events, + HasError: spanItem.HasError, + } + spans = append(spans, span) + } + + // Build span trees from the spans + roots, err := buildSpanTrees(&spans) + if err != nil { + return nil, err + } + targetSpan := &model.SpanForTraceDetails{} + + // Find the target span in the span trees + for _, root := range roots { + targetSpan, err = breadthFirstSearch(root, targetSpanId) + if targetSpan != nil { + break + } + if err != nil { + zap.S().Error("Error during BreadthFirstSearch(): ", err) + return nil, err + } + } + + // If the target span is not found, return span not found error + if targetSpan == nil { + return nil, errors.New("Span not found") + } + + // Build the final result + parents := []*model.SpanForTraceDetails{} + + // Get the parent spans of the target span up to the given levelUp parameter and spanLimit + preParent := targetSpan + for i := 0; i < levelUp+1; i++ { + if i == levelUp { + preParent.ParentID = "" + } + if spanLimit-len(preParent.Children) <= 0 { + parents = append(parents, preParent) + parents = append(parents, preParent.Children[:spanLimit]...) + spanLimit -= (len(preParent.Children[:spanLimit]) + 1) + preParent.ParentID = "" + break + } + parents = append(parents, preParent) + parents = append(parents, preParent.Children...) + spanLimit -= (len(preParent.Children) + 1) + preParent = preParent.ParentSpan + if preParent == nil { + break + } + } + + // Get the child spans of the target span until the given levelDown and spanLimit + preParents := []*model.SpanForTraceDetails{targetSpan} + children := []*model.SpanForTraceDetails{} + + for i := 0; i < levelDown && len(preParents) != 0 && spanLimit > 0; i++ { + parents := []*model.SpanForTraceDetails{} + for _, parent := range preParents { + if spanLimit-len(parent.Children) <= 0 { + children = append(children, parent.Children[:spanLimit]...) + spanLimit -= len(parent.Children[:spanLimit]) + break + } + children = append(children, parent.Children...) + parents = append(parents, parent.Children...) + } + preParents = parents + } + + // Store the final list of spans in the resultSpanSet map to avoid duplicates + resultSpansSet := make(map[*model.SpanForTraceDetails]struct{}) + resultSpansSet[targetSpan] = struct{}{} + for _, parent := range parents { + resultSpansSet[parent] = struct{}{} + } + for _, child := range children { + resultSpansSet[child] = struct{}{} + } + + searchSpansResult := []basemodel.SearchSpansResult{{ + Columns: []string{"__time", "SpanId", "TraceId", "ServiceName", "Name", "Kind", "DurationNano", "TagsKeys", "TagsValues", "References", "Events", "HasError"}, + Events: make([][]interface{}, len(resultSpansSet)), + }, + } + + // Convert the resultSpansSet map to searchSpansResult + i := 0 // index for spans + for item := range resultSpansSet { + references := []basemodel.OtelSpanRef{ + { + TraceId: item.TraceID, + SpanId: item.ParentID, + RefType: "CHILD_OF", + }, + } + + referencesStringArray := []string{} + for _, item := range references { + referencesStringArray = append(referencesStringArray, item.ToString()) + } + keys := make([]string, 0, len(item.TagMap)) + values := make([]string, 0, len(item.TagMap)) + + for k, v := range item.TagMap { + keys = append(keys, k) + values = append(values, v) + } + if item.Events == nil { + item.Events = []string{} + } + searchSpansResult[0].Events[i] = []interface{}{ + item.TimeUnixNano, + item.SpanID, + item.TraceID, + item.ServiceName, + item.Name, + strconv.Itoa(int(item.Kind)), + strconv.FormatInt(item.DurationNano, 10), + keys, + values, + referencesStringArray, + item.Events, + item.HasError, + } + i++ // increment index + } + return searchSpansResult, nil +} + +// buildSpanTrees builds trees of spans from a list of spans. +func buildSpanTrees(spansPtr *[]*model.SpanForTraceDetails) ([]*model.SpanForTraceDetails, error) { + + // Build a map of spanID to span for fast lookup + var roots []*model.SpanForTraceDetails + spans := *spansPtr + mapOfSpans := make(map[string]*model.SpanForTraceDetails, len(spans)) + + for _, span := range spans { + if span.ParentID == "" { + roots = append(roots, span) + } + mapOfSpans[span.SpanID] = span + } + + // Build the span tree by adding children to the parent spans + for _, span := range spans { + if span.ParentID == "" { + continue + } + parent := mapOfSpans[span.ParentID] + + // If the parent span is not found, add current span to list of roots + if parent == nil { + // zap.S().Debug("Parent Span not found parent_id: ", span.ParentID) + roots = append(roots, span) + span.ParentID = "" + continue + } + + span.ParentSpan = parent + parent.Children = append(parent.Children, span) + } + + return roots, nil +} + +// breadthFirstSearch performs a breadth-first search on the span tree to find the target span. +func breadthFirstSearch(spansPtr *model.SpanForTraceDetails, targetId string) (*model.SpanForTraceDetails, error) { + queue := []*model.SpanForTraceDetails{spansPtr} + visited := make(map[string]bool) + + for len(queue) > 0 { + current := queue[0] + visited[current.SpanID] = true + queue = queue[1:] + if current.SpanID == targetId { + return current, nil + } + + for _, child := range current.Children { + if ok, _ := visited[child.SpanID]; !ok { + queue = append(queue, child) + } + } + } + return nil, nil +} diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go new file mode 100644 index 0000000..469632a --- /dev/null +++ b/ee/query-service/app/server.go @@ -0,0 +1,687 @@ +package app + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + _ "net/http/pprof" // http profiler + "os" + "time" + + "github.com/gorilla/handlers" + "github.com/gorilla/mux" + "github.com/jmoiron/sqlx" + + "github.com/rs/cors" + "github.com/soheilhy/cmux" + "go.signoz.io/signoz/ee/query-service/app/api" + "go.signoz.io/signoz/ee/query-service/app/db" + "go.signoz.io/signoz/ee/query-service/auth" + "go.signoz.io/signoz/ee/query-service/constants" + "go.signoz.io/signoz/ee/query-service/dao" + "go.signoz.io/signoz/ee/query-service/interfaces" + baseauth "go.signoz.io/signoz/pkg/query-service/auth" + baseInterface "go.signoz.io/signoz/pkg/query-service/interfaces" + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" + + licensepkg "go.signoz.io/signoz/ee/query-service/license" + "go.signoz.io/signoz/ee/query-service/usage" + + "go.signoz.io/signoz/pkg/query-service/agentConf" + baseapp "go.signoz.io/signoz/pkg/query-service/app" + "go.signoz.io/signoz/pkg/query-service/app/dashboards" + baseexplorer "go.signoz.io/signoz/pkg/query-service/app/explorer" + "go.signoz.io/signoz/pkg/query-service/app/integrations" + "go.signoz.io/signoz/pkg/query-service/app/logparsingpipeline" + "go.signoz.io/signoz/pkg/query-service/app/opamp" + opAmpModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model" + "go.signoz.io/signoz/pkg/query-service/cache" + baseconst "go.signoz.io/signoz/pkg/query-service/constants" + "go.signoz.io/signoz/pkg/query-service/healthcheck" + basealm "go.signoz.io/signoz/pkg/query-service/integrations/alertManager" + baseint "go.signoz.io/signoz/pkg/query-service/interfaces" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + pqle "go.signoz.io/signoz/pkg/query-service/pqlEngine" + rules "go.signoz.io/signoz/pkg/query-service/rules" + "go.signoz.io/signoz/pkg/query-service/telemetry" + "go.signoz.io/signoz/pkg/query-service/utils" + "go.uber.org/zap" +) + +const AppDbEngine = "sqlite" + +type ServerOptions struct { + PromConfigPath string + SkipTopLvlOpsPath string + HTTPHostPort string + PrivateHostPort string + // alert specific params + DisableRules bool + RuleRepoURL string + PreferDelta bool + PreferSpanMetrics bool + MaxIdleConns int + MaxOpenConns int + DialTimeout time.Duration + CacheConfigPath string + FluxInterval string + Cluster string +} + +// Server runs HTTP api service +type Server struct { + serverOptions *ServerOptions + conn net.Listener + ruleManager *rules.Manager + separatePorts bool + + // public http router + httpConn net.Listener + httpServer *http.Server + + // private http + privateConn net.Listener + privateHTTP *http.Server + + // feature flags + featureLookup baseint.FeatureLookup + + // Usage manager + usageManager *usage.Manager + + opampServer *opamp.Server + + unavailableChannel chan healthcheck.Status +} + +// HealthCheckStatus returns health check status channel a client can subscribe to +func (s Server) HealthCheckStatus() chan healthcheck.Status { + return s.unavailableChannel +} + +// NewServer creates and initializes Server +func NewServer(serverOptions *ServerOptions) (*Server, error) { + + modelDao, err := dao.InitDao("sqlite", baseconst.RELATIONAL_DATASOURCE_PATH) + if err != nil { + return nil, err + } + + baseexplorer.InitWithDSN(baseconst.RELATIONAL_DATASOURCE_PATH) + + localDB, err := dashboards.InitDB(baseconst.RELATIONAL_DATASOURCE_PATH) + + if err != nil { + return nil, err + } + + localDB.SetMaxOpenConns(10) + + // initiate license manager + lm, err := licensepkg.StartManager("sqlite", localDB) + if err != nil { + return nil, err + } + + // set license manager as feature flag provider in dao + modelDao.SetFlagProvider(lm) + readerReady := make(chan bool) + + var reader interfaces.DataConnector + storage := os.Getenv("STORAGE") + if storage == "clickhouse" { + zap.S().Info("Using ClickHouse as datastore ...") + qb := db.NewDataConnector( + localDB, + serverOptions.PromConfigPath, + lm, + serverOptions.MaxIdleConns, + serverOptions.MaxOpenConns, + serverOptions.DialTimeout, + serverOptions.Cluster, + ) + go qb.Start(readerReady) + reader = qb + } else { + return nil, fmt.Errorf("storage type: %s is not supported in query service", storage) + } + skipConfig := &basemodel.SkipConfig{} + if serverOptions.SkipTopLvlOpsPath != "" { + // read skip config + skipConfig, err = basemodel.ReadSkipConfig(serverOptions.SkipTopLvlOpsPath) + if err != nil { + return nil, err + } + } + + <-readerReady + rm, err := makeRulesManager(serverOptions.PromConfigPath, + baseconst.GetAlertManagerApiPrefix(), + serverOptions.RuleRepoURL, + localDB, + reader, + serverOptions.DisableRules, + lm) + + if err != nil { + return nil, err + } + + // initiate opamp + _, err = opAmpModel.InitDB(localDB) + if err != nil { + return nil, err + } + + integrationsController, err := integrations.NewController(localDB) + if err != nil { + return nil, fmt.Errorf( + "couldn't create integrations controller: %w", err, + ) + } + + // ingestion pipelines manager + logParsingPipelineController, err := logparsingpipeline.NewLogParsingPipelinesController( + localDB, "sqlite", integrationsController.GetPipelinesForInstalledIntegrations, + ) + if err != nil { + return nil, err + } + + // initiate agent config handler + agentConfMgr, err := agentConf.Initiate(&agentConf.ManagerOptions{ + DB: localDB, + DBEngine: AppDbEngine, + AgentFeatures: []agentConf.AgentFeature{logParsingPipelineController}, + }) + if err != nil { + return nil, err + } + + // start the usagemanager + usageManager, err := usage.New("sqlite", modelDao, lm.GetRepo(), reader.GetConn()) + if err != nil { + return nil, err + } + err = usageManager.Start() + if err != nil { + return nil, err + } + + telemetry.GetInstance().SetReader(reader) + telemetry.GetInstance().SetSaasOperator(constants.SaasSegmentKey) + + var c cache.Cache + if serverOptions.CacheConfigPath != "" { + cacheOpts, err := cache.LoadFromYAMLCacheConfigFile(serverOptions.CacheConfigPath) + if err != nil { + return nil, err + } + c = cache.NewCache(cacheOpts) + } + + fluxInterval, err := time.ParseDuration(serverOptions.FluxInterval) + + if err != nil { + return nil, err + } + + apiOpts := api.APIHandlerOptions{ + DataConnector: reader, + SkipConfig: skipConfig, + PreferDelta: serverOptions.PreferDelta, + PreferSpanMetrics: serverOptions.PreferSpanMetrics, + MaxIdleConns: serverOptions.MaxIdleConns, + MaxOpenConns: serverOptions.MaxOpenConns, + DialTimeout: serverOptions.DialTimeout, + AppDao: modelDao, + RulesManager: rm, + UsageManager: usageManager, + FeatureFlags: lm, + LicenseManager: lm, + IntegrationsController: integrationsController, + LogsParsingPipelineController: logParsingPipelineController, + Cache: c, + FluxInterval: fluxInterval, + } + + apiHandler, err := api.NewAPIHandler(apiOpts) + if err != nil { + return nil, err + } + + s := &Server{ + // logger: logger, + // tracer: tracer, + ruleManager: rm, + serverOptions: serverOptions, + unavailableChannel: make(chan healthcheck.Status), + usageManager: usageManager, + } + + httpServer, err := s.createPublicServer(apiHandler) + + if err != nil { + return nil, err + } + + s.httpServer = httpServer + + privateServer, err := s.createPrivateServer(apiHandler) + if err != nil { + return nil, err + } + + s.privateHTTP = privateServer + + s.opampServer = opamp.InitializeServer( + &opAmpModel.AllAgents, agentConfMgr, + ) + + return s, nil +} + +func (s *Server) createPrivateServer(apiHandler *api.APIHandler) (*http.Server, error) { + + r := mux.NewRouter() + + r.Use(baseapp.LogCommentEnricher) + r.Use(setTimeoutMiddleware) + r.Use(s.analyticsMiddleware) + r.Use(loggingMiddlewarePrivate) + + apiHandler.RegisterPrivateRoutes(r) + + c := cors.New(cors.Options{ + //todo(amol): find out a way to add exact domain or + // ip here for alert manager + AllowedOrigins: []string{"*"}, + AllowedMethods: []string{"GET", "DELETE", "POST", "PUT", "PATCH"}, + AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "SIGNOZ-API-KEY"}, + }) + + handler := c.Handler(r) + handler = handlers.CompressHandler(handler) + + return &http.Server{ + Handler: handler, + }, nil +} + +func (s *Server) createPublicServer(apiHandler *api.APIHandler) (*http.Server, error) { + + r := mux.NewRouter() + + // add auth middleware + getUserFromRequest := func(r *http.Request) (*basemodel.UserPayload, error) { + return auth.GetUserFromRequest(r, apiHandler) + } + am := baseapp.NewAuthMiddleware(getUserFromRequest) + + r.Use(baseapp.LogCommentEnricher) + r.Use(setTimeoutMiddleware) + r.Use(s.analyticsMiddleware) + r.Use(loggingMiddleware) + + apiHandler.RegisterRoutes(r, am) + apiHandler.RegisterMetricsRoutes(r, am) + apiHandler.RegisterLogsRoutes(r, am) + apiHandler.RegisterIntegrationRoutes(r, am) + apiHandler.RegisterQueryRangeV3Routes(r, am) + apiHandler.RegisterQueryRangeV4Routes(r, am) + + c := cors.New(cors.Options{ + AllowedOrigins: []string{"*"}, + AllowedMethods: []string{"GET", "DELETE", "POST", "PUT", "PATCH", "OPTIONS"}, + AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "cache-control"}, + }) + + handler := c.Handler(r) + + handler = handlers.CompressHandler(handler) + + return &http.Server{ + Handler: handler, + }, nil +} + +// loggingMiddleware is used for logging public api calls +func loggingMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + route := mux.CurrentRoute(r) + path, _ := route.GetPathTemplate() + startTime := time.Now() + next.ServeHTTP(w, r) + zap.L().Info(path+"\ttimeTaken:"+time.Now().Sub(startTime).String(), zap.Duration("timeTaken", time.Now().Sub(startTime)), zap.String("path", path)) + }) +} + +// loggingMiddlewarePrivate is used for logging private api calls +// from internal services like alert manager +func loggingMiddlewarePrivate(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + route := mux.CurrentRoute(r) + path, _ := route.GetPathTemplate() + startTime := time.Now() + next.ServeHTTP(w, r) + zap.L().Info(path+"\tprivatePort: true \ttimeTaken"+time.Now().Sub(startTime).String(), zap.Duration("timeTaken", time.Now().Sub(startTime)), zap.String("path", path), zap.Bool("tprivatePort", true)) + }) +} + +type loggingResponseWriter struct { + http.ResponseWriter + statusCode int +} + +func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter { + // WriteHeader(int) is not called if our response implicitly returns 200 OK, so + // we default to that status code. + return &loggingResponseWriter{w, http.StatusOK} +} + +func (lrw *loggingResponseWriter) WriteHeader(code int) { + lrw.statusCode = code + lrw.ResponseWriter.WriteHeader(code) +} + +// Flush implements the http.Flush interface. +func (lrw *loggingResponseWriter) Flush() { + lrw.ResponseWriter.(http.Flusher).Flush() +} + +func extractQueryRangeV3Data(path string, r *http.Request) (map[string]interface{}, bool) { + pathToExtractBodyFrom := "/api/v3/query_range" + + data := map[string]interface{}{} + var postData *v3.QueryRangeParamsV3 + + if path == pathToExtractBodyFrom && (r.Method == "POST") { + if r.Body != nil { + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + return nil, false + } + r.Body.Close() // must close + r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + json.Unmarshal(bodyBytes, &postData) + + } else { + return nil, false + } + + } else { + return nil, false + } + + signozMetricsUsed := false + signozLogsUsed := false + dataSources := []string{} + if postData != nil { + + if postData.CompositeQuery != nil { + data["queryType"] = postData.CompositeQuery.QueryType + data["panelType"] = postData.CompositeQuery.PanelType + + signozLogsUsed, signozMetricsUsed, _ = telemetry.GetInstance().CheckSigNozSignals(postData) + } + } + + if signozMetricsUsed || signozLogsUsed { + if signozMetricsUsed { + dataSources = append(dataSources, "metrics") + telemetry.GetInstance().AddActiveMetricsUser() + } + if signozLogsUsed { + dataSources = append(dataSources, "logs") + telemetry.GetInstance().AddActiveLogsUser() + } + data["dataSources"] = dataSources + userEmail, err := baseauth.GetEmailFromJwt(r.Context()) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_QUERY_RANGE_V3, data, userEmail, true) + } + } + return data, true +} + +func getActiveLogs(path string, r *http.Request) { + // if path == "/api/v1/dashboards/{uuid}" { + // telemetry.GetInstance().AddActiveMetricsUser() + // } + if path == "/api/v1/logs" { + hasFilters := len(r.URL.Query().Get("q")) + if hasFilters > 0 { + telemetry.GetInstance().AddActiveLogsUser() + } + + } + +} + +func (s *Server) analyticsMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := baseauth.AttachJwtToContext(r.Context(), r) + r = r.WithContext(ctx) + route := mux.CurrentRoute(r) + path, _ := route.GetPathTemplate() + + queryRangeV3data, metadataExists := extractQueryRangeV3Data(path, r) + getActiveLogs(path, r) + + lrw := NewLoggingResponseWriter(w) + next.ServeHTTP(lrw, r) + + data := map[string]interface{}{"path": path, "statusCode": lrw.statusCode} + if metadataExists { + for key, value := range queryRangeV3data { + data[key] = value + } + } + + if _, ok := telemetry.EnabledPaths()[path]; ok { + userEmail, err := baseauth.GetEmailFromJwt(r.Context()) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_PATH, data, userEmail) + } + } + + }) +} + +func setTimeoutMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + var cancel context.CancelFunc + // check if route is not excluded + url := r.URL.Path + if _, ok := baseconst.TimeoutExcludedRoutes[url]; !ok { + ctx, cancel = context.WithTimeout(r.Context(), baseconst.ContextTimeout) + defer cancel() + } + + r = r.WithContext(ctx) + next.ServeHTTP(w, r) + }) +} + +// initListeners initialises listeners of the server +func (s *Server) initListeners() error { + // listen on public port + var err error + publicHostPort := s.serverOptions.HTTPHostPort + if publicHostPort == "" { + return fmt.Errorf("baseconst.HTTPHostPort is required") + } + + s.httpConn, err = net.Listen("tcp", publicHostPort) + if err != nil { + return err + } + + zap.S().Info(fmt.Sprintf("Query server started listening on %s...", s.serverOptions.HTTPHostPort)) + + // listen on private port to support internal services + privateHostPort := s.serverOptions.PrivateHostPort + + if privateHostPort == "" { + return fmt.Errorf("baseconst.PrivateHostPort is required") + } + + s.privateConn, err = net.Listen("tcp", privateHostPort) + if err != nil { + return err + } + zap.S().Info(fmt.Sprintf("Query server started listening on private port %s...", s.serverOptions.PrivateHostPort)) + + return nil +} + +// Start listening on http and private http port concurrently +func (s *Server) Start() error { + + // initiate rule manager first + if !s.serverOptions.DisableRules { + s.ruleManager.Start() + } else { + zap.S().Info("msg: Rules disabled as rules.disable is set to TRUE") + } + + err := s.initListeners() + if err != nil { + return err + } + + var httpPort int + if port, err := utils.GetPort(s.httpConn.Addr()); err == nil { + httpPort = port + } + + go func() { + zap.S().Info("Starting HTTP server", zap.Int("port", httpPort), zap.String("addr", s.serverOptions.HTTPHostPort)) + + switch err := s.httpServer.Serve(s.httpConn); err { + case nil, http.ErrServerClosed, cmux.ErrListenerClosed: + // normal exit, nothing to do + default: + zap.S().Error("Could not start HTTP server", zap.Error(err)) + } + s.unavailableChannel <- healthcheck.Unavailable + }() + + go func() { + zap.S().Info("Starting pprof server", zap.String("addr", baseconst.DebugHttpPort)) + + err = http.ListenAndServe(baseconst.DebugHttpPort, nil) + if err != nil { + zap.S().Error("Could not start pprof server", zap.Error(err)) + } + }() + + var privatePort int + if port, err := utils.GetPort(s.privateConn.Addr()); err == nil { + privatePort = port + } + + go func() { + zap.S().Info("Starting Private HTTP server", zap.Int("port", privatePort), zap.String("addr", s.serverOptions.PrivateHostPort)) + + switch err := s.privateHTTP.Serve(s.privateConn); err { + case nil, http.ErrServerClosed, cmux.ErrListenerClosed: + // normal exit, nothing to do + zap.S().Info("private http server closed") + default: + zap.S().Error("Could not start private HTTP server", zap.Error(err)) + } + + s.unavailableChannel <- healthcheck.Unavailable + + }() + + go func() { + zap.S().Info("Starting OpAmp Websocket server", zap.String("addr", baseconst.OpAmpWsEndpoint)) + err := s.opampServer.Start(baseconst.OpAmpWsEndpoint) + if err != nil { + zap.S().Info("opamp ws server failed to start", err) + s.unavailableChannel <- healthcheck.Unavailable + } + }() + + return nil +} + +func (s *Server) Stop() error { + if s.httpServer != nil { + if err := s.httpServer.Shutdown(context.Background()); err != nil { + return err + } + } + + if s.privateHTTP != nil { + if err := s.privateHTTP.Shutdown(context.Background()); err != nil { + return err + } + } + + s.opampServer.Stop() + + if s.ruleManager != nil { + s.ruleManager.Stop() + } + + // stop usage manager + s.usageManager.Stop() + + return nil +} + +func makeRulesManager( + promConfigPath, + alertManagerURL string, + ruleRepoURL string, + db *sqlx.DB, + ch baseint.Reader, + disableRules bool, + fm baseInterface.FeatureLookup) (*rules.Manager, error) { + + // create engine + pqle, err := pqle.FromConfigPath(promConfigPath) + if err != nil { + return nil, fmt.Errorf("failed to create pql engine : %v", err) + } + + // notifier opts + notifierOpts := basealm.NotifierOptions{ + QueueCapacity: 10000, + Timeout: 1 * time.Second, + AlertManagerURLs: []string{alertManagerURL}, + } + + // create manager opts + managerOpts := &rules.ManagerOptions{ + NotifierOpts: notifierOpts, + Queriers: &rules.Queriers{ + PqlEngine: pqle, + Ch: ch.GetConn(), + }, + RepoURL: ruleRepoURL, + DBConn: db, + Context: context.Background(), + Logger: nil, + DisableRules: disableRules, + FeatureFlags: fm, + } + + // create Manager + manager, err := rules.NewManager(managerOpts) + if err != nil { + return nil, fmt.Errorf("rule manager error: %v", err) + } + + zap.S().Info("rules manager is ready") + + return manager, nil +} diff --git a/ee/query-service/auth/auth.go b/ee/query-service/auth/auth.go new file mode 100644 index 0000000..8c06384 --- /dev/null +++ b/ee/query-service/auth/auth.go @@ -0,0 +1,56 @@ +package auth + +import ( + "context" + "fmt" + "net/http" + "time" + + "go.signoz.io/signoz/ee/query-service/app/api" + baseauth "go.signoz.io/signoz/pkg/query-service/auth" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.signoz.io/signoz/pkg/query-service/telemetry" + + "go.uber.org/zap" +) + +func GetUserFromRequest(r *http.Request, apiHandler *api.APIHandler) (*basemodel.UserPayload, error) { + patToken := r.Header.Get("SIGNOZ-API-KEY") + if len(patToken) > 0 { + zap.S().Debugf("Received a non-zero length PAT token") + ctx := context.Background() + dao := apiHandler.AppDao() + + pat, err := dao.GetPAT(ctx, patToken) + if err == nil && pat != nil { + zap.S().Debugf("Found valid PAT: %+v", pat) + if pat.ExpiresAt < time.Now().Unix() && pat.ExpiresAt != 0 { + zap.S().Debugf("PAT has expired: %+v", pat) + return nil, fmt.Errorf("PAT has expired") + } + group, apiErr := dao.GetGroupByName(ctx, pat.Role) + if apiErr != nil { + zap.S().Debugf("Error while getting group for PAT: %+v", apiErr) + return nil, apiErr + } + user, err := dao.GetUser(ctx, pat.UserID) + if err != nil { + zap.S().Debugf("Error while getting user for PAT: %+v", err) + return nil, err + } + telemetry.GetInstance().SetPatTokenUser() + dao.UpdatePATLastUsed(ctx, patToken, time.Now().Unix()) + user.User.GroupId = group.Id + user.User.Id = pat.Id + return &basemodel.UserPayload{ + User: user.User, + Role: pat.Role, + }, nil + } + if err != nil { + zap.S().Debugf("Error while getting user for PAT: %+v", err) + return nil, err + } + } + return baseauth.GetUserFromRequest(r) +} diff --git a/ee/query-service/constants/constants.go b/ee/query-service/constants/constants.go new file mode 100644 index 0000000..aeeea03 --- /dev/null +++ b/ee/query-service/constants/constants.go @@ -0,0 +1,31 @@ +package constants + +import ( + "os" +) + +const ( + DefaultSiteURL = "https://localhost:3301" +) + +var LicenseSignozIo = "https://license.signoz.io/api/v1" +var LicenseAPIKey = GetOrDefaultEnv("SIGNOZ_LICENSE_API_KEY", "") +var SaasSegmentKey = GetOrDefaultEnv("SIGNOZ_SAAS_SEGMENT_KEY", "") +var SpanLimitStr = GetOrDefaultEnv("SPAN_LIMIT", "5000") + +func GetOrDefaultEnv(key string, fallback string) string { + v := os.Getenv(key) + if len(v) == 0 { + return fallback + } + return v +} + +// constant functions that override env vars + +// GetDefaultSiteURL returns default site url, primarily +// used to send saml request and allowing backend to +// handle http redirect +func GetDefaultSiteURL() string { + return GetOrDefaultEnv("SIGNOZ_SITE_URL", DefaultSiteURL) +} diff --git a/ee/query-service/dao/factory.go b/ee/query-service/dao/factory.go new file mode 100644 index 0000000..f623e17 --- /dev/null +++ b/ee/query-service/dao/factory.go @@ -0,0 +1,18 @@ +package dao + +import ( + "fmt" + + "go.signoz.io/signoz/ee/query-service/dao/sqlite" +) + +func InitDao(engine, path string) (ModelDao, error) { + + switch engine { + case "sqlite": + return sqlite.InitDB(path) + default: + return nil, fmt.Errorf("qsdb type: %s is not supported in query service", engine) + } + +} diff --git a/ee/query-service/dao/interface.go b/ee/query-service/dao/interface.go new file mode 100644 index 0000000..695ff86 --- /dev/null +++ b/ee/query-service/dao/interface.go @@ -0,0 +1,44 @@ +package dao + +import ( + "context" + "net/url" + + "github.com/google/uuid" + "github.com/jmoiron/sqlx" + "go.signoz.io/signoz/ee/query-service/model" + basedao "go.signoz.io/signoz/pkg/query-service/dao" + baseint "go.signoz.io/signoz/pkg/query-service/interfaces" + basemodel "go.signoz.io/signoz/pkg/query-service/model" +) + +type ModelDao interface { + basedao.ModelDao + + // SetFlagProvider sets the feature lookup provider + SetFlagProvider(flags baseint.FeatureLookup) + + DB() *sqlx.DB + + // auth methods + CanUsePassword(ctx context.Context, email string) (bool, basemodel.BaseApiError) + PrepareSsoRedirect(ctx context.Context, redirectUri, email string) (redirectURL string, apierr basemodel.BaseApiError) + GetDomainFromSsoResponse(ctx context.Context, relayState *url.URL) (*model.OrgDomain, error) + + // org domain (auth domains) CRUD ops + ListDomains(ctx context.Context, orgId string) ([]model.OrgDomain, basemodel.BaseApiError) + GetDomain(ctx context.Context, id uuid.UUID) (*model.OrgDomain, basemodel.BaseApiError) + CreateDomain(ctx context.Context, d *model.OrgDomain) basemodel.BaseApiError + UpdateDomain(ctx context.Context, domain *model.OrgDomain) basemodel.BaseApiError + DeleteDomain(ctx context.Context, id uuid.UUID) basemodel.BaseApiError + GetDomainByEmail(ctx context.Context, email string) (*model.OrgDomain, basemodel.BaseApiError) + + CreatePAT(ctx context.Context, p model.PAT) (model.PAT, basemodel.BaseApiError) + UpdatePAT(ctx context.Context, p model.PAT, id string) (basemodel.BaseApiError) + GetPAT(ctx context.Context, pat string) (*model.PAT, basemodel.BaseApiError) + UpdatePATLastUsed(ctx context.Context, pat string, lastUsed int64) basemodel.BaseApiError + GetPATByID(ctx context.Context, id string) (*model.PAT, basemodel.BaseApiError) + GetUserByPAT(ctx context.Context, token string) (*basemodel.UserPayload, basemodel.BaseApiError) + ListPATs(ctx context.Context) ([]model.PAT, basemodel.BaseApiError) + RevokePAT(ctx context.Context, id string, userID string) basemodel.BaseApiError +} diff --git a/ee/query-service/dao/sqlite/auth.go b/ee/query-service/dao/sqlite/auth.go new file mode 100644 index 0000000..664323e --- /dev/null +++ b/ee/query-service/dao/sqlite/auth.go @@ -0,0 +1,197 @@ +package sqlite + +import ( + "context" + "fmt" + "net/url" + "strings" + "time" + + "github.com/google/uuid" + "go.signoz.io/signoz/ee/query-service/constants" + "go.signoz.io/signoz/ee/query-service/model" + baseauth "go.signoz.io/signoz/pkg/query-service/auth" + baseconst "go.signoz.io/signoz/pkg/query-service/constants" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.signoz.io/signoz/pkg/query-service/utils" + "go.uber.org/zap" +) + +func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (*basemodel.User, basemodel.BaseApiError) { + // get auth domain from email domain + domain, apierr := m.GetDomainByEmail(ctx, email) + + if apierr != nil { + zap.S().Errorf("failed to get domain from email", apierr) + return nil, model.InternalErrorStr("failed to get domain from email") + } + + hash, err := baseauth.PasswordHash(utils.GeneratePassowrd()) + if err != nil { + zap.S().Errorf("failed to generate password hash when registering a user via SSO redirect", zap.Error(err)) + return nil, model.InternalErrorStr("failed to generate password hash") + } + + group, apiErr := m.GetGroupByName(ctx, baseconst.ViewerGroup) + if apiErr != nil { + zap.S().Debugf("GetGroupByName failed, err: %v\n", apiErr.Err) + return nil, apiErr + } + + user := &basemodel.User{ + Id: uuid.NewString(), + Name: "", + Email: email, + Password: hash, + CreatedAt: time.Now().Unix(), + ProfilePictureURL: "", // Currently unused + GroupId: group.Id, + OrgId: domain.OrgId, + } + + user, apiErr = m.CreateUser(ctx, user, false) + if apiErr != nil { + zap.S().Debugf("CreateUser failed, err: %v\n", apiErr.Err) + return nil, apiErr + } + + return user, nil + +} + +// PrepareSsoRedirect prepares redirect page link after SSO response +// is successfully parsed (i.e. valid email is available) +func (m *modelDao) PrepareSsoRedirect(ctx context.Context, redirectUri, email string) (redirectURL string, apierr basemodel.BaseApiError) { + + userPayload, apierr := m.GetUserByEmail(ctx, email) + if !apierr.IsNil() { + zap.S().Errorf(" failed to get user with email received from auth provider", apierr.Error()) + return "", model.BadRequestStr("invalid user email received from the auth provider") + } + + user := &basemodel.User{} + + if userPayload == nil { + newUser, apiErr := m.createUserForSAMLRequest(ctx, email) + user = newUser + if apiErr != nil { + zap.S().Errorf("failed to create user with email received from auth provider: %v", apierr.Error()) + return "", apiErr + } + } else { + user = &userPayload.User + } + + tokenStore, err := baseauth.GenerateJWTForUser(user) + if err != nil { + zap.S().Errorf("failed to generate token for SSO login user", err) + return "", model.InternalErrorStr("failed to generate token for the user") + } + + return fmt.Sprintf("%s?jwt=%s&usr=%s&refreshjwt=%s", + redirectUri, + tokenStore.AccessJwt, + user.Id, + tokenStore.RefreshJwt), nil +} + +func (m *modelDao) CanUsePassword(ctx context.Context, email string) (bool, basemodel.BaseApiError) { + domain, apierr := m.GetDomainByEmail(ctx, email) + if apierr != nil { + return false, apierr + } + + if domain != nil && domain.SsoEnabled { + // sso is enabled, check if the user has admin role + userPayload, baseapierr := m.GetUserByEmail(ctx, email) + + if baseapierr != nil || userPayload == nil { + return false, baseapierr + } + + if userPayload.Role != baseconst.AdminGroup { + return false, model.BadRequest(fmt.Errorf("auth method not supported")) + } + + } + + return true, nil +} + +// PrecheckLogin is called when the login or signup page is loaded +// to check sso login is to be prompted +func (m *modelDao) PrecheckLogin(ctx context.Context, email, sourceUrl string) (*basemodel.PrecheckResponse, basemodel.BaseApiError) { + + // assume user is valid unless proven otherwise + resp := &basemodel.PrecheckResponse{IsUser: true, CanSelfRegister: false} + + // check if email is a valid user + userPayload, baseApiErr := m.GetUserByEmail(ctx, email) + if baseApiErr != nil { + return resp, baseApiErr + } + + if userPayload == nil { + resp.IsUser = false + } + + ssoAvailable := true + err := m.checkFeature(model.SSO) + if err != nil { + switch err.(type) { + case basemodel.ErrFeatureUnavailable: + // do nothing, just skip sso + ssoAvailable = false + default: + zap.S().Errorf("feature check failed", zap.String("featureKey", model.SSO), zap.Error(err)) + return resp, model.BadRequest(err) + } + } + + if ssoAvailable { + + resp.IsUser = true + + // find domain from email + orgDomain, apierr := m.GetDomainByEmail(ctx, email) + if apierr != nil { + var emailDomain string + emailComponents := strings.Split(email, "@") + if len(emailComponents) > 0 { + emailDomain = emailComponents[1] + } + zap.S().Errorf("failed to get org domain from email", zap.String("emailDomain", emailDomain), apierr.ToError()) + return resp, apierr + } + + if orgDomain != nil && orgDomain.SsoEnabled { + // saml is enabled for this domain, lets prepare sso url + + if sourceUrl == "" { + sourceUrl = constants.GetDefaultSiteURL() + } + + // parse source url that generated the login request + var err error + escapedUrl, _ := url.QueryUnescape(sourceUrl) + siteUrl, err := url.Parse(escapedUrl) + if err != nil { + zap.S().Errorf("failed to parse referer", err) + return resp, model.InternalError(fmt.Errorf("failed to generate login request")) + } + + // build Idp URL that will authenticat the user + // the front-end will redirect user to this url + resp.SsoUrl, err = orgDomain.BuildSsoUrl(siteUrl) + + if err != nil { + zap.S().Errorf("failed to prepare saml request for domain", zap.String("domain", orgDomain.Name), err) + return resp, model.InternalError(err) + } + + // set SSO to true, as the url is generated correctly + resp.SSO = true + } + } + return resp, nil +} diff --git a/ee/query-service/dao/sqlite/domain.go b/ee/query-service/dao/sqlite/domain.go new file mode 100644 index 0000000..b515af4 --- /dev/null +++ b/ee/query-service/dao/sqlite/domain.go @@ -0,0 +1,253 @@ +package sqlite + +import ( + "context" + "database/sql" + "encoding/json" + "fmt" + "net/url" + "strings" + "time" + + "github.com/google/uuid" + "go.signoz.io/signoz/ee/query-service/model" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.uber.org/zap" +) + +// StoredDomain represents stored database record for org domain + +type StoredDomain struct { + Id uuid.UUID `db:"id"` + Name string `db:"name"` + OrgId string `db:"org_id"` + Data string `db:"data"` + CreatedAt int64 `db:"created_at"` + UpdatedAt int64 `db:"updated_at"` +} + +// GetDomainFromSsoResponse uses relay state received from IdP to fetch +// user domain. The domain is further used to process validity of the response. +// when sending login request to IdP we send relay state as URL (site url) +// with domainId or domainName as query parameter. +func (m *modelDao) GetDomainFromSsoResponse(ctx context.Context, relayState *url.URL) (*model.OrgDomain, error) { + // derive domain id from relay state now + var domainIdStr string + var domainNameStr string + var domain *model.OrgDomain + + for k, v := range relayState.Query() { + if k == "domainId" && len(v) > 0 { + domainIdStr = strings.Replace(v[0], ":", "-", -1) + } + if k == "domainName" && len(v) > 0 { + domainNameStr = v[0] + } + } + + if domainIdStr != "" { + domainId, err := uuid.Parse(domainIdStr) + if err != nil { + zap.S().Errorf("failed to parse domainId from relay state", err) + return nil, fmt.Errorf("failed to parse domainId from IdP response") + } + + domain, err = m.GetDomain(ctx, domainId) + if (err != nil) || domain == nil { + zap.S().Errorf("failed to find domain from domainId received in IdP response", err.Error()) + return nil, fmt.Errorf("invalid credentials") + } + } + + if domainNameStr != "" { + + domainFromDB, err := m.GetDomainByName(ctx, domainNameStr) + domain = domainFromDB + if (err != nil) || domain == nil { + zap.S().Errorf("failed to find domain from domainName received in IdP response", err.Error()) + return nil, fmt.Errorf("invalid credentials") + } + } + if domain != nil { + return domain, nil + } + + return nil, fmt.Errorf("failed to find domain received in IdP response") +} + +// GetDomainByName returns org domain for a given domain name +func (m *modelDao) GetDomainByName(ctx context.Context, name string) (*model.OrgDomain, basemodel.BaseApiError) { + + stored := StoredDomain{} + err := m.DB().Get(&stored, `SELECT * FROM org_domains WHERE name=$1 LIMIT 1`, name) + + if err != nil { + if err == sql.ErrNoRows { + return nil, model.BadRequest(fmt.Errorf("invalid domain name")) + } + return nil, model.InternalError(err) + } + + domain := &model.OrgDomain{Id: stored.Id, Name: stored.Name, OrgId: stored.OrgId} + if err := domain.LoadConfig(stored.Data); err != nil { + return nil, model.InternalError(err) + } + return domain, nil +} + +// GetDomain returns org domain for a given domain id +func (m *modelDao) GetDomain(ctx context.Context, id uuid.UUID) (*model.OrgDomain, basemodel.BaseApiError) { + + stored := StoredDomain{} + err := m.DB().Get(&stored, `SELECT * FROM org_domains WHERE id=$1 LIMIT 1`, id) + + if err != nil { + if err == sql.ErrNoRows { + return nil, model.BadRequest(fmt.Errorf("invalid domain id")) + } + return nil, model.InternalError(err) + } + + domain := &model.OrgDomain{Id: stored.Id, Name: stored.Name, OrgId: stored.OrgId} + if err := domain.LoadConfig(stored.Data); err != nil { + return nil, model.InternalError(err) + } + return domain, nil +} + +// ListDomains gets the list of auth domains by org id +func (m *modelDao) ListDomains(ctx context.Context, orgId string) ([]model.OrgDomain, basemodel.BaseApiError) { + domains := []model.OrgDomain{} + + stored := []StoredDomain{} + err := m.DB().SelectContext(ctx, &stored, `SELECT * FROM org_domains WHERE org_id=$1`, orgId) + + if err != nil { + if err == sql.ErrNoRows { + return []model.OrgDomain{}, nil + } + return nil, model.InternalError(err) + } + + for _, s := range stored { + domain := model.OrgDomain{Id: s.Id, Name: s.Name, OrgId: s.OrgId} + if err := domain.LoadConfig(s.Data); err != nil { + zap.S().Errorf("ListDomains() failed", zap.Error(err)) + } + domains = append(domains, domain) + } + + return domains, nil +} + +// CreateDomain creates a new auth domain +func (m *modelDao) CreateDomain(ctx context.Context, domain *model.OrgDomain) basemodel.BaseApiError { + + if domain.Id == uuid.Nil { + domain.Id = uuid.New() + } + + if domain.OrgId == "" || domain.Name == "" { + return model.BadRequest(fmt.Errorf("domain creation failed, missing fields: OrgId, Name ")) + } + + configJson, err := json.Marshal(domain) + if err != nil { + zap.S().Errorf("failed to unmarshal domain config", zap.Error(err)) + return model.InternalError(fmt.Errorf("domain creation failed")) + } + + _, err = m.DB().ExecContext(ctx, + "INSERT INTO org_domains (id, name, org_id, data, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)", + domain.Id, + domain.Name, + domain.OrgId, + configJson, + time.Now().Unix(), + time.Now().Unix()) + + if err != nil { + zap.S().Errorf("failed to insert domain in db", zap.Error(err)) + return model.InternalError(fmt.Errorf("domain creation failed")) + } + + return nil +} + +// UpdateDomain updates stored config params for a domain +func (m *modelDao) UpdateDomain(ctx context.Context, domain *model.OrgDomain) basemodel.BaseApiError { + + if domain.Id == uuid.Nil { + zap.S().Errorf("domain update failed", zap.Error(fmt.Errorf("OrgDomain.Id is null"))) + return model.InternalError(fmt.Errorf("domain update failed")) + } + + configJson, err := json.Marshal(domain) + if err != nil { + zap.S().Errorf("domain update failed", zap.Error(err)) + return model.InternalError(fmt.Errorf("domain update failed")) + } + + _, err = m.DB().ExecContext(ctx, + "UPDATE org_domains SET data = $1, updated_at = $2 WHERE id = $3", + configJson, + time.Now().Unix(), + domain.Id) + + if err != nil { + zap.S().Errorf("domain update failed", zap.Error(err)) + return model.InternalError(fmt.Errorf("domain update failed")) + } + + return nil +} + +// DeleteDomain deletes an org domain +func (m *modelDao) DeleteDomain(ctx context.Context, id uuid.UUID) basemodel.BaseApiError { + + if id == uuid.Nil { + zap.S().Errorf("domain delete failed", zap.Error(fmt.Errorf("OrgDomain.Id is null"))) + return model.InternalError(fmt.Errorf("domain delete failed")) + } + + _, err := m.DB().ExecContext(ctx, + "DELETE FROM org_domains WHERE id = $1", + id) + + if err != nil { + zap.S().Errorf("domain delete failed", zap.Error(err)) + return model.InternalError(fmt.Errorf("domain delete failed")) + } + + return nil +} + +func (m *modelDao) GetDomainByEmail(ctx context.Context, email string) (*model.OrgDomain, basemodel.BaseApiError) { + + if email == "" { + return nil, model.BadRequest(fmt.Errorf("could not find auth domain, missing fields: email ")) + } + + components := strings.Split(email, "@") + if len(components) < 2 { + return nil, model.BadRequest(fmt.Errorf("invalid email address")) + } + + parsedDomain := components[1] + + stored := StoredDomain{} + err := m.DB().Get(&stored, `SELECT * FROM org_domains WHERE name=$1 LIMIT 1`, parsedDomain) + + if err != nil { + if err == sql.ErrNoRows { + return nil, nil + } + return nil, model.InternalError(err) + } + + domain := &model.OrgDomain{Id: stored.Id, Name: stored.Name, OrgId: stored.OrgId} + if err := domain.LoadConfig(stored.Data); err != nil { + return nil, model.InternalError(err) + } + return domain, nil +} diff --git a/ee/query-service/dao/sqlite/modelDao.go b/ee/query-service/dao/sqlite/modelDao.go new file mode 100644 index 0000000..02b4367 --- /dev/null +++ b/ee/query-service/dao/sqlite/modelDao.go @@ -0,0 +1,144 @@ +package sqlite + +import ( + "fmt" + + "github.com/jmoiron/sqlx" + basedao "go.signoz.io/signoz/pkg/query-service/dao" + basedsql "go.signoz.io/signoz/pkg/query-service/dao/sqlite" + baseint "go.signoz.io/signoz/pkg/query-service/interfaces" + "go.uber.org/zap" +) + +type modelDao struct { + *basedsql.ModelDaoSqlite + flags baseint.FeatureLookup +} + +// SetFlagProvider sets the feature lookup provider +func (m *modelDao) SetFlagProvider(flags baseint.FeatureLookup) { + m.flags = flags +} + +// CheckFeature confirms if a feature is available +func (m *modelDao) checkFeature(key string) error { + if m.flags == nil { + return fmt.Errorf("flag provider not set") + } + + return m.flags.CheckFeature(key) +} + +func columnExists(db *sqlx.DB, tableName, columnName string) bool { + query := fmt.Sprintf("PRAGMA table_info(%s);", tableName) + rows, err := db.Query(query) + if err != nil { + zap.L().Error("Failed to query table info", zap.Error(err)) + return false + } + defer rows.Close() + + var ( + cid int + name string + ctype string + notnull int + dflt_value *string + pk int + ) + for rows.Next() { + err := rows.Scan(&cid, &name, &ctype, ¬null, &dflt_value, &pk) + if err != nil { + zap.L().Error("Failed to scan table info", zap.Error(err)) + return false + } + if name == columnName { + return true + } + } + err = rows.Err() + if err != nil { + zap.L().Error("Failed to scan table info", zap.Error(err)) + return false + } + return false +} + +// InitDB creates and extends base model DB repository +func InitDB(dataSourceName string) (*modelDao, error) { + dao, err := basedsql.InitDB(dataSourceName) + if err != nil { + return nil, err + } + // set package variable so dependent base methods (e.g. AuthCache) will work + basedao.SetDB(dao) + m := &modelDao{ModelDaoSqlite: dao} + + table_schema := ` + PRAGMA foreign_keys = ON; + CREATE TABLE IF NOT EXISTS org_domains( + id TEXT PRIMARY KEY, + org_id TEXT NOT NULL, + name VARCHAR(50) NOT NULL UNIQUE, + created_at INTEGER NOT NULL, + updated_at INTEGER, + data TEXT NOT NULL, + FOREIGN KEY(org_id) REFERENCES organizations(id) + ); + CREATE TABLE IF NOT EXISTS personal_access_tokens ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + role TEXT NOT NULL, + user_id TEXT NOT NULL, + token TEXT NOT NULL UNIQUE, + name TEXT NOT NULL, + created_at INTEGER NOT NULL, + expires_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL, + last_used INTEGER NOT NULL, + revoked BOOLEAN NOT NULL, + updated_by_user_id TEXT NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(id) + ); + ` + + _, err = m.DB().Exec(table_schema) + if err != nil { + return nil, fmt.Errorf("error in creating tables: %v", err.Error()) + } + + if !columnExists(m.DB(), "personal_access_tokens", "role") { + _, err = m.DB().Exec("ALTER TABLE personal_access_tokens ADD COLUMN role TEXT NOT NULL DEFAULT 'ADMIN';") + if err != nil { + return nil, fmt.Errorf("error in adding column: %v", err.Error()) + } + } + if !columnExists(m.DB(), "personal_access_tokens", "updated_at") { + _, err = m.DB().Exec("ALTER TABLE personal_access_tokens ADD COLUMN updated_at INTEGER NOT NULL DEFAULT 0;") + if err != nil { + return nil, fmt.Errorf("error in adding column: %v", err.Error()) + } + } + if !columnExists(m.DB(), "personal_access_tokens", "last_used") { + _, err = m.DB().Exec("ALTER TABLE personal_access_tokens ADD COLUMN last_used INTEGER NOT NULL DEFAULT 0;") + if err != nil { + return nil, fmt.Errorf("error in adding column: %v", err.Error()) + } + } + if !columnExists(m.DB(), "personal_access_tokens", "revoked") { + _, err = m.DB().Exec("ALTER TABLE personal_access_tokens ADD COLUMN revoked BOOLEAN NOT NULL DEFAULT FALSE;") + if err != nil { + return nil, fmt.Errorf("error in adding column: %v", err.Error()) + } + } + if !columnExists(m.DB(), "personal_access_tokens", "updated_by_user_id") { + _, err = m.DB().Exec("ALTER TABLE personal_access_tokens ADD COLUMN updated_by_user_id TEXT NOT NULL DEFAULT '';") + if err != nil { + return nil, fmt.Errorf("error in adding column: %v", err.Error()) + } + } + return m, nil +} + +func (m *modelDao) DB() *sqlx.DB { + return m.ModelDaoSqlite.DB() +} diff --git a/ee/query-service/dao/sqlite/pat.go b/ee/query-service/dao/sqlite/pat.go new file mode 100644 index 0000000..b2af164 --- /dev/null +++ b/ee/query-service/dao/sqlite/pat.go @@ -0,0 +1,199 @@ +package sqlite + +import ( + "context" + "fmt" + "strconv" + "time" + + "go.signoz.io/signoz/ee/query-service/model" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.uber.org/zap" +) + +func (m *modelDao) CreatePAT(ctx context.Context, p model.PAT) (model.PAT, basemodel.BaseApiError) { + result, err := m.DB().ExecContext(ctx, + "INSERT INTO personal_access_tokens (user_id, token, role, name, created_at, expires_at, updated_at, updated_by_user_id, last_used, revoked) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", + p.UserID, + p.Token, + p.Role, + p.Name, + p.CreatedAt, + p.ExpiresAt, + p.UpdatedAt, + p.UpdatedByUserID, + p.LastUsed, + p.Revoked, + ) + if err != nil { + zap.S().Errorf("Failed to insert PAT in db, err: %v", zap.Error(err)) + return model.PAT{}, model.InternalError(fmt.Errorf("PAT insertion failed")) + } + id, err := result.LastInsertId() + if err != nil { + zap.S().Errorf("Failed to get last inserted id, err: %v", zap.Error(err)) + return model.PAT{}, model.InternalError(fmt.Errorf("PAT insertion failed")) + } + p.Id = strconv.Itoa(int(id)) + createdByUser, _ := m.GetUser(ctx, p.UserID) + if createdByUser == nil { + p.CreatedByUser = model.User{ + NotFound: true, + } + } else { + p.CreatedByUser = model.User{ + Id: createdByUser.Id, + Name: createdByUser.Name, + Email: createdByUser.Email, + CreatedAt: createdByUser.CreatedAt, + ProfilePictureURL: createdByUser.ProfilePictureURL, + NotFound: false, + } + } + return p, nil +} + +func (m *modelDao) UpdatePAT(ctx context.Context, p model.PAT, id string) basemodel.BaseApiError { + _, err := m.DB().ExecContext(ctx, + "UPDATE personal_access_tokens SET role=$1, name=$2, updated_at=$3, updated_by_user_id=$4 WHERE id=$5 and revoked=false;", + p.Role, + p.Name, + p.UpdatedAt, + p.UpdatedByUserID, + id) + if err != nil { + zap.S().Errorf("Failed to update PAT in db, err: %v", zap.Error(err)) + return model.InternalError(fmt.Errorf("PAT update failed")) + } + return nil +} + +func (m *modelDao) UpdatePATLastUsed(ctx context.Context, token string, lastUsed int64) basemodel.BaseApiError { + _, err := m.DB().ExecContext(ctx, + "UPDATE personal_access_tokens SET last_used=$1 WHERE token=$2 and revoked=false;", + lastUsed, + token) + if err != nil { + zap.S().Errorf("Failed to update PAT last used in db, err: %v", zap.Error(err)) + return model.InternalError(fmt.Errorf("PAT last used update failed")) + } + return nil +} + +func (m *modelDao) ListPATs(ctx context.Context) ([]model.PAT, basemodel.BaseApiError) { + pats := []model.PAT{} + + if err := m.DB().Select(&pats, "SELECT * FROM personal_access_tokens WHERE revoked=false ORDER by updated_at DESC;"); err != nil { + zap.S().Errorf("Failed to fetch PATs err: %v", zap.Error(err)) + return nil, model.InternalError(fmt.Errorf("failed to fetch PATs")) + } + for i := range pats { + createdByUser, _ := m.GetUser(ctx, pats[i].UserID) + if createdByUser == nil { + pats[i].CreatedByUser = model.User{ + NotFound: true, + } + } else { + pats[i].CreatedByUser = model.User{ + Id: createdByUser.Id, + Name: createdByUser.Name, + Email: createdByUser.Email, + CreatedAt: createdByUser.CreatedAt, + ProfilePictureURL: createdByUser.ProfilePictureURL, + NotFound: false, + } + } + + updatedByUser, _ := m.GetUser(ctx, pats[i].UpdatedByUserID) + if updatedByUser == nil { + pats[i].UpdatedByUser = model.User{ + NotFound: true, + } + } else { + pats[i].UpdatedByUser = model.User{ + Id: updatedByUser.Id, + Name: updatedByUser.Name, + Email: updatedByUser.Email, + CreatedAt: updatedByUser.CreatedAt, + ProfilePictureURL: updatedByUser.ProfilePictureURL, + NotFound: false, + } + } + } + return pats, nil +} + +func (m *modelDao) RevokePAT(ctx context.Context, id string, userID string) basemodel.BaseApiError { + updatedAt := time.Now().Unix() + _, err := m.DB().ExecContext(ctx, + "UPDATE personal_access_tokens SET revoked=true, updated_by_user_id = $1, updated_at=$2 WHERE id=$3", + userID, updatedAt, id) + if err != nil { + zap.S().Errorf("Failed to revoke PAT in db, err: %v", zap.Error(err)) + return model.InternalError(fmt.Errorf("PAT revoke failed")) + } + return nil +} + +func (m *modelDao) GetPAT(ctx context.Context, token string) (*model.PAT, basemodel.BaseApiError) { + pats := []model.PAT{} + + if err := m.DB().Select(&pats, `SELECT * FROM personal_access_tokens WHERE token=? and revoked=false;`, token); err != nil { + return nil, model.InternalError(fmt.Errorf("failed to fetch PAT")) + } + + if len(pats) != 1 { + return nil, &model.ApiError{ + Typ: model.ErrorInternal, + Err: fmt.Errorf("found zero or multiple PATs with same token, %s", token), + } + } + + return &pats[0], nil +} + +func (m *modelDao) GetPATByID(ctx context.Context, id string) (*model.PAT, basemodel.BaseApiError) { + pats := []model.PAT{} + + if err := m.DB().Select(&pats, `SELECT * FROM personal_access_tokens WHERE id=? and revoked=false;`, id); err != nil { + return nil, model.InternalError(fmt.Errorf("failed to fetch PAT")) + } + + if len(pats) != 1 { + return nil, &model.ApiError{ + Typ: model.ErrorInternal, + Err: fmt.Errorf("found zero or multiple PATs with same token"), + } + } + + return &pats[0], nil +} + +// deprecated +func (m *modelDao) GetUserByPAT(ctx context.Context, token string) (*basemodel.UserPayload, basemodel.BaseApiError) { + users := []basemodel.UserPayload{} + + query := `SELECT + u.id, + u.name, + u.email, + u.password, + u.created_at, + u.profile_picture_url, + u.org_id, + u.group_id + FROM users u, personal_access_tokens p + WHERE u.id = p.user_id and p.token=? and p.expires_at >= strftime('%s', 'now');` + + if err := m.DB().Select(&users, query, token); err != nil { + return nil, model.InternalError(fmt.Errorf("failed to fetch user from PAT, err: %v", err)) + } + + if len(users) != 1 { + return nil, &model.ApiError{ + Typ: model.ErrorInternal, + Err: fmt.Errorf("found zero or multiple users with same PAT token"), + } + } + return &users[0], nil +} diff --git a/ee/query-service/integrations/signozio/response.go b/ee/query-service/integrations/signozio/response.go new file mode 100644 index 0000000..c881210 --- /dev/null +++ b/ee/query-service/integrations/signozio/response.go @@ -0,0 +1,20 @@ +package signozio + +type status string + +const ( + statusSuccess status = "success" + statusError status = "error" +) + +type ActivationResult struct { + Status status `json:"status"` + Data *ActivationResponse `json:"data,omitempty"` + ErrorType string `json:"errorType,omitempty"` + Error string `json:"error,omitempty"` +} + +type ActivationResponse struct { + ActivationId string `json:"ActivationId"` + PlanDetails string `json:"PlanDetails"` +} diff --git a/ee/query-service/integrations/signozio/signozio.go b/ee/query-service/integrations/signozio/signozio.go new file mode 100644 index 0000000..c1ad5e5 --- /dev/null +++ b/ee/query-service/integrations/signozio/signozio.go @@ -0,0 +1,159 @@ +package signozio + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/pkg/errors" + "go.uber.org/zap" + + "go.signoz.io/signoz/ee/query-service/constants" + "go.signoz.io/signoz/ee/query-service/model" +) + +var C *Client + +const ( + POST = "POST" + APPLICATION_JSON = "application/json" +) + +type Client struct { + Prefix string +} + +func New() *Client { + return &Client{ + Prefix: constants.LicenseSignozIo, + } +} + +func init() { + C = New() +} + +// ActivateLicense sends key to license.signoz.io and gets activation data +func ActivateLicense(key, siteId string) (*ActivationResponse, *model.ApiError) { + licenseReq := map[string]string{ + "key": key, + "siteId": siteId, + } + + reqString, _ := json.Marshal(licenseReq) + httpResponse, err := http.Post(C.Prefix+"/licenses/activate", APPLICATION_JSON, bytes.NewBuffer(reqString)) + + if err != nil { + zap.S().Errorf("failed to connect to license.signoz.io", err) + return nil, model.BadRequest(fmt.Errorf("unable to connect with license.signoz.io, please check your network connection")) + } + + httpBody, err := io.ReadAll(httpResponse.Body) + if err != nil { + zap.S().Errorf("failed to read activation response from license.signoz.io", err) + return nil, model.BadRequest(fmt.Errorf("failed to read activation response from license.signoz.io")) + } + + defer httpResponse.Body.Close() + + // read api request result + result := ActivationResult{} + err = json.Unmarshal(httpBody, &result) + if err != nil { + zap.S().Errorf("failed to marshal activation response from license.signoz.io", err) + return nil, model.InternalError(errors.Wrap(err, "failed to marshal license activation response")) + } + + switch httpResponse.StatusCode { + case 200, 201: + return result.Data, nil + case 400, 401: + return nil, model.BadRequest(fmt.Errorf(fmt.Sprintf("failed to activate: %s", result.Error))) + default: + return nil, model.InternalError(fmt.Errorf(fmt.Sprintf("failed to activate: %s", result.Error))) + } + +} + +// ValidateLicense validates the license key +func ValidateLicense(activationId string) (*ActivationResponse, *model.ApiError) { + validReq := map[string]string{ + "activationId": activationId, + } + + reqString, _ := json.Marshal(validReq) + response, err := http.Post(C.Prefix+"/licenses/validate", APPLICATION_JSON, bytes.NewBuffer(reqString)) + + if err != nil { + return nil, model.BadRequest(errors.Wrap(err, "unable to connect with license.signoz.io, please check your network connection")) + } + + body, err := io.ReadAll(response.Body) + if err != nil { + return nil, model.BadRequest(errors.Wrap(err, "failed to read validation response from license.signoz.io")) + } + + defer response.Body.Close() + + switch response.StatusCode { + case 200, 201: + a := ActivationResult{} + err = json.Unmarshal(body, &a) + if err != nil { + return nil, model.BadRequest(errors.Wrap(err, "failed to marshal license validation response")) + } + return a.Data, nil + case 400, 401: + return nil, model.BadRequest(errors.Wrap(fmt.Errorf(string(body)), + "bad request error received from license.signoz.io")) + default: + return nil, model.InternalError(errors.Wrap(fmt.Errorf(string(body)), + "internal error received from license.signoz.io")) + } + +} + +func NewPostRequestWithCtx(ctx context.Context, url string, contentType string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequestWithContext(ctx, POST, url, body) + if err != nil { + return nil, err + } + req.Header.Add("Content-Type", contentType) + return req, err + +} + +// SendUsage reports the usage of signoz to license server +func SendUsage(ctx context.Context, usage model.UsagePayload) *model.ApiError { + reqString, _ := json.Marshal(usage) + req, err := NewPostRequestWithCtx(ctx, C.Prefix+"/usage", APPLICATION_JSON, bytes.NewBuffer(reqString)) + if err != nil { + return model.BadRequest(errors.Wrap(err, "unable to create http request")) + } + + res, err := http.DefaultClient.Do(req) + if err != nil { + return model.BadRequest(errors.Wrap(err, "unable to connect with license.signoz.io, please check your network connection")) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return model.BadRequest(errors.Wrap(err, "failed to read usage response from license.signoz.io")) + } + + defer res.Body.Close() + + switch res.StatusCode { + case 200, 201: + return nil + case 400, 401: + return model.BadRequest(errors.Wrap(fmt.Errorf(string(body)), + "bad request error received from license.signoz.io")) + default: + return model.InternalError(errors.Wrap(fmt.Errorf(string(body)), + "internal error received from license.signoz.io")) + } +} diff --git a/ee/query-service/interfaces/connector.go b/ee/query-service/interfaces/connector.go new file mode 100644 index 0000000..5428e42 --- /dev/null +++ b/ee/query-service/interfaces/connector.go @@ -0,0 +1,12 @@ +package interfaces + +import ( + baseint "go.signoz.io/signoz/pkg/query-service/interfaces" +) + +// Connector defines methods for interaction +// with o11y data. for example - clickhouse +type DataConnector interface { + Start(readerReady chan bool) + baseint.Reader +} diff --git a/ee/query-service/license/db.go b/ee/query-service/license/db.go new file mode 100644 index 0000000..8d2f706 --- /dev/null +++ b/ee/query-service/license/db.go @@ -0,0 +1,205 @@ +package license + +import ( + "context" + "database/sql" + "fmt" + "time" + + "github.com/jmoiron/sqlx" + + "go.signoz.io/signoz/ee/query-service/license/sqlite" + "go.signoz.io/signoz/ee/query-service/model" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.uber.org/zap" +) + +// Repo is license repo. stores license keys in a secured DB +type Repo struct { + db *sqlx.DB +} + +// NewLicenseRepo initiates a new license repo +func NewLicenseRepo(db *sqlx.DB) Repo { + return Repo{ + db: db, + } +} + +func (r *Repo) InitDB(engine string) error { + switch engine { + case "sqlite3", "sqlite": + return sqlite.InitDB(r.db) + default: + return fmt.Errorf("unsupported db") + } +} + +func (r *Repo) GetLicenses(ctx context.Context) ([]model.License, error) { + licenses := []model.License{} + + query := "SELECT key, activationId, planDetails, validationMessage FROM licenses" + + err := r.db.Select(&licenses, query) + if err != nil { + return nil, fmt.Errorf("failed to get licenses from db: %v", err) + } + + return licenses, nil +} + +// GetActiveLicense fetches the latest active license from DB +func (r *Repo) GetActiveLicense(ctx context.Context) (*model.License, error) { + var err error + licenses := []model.License{} + + query := "SELECT key, activationId, planDetails, validationMessage FROM licenses" + + err = r.db.Select(&licenses, query) + if err != nil { + return nil, fmt.Errorf("failed to get active licenses from db: %v", err) + } + + var active *model.License + for _, l := range licenses { + l.ParsePlan() + if active == nil && + (l.ValidFrom != 0) && + (l.ValidUntil == -1 || l.ValidUntil > time.Now().Unix()) { + active = &l + } + if active != nil && + l.ValidFrom > active.ValidFrom && + (l.ValidUntil == -1 || l.ValidUntil > time.Now().Unix()) { + active = &l + } + } + + return active, nil +} + +// InsertLicense inserts a new license in db +func (r *Repo) InsertLicense(ctx context.Context, l *model.License) error { + + if l.Key == "" { + return fmt.Errorf("insert license failed: license key is required") + } + + query := `INSERT INTO licenses + (key, planDetails, activationId, validationmessage) + VALUES ($1, $2, $3, $4)` + + _, err := r.db.ExecContext(ctx, + query, + l.Key, + l.PlanDetails, + l.ActivationId, + l.ValidationMessage) + + if err != nil { + zap.S().Errorf("error in inserting license data: ", zap.Error(err)) + return fmt.Errorf("failed to insert license in db: %v", err) + } + + return nil +} + +// UpdatePlanDetails writes new plan details to the db +func (r *Repo) UpdatePlanDetails(ctx context.Context, + key, + planDetails string) error { + + if key == "" { + return fmt.Errorf("Update Plan Details failed: license key is required") + } + + query := `UPDATE licenses + SET planDetails = $1, + updatedAt = $2 + WHERE key = $3` + + _, err := r.db.ExecContext(ctx, query, planDetails, time.Now(), key) + + if err != nil { + zap.S().Errorf("error in updating license: ", zap.Error(err)) + return fmt.Errorf("failed to update license in db: %v", err) + } + + return nil +} + +func (r *Repo) CreateFeature(req *basemodel.Feature) *basemodel.ApiError { + + _, err := r.db.Exec( + `INSERT INTO feature_status (name, active, usage, usage_limit, route) + VALUES (?, ?, ?, ?, ?);`, + req.Name, req.Active, req.Usage, req.UsageLimit, req.Route) + if err != nil { + return &basemodel.ApiError{Typ: basemodel.ErrorInternal, Err: err} + } + return nil +} + +func (r *Repo) GetFeature(featureName string) (basemodel.Feature, error) { + + var feature basemodel.Feature + + err := r.db.Get(&feature, + `SELECT * FROM feature_status WHERE name = ?;`, featureName) + if err != nil { + return feature, err + } + if feature.Name == "" { + return feature, basemodel.ErrFeatureUnavailable{Key: featureName} + } + return feature, nil +} + +func (r *Repo) GetAllFeatures() ([]basemodel.Feature, error) { + + var feature []basemodel.Feature + + err := r.db.Select(&feature, + `SELECT * FROM feature_status;`) + if err != nil { + return feature, err + } + + return feature, nil +} + +func (r *Repo) UpdateFeature(req basemodel.Feature) error { + + _, err := r.db.Exec( + `UPDATE feature_status SET active = ?, usage = ?, usage_limit = ?, route = ? WHERE name = ?;`, + req.Active, req.Usage, req.UsageLimit, req.Route, req.Name) + if err != nil { + return err + } + return nil +} + +func (r *Repo) InitFeatures(req basemodel.FeatureSet) error { + // get a feature by name, if it doesn't exist, create it. If it does exist, update it. + for _, feature := range req { + currentFeature, err := r.GetFeature(feature.Name) + if err != nil && err == sql.ErrNoRows { + err := r.CreateFeature(&feature) + if err != nil { + return err + } + continue + } else if err != nil { + return err + } + feature.Usage = currentFeature.Usage + if feature.Usage >= feature.UsageLimit && feature.UsageLimit != -1 { + feature.Active = false + } + err = r.UpdateFeature(feature) + if err != nil { + return err + } + } + return nil +} diff --git a/ee/query-service/license/manager.go b/ee/query-service/license/manager.go new file mode 100644 index 0000000..dcfa823 --- /dev/null +++ b/ee/query-service/license/manager.go @@ -0,0 +1,336 @@ +package license + +import ( + "context" + "fmt" + "sync/atomic" + "time" + + "github.com/jmoiron/sqlx" + + "sync" + + "go.signoz.io/signoz/pkg/query-service/auth" + baseconstants "go.signoz.io/signoz/pkg/query-service/constants" + + validate "go.signoz.io/signoz/ee/query-service/integrations/signozio" + "go.signoz.io/signoz/ee/query-service/model" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.signoz.io/signoz/pkg/query-service/telemetry" + "go.uber.org/zap" +) + +var LM *Manager + +// validate and update license every 24 hours +var validationFrequency = 24 * 60 * time.Minute + +type Manager struct { + repo *Repo + mutex sync.Mutex + + validatorRunning bool + + // end the license validation, this is important to gracefully + // stopping validation and protect in-consistent updates + done chan struct{} + + // terminated waits for the validate go routine to end + terminated chan struct{} + + // last time the license was validated + lastValidated int64 + + // keep track of validation failure attempts + failedAttempts uint64 + + // keep track of active license and features + activeLicense *model.License + activeFeatures basemodel.FeatureSet +} + +func StartManager(dbType string, db *sqlx.DB) (*Manager, error) { + + if LM != nil { + return LM, nil + } + + repo := NewLicenseRepo(db) + err := repo.InitDB(dbType) + + if err != nil { + return nil, fmt.Errorf("failed to initiate license repo: %v", err) + } + + m := &Manager{ + repo: &repo, + } + + if err := m.start(); err != nil { + return m, err + } + LM = m + return m, nil +} + +// start loads active license in memory and initiates validator +func (lm *Manager) start() error { + err := lm.LoadActiveLicense() + + return err +} + +func (lm *Manager) Stop() { + close(lm.done) + <-lm.terminated +} + +func (lm *Manager) SetActive(l *model.License) { + lm.mutex.Lock() + defer lm.mutex.Unlock() + + if l == nil { + return + } + + lm.activeLicense = l + lm.activeFeatures = l.FeatureSet + // set default features + setDefaultFeatures(lm) + + err := lm.InitFeatures(lm.activeFeatures) + if err != nil { + zap.S().Panicf("Couldn't activate features: %v", err) + } + if !lm.validatorRunning { + // we want to make sure only one validator runs, + // we already have lock() so good to go + lm.validatorRunning = true + go lm.Validator(context.Background()) + } + +} + +func setDefaultFeatures(lm *Manager) { + lm.activeFeatures = append(lm.activeFeatures, baseconstants.DEFAULT_FEATURE_SET...) +} + +// LoadActiveLicense loads the most recent active license +func (lm *Manager) LoadActiveLicense() error { + var err error + active, err := lm.repo.GetActiveLicense(context.Background()) + if err != nil { + return err + } + if active != nil { + lm.SetActive(active) + } else { + zap.S().Info("No active license found, defaulting to basic plan") + // if no active license is found, we default to basic(free) plan with all default features + lm.activeFeatures = model.BasicPlan + setDefaultFeatures(lm) + err := lm.InitFeatures(lm.activeFeatures) + if err != nil { + zap.S().Error("Couldn't initialize features: ", err) + return err + } + } + + return nil +} + +func (lm *Manager) GetLicenses(ctx context.Context) (response []model.License, apiError *model.ApiError) { + + licenses, err := lm.repo.GetLicenses(ctx) + if err != nil { + return nil, model.InternalError(err) + } + + for _, l := range licenses { + l.ParsePlan() + + if l.Key == lm.activeLicense.Key { + l.IsCurrent = true + } + + if l.ValidUntil == -1 { + // for subscriptions, there is no end-date as such + // but for showing user some validity we default one year timespan + l.ValidUntil = l.ValidFrom + 31556926 + } + + response = append(response, l) + } + + return +} + +// Validator validates license after an epoch of time +func (lm *Manager) Validator(ctx context.Context) { + defer close(lm.terminated) + tick := time.NewTicker(validationFrequency) + defer tick.Stop() + + lm.Validate(ctx) + + for { + select { + case <-lm.done: + return + default: + select { + case <-lm.done: + return + case <-tick.C: + lm.Validate(ctx) + } + } + + } +} + +// Validate validates the current active license +func (lm *Manager) Validate(ctx context.Context) (reterr error) { + zap.S().Info("License validation started") + if lm.activeLicense == nil { + return nil + } + + defer func() { + lm.mutex.Lock() + + lm.lastValidated = time.Now().Unix() + if reterr != nil { + zap.S().Errorf("License validation completed with error", reterr) + atomic.AddUint64(&lm.failedAttempts, 1) + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_LICENSE_CHECK_FAILED, + map[string]interface{}{"err": reterr.Error()}, "") + } else { + zap.S().Info("License validation completed with no errors") + } + + lm.mutex.Unlock() + }() + + response, apiError := validate.ValidateLicense(lm.activeLicense.ActivationId) + if apiError != nil { + zap.S().Errorf("failed to validate license", apiError) + return apiError.Err + } + + if response.PlanDetails == lm.activeLicense.PlanDetails { + // license plan hasnt changed, nothing to do + return nil + } + + if response.PlanDetails != "" { + + // copy and replace the active license record + l := model.License{ + Key: lm.activeLicense.Key, + CreatedAt: lm.activeLicense.CreatedAt, + PlanDetails: response.PlanDetails, + ValidationMessage: lm.activeLicense.ValidationMessage, + ActivationId: lm.activeLicense.ActivationId, + } + + if err := l.ParsePlan(); err != nil { + zap.S().Errorf("failed to parse updated license", zap.Error(err)) + return err + } + + // updated plan is parsable, check if plan has changed + if lm.activeLicense.PlanDetails != response.PlanDetails { + err := lm.repo.UpdatePlanDetails(ctx, lm.activeLicense.Key, response.PlanDetails) + if err != nil { + // unexpected db write issue but we can let the user continue + // and wait for update to work in next cycle. + zap.S().Errorf("failed to validate license", zap.Error(err)) + } + } + + // activate the update license plan + lm.SetActive(&l) + } + + return nil +} + +// Activate activates a license key with signoz server +func (lm *Manager) Activate(ctx context.Context, key string) (licenseResponse *model.License, errResponse *model.ApiError) { + defer func() { + if errResponse != nil { + userEmail, err := auth.GetEmailFromJwt(ctx) + if err == nil { + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_LICENSE_ACT_FAILED, + map[string]interface{}{"err": errResponse.Err.Error()}, userEmail) + } + } + }() + + response, apiError := validate.ActivateLicense(key, "") + if apiError != nil { + zap.S().Errorf("failed to activate license", zap.Error(apiError.Err)) + return nil, apiError + } + + l := &model.License{ + Key: key, + ActivationId: response.ActivationId, + PlanDetails: response.PlanDetails, + } + + // parse validity and features from the plan details + err := l.ParsePlan() + + if err != nil { + zap.S().Errorf("failed to activate license", zap.Error(err)) + return nil, model.InternalError(err) + } + + // store the license before activating it + err = lm.repo.InsertLicense(ctx, l) + if err != nil { + zap.S().Errorf("failed to activate license", zap.Error(err)) + return nil, model.InternalError(err) + } + + // license is valid, activate it + lm.SetActive(l) + return l, nil +} + +// CheckFeature will be internally used by backend routines +// for feature gating +func (lm *Manager) CheckFeature(featureKey string) error { + feature, err := lm.repo.GetFeature(featureKey) + if err != nil { + return err + } + if feature.Active { + return nil + } + return basemodel.ErrFeatureUnavailable{Key: featureKey} +} + +// GetFeatureFlags returns current active features +func (lm *Manager) GetFeatureFlags() (basemodel.FeatureSet, error) { + return lm.repo.GetAllFeatures() +} + +func (lm *Manager) InitFeatures(features basemodel.FeatureSet) error { + return lm.repo.InitFeatures(features) +} + +func (lm *Manager) UpdateFeatureFlag(feature basemodel.Feature) error { + return lm.repo.UpdateFeature(feature) +} + +func (lm *Manager) GetFeatureFlag(key string) (basemodel.Feature, error) { + return lm.repo.GetFeature(key) +} + +// GetRepo return the license repo +func (lm *Manager) GetRepo() *Repo { + return lm.repo +} diff --git a/ee/query-service/license/sqlite/init.go b/ee/query-service/license/sqlite/init.go new file mode 100644 index 0000000..e500ddb --- /dev/null +++ b/ee/query-service/license/sqlite/init.go @@ -0,0 +1,52 @@ +package sqlite + +import ( + "fmt" + + "github.com/jmoiron/sqlx" +) + +func InitDB(db *sqlx.DB) error { + var err error + if db == nil { + return fmt.Errorf("invalid db connection") + } + + table_schema := `CREATE TABLE IF NOT EXISTS licenses( + key TEXT PRIMARY KEY, + createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + planDetails TEXT, + activationId TEXT, + validationMessage TEXT, + lastValidated TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS sites( + uuid TEXT PRIMARY KEY, + alias VARCHAR(180) DEFAULT 'PROD', + url VARCHAR(300), + createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + ` + + _, err = db.Exec(table_schema) + if err != nil { + return fmt.Errorf("Error in creating licenses table: %s", err.Error()) + } + + table_schema = `CREATE TABLE IF NOT EXISTS feature_status ( + name TEXT PRIMARY KEY, + active bool, + usage INTEGER DEFAULT 0, + usage_limit INTEGER DEFAULT 0, + route TEXT + );` + + _, err = db.Exec(table_schema) + if err != nil { + return fmt.Errorf("Error in creating feature_status table: %s", err.Error()) + } + + return nil +} diff --git a/ee/query-service/main.go b/ee/query-service/main.go new file mode 100644 index 0000000..427f780 --- /dev/null +++ b/ee/query-service/main.go @@ -0,0 +1,170 @@ +package main + +import ( + "context" + "flag" + "log" + "os" + "os/signal" + "strconv" + "syscall" + "time" + + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "go.signoz.io/signoz/ee/query-service/app" + "go.signoz.io/signoz/pkg/query-service/auth" + "go.signoz.io/signoz/pkg/query-service/constants" + baseconst "go.signoz.io/signoz/pkg/query-service/constants" + "go.signoz.io/signoz/pkg/query-service/version" + "google.golang.org/grpc" + + zapotlpencoder "github.com/SigNoz/zap_otlp/zap_otlp_encoder" + zapotlpsync "github.com/SigNoz/zap_otlp/zap_otlp_sync" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func initZapLog(enableQueryServiceLogOTLPExport bool) *zap.Logger { + config := zap.NewDevelopmentConfig() + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) + defer stop() + + config.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder + otlpEncoder := zapotlpencoder.NewOTLPEncoder(config.EncoderConfig) + consoleEncoder := zapcore.NewConsoleEncoder(config.EncoderConfig) + defaultLogLevel := zapcore.DebugLevel + config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + config.EncoderConfig.TimeKey = "timestamp" + config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + + res := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String("query-service"), + ) + + core := zapcore.NewTee( + zapcore.NewCore(consoleEncoder, os.Stdout, defaultLogLevel), + ) + + if enableQueryServiceLogOTLPExport == true { + conn, err := grpc.DialContext(ctx, constants.OTLPTarget, grpc.WithBlock(), grpc.WithInsecure(), grpc.WithTimeout(time.Second*30)) + if err != nil { + log.Println("failed to connect to otlp collector to export query service logs with error:", err) + } else { + logExportBatchSizeInt, err := strconv.Atoi(baseconst.LogExportBatchSize) + if err != nil { + logExportBatchSizeInt = 1000 + } + ws := zapcore.AddSync(zapotlpsync.NewOtlpSyncer(conn, zapotlpsync.Options{ + BatchSize: logExportBatchSizeInt, + ResourceSchema: semconv.SchemaURL, + Resource: res, + })) + core = zapcore.NewTee( + zapcore.NewCore(consoleEncoder, os.Stdout, defaultLogLevel), + zapcore.NewCore(otlpEncoder, zapcore.NewMultiWriteSyncer(ws), defaultLogLevel), + ) + } + } + logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel)) + + return logger +} + +func main() { + var promConfigPath, skipTopLvlOpsPath string + + // disables rule execution but allows change to the rule definition + var disableRules bool + + // the url used to build link in the alert messages in slack and other systems + var ruleRepoURL string + var cluster string + + var cacheConfigPath, fluxInterval string + var enableQueryServiceLogOTLPExport bool + var preferDelta bool + var preferSpanMetrics bool + + var maxIdleConns int + var maxOpenConns int + var dialTimeout time.Duration + + flag.StringVar(&promConfigPath, "config", "./config/prometheus.yml", "(prometheus config to read metrics)") + flag.StringVar(&skipTopLvlOpsPath, "skip-top-level-ops", "", "(config file to skip top level operations)") + flag.BoolVar(&disableRules, "rules.disable", false, "(disable rule evaluation)") + flag.BoolVar(&preferDelta, "prefer-delta", false, "(prefer delta over cumulative metrics)") + flag.BoolVar(&preferSpanMetrics, "prefer-span-metrics", false, "(prefer span metrics for service level metrics)") + flag.IntVar(&maxIdleConns, "max-idle-conns", 50, "(number of connections to maintain in the pool.)") + flag.IntVar(&maxOpenConns, "max-open-conns", 100, "(max connections for use at any time.)") + flag.DurationVar(&dialTimeout, "dial-timeout", 5*time.Second, "(the maximum time to establish a connection.)") + flag.StringVar(&ruleRepoURL, "rules.repo-url", baseconst.AlertHelpPage, "(host address used to build rule link in alert messages)") + flag.StringVar(&cacheConfigPath, "experimental.cache-config", "", "(cache config to use)") + flag.StringVar(&fluxInterval, "flux-interval", "5m", "(cache config to use)") + flag.BoolVar(&enableQueryServiceLogOTLPExport, "enable.query.service.log.otlp.export", false, "(enable query service log otlp export)") + flag.StringVar(&cluster, "cluster", "cluster", "(cluster name - defaults to 'cluster')") + + flag.Parse() + + loggerMgr := initZapLog(enableQueryServiceLogOTLPExport) + + zap.ReplaceGlobals(loggerMgr) + defer loggerMgr.Sync() // flushes buffer, if any + + logger := loggerMgr.Sugar() + version.PrintVersion() + + serverOptions := &app.ServerOptions{ + HTTPHostPort: baseconst.HTTPHostPort, + PromConfigPath: promConfigPath, + SkipTopLvlOpsPath: skipTopLvlOpsPath, + PreferDelta: preferDelta, + PreferSpanMetrics: preferSpanMetrics, + PrivateHostPort: baseconst.PrivateHostPort, + DisableRules: disableRules, + RuleRepoURL: ruleRepoURL, + MaxIdleConns: maxIdleConns, + MaxOpenConns: maxOpenConns, + DialTimeout: dialTimeout, + CacheConfigPath: cacheConfigPath, + FluxInterval: fluxInterval, + Cluster: cluster, + } + + // Read the jwt secret key + auth.JwtSecret = os.Getenv("SIGNOZ_JWT_SECRET") + + if len(auth.JwtSecret) == 0 { + zap.S().Warn("No JWT secret key is specified.") + } else { + zap.S().Info("No JWT secret key set successfully.") + } + + server, err := app.NewServer(serverOptions) + if err != nil { + logger.Fatal("Failed to create server", zap.Error(err)) + } + + if err := server.Start(); err != nil { + logger.Fatal("Could not start servers", zap.Error(err)) + } + + if err := auth.InitAuthCache(context.Background()); err != nil { + logger.Fatal("Failed to initialize auth cache", zap.Error(err)) + } + + signalsChannel := make(chan os.Signal, 1) + signal.Notify(signalsChannel, os.Interrupt, syscall.SIGTERM) + + for { + select { + case status := <-server.HealthCheckStatus(): + logger.Info("Received HealthCheck status: ", zap.Int("status", int(status))) + case <-signalsChannel: + logger.Fatal("Received OS Interrupt Signal ... ") + server.Stop() + } + } +} diff --git a/ee/query-service/model/auth.go b/ee/query-service/model/auth.go new file mode 100644 index 0000000..9ad83cb --- /dev/null +++ b/ee/query-service/model/auth.go @@ -0,0 +1,12 @@ +package model + +import ( + basemodel "go.signoz.io/signoz/pkg/query-service/model" +) + +// GettableInvitation overrides base object and adds precheck into +// response +type GettableInvitation struct { + *basemodel.InvitationResponseObject + Precheck *basemodel.PrecheckResponse `json:"precheck"` +} diff --git a/ee/query-service/model/domain.go b/ee/query-service/model/domain.go new file mode 100644 index 0000000..beadd66 --- /dev/null +++ b/ee/query-service/model/domain.go @@ -0,0 +1,184 @@ +package model + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" + + "github.com/google/uuid" + "github.com/pkg/errors" + saml2 "github.com/russellhaering/gosaml2" + "go.signoz.io/signoz/ee/query-service/sso/saml" + "go.signoz.io/signoz/ee/query-service/sso" + basemodel "go.signoz.io/signoz/pkg/query-service/model" + "go.uber.org/zap" +) + +type SSOType string + +const ( + SAML SSOType = "SAML" + GoogleAuth SSOType = "GOOGLE_AUTH" +) + +// OrgDomain identify org owned web domains for auth and other purposes +type OrgDomain struct { + Id uuid.UUID `json:"id"` + Name string `json:"name"` + OrgId string `json:"orgId"` + SsoEnabled bool `json:"ssoEnabled"` + SsoType SSOType `json:"ssoType"` + + SamlConfig *SamlConfig `json:"samlConfig"` + GoogleAuthConfig *GoogleOAuthConfig `json:"googleAuthConfig"` + + Org *basemodel.Organization +} + +func (od *OrgDomain) String() string { + return fmt.Sprintf("[%s]%s-%s ", od.Name, od.Id.String(), od.SsoType) +} + +// Valid is used a pipeline function to check if org domain +// loaded from db is valid +func (od *OrgDomain) Valid(err error) error { + if err != nil { + return err + } + + if od.Id == uuid.Nil || od.OrgId == "" { + return fmt.Errorf("both id and orgId are required") + } + + return nil +} + +// ValidNew cheks if the org domain is valid for insertion in db +func (od *OrgDomain) ValidNew() error { + + if od.OrgId == "" { + return fmt.Errorf("orgId is required") + } + + if od.Name == "" { + return fmt.Errorf("name is required") + } + + return nil +} + +// LoadConfig loads config params from json text +func (od *OrgDomain) LoadConfig(jsondata string) error { + d := *od + err := json.Unmarshal([]byte(jsondata), &d) + if err != nil { + return errors.Wrap(err, "failed to marshal json to OrgDomain{}") + } + *od = d + return nil +} + +func (od *OrgDomain) GetSAMLEntityID() string { + if od.SamlConfig != nil { + return od.SamlConfig.SamlEntity + } + return "" +} + +func (od *OrgDomain) GetSAMLIdpURL() string { + if od.SamlConfig != nil { + return od.SamlConfig.SamlIdp + } + return "" +} + +func (od *OrgDomain) GetSAMLCert() string { + if od.SamlConfig != nil { + return od.SamlConfig.SamlCert + } + return "" +} + +// PrepareGoogleOAuthProvider creates GoogleProvider that is used in +// requesting OAuth and also used in processing response from google +func (od *OrgDomain) PrepareGoogleOAuthProvider(siteUrl *url.URL) (sso.OAuthCallbackProvider, error) { + if od.GoogleAuthConfig == nil { + return nil, fmt.Errorf("Google auth is not setup correctly for this domain") + } + + return od.GoogleAuthConfig.GetProvider(od.Name, siteUrl) +} + +// PrepareSamlRequest creates a request accordingly gosaml2 +func (od *OrgDomain) PrepareSamlRequest(siteUrl *url.URL) (*saml2.SAMLServiceProvider, error) { + + // this is the url Idp will call after login completion + acs := fmt.Sprintf("%s://%s/%s", + siteUrl.Scheme, + siteUrl.Host, + "api/v1/complete/saml") + + // this is the address of the calling url, useful to redirect user + sourceUrl := fmt.Sprintf("%s://%s%s", + siteUrl.Scheme, + siteUrl.Host, + siteUrl.Path) + + // ideally this should be some unique ID for each installation + // but since we dont have UI to support it, we default it to + // host. this issuer is an identifier of service provider (signoz) + // on id provider (e.g. azure, okta). Azure requires this id to be configured + // in their system, while others seem to not care about it. + // currently we default it to host from window.location (received from browser) + issuer := siteUrl.Host + + return saml.PrepareRequest(issuer, acs, sourceUrl, od.GetSAMLEntityID(), od.GetSAMLIdpURL(), od.GetSAMLCert()) +} + +func (od *OrgDomain) BuildSsoUrl(siteUrl *url.URL) (ssoUrl string, err error) { + + + fmtDomainId := strings.Replace(od.Id.String(), "-", ":", -1) + + // build redirect url from window.location sent by frontend + redirectURL := fmt.Sprintf("%s://%s%s", siteUrl.Scheme, siteUrl.Host, siteUrl.Path) + + // prepare state that gets relayed back when the auth provider + // calls back our url. here we pass the app url (where signoz runs) + // and the domain Id. The domain Id helps in identifying sso config + // when the call back occurs and the app url is useful in redirecting user + // back to the right path. + // why do we need to pass app url? the callback typically is handled by backend + // and sometimes backend might right at a different port or is unaware of frontend + // endpoint (unless SITE_URL param is set). hence, we receive this build sso request + // along with frontend window.location and use it to relay the information through + // auth provider to the backend (HandleCallback or HandleSSO method). + relayState := fmt.Sprintf("%s?domainId=%s", redirectURL, fmtDomainId) + + + switch (od.SsoType) { + case SAML: + + sp, err := od.PrepareSamlRequest(siteUrl) + if err != nil { + return "", err + } + + return sp.BuildAuthURL(relayState) + + case GoogleAuth: + + googleProvider, err := od.PrepareGoogleOAuthProvider(siteUrl) + if err != nil { + return "", err + } + return googleProvider.BuildAuthURL(relayState) + + default: + zap.S().Errorf("found unsupported SSO config for the org domain", zap.String("orgDomain", od.Name)) + return "", fmt.Errorf("unsupported SSO config for the domain") + } + + +} diff --git a/ee/query-service/model/errors.go b/ee/query-service/model/errors.go new file mode 100644 index 0000000..7e7b841 --- /dev/null +++ b/ee/query-service/model/errors.go @@ -0,0 +1,109 @@ +package model + +import ( + "fmt" + + basemodel "go.signoz.io/signoz/pkg/query-service/model" +) + +type ApiError struct { + Typ basemodel.ErrorType + Err error +} + +func (a *ApiError) Type() basemodel.ErrorType { + return a.Typ +} + +func (a *ApiError) ToError() error { + if a != nil { + return a.Err + } + return a.Err +} + +func (a *ApiError) Error() string { + return a.Err.Error() +} + +func (a *ApiError) IsNil() bool { + return a == nil || a.Err == nil +} + +// NewApiError returns a ApiError object of given type +func NewApiError(typ basemodel.ErrorType, err error) *ApiError { + return &ApiError{ + Typ: typ, + Err: err, + } +} + +// BadRequest returns a ApiError object of bad request +func BadRequest(err error) *ApiError { + return &ApiError{ + Typ: basemodel.ErrorBadData, + Err: err, + } +} + +// BadRequestStr returns a ApiError object of bad request for string input +func BadRequestStr(s string) *ApiError { + return &ApiError{ + Typ: basemodel.ErrorBadData, + Err: fmt.Errorf(s), + } +} + +// InternalError returns a ApiError object of internal type +func InternalError(err error) *ApiError { + return &ApiError{ + Typ: basemodel.ErrorInternal, + Err: err, + } +} + +// InternalErrorStr returns a ApiError object of internal type for string input +func InternalErrorStr(s string) *ApiError { + return &ApiError{ + Typ: basemodel.ErrorInternal, + Err: fmt.Errorf(s), + } +} + +var ( + ErrorNone basemodel.ErrorType = "" + ErrorTimeout basemodel.ErrorType = "timeout" + ErrorCanceled basemodel.ErrorType = "canceled" + ErrorExec basemodel.ErrorType = "execution" + ErrorBadData basemodel.ErrorType = "bad_data" + ErrorInternal basemodel.ErrorType = "internal" + ErrorUnavailable basemodel.ErrorType = "unavailable" + ErrorNotFound basemodel.ErrorType = "not_found" + ErrorNotImplemented basemodel.ErrorType = "not_implemented" + ErrorUnauthorized basemodel.ErrorType = "unauthorized" + ErrorForbidden basemodel.ErrorType = "forbidden" + ErrorConflict basemodel.ErrorType = "conflict" + ErrorStreamingNotSupported basemodel.ErrorType = "streaming is not supported" +) + +func init() { + ErrorNone = basemodel.ErrorNone + ErrorTimeout = basemodel.ErrorTimeout + ErrorCanceled = basemodel.ErrorCanceled + ErrorExec = basemodel.ErrorExec + ErrorBadData = basemodel.ErrorBadData + ErrorInternal = basemodel.ErrorInternal + ErrorUnavailable = basemodel.ErrorUnavailable + ErrorNotFound = basemodel.ErrorNotFound + ErrorNotImplemented = basemodel.ErrorNotImplemented + ErrorUnauthorized = basemodel.ErrorUnauthorized + ErrorForbidden = basemodel.ErrorForbidden + ErrorConflict = basemodel.ErrorConflict + ErrorStreamingNotSupported = basemodel.ErrorStreamingNotSupported +} + +type ErrUnsupportedAuth struct{} + +func (errUnsupportedAuth ErrUnsupportedAuth) Error() string { + return "this authentication method not supported" +} diff --git a/ee/query-service/model/license.go b/ee/query-service/model/license.go new file mode 100644 index 0000000..7ad349c --- /dev/null +++ b/ee/query-service/model/license.go @@ -0,0 +1,106 @@ +package model + +import ( + "encoding/base64" + "encoding/json" + "time" + + "github.com/pkg/errors" + basemodel "go.signoz.io/signoz/pkg/query-service/model" +) + +type License struct { + Key string `json:"key" db:"key"` + ActivationId string `json:"activationId" db:"activationId"` + CreatedAt time.Time `db:"created_at"` + + // PlanDetails contains the encrypted plan info + PlanDetails string `json:"planDetails" db:"planDetails"` + + // stores parsed license details + LicensePlan + + FeatureSet basemodel.FeatureSet + + // populated in case license has any errors + ValidationMessage string `db:"validationMessage"` + + // used only for sending details to front-end + IsCurrent bool `json:"isCurrent"` +} + +func (l *License) MarshalJSON() ([]byte, error) { + + return json.Marshal(&struct { + Key string `json:"key" db:"key"` + ActivationId string `json:"activationId" db:"activationId"` + ValidationMessage string `db:"validationMessage"` + IsCurrent bool `json:"isCurrent"` + PlanKey string `json:"planKey"` + ValidFrom time.Time `json:"ValidFrom"` + ValidUntil time.Time `json:"ValidUntil"` + Status string `json:"status"` + }{ + Key: l.Key, + ActivationId: l.ActivationId, + IsCurrent: l.IsCurrent, + PlanKey: l.PlanKey, + ValidFrom: time.Unix(l.ValidFrom, 0), + ValidUntil: time.Unix(l.ValidUntil, 0), + Status: l.Status, + ValidationMessage: l.ValidationMessage, + }) +} + +type LicensePlan struct { + PlanKey string `json:"planKey"` + ValidFrom int64 `json:"validFrom"` + ValidUntil int64 `json:"validUntil"` + Status string `json:"status"` +} + +func (l *License) ParsePlan() error { + l.LicensePlan = LicensePlan{} + + planData, err := base64.StdEncoding.DecodeString(l.PlanDetails) + if err != nil { + return err + } + + plan := LicensePlan{} + err = json.Unmarshal([]byte(planData), &plan) + if err != nil { + l.ValidationMessage = "failed to parse plan from license" + return errors.Wrap(err, "failed to parse plan from license") + } + + l.LicensePlan = plan + l.ParseFeatures() + return nil +} + +func (l *License) ParseFeatures() { + switch l.PlanKey { + case Pro: + l.FeatureSet = ProPlan + case Enterprise: + l.FeatureSet = EnterprisePlan + default: + l.FeatureSet = BasicPlan + } +} + +type Licenses struct { + TrialStart int64 `json:"trialStart"` + TrialEnd int64 `json:"trialEnd"` + OnTrial bool `json:"onTrial"` + WorkSpaceBlock bool `json:"workSpaceBlock"` + TrialConvertedToSubscription bool `json:"trialConvertedToSubscription"` + GracePeriodEnd int64 `json:"gracePeriodEnd"` + Licenses []License `json:"licenses"` +} + +type SubscriptionServerResp struct { + Status string `json:"status"` + Data Licenses `json:"data"` +} diff --git a/ee/query-service/model/pat.go b/ee/query-service/model/pat.go new file mode 100644 index 0000000..ef683a0 --- /dev/null +++ b/ee/query-service/model/pat.go @@ -0,0 +1,32 @@ +package model + +type User struct { + Id string `json:"id" db:"id"` + Name string `json:"name" db:"name"` + Email string `json:"email" db:"email"` + CreatedAt int64 `json:"createdAt" db:"created_at"` + ProfilePictureURL string `json:"profilePictureURL" db:"profile_picture_url"` + NotFound bool `json:"notFound"` +} + +type CreatePATRequestBody struct { + Name string `json:"name"` + Role string `json:"role"` + ExpiresInDays int64 `json:"expiresInDays"` +} + +type PAT struct { + Id string `json:"id" db:"id"` + UserID string `json:"userId" db:"user_id"` + CreatedByUser User `json:"createdByUser"` + UpdatedByUser User `json:"updatedByUser"` + Token string `json:"token" db:"token"` + Role string `json:"role" db:"role"` + Name string `json:"name" db:"name"` + CreatedAt int64 `json:"createdAt" db:"created_at"` + ExpiresAt int64 `json:"expiresAt" db:"expires_at"` + UpdatedAt int64 `json:"updatedAt" db:"updated_at"` + LastUsed int64 `json:"lastUsed" db:"last_used"` + Revoked bool `json:"revoked" db:"revoked"` + UpdatedByUserID string `json:"updatedByUserId" db:"updated_by_user_id"` +} diff --git a/ee/query-service/model/plans.go b/ee/query-service/model/plans.go new file mode 100644 index 0000000..09a88bb --- /dev/null +++ b/ee/query-service/model/plans.go @@ -0,0 +1,316 @@ +package model + +import ( + basemodel "go.signoz.io/signoz/pkg/query-service/model" +) + +const SSO = "SSO" +const Basic = "BASIC_PLAN" +const Pro = "PRO_PLAN" +const Enterprise = "ENTERPRISE_PLAN" +const DisableUpsell = "DISABLE_UPSELL" +const Onboarding = "ONBOARDING" +const ChatSupport = "CHAT_SUPPORT" + +var BasicPlan = basemodel.FeatureSet{ + basemodel.Feature{ + Name: SSO, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.OSS, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: DisableUpsell, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.SmartTraceDetail, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.CustomMetricsFunction, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.QueryBuilderPanels, + Active: true, + Usage: 0, + UsageLimit: 20, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.QueryBuilderAlerts, + Active: true, + Usage: 0, + UsageLimit: 10, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelSlack, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelWebhook, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelPagerduty, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelOpsgenie, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelEmail, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelMsTeams, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.UseSpanMetrics, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, +} + +var ProPlan = basemodel.FeatureSet{ + basemodel.Feature{ + Name: SSO, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.OSS, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.SmartTraceDetail, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.CustomMetricsFunction, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.QueryBuilderPanels, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.QueryBuilderAlerts, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelSlack, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelWebhook, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelPagerduty, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelOpsgenie, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelEmail, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelMsTeams, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.UseSpanMetrics, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, +} + +var EnterprisePlan = basemodel.FeatureSet{ + basemodel.Feature{ + Name: SSO, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.OSS, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.SmartTraceDetail, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.CustomMetricsFunction, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.QueryBuilderPanels, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.QueryBuilderAlerts, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelSlack, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelWebhook, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelPagerduty, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelOpsgenie, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelEmail, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.AlertChannelMsTeams, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: basemodel.UseSpanMetrics, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: Onboarding, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, + basemodel.Feature{ + Name: ChatSupport, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, +} diff --git a/ee/query-service/model/sso.go b/ee/query-service/model/sso.go new file mode 100644 index 0000000..8e8e847 --- /dev/null +++ b/ee/query-service/model/sso.go @@ -0,0 +1,68 @@ +package model + +import ( + "fmt" + "context" + "net/url" + "golang.org/x/oauth2" + "github.com/coreos/go-oidc/v3/oidc" + "go.signoz.io/signoz/ee/query-service/sso" +) + +// SamlConfig contans SAML params to generate and respond to the requests +// from SAML provider +type SamlConfig struct { + SamlEntity string `json:"samlEntity"` + SamlIdp string `json:"samlIdp"` + SamlCert string `json:"samlCert"` +} + +// GoogleOauthConfig contains a generic config to support oauth +type GoogleOAuthConfig struct { + ClientID string `json:"clientId"` + ClientSecret string `json:"clientSecret"` + RedirectURI string `json:"redirectURI"` +} + + +const ( + googleIssuerURL = "https://accounts.google.com" +) + +func (g *GoogleOAuthConfig) GetProvider(domain string, siteUrl *url.URL) (sso.OAuthCallbackProvider, error) { + + ctx, cancel := context.WithCancel(context.Background()) + + provider, err := oidc.NewProvider(ctx, googleIssuerURL) + if err != nil { + cancel() + return nil, fmt.Errorf("failed to get provider: %v", err) + } + + // default to email and profile scope as we just use google auth + // to verify identity and start a session. + scopes := []string{"email"} + + // this is the url google will call after login completion + redirectURL := fmt.Sprintf("%s://%s/%s", + siteUrl.Scheme, + siteUrl.Host, + "api/v1/complete/google") + + return &sso.GoogleOAuthProvider{ + RedirectURI: g.RedirectURI, + OAuth2Config: &oauth2.Config{ + ClientID: g.ClientID, + ClientSecret: g.ClientSecret, + Endpoint: provider.Endpoint(), + Scopes: scopes, + RedirectURL: redirectURL, + }, + Verifier: provider.Verifier( + &oidc.Config{ClientID: g.ClientID}, + ), + Cancel: cancel, + HostedDomain: domain, + }, nil +} + diff --git a/ee/query-service/model/trace.go b/ee/query-service/model/trace.go new file mode 100644 index 0000000..708d6d1 --- /dev/null +++ b/ee/query-service/model/trace.go @@ -0,0 +1,22 @@ +package model + +type SpanForTraceDetails struct { + TimeUnixNano uint64 `json:"timestamp"` + SpanID string `json:"spanID"` + TraceID string `json:"traceID"` + ParentID string `json:"parentID"` + ParentSpan *SpanForTraceDetails `json:"parentSpan"` + ServiceName string `json:"serviceName"` + Name string `json:"name"` + Kind int32 `json:"kind"` + DurationNano int64 `json:"durationNano"` + TagMap map[string]string `json:"tagMap"` + Events []string `json:"event"` + HasError bool `json:"hasError"` + Children []*SpanForTraceDetails `json:"children"` +} + +type GetSpansSubQueryDBResponse struct { + SpanID string `ch:"spanID"` + TraceID string `ch:"traceID"` +} diff --git a/ee/query-service/model/usage.go b/ee/query-service/model/usage.go new file mode 100644 index 0000000..3cedb05 --- /dev/null +++ b/ee/query-service/model/usage.go @@ -0,0 +1,34 @@ +package model + +import ( + "time" + + "github.com/google/uuid" +) + +type UsagePayload struct { + InstallationId uuid.UUID `json:"installationId"` + LicenseKey uuid.UUID `json:"licenseKey"` + Usage []Usage `json:"usage"` +} + +type Usage struct { + CollectorID string `json:"collectorId"` + ExporterID string `json:"exporterId"` + Type string `json:"type"` + Tenant string `json:"tenant"` + TimeStamp time.Time `json:"timestamp"` + Count int64 `json:"count"` + Size int64 `json:"size"` + OrgName string `json:"orgName"` + TenantId string `json:"tenantId"` +} + +type UsageDB struct { + CollectorID string `ch:"collector_id" json:"collectorId"` + ExporterID string `ch:"exporter_id" json:"exporterId"` + Type string `ch:"-" json:"type"` + TimeStamp time.Time `ch:"timestamp" json:"timestamp"` + Tenant string `ch:"tenant" json:"tenant"` + Data string `ch:"data" json:"data"` +} diff --git a/ee/query-service/sso/google.go b/ee/query-service/sso/google.go new file mode 100644 index 0000000..a27a38e --- /dev/null +++ b/ee/query-service/sso/google.go @@ -0,0 +1,92 @@ +package sso + +import ( + "fmt" + "errors" + "context" + "net/http" + "github.com/coreos/go-oidc/v3/oidc" + "golang.org/x/oauth2" +) + +type GoogleOAuthProvider struct { + RedirectURI string + OAuth2Config *oauth2.Config + Verifier *oidc.IDTokenVerifier + Cancel context.CancelFunc + HostedDomain string +} + + +func (g *GoogleOAuthProvider) BuildAuthURL(state string) (string, error) { + var opts []oauth2.AuthCodeOption + + // set hosted domain. google supports multiple hosted domains but in our case + // we have one config per host domain. + opts = append(opts, oauth2.SetAuthURLParam("hd", g.HostedDomain)) + + return g.OAuth2Config.AuthCodeURL(state, opts...), nil +} + +type oauth2Error struct{ + error string + errorDescription string +} + +func (e *oauth2Error) Error() string { + if e.errorDescription == "" { + return e.error + } + return e.error + ": " + e.errorDescription +} + +func (g *GoogleOAuthProvider) HandleCallback(r *http.Request) (identity *SSOIdentity, err error) { + q := r.URL.Query() + if errType := q.Get("error"); errType != "" { + return identity, &oauth2Error{errType, q.Get("error_description")} + } + + token, err := g.OAuth2Config.Exchange(r.Context(), q.Get("code")) + if err != nil { + return identity, fmt.Errorf("google: failed to get token: %v", err) + } + + return g.createIdentity(r.Context(), token) +} + + +func (g *GoogleOAuthProvider) createIdentity(ctx context.Context, token *oauth2.Token) (identity *SSOIdentity, err error) { + rawIDToken, ok := token.Extra("id_token").(string) + if !ok { + return identity, errors.New("google: no id_token in token response") + } + idToken, err := g.Verifier.Verify(ctx, rawIDToken) + if err != nil { + return identity, fmt.Errorf("google: failed to verify ID Token: %v", err) + } + + var claims struct { + Username string `json:"name"` + Email string `json:"email"` + EmailVerified bool `json:"email_verified"` + HostedDomain string `json:"hd"` + } + if err := idToken.Claims(&claims); err != nil { + return identity, fmt.Errorf("oidc: failed to decode claims: %v", err) + } + + if claims.HostedDomain != g.HostedDomain { + return identity, fmt.Errorf("oidc: unexpected hd claim %v", claims.HostedDomain) + } + + identity = &SSOIdentity{ + UserID: idToken.Subject, + Username: claims.Username, + Email: claims.Email, + EmailVerified: claims.EmailVerified, + ConnectorData: []byte(token.RefreshToken), + } + + return identity, nil +} + diff --git a/ee/query-service/sso/model.go b/ee/query-service/sso/model.go new file mode 100644 index 0000000..3e5f103 --- /dev/null +++ b/ee/query-service/sso/model.go @@ -0,0 +1,31 @@ +package sso + +import ( + "net/http" +) + +// SSOIdentity contains details of user received from SSO provider +type SSOIdentity struct { + UserID string + Username string + PreferredUsername string + Email string + EmailVerified bool + ConnectorData []byte +} + +// OAuthCallbackProvider is an interface implemented by connectors which use an OAuth +// style redirect flow to determine user information. +type OAuthCallbackProvider interface { + // The initial URL user would be redirect to. + // OAuth2 implementations support various scopes but we only need profile and user as + // the roles are still being managed in SigNoz. + BuildAuthURL(state string) (string, error) + + // Handle the callback to the server (after login at oauth provider site) + // and return a email identity. + // At the moment we dont support auto signup flow (based on domain), so + // the full identity (including name, group etc) is not required outside of the + // connector + HandleCallback(r *http.Request) (identity *SSOIdentity, err error) +} diff --git a/ee/query-service/sso/saml/request.go b/ee/query-service/sso/saml/request.go new file mode 100644 index 0000000..01af7af --- /dev/null +++ b/ee/query-service/sso/saml/request.go @@ -0,0 +1,107 @@ +package saml + +import ( + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + "strings" + + saml2 "github.com/russellhaering/gosaml2" + dsig "github.com/russellhaering/goxmldsig" + "go.signoz.io/signoz/pkg/query-service/constants" + "go.uber.org/zap" +) + +func LoadCertificateStore(certString string) (dsig.X509CertificateStore, error) { + certStore := &dsig.MemoryX509CertificateStore{ + Roots: []*x509.Certificate{}, + } + + certData, err := base64.StdEncoding.DecodeString(certString) + if err != nil { + return certStore, fmt.Errorf(fmt.Sprintf("failed to read certificate: %v", err)) + } + + idpCert, err := x509.ParseCertificate(certData) + if err != nil { + return certStore, fmt.Errorf(fmt.Sprintf("failed to prepare saml request, invalid cert: %s", err.Error())) + } + + certStore.Roots = append(certStore.Roots, idpCert) + + return certStore, nil +} + +func LoadCertFromPem(certString string) (dsig.X509CertificateStore, error) { + certStore := &dsig.MemoryX509CertificateStore{ + Roots: []*x509.Certificate{}, + } + + block, _ := pem.Decode([]byte(certString)) + if block == nil { + return certStore, fmt.Errorf("no valid pem cert found") + } + + idpCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return certStore, fmt.Errorf(fmt.Sprintf("failed to parse pem cert: %s", err.Error())) + } + + certStore.Roots = append(certStore.Roots, idpCert) + + return certStore, nil +} + +// PrepareRequest prepares authorization URL (Idp Provider URL) +func PrepareRequest(issuer, acsUrl, audience, entity, idp, certString string) (*saml2.SAMLServiceProvider, error) { + var certStore dsig.X509CertificateStore + if certString == "" { + return nil, fmt.Errorf("invalid certificate data") + } + + var err error + if strings.Contains(certString, "-----BEGIN CERTIFICATE-----") { + certStore, err = LoadCertFromPem(certString) + } else { + certStore, err = LoadCertificateStore(certString) + } + // certificate store can not be created, throw error + if err != nil { + return nil, err + } + + randomKeyStore := dsig.RandomKeyStoreForTest() + + // SIGNOZ_SAML_RETURN_URL env var would support overriding window.location + // as return destination after saml request is complete from IdP side. + // this var is also useful for development, as it is easy to override with backend endpoint + // e.g. http://localhost:8080/api/v1/complete/saml + acsUrl = constants.GetOrDefaultEnv("SIGNOZ_SAML_RETURN_URL", acsUrl) + + sp := &saml2.SAMLServiceProvider{ + IdentityProviderSSOURL: idp, + IdentityProviderIssuer: entity, + ServiceProviderIssuer: issuer, + AssertionConsumerServiceURL: acsUrl, + SignAuthnRequests: true, + AllowMissingAttributes: true, + + // about cert stores -sender(signoz app) and receiver (idp) + // The random key (random key store) is sender cert. The public cert store(IDPCertificateStore) that you see on org domain is receiver cert (idp provided). + // At the moment, the library we use doesn't bother about sender cert and IdP too. It just adds additional layer of security, which we can explore in future versions + // The receiver (Idp) cert will be different for each org domain. Imagine cloud setup where each company setups their domain that integrates with their Idp. + // @signoz.io + // @next.io + // Each of above will have their own Idp setup and hence separate public cert to decrypt the response. + // The way SAML request travels is - + // SigNoz Backend -> IdP Login Screen -> SigNoz Backend -> SigNoz Frontend + // ---------------- | -------------------| ------------------------------------- + // The dotted lines indicate request boundries. So if you notice, the response from Idp starts a new request. hence we need relay state to pass the context around. + + IDPCertificateStore: certStore, + SPKeyStore: randomKeyStore, + } + zap.S().Debugf("SAML request:", sp) + return sp, nil +} diff --git a/ee/query-service/usage/manager.go b/ee/query-service/usage/manager.go new file mode 100644 index 0000000..99158b4 --- /dev/null +++ b/ee/query-service/usage/manager.go @@ -0,0 +1,209 @@ +package usage + +import ( + "context" + "encoding/json" + "fmt" + "os" + "regexp" + "strings" + "sync/atomic" + "time" + + "github.com/ClickHouse/clickhouse-go/v2" + "github.com/go-co-op/gocron" + "github.com/google/uuid" + + "go.uber.org/zap" + + "go.signoz.io/signoz/ee/query-service/dao" + licenseserver "go.signoz.io/signoz/ee/query-service/integrations/signozio" + "go.signoz.io/signoz/ee/query-service/license" + "go.signoz.io/signoz/ee/query-service/model" + "go.signoz.io/signoz/pkg/query-service/utils/encryption" +) + +const ( + MaxRetries = 3 + RetryInterval = 5 * time.Second + stateUnlocked uint32 = 0 + stateLocked uint32 = 1 +) + +var ( + locker = stateUnlocked +) + +type Manager struct { + clickhouseConn clickhouse.Conn + + licenseRepo *license.Repo + + scheduler *gocron.Scheduler + + modelDao dao.ModelDao + + tenantID string +} + +func New(dbType string, modelDao dao.ModelDao, licenseRepo *license.Repo, clickhouseConn clickhouse.Conn) (*Manager, error) { + hostNameRegex := regexp.MustCompile(`tcp://(?P.*):`) + hostNameRegexMatches := hostNameRegex.FindStringSubmatch(os.Getenv("ClickHouseUrl")) + + tenantID := "" + if len(hostNameRegexMatches) == 2 { + tenantID = hostNameRegexMatches[1] + tenantID = strings.TrimRight(tenantID, "-clickhouse") + } + + m := &Manager{ + // repository: repo, + clickhouseConn: clickhouseConn, + licenseRepo: licenseRepo, + scheduler: gocron.NewScheduler(time.UTC).Every(1).Day().At("00:00"), // send usage every at 00:00 UTC + modelDao: modelDao, + tenantID: tenantID, + } + return m, nil +} + +// start loads collects and exports any exported snapshot and starts the exporter +func (lm *Manager) Start() error { + // compares the locker and stateUnlocked if both are same lock is applied else returns error + if !atomic.CompareAndSwapUint32(&locker, stateUnlocked, stateLocked) { + return fmt.Errorf("usage exporter is locked") + } + + _, err := lm.scheduler.Do(func() { lm.UploadUsage() }) + if err != nil { + return err + } + + // upload usage once when starting the service + lm.UploadUsage() + + lm.scheduler.StartAsync() + + return nil +} +func (lm *Manager) UploadUsage() { + ctx := context.Background() + // check if license is present or not + license, err := lm.licenseRepo.GetActiveLicense(ctx) + if err != nil { + zap.S().Errorf("failed to get active license: %v", zap.Error(err)) + return + } + if license == nil { + // we will not start the usage reporting if license is not present. + zap.S().Info("no license present, skipping usage reporting") + return + } + + usages := []model.UsageDB{} + + // get usage from clickhouse + dbs := []string{"signoz_logs", "signoz_traces", "signoz_metrics"} + query := ` + SELECT tenant, collector_id, exporter_id, timestamp, data + FROM %s.distributed_usage as u1 + GLOBAL INNER JOIN + (SELECT + tenant, collector_id, exporter_id, MAX(timestamp) as ts + FROM %s.distributed_usage as u2 + where timestamp >= $1 + GROUP BY tenant, collector_id, exporter_id + ) as t1 + ON + u1.tenant = t1.tenant AND u1.collector_id = t1.collector_id AND u1.exporter_id = t1.exporter_id and u1.timestamp = t1.ts + order by timestamp + ` + + for _, db := range dbs { + dbusages := []model.UsageDB{} + err := lm.clickhouseConn.Select(ctx, &dbusages, fmt.Sprintf(query, db, db), time.Now().Add(-(24 * time.Hour))) + if err != nil && !strings.Contains(err.Error(), "doesn't exist") { + zap.S().Errorf("failed to get usage from clickhouse: %v", zap.Error(err)) + return + } + for _, u := range dbusages { + u.Type = db + usages = append(usages, u) + } + } + + if len(usages) <= 0 { + zap.S().Info("no snapshots to upload, skipping.") + return + } + + zap.S().Info("uploading usage data") + + orgName := "" + orgNames, orgError := lm.modelDao.GetOrgs(ctx) + if orgError != nil { + zap.S().Errorf("failed to get org data: %v", zap.Error(orgError)) + } + if len(orgNames) == 1 { + orgName = orgNames[0].Name + } + + usagesPayload := []model.Usage{} + for _, usage := range usages { + usageDataBytes, err := encryption.Decrypt([]byte(usage.ExporterID[:32]), []byte(usage.Data)) + if err != nil { + zap.S().Errorf("error while decrypting usage data: %v", zap.Error(err)) + return + } + + usageData := model.Usage{} + err = json.Unmarshal(usageDataBytes, &usageData) + if err != nil { + zap.S().Errorf("error while unmarshalling usage data: %v", zap.Error(err)) + return + } + + usageData.CollectorID = usage.CollectorID + usageData.ExporterID = usage.ExporterID + usageData.Type = usage.Type + usageData.Tenant = usage.Tenant + usageData.OrgName = orgName + usageData.TenantId = lm.tenantID + usagesPayload = append(usagesPayload, usageData) + } + + key, _ := uuid.Parse(license.Key) + payload := model.UsagePayload{ + LicenseKey: key, + Usage: usagesPayload, + } + lm.UploadUsageWithExponentalBackOff(ctx, payload) +} + +func (lm *Manager) UploadUsageWithExponentalBackOff(ctx context.Context, payload model.UsagePayload) { + for i := 1; i <= MaxRetries; i++ { + apiErr := licenseserver.SendUsage(ctx, payload) + if apiErr != nil && i == MaxRetries { + zap.S().Errorf("retries stopped : %v", zap.Error(apiErr)) + // not returning error here since it is captured in the failed count + return + } else if apiErr != nil { + // sleeping for exponential backoff + sleepDuration := RetryInterval * time.Duration(i) + zap.S().Errorf("failed to upload snapshot retrying after %v secs : %v", sleepDuration.Seconds(), zap.Error(apiErr.Err)) + time.Sleep(sleepDuration) + } else { + break + } + } +} + +func (lm *Manager) Stop() { + lm.scheduler.Stop() + + zap.S().Debug("sending usage data before shutting down") + // send usage before shutting down + lm.UploadUsage() + + atomic.StoreUint32(&locker, stateUnlocked) +} diff --git a/frontend/.babelrc b/frontend/.babelrc new file mode 100644 index 0000000..9efe6ca --- /dev/null +++ b/frontend/.babelrc @@ -0,0 +1,16 @@ +{ + "presets": [ + "@babel/preset-env", + ["@babel/preset-react", { "runtime": "automatic" }], + "@babel/preset-typescript" + ], + "plugins": [ + "react-hot-loader/babel", + "@babel/plugin-proposal-class-properties" + ], + "env": { + "production": { + "presets": ["minify"] + } + } +} diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..840adcb --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,3 @@ +node_modules +.vscode +.git diff --git a/frontend/.eslintignore b/frontend/.eslintignore new file mode 100644 index 0000000..402f7ae --- /dev/null +++ b/frontend/.eslintignore @@ -0,0 +1,4 @@ +node_modules +build +*.typegen.ts +i18-generate-hash.js \ No newline at end of file diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js new file mode 100644 index 0000000..5034915 --- /dev/null +++ b/frontend/.eslintrc.js @@ -0,0 +1,121 @@ +module.exports = { + env: { + browser: true, + es2021: true, + node: true, + 'jest/globals': true, + }, + extends: [ + 'airbnb', + 'airbnb-typescript', + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:prettier/recommended', + 'plugin:sonarjs/recommended', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:react/jsx-runtime', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 12, + sourceType: 'module', + }, + plugins: [ + 'react', + '@typescript-eslint', + 'simple-import-sort', + 'react-hooks', + 'prettier', + 'jest', + ], + settings: { + react: { + version: 'detect', + }, + 'import/resolver': { + node: { + paths: ['src'], + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + }, + }, + rules: { + 'react/jsx-filename-extension': [ + 'error', + { + extensions: ['.tsx', '.js', '.jsx'], + }, + ], + 'react/prop-types': 'off', + '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/no-var-requires': 'error', + 'react/no-array-index-key': 'error', + 'linebreak-style': [ + 'error', + process.env.platform === 'win32' ? 'windows' : 'unix', + ], + '@typescript-eslint/default-param-last': 'off', + + // simple sort error + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', + + // hooks + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'error', + + // airbnb + 'no-underscore-dangle': 'off', + 'no-console': 'off', + 'import/prefer-default-export': 'off', + 'import/extensions': [ + 'error', + 'ignorePackages', + { + js: 'never', + jsx: 'never', + ts: 'never', + tsx: 'never', + }, + ], + 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], + 'no-plusplus': 'off', + 'jsx-a11y/label-has-associated-control': [ + 'error', + { + required: { + some: ['nesting', 'id'], + }, + }, + ], + 'jsx-a11y/label-has-for': [ + 'error', + { + required: { + some: ['nesting', 'id'], + }, + }, + ], + '@typescript-eslint/no-unused-vars': 'error', + 'func-style': ['error', 'declaration', { allowArrowFunctions: true }], + 'arrow-body-style': ['error', 'as-needed'], + + // eslint rules need to remove + '@typescript-eslint/no-shadow': 'off', + 'import/no-cycle': 'off', + 'prettier/prettier': [ + 'error', + {}, + { + usePrettierrc: true, + }, + ], + }, +}; diff --git a/frontend/.husky/commit-msg b/frontend/.husky/commit-msg new file mode 100755 index 0000000..cb50d87 --- /dev/null +++ b/frontend/.husky/commit-msg @@ -0,0 +1,20 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +cd frontend && yarn run commitlint --edit $1 + +branch="$(git rev-parse --abbrev-ref HEAD)" + +color_red="$(tput setaf 1)" +bold="$(tput bold)" +reset="$(tput sgr0)" + +if [ "$branch" = "main" ]; then + echo "${color_red}${bold}You can't commit directly to the main branch${reset}" + exit 1 +fi + +if [ "$branch" = "develop" ]; then + echo "${color_red}${bold}You can't commit directly to the develop branch${reset}" + exit 1 +fi \ No newline at end of file diff --git a/frontend/.husky/pre-commit b/frontend/.husky/pre-commit new file mode 100755 index 0000000..d801fab --- /dev/null +++ b/frontend/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +cd frontend && yarn lint-staged diff --git a/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 0000000..31dfb02 --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1 @@ +registry = 'https://registry.npmjs.org/' \ No newline at end of file diff --git a/frontend/.nvmrc b/frontend/.nvmrc new file mode 100644 index 0000000..c818c7b --- /dev/null +++ b/frontend/.nvmrc @@ -0,0 +1 @@ +16.15.0 \ No newline at end of file diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 0000000..69797b1 --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,6 @@ +# Ignore artifacts: +build +coverage + +# Ignore all MD files: +**/*.md \ No newline at end of file diff --git a/frontend/.prettierrc.json b/frontend/.prettierrc.json new file mode 100644 index 0000000..a147736 --- /dev/null +++ b/frontend/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "trailingComma": "all", + "useTabs": true, + "tabWidth": 1, + "singleQuote": true, + "jsxSingleQuote": false, + "semi": true +} diff --git a/frontend/.yarnrc b/frontend/.yarnrc new file mode 100644 index 0000000..843c88f --- /dev/null +++ b/frontend/.yarnrc @@ -0,0 +1,2 @@ +network-timeout 600000 +save-prefix "" diff --git a/frontend/CONTRIBUTIONS.md b/frontend/CONTRIBUTIONS.md new file mode 100644 index 0000000..0b3d762 --- /dev/null +++ b/frontend/CONTRIBUTIONS.md @@ -0,0 +1,56 @@ +# **Frontend Guidelines** + +Embrace the spirit of collaboration and contribute to the success of our open-source project by adhering to these frontend development guidelines with precision and passion. + +### React and Components + +- Strive to create small and modular components, ensuring they are divided into individual pieces for improved maintainability and reusability. +- Avoid passing inline objects or functions as props to React components, as they are recreated with each render cycle. + Utilize careful memoization of functions and variables, balancing optimization efforts to prevent potential performance issues. [When to useMemo and useCallback](https://kentcdodds.com/blog/usememo-and-usecallback) by Kent C. Dodds is quite helpful for this scenario. +- Minimize the use of inline functions whenever possible to enhance code readability and improve overall comprehension. +- Employ the appropriate usage of useMemo and useCallback hooks for effective memoization of values and functions. +- Determine the appropriate placement of components: + - Pages should contain an aggregation of all components and containers. + - Commonly used components should reside in the 'components' directory. + - Parent components responsible for data manipulation should be placed in the 'container' directory. +- Strategically decide where to store data, either in global state or local components: + - Begin by storing data in local components and gradually transition to global state as necessary. +- Avoid importing default namespace `React` as the project is using `v18` and `import React from 'react'` is not needed anymore. +- When a function requires more than three arguments (except when memoized), encapsulate them within an object to enhance readability and reduce potential parameter complexity. + +### API and Services + +- Avoid incorporating business logic within API/Service files to maintain flexibility for consumers to handle it according to their specific needs. +- Employ the use of the useQuery hook for fetching data and the useMutation hook for updating data, ensuring a consistent and efficient approach. +- Utilize the useQueryClient hook when updating the cache, facilitating smooth and effective management of data within the application. + +**Note -** In our project, we are utilizing React Query v3. To gain a comprehensive understanding of its features and implementation, we recommend referring to the [official documentation](https://tanstack.com/query/v3/docs/react/overview) as a valuable resource. + +### Styling + +- Refrain from using inline styling within React components to maintain separation of concerns and promote a more maintainable codebase. +- Opt for using the rem unit instead of px values to ensure better scalability and responsiveness across different devices and screen sizes. + +### Linting and Setup + +- It is crucial to refrain from disabling ESLint and TypeScript errors within the project. If there is a specific rule that needs to be disabled, provide a clear and justified explanation for doing so. Maintaining the integrity of the linting and type-checking processes ensures code quality and consistency throughout the codebase. +- In our project, we rely on several essential ESLint plugins, namely: + - [plugin:@typescript-eslint](https://typescript-eslint.io/rules/) + - [airbnb styleguide](https://github.com/airbnb/javascript) + - [plugin:sonarjs](https://github.com/SonarSource/eslint-plugin-sonarjs) + + To ensure compliance with our coding standards and best practices, we encourage you to refer to the documentation of these plugins. Familiarizing yourself with the ESLint rules they provide will help maintain code quality and consistency throughout the project. + +### Naming Conventions + +- Ensure that component names are written in Capital Case, while the folder names should be in lowercase. +- Keep all other elements, such as variables, functions, and file names, in lowercase. + +### Miscellaneous + +- Ensure that functions are modularized and follow the Single Responsibility Principle (SRP). The function's name should accurately convey its purpose and functionality. +- Semantic division of functions into smaller units should be prioritized for improved readability and maintainability. + Aim to keep functions concise and avoid exceeding a maximum length of 40 lines to enhance code understandability and ease of maintenance. +- Eliminate the use of hard-coded strings or enums, favoring a more flexible and maintainable approach. +- Strive to internationalize all strings within the codebase to support localization and improve accessibility for users across different languages. +- Minimize the usage of multiple if statements or switch cases within a function. Consider creating a mapper and separating logic into multiple functions for better code organization. diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..ddbf9ed --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,18 @@ +FROM nginx:1.25.2-alpine + +# Add Maintainer Info +LABEL maintainer="signoz" + +# Set working directory +WORKDIR /frontend + +# Remove default nginx index page +RUN rm -rf /usr/share/nginx/html/* + +# Copy custom nginx config and static files +COPY conf/default.conf /etc/nginx/conf.d/default.conf +COPY build /usr/share/nginx/html + +EXPOSE 3301 + +ENTRYPOINT ["nginx", "-g", "daemon off;"] diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..99a3671 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,105 @@ +# Configuring Over Local +1. Docker +1. Without Docker + +## With Docker + +**Building image** + +``docker-compose up` +/ This will also run + +or +`docker build . -t tagname` + +**Tag to remote url- Introduce versinoing later on** + +``` +docker tag signoz/frontend:latest 7296823551/signoz:latest +``` + +``` +docker-compose up +``` + +## Without Docker +Follow the steps below + +1. ```git clone https://github.com/SigNoz/signoz.git && cd signoz/frontend``` +1. change baseURL to `````` in file ```src/constants/env.ts``` + +1. ```yarn install``` +1. ```yarn dev``` + +```Note: Please ping us in #contributing channel in our slack community and we will DM you with ``` + +# Getting Started with Create React App + +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `yarn start` + +Runs the app in the development mode.\ +Open [http://localhost:3301](http://localhost:3301) to view it in the browser. + +The page will reload if you make edits.\ +You will also see any lint errors in the console. + +### `yarn test` + +Launches the test runner in the interactive watch mode.\ +See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `yarn build` + +Builds the app for production to the `build` folder.\ +It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.\ +Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + +### `yarn eject` + +**Note: this is a one-way operation. Once you `eject`, you can’t go back!** + +If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. + +You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. + +## Learn More + +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + +To learn React, check out the [React documentation](https://reactjs.org/). + +### Code Splitting + +This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) + +### Analyzing the Bundle Size + +This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) + +### Making a Progressive Web App + +This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) + +### Advanced Configuration + +This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) + +### Deployment + +This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) + +### `yarn build` fails to minify + +This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) diff --git a/frontend/__mocks__/cssMock.ts b/frontend/__mocks__/cssMock.ts new file mode 100644 index 0000000..ff8b4c5 --- /dev/null +++ b/frontend/__mocks__/cssMock.ts @@ -0,0 +1 @@ +export default {}; diff --git a/frontend/babel.config.js b/frontend/babel.config.js new file mode 100644 index 0000000..b3df57d --- /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/bundlesize.config.json b/frontend/bundlesize.config.json new file mode 100644 index 0000000..1467ff0 --- /dev/null +++ b/frontend/bundlesize.config.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "path": "./build/**.js", + "maxSize": "1.2MB" + } + ] +} diff --git a/frontend/commitlint.config.ts b/frontend/commitlint.config.ts new file mode 100644 index 0000000..3f5e287 --- /dev/null +++ b/frontend/commitlint.config.ts @@ -0,0 +1 @@ +export default { extends: ['@commitlint/config-conventional'] }; diff --git a/frontend/conf/default.conf b/frontend/conf/default.conf new file mode 100644 index 0000000..8c1eafe --- /dev/null +++ b/frontend/conf/default.conf @@ -0,0 +1,33 @@ +server { + listen 3301; + server_name _; + + gzip on; + gzip_static on; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_proxied any; + gzip_vary on; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + + # to handle uri issue 414 from nginx + client_max_body_size 24M; + large_client_header_buffers 8 128k; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + location = /api { + proxy_pass http://signoz-query-service:8080/api; + } + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file diff --git a/frontend/docker-compose.yml b/frontend/docker-compose.yml new file mode 100644 index 0000000..8bc085d --- /dev/null +++ b/frontend/docker-compose.yml @@ -0,0 +1,7 @@ +version: "3.9" +services: + web: + build: . + image: signoz/frontend:latest + ports: + - "3301:3301" diff --git a/frontend/example.env b/frontend/example.env new file mode 100644 index 0000000..0ddbfb2 --- /dev/null +++ b/frontend/example.env @@ -0,0 +1,7 @@ +NODE_ENV="development" +BUNDLE_ANALYSER="true" +FRONTEND_API_ENDPOINT="http://localhost:3301/" +INTERCOM_APP_ID="intercom-app-id" + +PLAYWRIGHT_TEST_BASE_URL="http://localhost:3301" +CI="1" \ No newline at end of file diff --git a/frontend/i18-generate-hash.js b/frontend/i18-generate-hash.js new file mode 100644 index 0000000..cbc03f9 --- /dev/null +++ b/frontend/i18-generate-hash.js @@ -0,0 +1,20 @@ +const crypto = require('crypto'); +const fs = require('fs'); +const glob = require('glob'); + +function generateChecksum(str, algorithm, encoding) { + return crypto + .createHash(algorithm || 'md5') + .update(str, 'utf8') + .digest(encoding || 'hex'); +} + +const result = {}; + +glob.sync(`public/locales/**/*.json`).forEach((path) => { + const [_, lang] = path.split('public/locales'); + const content = fs.readFileSync(path, { encoding: 'utf-8' }); + result[lang.replace('.json', '')] = generateChecksum(content); +}); + +fs.writeFileSync('./i18n-translations-hash.json', JSON.stringify(result)); diff --git a/frontend/jest.config.ts b/frontend/jest.config.ts new file mode 100644 index 0000000..7b52ca5 --- /dev/null +++ b/frontend/jest.config.ts @@ -0,0 +1,40 @@ +import type { Config } from '@jest/types'; + +const config: Config.InitialOptions = { + clearMocks: true, + coverageDirectory: 'coverage', + coverageReporters: ['text', 'cobertura', 'html', 'json-summary'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], + modulePathIgnorePatterns: ['dist'], + moduleNameMapper: { + '\\.(css|less|scss)$': '/__mocks__/cssMock.ts', + }, + 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', + '^.+\\.(css|scss|sass|less)$': 'jest-preview/transforms/css', + '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)': 'jest-preview/transforms/file', + }, + transformIgnorePatterns: [ + 'node_modules/(?!(lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@signozhq/design-tokens|d3-interpolate|d3-color)/)', + ], + setupFilesAfterEnv: ['jest.setup.ts'], + testPathIgnorePatterns: ['/node_modules/', '/public/'], + moduleDirectories: ['node_modules', 'src'], + testEnvironment: 'jest-environment-jsdom', + testEnvironmentOptions: { + 'jest-playwright': { + browsers: ['chromium', 'firefox', 'webkit'], + }, + }, +}; + +export default config; diff --git a/frontend/jest.setup.ts b/frontend/jest.setup.ts new file mode 100644 index 0000000..4c3aad2 --- /dev/null +++ b/frontend/jest.setup.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +/* eslint-disable object-shorthand */ +/* eslint-disable func-names */ + +/** + * Adds custom matchers from the react testing library to all tests + */ +import '@testing-library/jest-dom'; +import 'jest-styled-components'; +import './src/styles.scss'; + +import { server } from './src/mocks-server/server'; +// Establish API mocking before all tests. + +// Mock window.matchMedia +window.matchMedia = + window.matchMedia || + function (): any { + return { + matches: false, + addListener: function () {}, + removeListener: function () {}, + }; + }; + +beforeAll(() => server.listen()); + +afterEach(() => server.resetHandlers()); + +afterAll(() => server.close()); diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..e7d1861 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,236 @@ +{ + "name": "frontend", + "version": "1.0.0", + "description": "", + "main": "webpack.config.js", + "scripts": { + "i18n:generate-hash": "node ./i18-generate-hash.js", + "dev": "npm run i18n:generate-hash && cross-env NODE_ENV=development webpack serve --progress", + "build": "npm run i18n:generate-hash && webpack --config=webpack.config.prod.js --progress", + "prettify": "prettier --write .", + "lint": "npm run i18n:generate-hash && eslint ./src", + "lint:fix": "npm run i18n:generate-hash && eslint ./src --fix", + "jest": "jest", + "jest:coverage": "jest --coverage", + "jest:watch": "jest --watch", + "jest-preview": "jest-preview", + "test:debug": "npm-run-all -p test jest-preview", + "postinstall": "is-ci || yarn husky:configure", + "playwright": "npm run i18n:generate-hash && NODE_ENV=testing playwright test --config=./playwright.config.ts", + "playwright:local:debug": "PWDEBUG=console yarn playwright --headed --browser=chromium", + "playwright:codegen:local": "playwright codegen http://localhost:3301", + "playwright:codegen:local:auth": "yarn playwright:codegen:local --load-storage=tests/auth.json", + "husky:configure": "cd .. && husky install frontend/.husky && cd frontend && chmod ug+x .husky/*", + "commitlint": "commitlint --edit $1" + }, + "engines": { + "node": ">=16.15.0" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@ant-design/colors": "6.0.0", + "@ant-design/icons": "4.8.0", + "@dnd-kit/core": "6.1.0", + "@dnd-kit/modifiers": "7.0.0", + "@dnd-kit/sortable": "8.0.0", + "@grafana/data": "^9.5.2", + "@mdx-js/loader": "2.3.0", + "@mdx-js/react": "2.3.0", + "@monaco-editor/react": "^4.3.1", + "@radix-ui/react-tabs": "1.0.4", + "@radix-ui/react-tooltip": "1.0.7", + "@sentry/react": "7.102.1", + "@sentry/webpack-plugin": "2.14.2", + "@signozhq/design-tokens": "0.0.8", + "@uiw/react-md-editor": "3.23.5", + "@xstate/react": "^3.0.0", + "ansi-to-html": "0.7.2", + "antd": "5.11.0", + "antd-table-saveas-excel": "2.2.1", + "axios": "1.6.2", + "babel-eslint": "^10.1.0", + "babel-jest": "^29.6.4", + "babel-loader": "9.1.3", + "babel-plugin-named-asset-import": "^0.3.7", + "babel-preset-minify": "^0.5.1", + "babel-preset-react-app": "^10.0.1", + "chart.js": "3.9.1", + "chartjs-adapter-date-fns": "^2.0.0", + "chartjs-plugin-annotation": "^1.4.0", + "classnames": "2.3.2", + "color": "^4.2.1", + "color-alpha": "1.1.3", + "cross-env": "^7.0.3", + "css-loader": "5.0.0", + "css-minimizer-webpack-plugin": "5.0.1", + "dayjs": "^1.10.7", + "dompurify": "3.0.0", + "dotenv": "8.2.0", + "event-source-polyfill": "1.0.31", + "eventemitter3": "5.0.1", + "file-loader": "6.1.1", + "fontfaceobserver": "2.3.0", + "history": "4.10.1", + "html-webpack-plugin": "5.5.0", + "http-proxy-middleware": "2.0.6", + "i18next": "^21.6.12", + "i18next-browser-languagedetector": "^6.1.3", + "i18next-http-backend": "^1.3.2", + "jest": "^27.5.1", + "js-base64": "^3.7.2", + "less": "^4.1.2", + "less-loader": "^10.2.0", + "lodash-es": "^4.17.21", + "lucide-react": "0.321.0", + "mini-css-extract-plugin": "2.4.5", + "papaparse": "5.4.1", + "react": "18.2.0", + "react-addons-update": "15.6.3", + "react-beautiful-dnd": "13.1.1", + "react-dnd": "16.0.1", + "react-dnd-html5-backend": "16.0.1", + "react-dom": "18.2.0", + "react-drag-listview": "2.0.0", + "react-error-boundary": "4.0.11", + "react-force-graph": "^1.43.0", + "react-full-screen": "1.1.1", + "react-grid-layout": "^1.3.4", + "react-helmet-async": "1.3.0", + "react-i18next": "^11.16.1", + "react-markdown": "8.0.7", + "react-query": "3.39.3", + "react-redux": "^7.2.2", + "react-router-dom": "^5.2.0", + "react-syntax-highlighter": "15.5.0", + "react-use": "^17.3.2", + "react-virtuoso": "4.0.3", + "redux": "^4.0.5", + "redux-thunk": "^2.3.0", + "rehype-raw": "7.0.0", + "stream": "^0.0.2", + "style-loader": "1.3.0", + "styled-components": "^5.3.11", + "terser-webpack-plugin": "^5.2.5", + "timestamp-nano": "^1.0.0", + "ts-node": "^10.2.1", + "tsconfig-paths-webpack-plugin": "^3.5.1", + "typescript": "^4.0.5", + "uplot": "1.6.26", + "uuid": "^8.3.2", + "web-vitals": "^0.2.4", + "webpack": "5.88.2", + "webpack-dev-server": "^4.15.1", + "xstate": "^4.31.0" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@babel/core": "^7.22.11", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/preset-env": "^7.22.14", + "@babel/preset-react": "^7.12.13", + "@babel/preset-typescript": "^7.21.4", + "@commitlint/cli": "^16.3.0", + "@commitlint/config-conventional": "^16.2.4", + "@jest/globals": "^27.5.1", + "@playwright/test": "^1.22.0", + "@testing-library/jest-dom": "5.16.5", + "@testing-library/react": "13.4.0", + "@testing-library/user-event": "14.4.3", + "@types/color": "^3.0.3", + "@types/compression-webpack-plugin": "^9.0.0", + "@types/copy-webpack-plugin": "^8.0.1", + "@types/dompurify": "^2.4.0", + "@types/event-source-polyfill": "^1.0.0", + "@types/fontfaceobserver": "2.1.0", + "@types/jest": "^27.5.1", + "@types/lodash-es": "^4.17.4", + "@types/mini-css-extract-plugin": "^2.5.1", + "@types/node": "^16.10.3", + "@types/papaparse": "5.3.7", + "@types/react": "18.0.26", + "@types/react-addons-update": "0.14.21", + "@types/react-beautiful-dnd": "13.1.8", + "@types/react-dom": "18.0.10", + "@types/react-grid-layout": "^1.1.2", + "@types/react-helmet-async": "1.0.3", + "@types/react-redux": "^7.1.11", + "@types/react-resizable": "3.0.3", + "@types/react-router-dom": "^5.1.6", + "@types/react-syntax-highlighter": "15.5.7", + "@types/redux-mock-store": "1.0.4", + "@types/styled-components": "^5.1.4", + "@types/uuid": "^8.3.1", + "@types/webpack": "^5.28.0", + "@types/webpack-dev-server": "^4.7.2", + "@typescript-eslint/eslint-plugin": "^4.33.0", + "@typescript-eslint/parser": "^4.33.0", + "autoprefixer": "^9.0.0", + "babel-plugin-styled-components": "^1.12.0", + "compression-webpack-plugin": "9.0.0", + "copy-webpack-plugin": "^8.1.0", + "critters-webpack-plugin": "^3.0.1", + "eslint": "^7.32.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-typescript": "^16.1.4", + "eslint-config-prettier": "^8.3.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jest": "^26.9.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-promise": "^5.1.0", + "eslint-plugin-react": "^7.24.0", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-simple-import-sort": "^7.0.0", + "eslint-plugin-sonarjs": "^0.12.0", + "husky": "^7.0.4", + "is-ci": "^3.0.1", + "jest-playwright-preset": "^1.7.2", + "jest-preview": "0.3.1", + "jest-styled-components": "^7.0.8", + "lint-staged": "^12.5.0", + "msw": "1.3.2", + "npm-run-all": "latest", + "portfinder-sync": "^0.0.2", + "prettier": "2.2.1", + "raw-loader": "4.0.2", + "react-hooks-testing-library": "0.6.0", + "react-hot-loader": "^4.13.0", + "react-resizable": "3.0.4", + "redux-mock-store": "1.5.4", + "sass": "1.66.1", + "sass-loader": "13.3.2", + "ts-jest": "^27.1.5", + "ts-node": "^10.2.1", + "typescript-plugin-css-modules": "5.0.1", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-cli": "^4.9.2" + }, + "lint-staged": { + "*.(js|jsx|ts|tsx)": [ + "eslint --fix", + "sh scripts/typecheck-staged.sh" + ] + }, + "resolutions": { + "@types/react": "18.0.26", + "@types/react-dom": "18.0.10", + "debug": "4.3.4", + "semver": "7.5.4", + "xml2js": "0.5.0" + } +} diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts new file mode 100644 index 0000000..0b24052 --- /dev/null +++ b/frontend/playwright.config.ts @@ -0,0 +1,23 @@ +import { PlaywrightTestConfig } from '@playwright/test'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const config: PlaywrightTestConfig = { + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + preserveOutput: 'always', + name: 'Signoz', + testDir: './tests', + use: { + trace: 'retain-on-failure', + baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || 'http://localhost:3301', + }, + updateSnapshots: 'all', + fullyParallel: !!process.env.CI, + quiet: false, + testMatch: ['**/*.spec.ts'], + reporter: process.env.CI ? 'github' : 'list', +}; + +export default config; diff --git a/frontend/public/Icons/awwSnap.svg b/frontend/public/Icons/awwSnap.svg new file mode 100644 index 0000000..19088a1 --- /dev/null +++ b/frontend/public/Icons/awwSnap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Icons/emptyState.svg b/frontend/public/Icons/emptyState.svg new file mode 100644 index 0000000..b00fbb6 --- /dev/null +++ b/frontend/public/Icons/emptyState.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Icons/loading-plane.gif b/frontend/public/Icons/loading-plane.gif new file mode 100644 index 0000000000000000000000000000000000000000..9d5817746be3a7ca885a3742c591244545621dc7 GIT binary patch literal 89667 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%0DT6l}mp9J^Wsn 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 \ No newline at end of file diff --git a/frontend/public/Icons/redis-logo.svg b/frontend/public/Icons/redis-logo.svg new file mode 100644 index 0000000..424f1e5 --- /dev/null +++ b/frontend/public/Icons/redis-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Icons/tetra-pack.svg b/frontend/public/Icons/tetra-pack.svg new file mode 100644 index 0000000..13fd4f2 --- /dev/null +++ b/frontend/public/Icons/tetra-pack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Images/eyesEmoji.svg b/frontend/public/Images/eyesEmoji.svg new file mode 100644 index 0000000..5d7e56a --- /dev/null +++ b/frontend/public/Images/eyesEmoji.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/Images/notFound404.png b/frontend/public/Images/notFound404.png new file mode 100644 index 0000000000000000000000000000000000000000..f80372413856425983d078ae25e1aac63272e8ad GIT binary patch literal 43988 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 literal 0 HcmV?d00001 diff --git a/frontend/public/Logos/cloudwatch.png b/frontend/public/Logos/cloudwatch.png new file mode 100644 index 0000000000000000000000000000000000000000..57cd671f42cbfcfebb76d5fefd96e27e963c0b36 GIT binary patch literal 52632 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

    yuFtSnpO6i|ME}3*n4r8Gn-2;^rr7;RCq&& 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 PKq#|~TYt \ No newline at end of file diff --git a/frontend/public/Logos/docker.svg b/frontend/public/Logos/docker.svg new file mode 100644 index 0000000..ff2b2b4 --- /dev/null +++ b/frontend/public/Logos/docker.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Logos/dotnet.png b/frontend/public/Logos/dotnet.png new file mode 100644 index 0000000000000000000000000000000000000000..53c3da3c7a9deebf92cc5ffebfd8b7460c252c07 GIT binary patch literal 3778 zcmeHK>049B8b65vSp*cX!eui`)Js|P(h5|r<_MMq*+kkPVG$IehE28xkS(QPDO*Hj z6_lbWlqAS*!V-{HK$IoQ4nctsAS^`*LPFpU_HVfNlON_e^UgEp%=`Y9IWx%@?Qu#9 z8VV2uDOuysUxFY+$4--z1tTwtqvpXw{wCff6oM4@?=%FIe_tI8B0?|WEFfmL)*J{> zzUS=DK~QCe;-;4j1SvaPpFj6)1Y&Xg3VkqKyL~0&m{O}{RfS<#^s4*w8|QxG_HGXyd(42!XDxL;4=er>1}dXHJ0yGTK%1cA}B*Rrld~_);hpBXYOo1r* zN}wL}+8*sv8TE!8`q2%M>x1lsm9@mb4C6h2m&fe-x2C+UImO!(ymMpbES_+^nEXb3 zcbVg!YcNnZ})`r0NRYf2kV;^sCBw!f&h zMZ4S+D;0NJ6-93?Cw4kcnFXj_-JWlc(<@n9U13ObhKOcT?k)ZwePo4_sh{tC_a#_a zm*U<3(hB(+kG}EDA9FGQmFZKoaORu*G!!in=Gvx}fGmk}7&}ne&H#C7LBzdKn1;#? zLt0O)=;qkyhns|ON(i3MeVp?Z1PPpkYa??=9 z3Sn-2Y+Dmw#1l{ru_^TGI}K}r2GCtRdamIVXT9KoyMdv$S>rIs}z#Jq_rT3isO<CLOW@xm@0l z&;Apn=?9ciUVX=KuovLw4W=r1tJdwi`mI1snh_9@WZ5V3U?>eQ;Rr}Yc3v(8bv& zHXsPc&L)>ErW1w!)66DK#TGOwvohrUhQ6Jzu8J1V-$YgYXV{o9KT7`_64}Gl)WQM3 zS>X4Cz4+&s08t?(?~{FXwq804CC;N#DP>NVn)-ME$N1nn5Iw`|$VSN0GGODnzuBe# z1RDz5V{vlYjo;dMkeJ2-P+fC_hALyqV`HG;iuLL#R@`uhtZ$(N8Qa z%t|3Vn13mQ=)hU^xbfI%@25C)q2qec17zWT{4x8qYhA%P0YhqKbNMXJFzZ3ZsS%}6ck=Lgx{R!ii= z#=QuN8%^VRl;$5r?VPdg537U1A$WtJ*KlZbeMPyiB!$1)wa9xqT|GY-0kp>pA`Bdi z&(^9mvW&T~DN4B_Syt}h$8^rGx7_87T{@^7U=&u~+^^7^f4rliG&owkUPlq|=Py)? z!$POq<3y1zxIhL#GO@!TK$_HJH$9#!8l*~DK=r&<(o}@anJ9PgQ`(&QO-*rp_E7Cv zF}%zf7ATVI7d8F5LMvjMZ1cqvEdFHF)BQL)`64;IZltE_*0Pm@nKlrsGK|i-NDdpw z2_UkiIu;}4IEJsiyCGyKH?6w1`L>lyOq*i1H(#7hKNSY+BUwM;bPDy|*J-1=r~408 zR3<5Z52F)|D&P@unnZED3dH9?j6ELK#J&0ER2Wo#nA$2ujM9k23Ut*|lj!!f^eouu z-Kx#_*t3-vip~mc-woac`y1f`$R5ifN$qN_1_cFeg3<%(Kfy-DBGZ0xRY&rK;X$hs z=0jjPph(_1DQ)EHw-2A!d64;Y!8P-rc5oKzy%FJ7lYLA`G&nYVy`5C=jC-YV+Vkkx zZq}YtnJH>PSyJps&94$!p=MUkS`m>Q9TLs|==_2$o1)}%^liA(_?adw)30&QRrQ?p zB6CTs9?n;i+q`{B{hH`Z!l0%$KSRP18ax%;GVdNYFc<~?>teQ>7_q_{b>0<5E|R_$ zy~pNDEfAiwfeO>{13lxeb%7iXr@od+D|bi^Z-t#d+=ce^+u`WDe#eoD_9AFGIbqP} rQ}*Xe#{cZfwIq~2b%EkX$2V1=eg~Yh;>Vp!DP;YP{duN^=a2sd+u^2@ literal 0 HcmV?d00001 diff --git a/frontend/public/Logos/ec2.svg b/frontend/public/Logos/ec2.svg new file mode 100644 index 0000000..14f083f --- /dev/null +++ b/frontend/public/Logos/ec2.svg @@ -0,0 +1,18 @@ + + + + Icon-Architecture/64/Arch_Amazon-EC2_64 + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/Logos/ecs.svg b/frontend/public/Logos/ecs.svg new file mode 100644 index 0000000..c2ef4c2 --- /dev/null +++ b/frontend/public/Logos/ecs.svg @@ -0,0 +1,18 @@ + + + + Icon-Architecture/64/Arch_Amazon-Elastic-Container-Service_64 + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/Logos/eks.svg b/frontend/public/Logos/eks.svg new file mode 100644 index 0000000..b4a9336 --- /dev/null +++ b/frontend/public/Logos/eks.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/frontend/public/Logos/elixir.png b/frontend/public/Logos/elixir.png new file mode 100644 index 0000000000000000000000000000000000000000..909a736e0f07d0b771dd63c713e3947e8570b7fc GIT binary patch literal 4523 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 JPDHLkV1hZ3oKOG& literal 0 HcmV?d00001 diff --git a/frontend/public/Logos/fluent-bit.png b/frontend/public/Logos/fluent-bit.png new file mode 100644 index 0000000000000000000000000000000000000000..55c4e4ae4aec5dd05ac501001cd7f533bbb6c1fe GIT binary patch literal 49652 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 JU*}Nh{{v;g1399lyf@Do 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 fg!ku{4F~2mYQW| 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 literal 0 HcmV?d00001 diff --git a/frontend/public/Logos/java.png b/frontend/public/Logos/java.png new file mode 100644 index 0000000000000000000000000000000000000000..9e48da3616bb2283a1bdaddff6e0dca43300fa1c GIT binary patch literal 3259 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 \ No newline at end of file diff --git a/frontend/public/Logos/logstash.svg b/frontend/public/Logos/logstash.svg new file mode 100644 index 0000000..cf718cd --- /dev/null +++ b/frontend/public/Logos/logstash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Logos/ms-net-framework.png b/frontend/public/Logos/ms-net-framework.png new file mode 100644 index 0000000000000000000000000000000000000000..5b0baac0dee1486755120d26aebeeca49ebf648c GIT binary patch literal 4252 zcmV;N5M%F&P)ZYnoez-iIcSH*m06+r+>6F zwBvLdYFdp+EE=7}OUor7RwGc6a0ii#tRNS6S(d$>v*(=e(%&A~<9`UQZCy!8tJ7kTR!z`0(CV|!hwtq2Q<^}0}n zaM8PfckWk={ny}vZ08ifg}U+1)*l6MfwbQ=S`~o-zZ=bT`D)*!P`m}ioWMkgBqT#$g%e!?*rZkd@7%zT*m0;M9fImpX@=lp`$o5FZ;{?xN8-B zfomE(wi4x6l<>ZwecZ^^&sYUxg>qV%r#IdQ3&_LxX*~ScmoR+%KyK54ABQ?vhMT|f zo*B6HzFE47TkosItXrn=oj|4UhV(QO=;B>ki-Q8ZNEXTNPJznFv!4}W*KUCmQfJ&)-s_;2 zLl|zt2~+xxXwjpVyr7)dU>X%&fot@_3V4OI`gFj4Ilm1ZsdjFKW0*8@lw9bYq^1KM zGe?14b!|0PzPJYa-aLY_qP(95xWm`3!3AxfY|w zT@KGFK~qZ$szy}7q-Zf6G1^bH)9M^`+sIhlD&7T*r{LwrKHh#~2X0(2i(23SB@QM{ z8ISk29E2&P=q4dX&KgRe9Ef~r4&V95Z{WSUJ$UNhp26YW$55FXgxl}91tne?-dM8@ z^B2y=jG8g{(NnKr&GL=-%g4V0Z&Zw19;iV>TO*!a`eS73^Qam!4Bz^P2eACLKXDH<}MMW8R`0@X`-gBR-r%>-H?R?06UBCRQWUl0)_6E8z}u z(D-2!rp}#+cQ$Op$?dI(PlQ;zY(2)`I2K_;3pz3dv>eSKSKo$~hBg$Mb2xRV4fO{a zP`BlM9NJQc<|8c%oQgH7eWz~%aOkfBQWPuPg2DE8cH`>VqcNlQT5Nc2BVwOsM)Y?n zbvN+&8K7#Ahqjgs1yg}ADpTO(AgYx(m!q|0;L?J!d3vPNfhfu$+Z@1m(|GND;6xSK@0sXbG*ZbMvmp4V+;)w3jr& z9cYmrARrkZAuZ~?ZF@1T_A2Dk#QjTROqe+=jpZEzT%=|nK?l_9+ zGbbQZ(S%d!ET-Lb4R&nUV=YcJ>%81DREvzsVdYC}(6ILu7JPXg1v3u$TprIp@hTekH)2i2 z2K>#xJgSjr|MvIsv;X{WTBbo`T{FBqg-YSp6Pzncs_@9S9>OzAmT_Hy3PyNAZ_N~Y zFe0JLjq=F3F-mCxd1V_J0$&Nzlw&(5DUGwpdMSy%NJru-rY4QeTdJ)5=n7V`^S`g#DC{#9XB2PI%K-cs0 zGM;|625>2?FQg|k%Y|A&o z;C!Sa8PZiE;|eJW%D6C+8^&$l8Nfb3MS*_GJ|W$TfWa}iJz|GP|B5M;Fin!>$|i+C zku^GzvOu4f=dy3$iaM+?0$m-2kSfS35v4o@J6Fh(FXM(vnv4*qz|~KoNJ5!K9>u$W zXuRCu;qQD211dQY8SyfPuEyPr&k%Et)eqgsN;q^qZjj*Pd6+zV92VR+6DNqwKVI@v zMiZMS2!(y{oA=;zBP+NVs}W(0QG6zK70o^Uy`Q4Bu?<732jYQm{Gl>ENBRgTDM<-n z^A9V;*qkbw7O8l_cl(eHa$st5;@puKFq8ry$lYCfJD&XOA0Z?j zKzz)#iHqq>X2eJs8i!U_VM1*+>b8HxO~)D~95v@0#?Kjv&s|l?HF?@6cEnvihRs~7 zPZr7@W5D1tOsu(zYnSXGRu|A$g!-CLH{~R3uY3QuR<2h!5-W@7o+?1}!2%qS9Hner z>pwAFV(P5%n0m_uYCEcrw1xVrE-_Ea$P?XkTluzkfI?c0N(>LvtOOOUPaz}7Xpt>z=~H=p?e zEostdou`egaPz92@R?7zqHPfpvsA$om-7|e4$dDFPkc?Tisk{5rFEGmJFb0oJq~R9 z0F{?k;;x4mF$K;mR|u(DS(fDYlKKjP>dgN&VHY3QKcktQWNWeZ2$@|9&!PcK*b+QR zz)LQzKp30V8EgCszybrl%pvk+zc>ml2*PrH; zE{$jZ`DJ98xXf5M71c8?<7OmuiHW(2+M%(93oUTSRO%Lv3Ph^{ku6pTVhhsesHs2{ z%f-gjSS+Cv5;1&Diz6+LSohL}3(|{rW2dVCVv@N;@G1cv-%|^+eLZ!6pKVl7dFb&6;S9+$mGzTqP7z~vm9b1$g(p%Mu=G}H1t5qz8tguu zlbJr$1Cr`Y60Jn)=^%}lf3SjC-VqGCyaK=fwVO$N3g~VBad%Wpnt-E(PWjfS#)V;e z601X+Y9x`Rp!~xNouY**`j53vS6q>_cmc$nbv-B*wW!X&b`LKuT@EJ~W8r-_Vfb}J zY>X{(Oo2cFs`OwNgGQI3VoV9DMpt3rm`YSId{&Mcg3@7?XTzocn39qFdg@Q^9xw=r$2yc>jbV)OH;j$Dc~i^3Qc_ey>l^d@oaL9P|;fp zp)!8K#q#G?W98CcTEG58o9PP%cot<%rx5b$!*Y3XDSoo#=V<((6<1EW95>uGT}q>t zps8Qm8gS3~S$%4%R{*A{j=_&E16yWTCLOP`L$x^KbaL1YP!akS9k)1RMz z;(yTI5HJB6kLimhvObYf&@jEO#z}-AV-%Dng03ZF356CyWVF(gL-9UvT1&-<%=xwB zbe_)Wj^oa|uGT4l0?ALn#752zn)c`L@_($+#N}&`ei0>uyAIfUOyMSH14`?K{E+a( z%q$~g36ms9lFthXI)cG|!iDJ>n!6_jl}M5u@_`@$*VxwbY|W2UnS~Rz413;Y7cZ6} zRulQjNu#6rM_*d60!R&s@fSRmbgl* zLDS|;vWKe4U)O800cMtx!@GmhD5)+*>E}s=uK3t>d0MS0G&cX2$f5m{BuWz-3Rem+ z=wcnk38F_B6!V$NqoiwY8bd|qF@JF_o?o&Yo+s6oyv`>vTMtJ~&~xw5f-r=}|6KH{pd@nq};P}B)Xl)>YXzSEyXW!8kKNDG7|?vYMblDaD|(u=HXJ_1+MPWOmYm(P$DR=0mT5_* zGYR&%>MDX!JU-R+Hq(1?L>c*4_osEHLES2K7~T##az zqOZzntrz)c5r73Gho$ep@W6vrSv@HZ<)k;ITGX*jnN5RyRPI6uv4jL6GN{|P;&H^qpjMoKVf<02xXUT5>baEj3Bl(2Jk~0_(C@wo zbf;`*YhLdYnXN_FJqeDhA8k^5(Lxl0{sHO yMc(?OK(Q}sozZmO|7Ps1X9n39Civ;->i+@x?X5sWmSF7w0000 \ No newline at end of file diff --git a/frontend/public/Logos/php.png b/frontend/public/Logos/php.png new file mode 100644 index 0000000000000000000000000000000000000000..9abe0a96f6419598f7507e2603479f4d22e256ed GIT binary patch literal 3476 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 literal 0 HcmV?d00001 diff --git a/frontend/public/Logos/python.png b/frontend/public/Logos/python.png new file mode 100644 index 0000000000000000000000000000000000000000..664f75d425fc7027bde93c4b39beb0105b2ef7e0 GIT binary patch literal 2722 zcmV;T3SISyP)J0Hl+(nqIUEB98|?M&dXG8Y>zm7S?94LjFjSHDlSZ>| zcIWuM-}&AwKnW$3P(leMlu$wmjzDmGAl-+NwsFm0D66a%)*eb3et;E@nM@CMAH$DUtVHL-aw^7$GlAtZaX zL=ZHx^*cE22*M1ywneGeR#>> z?$|yT7=1k{65oJ5|kU0^Vil}aKuS{W-F22qRkN?@2;vjAXU zJv{P-Pm^TiR=BpdHh^p9nyy1z9K@&NHx-y*5lvbB(8f@8A2C`H*eR_;Nh@M)9wK7RbV`A|fb)35L(Z;sT>6v4zLz|2bBZx56-=8|N=k>EY zzW>~jE-2DvMiUOcWshxN9h)?JJ8g&WB!&lzrC!o)zp-2EAQ0Vt6DO-a!5*&dou)bLn@a1b>=@TI2hEiJVnC;~-^ zP%~>~q-yf~E!39Prc#EDU7vmHYaU~;olIFjW2`1bJN`=kkOh$MEyzMtM18A&`Ao}V zC?eF05dlrV_sh$u2Q0ULaEI~+*j^CXgP@={L}J2m@lcJmkL< zT;>JWB21mOvO?0rE*XMGUd`56C1>pvfe>>^B*rpmm_b^A`CJ=w>F|z?>nCkt5so-| zffgckUsvO6a!Jj7pMWC1DIhPAf$5>m7S^Z7K+Mx(x)5@-_E{ZALTH>@Zf-?D3|$(gmPMM21F_ci{P2*- z()H6$K9zU{XELX9B-cX?B`Dpk2HrfB=wXbwT!!)Act7!zZ&u8>=kA%m35U>fy&OtJ z4doHASp<6A??Yq3C0u)gkWFVKW<;7GlLCvQnuRgW@yWrLDzCo!i)~NT&2G3OvN#y< z-&^n|k}HEkDx2lXL!mkIXah8zmOa)?@@67n8s~!RwFkE%P_t&QW5wKzeFJ0`B|X@; z{l4-&e{S8j=*4K%zl!>|^SuaBiGb2-(=8g3mJ>w>qM|Kv{g?=%fcwEETqnlf?rYtY zoRF98aensaxlCJa{~!0ZerMs!(Q;~iM*)aV%Crgji4PYji+ShL8Pza%#U-^OMCd)3 zBLaUVzrn#)uOO+Oj-URlwRvqcQZ~==g2YJx=h9~q06PmHZM}V&PH+kL%~D54MilsV z0+xQj>J?Z_4xE1bL_=-)dix6zdjYUUOwdEX))T~=dX69p$-Zm3C!#Pob9|ikRWKeL1#q&qV2E)ix5qZZsOPBTY+5+_{N6;jTVlnCK$4fw z{F5$*3_)H#&_%(b)XdYGYVFmCan`3=4_mA`^1a~b=Jv-0L&%8a!sX1a0|zd0E_oFv z!s)-h_%WAG?k_w~uzH|f>P8WqiWY&Kd;516uAV*mbupJ| z!%8b`st|hbPmC@Kh22^cL@3}51#$$Ev?j@OJ=te}@Iv>W;5Mu=id}E)c(1Ff>AtyZ zzP8F=5u4#-12HUhIMnJQ#}8Ea1Dx`J0M;hP+SqWM*posUbEyOOf4f6kTjY!6GwSyaA`EmTPVN5jKCqdtuH7_qjAA);F%^_})#BDT z;lYIG1YY=vzOoeMiBB^OUhU}Pl{0Wypc9qv3e!TY@oaIFiNsc@bF!vQ3U63u$>T@#zY@`1P?{PdQMQY z_XtUwsju;vz|z+W;RQfw=gVe}$?q||gm)rF)(|1|6{P@V=n&xHQ$YHD3oO1Ob%K0HcXrEHp6)ydpCJ)o-+wL* zEnQgt{)C#~T>8Sa4uM^jFeF3+7{~&{e?s>e5Ck%KvNQF=r=IKn{rTQ(20VagB2bXs zf4h8nAcbF>(Ga{&SIc7brARd;%p$<%{=s~jylS80$;Eh5c=erQgHNt{xcyBkKpH&4 z?fP%CZ~gM>aO>lZvnSVu7SdN~i^FB4fn`x8O-fNJM_R;WIFlhgxt!R3b&&tyzh{#t zA9?27Rgf1My#0Uk?GnKVM%y+&TFb7P6Jh^xdRW+dxL0UzAL&5V#-fB0N+_X(5=tnc cg#SDK2UN38o*A2hx=6E$&3++&ouM;~3I zCi-AvOw=fe#*qPZ7!*MfP+-{Ua_5{Q?NDfGMKWUM`z1~9y*<76yZ`TO-)(izfM0dM zPW(^NfZu|kfD!>p1VM=aC4!(tfD%DaB0z~CC=sAU5R?c|A_z(ZC=moD0+a}X0lkRM zXhgqBlVrQ;=~Flx8}T#nU7lcwjrG{qoK9G;TtUYD`-oVu06z;o51*^T92~W^(CPKG;eaVK z6PAPoL@r$lOLjI4QBh*!_5@8s+xz#ZyLS)zurS1|U5ly{C(xJZSx%rED_*h$mYFk= za^wiYMvwMwtJ5JkB?Yl-*2r$Z@#DoCs!pDS*4m1`K##&f7ZQTh)2Cs*cCC8>+Hus> zz((G{iPAJ^5Rwlc#<0RdM39hu0ly*`$<Hf_-)$|Mnd?awYXT~Ka;w|#Jzh_yl^4(77I)n8PKSVf|@8U zr%rk~C_L<6zIc3}Z`?q+qbV;>a*w0F9`=d~Xg1r|LB_V7U)r?4Li2 zZD;fm5inEtYM}Ryv=kMgk@_1CI2`iGD}23PS*;pfRO^nS>C?sb<-mbjQFiv%?`33o zwRJQ%Ym^|hmx+mD_j`J})|i*4k=M9woHm=*R#v9HUAInbO1Exxe^!!{qrFH-aPRZE zx{R65*<4uY_4-x0xgNHB|9-YA3mU-+GLrfw{P9sz6hf78Zm!9tat86 zbxF<5l`_j^*9CiqF)08ZJ%SN&oVZ#tUdv>=c zsHK}pBioPr_Mzp~E3Y;!)DzTF1qh#D)ND^%CH8}n`xiUP9mhUK;ypVHYN>7KA1ZFr{*FSpH#oj{%R^ImU zBQ#1n+{`2$Is|_-=jWqf)F^~?(>wH|q?$}OsPEt3eT*xI%}YY-4eD;+M)b;+vK`aj zuDghsHxI9;P4f`SLj-426aJhtM^=gP_t%%#W(c@Sp#e2uK zgDx}_)?2q^JCIhqJIZM6&V~j=E?z8^r6cAkQ{L_G=BABvOfa?NoL6EuY(OOCny(l0Z1=s25S*MW&jA8~*A_ae z@hxxO__S%I&p0-jAewO_*?V0 z>rH>}|NZoS$cLQ!e(v);&z+q6oa0 z-o3bUM`29^s%-E!YiAz+EzKm;uY>8g_rmeOZ^(r3L+S6)_&(B|o4?l2rFo?Py*~M` zkZYyxSEGtQOa*ukbl%}R@XRfwuJmti4>~b0m)bBl(pHhp z6B}=!vrQz1y5wi8#i%YPx@F ziZ9;AA-pza3P2r!GU5latrd6ZM6tmnS-M0H#d{;WKKXYt*E9~Eq0*m*OxRMX#2JNV zD)lq?H);Dg2g{O zjcdw^sEDVNsw?Zw1@j|NtvGOELoFO}XqTy7B}du`jY0d|*0+l+nP zZf3j&CfOkXv7(=^cV8SofqmTpBpgot$gxVwcBNF*BH)mfgeebX;gc#{$6!%q7TrM8 zngou(%w0%5&uuK`BI)|R(}I8{CVP_0MQIQ^Wm-lLK9?6dV6T!sfAb2(+BMr>SvM7LnwHJj!rt86KtV_-rnH5GOum;CQ3^ zaJq7ENY`9Qn5vu{Pkuijo`;X#At5@d)Q3(x`PkOVpa;Hfp=&-pIj-_ikS!LK_zG@* z^{Nk_=jZG~NPggq=WhO1z40o_jy;a0fDPewh#>n#pTx=U&D$6keP8sK1d0;2jiv~?ZZ7%tw zZY>MvMcMa+_|w5e1-07Dxgw{AR+fPC?bYf@YGzA|r>3pEAd_A}ZavquJ}$n35u~$X zA;i1$=_NN^jL~D3)&z`Aoh< zaQ)&H$OL(bxIn=$aaVx&{oq+$!IU9j;g4X{f=dd0-*6yBo+2OXMUMp=-N;`V=E>W^(e6K=7IJLe=U*Zp+>h@reYYz zzJ#N>RVRxT&YhS;xbZ9bMAuAgn1FH(4$|;3K#hWGMd4owudva=6Tw`p+J< ziQtxgPW#V(?M>H76&?UA$?rqj`^6bWvOJ;?9*3qt%003wX-WdOK0S!cX-%T=;(AOT z0Dimp$|;9HP&s_c@G8=e5_Q%8nwGtB{Vezayw9S{h$=eb^U2ooz+93tky82l>nf}B zX6p+R0Zn~>jN*+78V}&SKHuHGu4~pQO4M9Mtb_o}&s97;E4O-F!Vv-A_4)ERQ($** z`n^$o&I&k||6+cf)VP9awtt)GWyJ8JvqFmR(5YAnO9XsSv45yn=CU6CP&AV9)cx4w zbct)IQkK?bv<&njzwe5C_=}@>BLSTtaa-s}y^^^kyvSIOwQk+zB3k8>hXut^@NnR_ zSu{blR-WpLFY)l&n1}5H0a^3)XjR($ZM2q|+LXC~DO)_Z_USmHa(nNFN!w=8KAxE_ zf1{d8<74F>n#E|U&lEy~n~Wcy6J*-Jfu|KcolnV?Atg&bu6M|>$l#En#G;YtY~=1M zVA{tP_uL2bRSZ@bwII39lnQPA6qEZ)BuKvf#hDAwnEK7u8-TWflR=STEHH;3k$9XwiHr0u;)|PwxR1SE zfUm2lBsax^RHu>jjqvn=d8$fK`EWHQHD55ZUThU)=on9gF~>Lei~8*^L>(X1N=PS2 zeHv*$;{OWj#~n&}7rw<&`0-dGkHHSUTe$5t(3rCd4!@GVFOnV0PT2*LgEcYO-3G|& zoO6QD7Db8~iX5OK#1xz(j3SBJ*_51rDR|-pe*yI3+aVNUn-EbC{tHg<)64q5`Eq%< zy}pr`vj)lqKM7B6dzU`2^%dbs!{-0yZYNZK(W|Fu)dJBgKiCb@SVAreyie~b-9A9w zK~{5smUTp+GQRRtz4Y5TQ-{`2KCnn&-`aBTSN4cnnv&U1}=tEaQ|$3@bB{08bNE(XPPZ)egjN#>F7bp z|E6ZT#XfS~`4K?4u!qucA7=cbbgH$4+)~((rTs}SZ2GoMiST5q@H)JLR&F=Wb9>vY zM1NJYp~4>4cB+z`zmHyze-gWKRQ#M?h==WmpbwCwPcpyMr5o>7gg@XN)r$36d^rnW_YO6Ul%~lRtZW1c1g4Xw6Y^-ZeXyLQPe*jEqwLpYGq(dg?bM~&l3?2Tiv0Y3 zf^c$wi0^~mP)`NvpiS6__qDH_Xud(qOzra18CT(#EeeAmWcn_%p7P0}x1-MW;mrv= zAJmbIdiBle?pIT!pHZFp;V|>XX$bIMs0UX_(WoZ}I;9DPQk znHPAglk7ep0n0zRKVg~Ab$h=Pvmju6LQIm8aSJJUt9MGL+2f4P9WaU=gwIf($}ITWJANhZiT%;|FC& zh{WE?u5?eMSVt4NaPnvfd(|D0oKIr4UDk=+2YbIz^@QGRd7jfjN(=R;8bB8Yf?dRTH3x8H7m+uT))YUl&^Xf~?K5|B6w4UG3dB-eu<_m4Rtnmmz zH*Xxt=cNhz7l(|n_%TbOH~fJXTe@1Zw`457?9zj*B;HEe5D-!ZyB7~k?s8{2r6Hl6 zentm9f0JO(%rWWWe_A~{CL5^>_QI4|4T21d2rZNyd;LOhb0#7S(mM2>2WzM#mz3uK zK-}W+!K@oPBxW_8?7#@q%ld)d&SW0GJK&K}Hfky?K$Be@jRt#Ts?|8hI=`{p(vcW) z-yW-af7{dwSo>h3BUhp{Ba;t!g}V8jxd(P=SU3V|nzXiX>j_qVEbOvDmahRPLV7s_ zeYn2svNldSJKZ*-9L4e~(d(`CA5Sa}?Is~8J?Z#N^&Lf>=2PZmx*hg+OQF%6;&>;` z5lm{U8ROd(4~eo{R!Hb<9@VsxYIf}WC6a)_Zvxyq-&h3axQBn57GH=9BC);3p8QJm z5;^Nbu<*uu|FD*B+zZH)gUJdP)T0lS#zYN_v{prDycNK41k4KOY(RqOoX6cLdanqX z*sCr;%=<$HRn)L$KE{~5ljB(v3q(+(=J;GM43V>xoJGK_VnP~3;MtK&fwI%M4G0*c zG?C@N>atz@i9iGb1}No;6<1FPyNot)=gC)UWJS~bU}`E5l$#$j}I*0HkV<=7y54V|`>vzcn~gEFdU zO%-vhN*LwLf+RWgo7zT8gWe1sg=ztiv9X(EnxZFMp*V!OhQ}|S2K*?d=Y}l>VT&QV zQ3f^5>~lMr$=?=->rd?80Rno_m%=I-VwWqr8p({CV#J7qCF4J9Q<)t|?&_L%?80{H z`A1~z%F06~7k$0k?W*fbZgAeu4`d4K_j!{ecXpO_w^ZFXhw*drk`l?Xr_m3Pe=Ms( zV>1oc&so1b!R9YD5W&qinU-$fFJ?4a=rW(?x7$NGDJ|Ay`B*ndDaKVuy_+tW$0Dto zb!gPcxGy-D@`-PQ*|~iKj5_W2j9n)(cvTN8+j4V704;6$eC)-J(V4^ zAl=>7`J25$l%sg!UFhs9+bg|VRG zQ2Az`^YD6}RcYRkx&ZwkL$F2a8o+7Uo{oWRn%5USswKreUF*Y0#Q+-X{K-rtyI!xR zIZRb!AVq@67;^d`>Czyy=7Ad+7UDMLP85Z){t0?y3|X9Gt4subJ=QbeYwD=crK-~5 z;Iqf93iX_a7#F8oY3UYwn(;PQ(^xD~X(C{OY+af(&IUAclKR@Wk8pp6vyG{yn>zP- z@^13Gy^y;8mw9ia{wlIf)o@mS^AhJ99(+Mmy#FWHM7fTw%jzZZ14tczq+{AXXN!&F z$G$dgJU;DIW@A0$Rsx$t<+|ep^FWMWbSmgYv-HyMShDVg7bC)3J5pQ(L?pzGL0dXR zCwLDiTHah|*=@5=DH{;jkQ_5BrHtFO_icKtseG6Pt5SdHu_2LS#^t?=WzJmI7SExkyOwHWmgSml;BzAKY)6dnRoiq=cL@B|Y%Dt< ztV`;KaFtthDy4;zd8W2jt+PJzEiPi`OSM0<#R$D$%#}>;qJ0}1%}{|SGIgw(2vQ=G zP{t6EQ}r~nXK@e_f3)PgKXZ{kpkdw1i6`4E}Ij? zxTOQZ!B=FeIL5(73zUWN^w(^o$jtaJIlz79s-(wxFc6=5Am-`(eIFE=p|EGPDXF8p z2=(L>Y$J3L?dq;68L7Dr2%ct_K@$8`iq28R853q>E&tMlFBU{0-gmNn;~tTM75-Tz zMUiR4w>XmEr zqG{aj|C(Ny`8xD#kmzNliTv_gd7G^Ofd|{nSLL#FB4Qy>?9zGpo0R$}qH{ZamJ;sU zcu!Or$xBMb%mu`_j2YR|t6{?7W50nukU;UP-+b?gsT=EZAtPwDA@ErjsZrFXr|~HH zxCav0-YrCj+>9Kgi2C3wsWyhe)t=JLU{gI-Uz2KK_l-IUF#ff>WL`o6@-r54=55jc zMrOAZzPT?Kv7f7i4NL$`Zxg2pTeN|EVmC`k+C_r-@|MBb6eyufm1{lDyW@Z$@hR+q z|D&^BD=k8xDWgsrxaNACcnMjHmixZ~f@{CnB%3`xqtC=sqV6q$Cc8VlM{4Ts5 zl*|8F_-wK!_FZn39w>p>-nfmoGJ-wVw1h?0fNf+Z{UtVMiHKxf0BzB?0BMjHURA?~ znZPl~pM?qg#$_|^DsMd$y#jtd4%hkyu}^2|AZW4_i8mdhq#M-0;xCeq{$Ypy`1}o^ zAU*|c_>Os%=3T7q-DJzs7vlxLwb@N3WRo3}gI#^1lTeP!`vHMY`lj-RTOflLyR(o}o&1Nf)N5%KPbmeMtUorA@RO*WrVwEsa2;4K{3cp|Ry9rrgN z0)%K7o=teKR}hU_QYTe=P39Se8`dL;{;+V79z-cm8z{gci%(wCB`dxhjHujql`}B; zX&L_cXcmS-$Zbi=cL1M+#ieQWmwAVmO?-^->q6ek+*W~opN*_24#dO`6}y03hv)b| zblnHIo|15aPb5^KQD(oLD-KX2HQ##1{j0XV05}&-Auzlbs#9Z%u)^&qQji^T0YN^* zyP@kEkjVvp*wQ8*INMllR4+a?<>|fq3NOZ7&6x`<-%e>}IK&}P!Y(^%Lkpg6;6sq_ z($G_=hQrMI)ji>-PUd0SaOaq^i?WI5h#R5CHZ|CeOIQUe1T&ySM%g&wqxM}!u_t1a zLFztWq=!B6;Ym-U8NjH<)J@?iS)xMfyJ z&c<-^Hl7k+%EV|diSnPR!0UHxDi!J{e9XWC;vw&IcP>h^*No*Vgj4Q?0Rv z5l_wHaZ=AZ3x34;(5vhh0%e3SabAfk`a-5Buuot(7a9ApL@L|Qo_htZMpYdsOlhp0 z@F{45@?83a%Lz>KT$MP&^GFm_{j}3V-@IX;{?vuuqXmPU-cSzfUlO%fIuNx#TPRX0 zXw`WUCivdjK{lD@H0hJTGNg6Uj3?`;=(Pw|%#hL_?4_Fjia-!74J1*7oI4DrsYL}H z#y?RG6I4!=BdR>h3?AZuA2w|wd(g7nsK z`CO@X?r-ej+rjgVj1F{*g_EvRs3wPOVZ7Sg@N-4$deI!dsJ_H=)_cW^gFAT$Qi|D! zw-*JB6}p(8i*4SNtZR>9ee8bdAi)k%+q>=S%3-&LQ9W0TpO^o2*&p(3<)V_ETm7@!{KZOEA3S;AqHszQlrl@3a2~*#p zksAb~nn|ucG z_XH+XLpERZ{1iV+2i~qW4K$EU6mKAeBevb%WAty)5^7G!F?s$&M!WNTUf`ekzsEm~ zs8-xX_z3Cz$|i^*)Q%P0NBg&Q5_$EWFZIke*=0}F>y+PGJ7}A;k58u%)JYtn`b&6x^ zPb84P|Kbu&C|Ch0jw%Pd^K8IRg~r+2cZ0+u2uzqR7XxyCRkrQ$CTq__hQ3#DX%6F1 z<*hcS$khWvpD!yuWh7hf^Q8E^`Ss5rfqMqhu*)h5 z=ND<$Gzf%^_i<2lirV*ri)wf}8?&w%lZ%UBt!zF_-UJ)4>2)s-fpPC!z(^uECx8DVHQHZ&LFYd%7mRQotDNp44AL&lpyx!S^UmhH?{6b4ia#M(QsuB)uDeJW zZA+XwU7JTIvw&%H{x~)#@d3$umvTxO0Ut~oMI7`|{~;6SzT0-+TLG6q$WUDYKohti z!<5nihO;RK3fkb2>jfTBRfpgXp0G<{j|_1zRzWgw!A{k-asaugVP`j{eI9owvt zRmw7m-V!`kelX7;+G8CBp#QHkwp?wLk%IFXJ^wcks ziEurix&U#z-0f>Lf{8U6b&PMG{N*w9m@{RSENH~G(};XrrKg3hM(>h5o4_PmzkkKt z@>B}vycrp?T}2{6HE18K?%jAxCT=`~9(~6A=bcJwTbtUyaj}EyUjTNiR403WXlnYG zWhE<(WLfMB63;wQS7&&S^?O|E6O_G>|Er?-K5(=e8It!P3BIdPN6j~TQHBtplYy-{ z^{d6|eckxQV7c5=pq8WoDR1Hy<6G=gLi)oKL!&biFsqyyq7?cH&l;LEodi$aE>H#4dgKVCS<-88#9-FWcMPkMn}G z+2XK<^)&7L^|(nOZf8iV^PTZDC$W_X<>0AAaqKr&K-hk^lqM4io2=LOY5Qcp!-NCN z*%3}h43K@2Zy0-kyCZqnn!z#!NPF}bCxOP9dVM&u-yi(4`>P1?R z2Vb+qVKSdrko`VtIzN!FE%f}?$ka%aJdfSZSY;9un1?&s;e#&j^a=Vl7>Hz~W1EB{ zCQ`}PMDk8z*9ctbMmf4O^;CuQ00k=Zr;2nt`zRKI#cjzMvfFnicBHC=(E43@Fg>~x zDYm#X5Z+hUzy*xyqR2is6(Rc4`Qg6M{_Z8U!y!7OIp$Mt)sAaqkM%jkK9nz5b>qW9 zZ_KL`^5ED)@@y+7LBY^ISRN18M_SVPcLDM&yd_+s^E)A=+8<^I~1iCwahH+dgoFFoV& z=XK5OSpn9}Y8$Ovu@bnHw8dc1u%7o&bknZY(H! zSwtT<5KCKiPszH1iF6tK=c*2_d~0xl_%UtjWuAUi_76>(Yvj6LC}ri)^Qzb1M5jaw zmlrwlNZBt|gXwh@nOv^63AMFx(Pvx%AT#zD3vGCHDj416@Hghe{G7!0hgU@N z@w(P`wvps! z+n|IgbVyFDI`t|Oyf3#5DMk({>Q&qZb5mM$px&gLnLsj_N@xJj2iun zuWOSoN1Ql-MxNf>2XIJY3j-*lu5>s>QEr&0FK#_i0Qr68hy9lb5yaf{kdIj_ZR<`@ zf@8Am-ufb0Cp>OwB=IOT#<v!{)qTnLJu2v_{s@jQFc3dM`HF`?rQ|ne7r5v)1ks+>{^ahQ+1F~EA$n7ko1xaDIl7Uo&E800r69@PCmBk zfUISlhw+&T+qbw6&v($-d#lQiE>8r28VhQqv=(w#5mo!7*Kjf&P_eK10Z3$=JbR;# z6zLMaQkqca%qW{DyYs}`1FmRIemYdJ@Ar=K`5rnOf-?QNsV>d+PTat4gt{vs6TbO} z^z8uYAiD9Q^|CL!W-~JvYaF~NIJV29N*~9f| z_s-c;-tEq-pYUF7lFtV_vK!<)4<-_6G79H&f=FQm^X`=pa$HQ$3nE6U11(uHHSIBF zv!8NBgN_kUjbc9xu1bMvH1kSXJ^+s{)B+rccaTlj(|1z7Z8C-@XTr@_$iZpfsh0y5 zeuwJUwEdG=4Jry@}cpaw_liN2{&N zJmiu3IONuq0X+F@5W&W^&M{_IOdHRm$=cZQeg?(4U^m=Q#}QWoU+^acPyhIWK$Vpi zD>1o6+*9TG9BpQa|&Xs}Hy-^!eU&v9s-rraz zii6wyv{q?5sOe^BoK)Lek)wVDDS9*Fn}VT?RzEsewJ{jrw&>I^V4E7Z)H1*w$WE>k z-ZoIiz!F#5C{O_XzertpskvT@J6! zj!vr<-Nd6N&>7hK{xmt3u#A1BF>Y_x-ja2SZm?fGYvXtg8DZ4H0Y{>PrF(|Tx9BZ4@W8Jg zKZIgZr1@VS>kKT#{L#09&xxAJL||+pmh3lkyb;ee#NLfoZJ?_;5uvCbxnP@+9MUJR z!Emv;iz89FZzgQwkRi2N=LJ+m(qn9EKcM=c5vyg86^^71OpZBEe2lG10~~QK{qDGc zE5a15D<6{1D~?f1utVs9d06t60U7%-YYhh3uF-%;f1OuLQ4B#4-zLzpfYF*p9~wRc z8Bo{7S6EDL#jW==%{WOECf20ajV9m#Vo6&L%oT3mHGC9>c{S`hb7oHmmg}a4(mvtI`NH)KpFrhos`wCp1pv57JtsDjP^EhJMkZver zfC6po6gp@~7MZF&g3j1+rK~=z>7zBkMgRG7nkRx;-;tBjgXy0L?UZt_ppmU*B-R@R zWb@7O-?@z^;{ZT;?1UU;46g@`&QQo(mUg{7R#7t(?dhXAI= zPKl<;iP8gfGLV8?vRq{BKZM=7Wa$4+*25T0AgaEY&d)z#ufN5LlZzcFZ9P`ta(4gjp-I`e}yo@4@HWd3%yqTO?(UY zJ))Suis$ZA5rq|Q>UqiC8xN%TcWDCehMw~iaoFqFsk>UV7wNn#tAE%VjTI*aNT$&l z67vjR!2Rivdl28bY0{c|;gDy(^~6p>K6UZR{|dVLfh||spCLFgs&Z z1RRaaWYlK~aQ%i?k|VgpiTo_DpOgGBrw zd(CW|K4q#jP95DFw%o}^FO9=KNMZZ!0G>D;+vRNFiXFTgEew7-f!<<&vVy50u3SpG z&L|^WjRjAjDJGRy5MLP)GZltPe-V8LI6W=N!C3-+y!#a%mFwb+F%;kdRl}uD{9r`oj$5omx=Mcgxx*d(qV*1?QieZ zHth>|*(#K#Iw_t@_rQ1Ak^mC#ZkqPXR{7TYH*Pj|ByI`M`r3~zq8a``a!x^jLnQN+ zVqN;{+uVENDtv}tuj1~XP?=>^&0e{Yu{cW>ukO%ZZcdN?II3ITC$xUG{auYr#Gf1- z{uUEYfopT0W9L6KZ;XJ{*y}_PLfrwrx0%ia#GS}miMgjN!?=k1i)aS7h!SX zYnxIiWR79TJ05qDTa5wKY63sZg$8&Xc{^N)4_QPH;QDLWg0z$yO0*I16 zGm-G7^OD5HG4ch5SjT zzK4i(YfPZJ-3Ek}m?jt^qMoKltK;It?|gZbV{&5@YHs8P5hXwPY#)_si=iFl;>o*3 zXKk1rsyvNYp%s7l-#MCS*h1;8b-KVNL7T+;M%%8&IkJWK}>?rlf-Gg>~i9{w|fZ^;6ok$8|druE%9TZ>+XHXlf4A~10 z97tEN$bVIKmb4YZ)54;0@R*&8x@)E3PaM_e_XkTL)*p(sM_WeF z7V$lKT~~_|3-I(WZ3v>JWFPNK#3q`tjY|iV7y$vWee(g%7zF2j!?;3C0zWC~k^i<3 z)CKF+JVTD5S!GAl{`euG<$>XD@i;~!ED#&3VpzKMN_Fz^hA1I+@+jB#;@cApQXbZ{ zPYQa^8AEBlDpYafwyB7&wY`(a-CThV106yb*cJZj`Wq=3K~fsyDT(^&A`ay)So2io z31@^-A>Xg0sh8|R@)gf*_4T@gUg+E|( zzlai>If*2gT(GdIa7u({lv)H!X)i|i=zZ;UV~kOqU`k55@3>nBVC_d@+++oDW5D#8Ua+Q4V?s?`wGi0IuE%9N;wy z6~x>b0WxwEt*|(Se8`urh9@2539VUu`yMrd%N`_exQKgesV(ff{34Jz_Lcu~5K-dN z5#Vl(3_aa3n}eJY#jvCu5V+!_7ygtwB%H1U!h4Ii3pobd^-|-IeH{xKQ$L!Z9``{4 zfw+bd7_lj$S^}X!MN&rbpHZ%^V%gVut{hyUanWyhzl6WJMw-sj3QUb=w9~#3K?MoXB=(w@tTvT7e|EnOv|MHoG{R-OBSnVG9k?0|D zT{W{M{P11>n%PjwQnq*hIBYgWTm?dgx1pX6wKdAq{$;8L7_eWs-GX{J{AlM2ROul` zsVP3?z!~Br^0VxP2Wa;8HIRi*0ljv(@W3wWHs`kT7moPKa-&VD2_m20{YI$iW_ash zFp0|W0d*TP)nkhiZ}`y=IP;5WY|pz+kMU_HV3Bf?yH>suoj zNz6=add!=MppY!9%1`pSKUJw-wl8?T=1jJ@Y9qPcxb7Rx zEtboTQh33p*YN^#W7Z-m{$!Q0o(72{B}&z)+4_GUxTT}(8T)QwCji_PHb1%>Bl{M% zCw#@+_jpp3Rr%u=W=UBtx{O-b6CAq``(X|dnBdkDypuUvl>Fn1>TI_}%x#CYza1g$ zO$TtTuQ1Hlx}vajw7x;=#WaY`El&#DoWQW!`PuZes91P+ck#41RHZH5-hHmh&$|2Z zIUAROL&fm02UlLMa7g*B6kVyK^w=H_i7yR>Qp^F6QrO~UfL+AS)EnZMzv4Ab;$nX$ zj0=mYDfuvD*9kd@zd}26f+sVYHlVRu8$E$&lW9U^=mPpUo8j1ur;}lKXCX+Eyp(F2 zPtx6-v{VFhdH4EI42<7`47ue6a-8AVyuPyEU_Zc6}PaTsvtD zSOv*rA5(Nj?)+a;J=;d?R7ICGun#8+@S%$LH{$MQfQs~y&+mv?cdXt3{^R{$5mYYq zT`>Jm15mk?cbdxmNPH?853u6b*2_A9IU+y=f5LGV$IKn?R`aL3d zRP1`5Ab#kbLO+#=^I+O9n-c41=sNrV#+kYqIV(TWo3^*FTs6S&)Aa+IochEBo;Dy^ zw=4HcgJ}Nwbl<0F=k=e>ayFtmcM}SI6&=7z6_Q3BC{~iqMW<-+?7Ackr+x5ZSh}e9 zR3=KL!4YXw|JDK2qvs%(wm;epZz=!X;9!8-Zdd#2lFX&VF#)b_*kavAMraO<{ zCcQm`-=>cy5;G#sW_Uk_E@H^BY(HW09Q;V!7&{?zHR(i3Y#vktA-B-{KO$~_%N zY@vsH{jgu%ZA6(I);rCYMrSM~F-8PGCmSweF>8_BKU-9PqC%6|WS3=HEo23RUkW_v zYIncU?GzPYi`_qB8k1s99pW!+>BxK7g-cB~_|G3p-Rs(IpbX)t^N$MFEHV3{|8(C^ zC82~z^e6hbl@Q1Lj(^QJkl+B=UY zAI#*k*>R*Sy%x1=Uoet4h&&3EPxE7{BKju~%D#;HBx`N&;xaZiGzOoyxqouu%ix}% zV0BE+Rgyo5w5M0#9s8*BAR$at!f9dz=9O?AkPy;tL)sSSr>oqM!})KLn7bI~JxS6l z1kcnsi%49Mb|N0*8de!UJSvcx2PfwuG=RK~IG%sc<9JSQ&E)C>)OdBf8+E9EW_w>B zC#PyVRXxkvJ!8;b!-|tQ5xaY;B6|QW4NAHe7{Wu)v@J5Q+YNP6*Ldo*S z0QnM}T1?ifj4DqMb?u!j7=XCXx7LHVmo@EaTiTmJlrYq7p@nD{um~?yfz)?~Bp*vdHqFJJ;dWGmA`Nwhz! zE|JSz^YQ4MbF9cvn1^q|KLxAcJLsGt8wX#+K=3Qb8!~RgU=;{tlR{54kCse2UoP64 z-oPZe{G_^urR>sKwAlPSQ1y4V3TgJaRkcDU=-9I41EUgfW-T5Jeaprl#}mC(K_(H9 zDgrAUJ%oW+oXYLjVrPF}Ib?pI`m8IGW3rjQ-*8JXcI8-M9D0x1q<-0LqWqIJPN1xM zqn=6E1n@Xv?WVC$r)c6)`aw_kLm$`J67mz3rwUy8Vx#8#uxpnN5u9-p6bSUU_?cxi zi2l;=H%_8`Smr|HWcSqrGvG`3oP3~5TsHO + + + + + + + + + + diff --git a/frontend/public/Logos/software-window.svg b/frontend/public/Logos/software-window.svg new file mode 100644 index 0000000..60bf068 --- /dev/null +++ b/frontend/public/Logos/software-window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Logos/swift.png b/frontend/public/Logos/swift.png new file mode 100644 index 0000000000000000000000000000000000000000..79429f1c950462b9e94f075c2cf696a9a1270d18 GIT binary patch literal 13089 zcmZ8|c|6qL_y6lPGhSwx35^(&7*Z(9U@Tc@>`IJM2{D$k6+$T6Ys_e|mll;}EJ=|R zA(R>WyVXvJp-ssaMY8ovq~| z06+*H5daz~_-QFo>I4ArC*9uJ%0A%i!XcfxVEvhM`iqhKR<0Q@p43~gATQl9{+43= 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 Z>lqg{-_h52!G9kBJ1Ymv3Ufx{{{z5L@QeTe literal 0 HcmV?d00001 diff --git a/frontend/public/Logos/syslogs.svg b/frontend/public/Logos/syslogs.svg new file mode 100644 index 0000000..40f9055 --- /dev/null +++ b/frontend/public/Logos/syslogs.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/public/Logos/vercel.png b/frontend/public/Logos/vercel.png new file mode 100644 index 0000000000000000000000000000000000000000..0ac66ce6adde1dedcc7ee9dd863e3f56f72a78e2 GIT binary patch literal 46474 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 literal 0 HcmV?d00001 diff --git a/frontend/public/SigNoz-dark.svg b/frontend/public/SigNoz-dark.svg new file mode 100644 index 0000000..41a6fb8 --- /dev/null +++ b/frontend/public/SigNoz-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/SigNoz-white.svg b/frontend/public/SigNoz-white.svg new file mode 100644 index 0000000..17cc833 --- /dev/null +++ b/frontend/public/SigNoz-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..98a6d5f7dc1fa355104d4436132c1df3c5c6d61f GIT binary patch literal 2218 zcmZ{mc{J4PAIBd{M&i!3Wr>WXEMdMg_8~PI%ZQnhQHq#hjEP|~<6>-M35DEnpg{Szl0E9yT%=rR<%LhP-UD$Hi25v~u4muLR#^zJfUY-NbY-Kxnasb#S zznLQ7QlT4L_Ecn+yl!Sr%B?V+-hmgKzK!^e&u!jRk&DB{qUCA2GHH!sH~4QmwK`9v zAxk9H5XFj&2@lMrF9-HvC(Z|xrOv3zz7wrQ{0TH9viZoRg*uuD1ZARe{-h9+otbtx+9;9OIGh>b-Hz~XO$>j0#=i)Ci7_V zmq=8Y#iChBy>y(Fp%kH9p?ifZI+E6nOepB-U6`r7QTeNxW~5ZVq}Cm=(RJix&bsis z3t5T)LO_VVN$ac0Bhv>Z@8v?1b9aY|tPxe3OnnZe%S;_%DqUwp*Lu}PVv9yv03lbSKgRj0UU-fD&on5i{^?7N;`fNH$Jkqpn_K)D6V=WF?TXDSTMipc^t8RoFF}j#$NYh)fDg48%D$zUL+*V zjIDfkV3O_GAtyz98A;ZH)xJjpMn^WgZeHb1YaG_Vh;{Gln;BvEwW6`*=Z)51X4)DX zzrr?DlmS7QF?>rKUte;+E*>h=J+Iha(9z(8Ytgo}MkhFrSfy5b`UKT>+#Ua3iJmEr z4wMeMU?#lsQ2zDF;l0<-wDInYkD=6qf@CYbl5kg<^}6a~x8Dprwf)#zz0>;8VpFWP ze(&h_Q_jjIrFQ!@w=0Em$VEHj=v^OTW7H@WnP*LnR`eA6?`(LOdKb{7+vUYOn!oRD zT{^$S#U%O6Gk5qBJ&-1TXL5tC8Ks1kvS$5QV*4&+1TUptCV{zOwU8XZKKhxsz z+a8MS+G6aLW4)$|;>t@24V{!Dg#i`b9J(_paBTHS+<8GpfEjydtQz>31bCW!N*#u8~ z;yU3n&?0!5)JJaqEofuX*lU;n5LU+L3Ur$u3ob1mj`5Lnb~eR6 z4?1mkjb4d9yL)QYN6`8`UXZi%7mhn#B!w@Q{ozBk$h@|iUE(R9D^xzQ*Nr?_F5M z@BPhD`it4*TB0bPSbr@{wIDs*PGq1nB*FKi)@RXZNnw}G3D^9o$H^kc4E3%hmKlpL zw|yn;WlBU6s2Y6YC-t)GF4830@LTC={b>5M!r^RwJy`^VG;-K}-fyu7Y;qDwOhZ_=(Whc7I=ykW^k^t(pAZYB8YZ=6KT zE4~mjz;3Zk2c5G@tq?WZTOvkf=qE4yThFGSP3RLdeA`J8-=AgWkQ#-LZdTEwtu@t# zaS1iaoGCejtbunu-Ho?_rHl>2X6q-h`=5@W(>k{hY`Wt9!c3m>HS2d6ve%u6S`c+i z=*Zg@zwIVRggg=t&SxeX?`w5{&l<3cYk!REPvz2aeqnUD04#*X7(rM#+_4Z24h*yh zf^ZO2B|_jEO6LdffE|Qji83n0g**z&|Rqvre6@s+}Og{ z(#Q&hg)pW@keLw%i#m)kM+I`ZY#bUL5fNd`+@#@zY2(l^#;cSMupv<1baW34XVGvt zLa1Ljiyp$o;SRwl2VlxKTG9Vv{$3M@3#2m{fn0#G!XiP~8JJP}!RLR>QKmmPa1(i6 zttkm3DnB5uOctFR#ioP5cZFt$F!tT@ApOh7)g7h)#vFsiT3KK*P;nL85=M|e_#9#K z=m4^_%Pq5g54SviXi?mVAb=T6hd&?93gHHaGSC6kUzmQOAsqBSKL(Ap@(Zvqv9d6? uFrisM=9VAgVuPY78rk;r(5lZh-F<01)j-gjze_#Qy@RF0k_e literal 0 HcmV?d00001 diff --git a/frontend/public/locales/en-GB/alerts.json b/frontend/public/locales/en-GB/alerts.json new file mode 100644 index 0000000..fb360e5 --- /dev/null +++ b/frontend/public/locales/en-GB/alerts.json @@ -0,0 +1,117 @@ +{ + "target_missing": "Please enter a threshold to proceed", + "rule_test_fired": "Test notification sent successfully", + "no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.", + "button_testrule": "Test Notification", + "label_channel_select": "Notification Channels", + "placeholder_channel_select": "select one or more channels", + "channel_select_tooltip": "Leave empty to send this alert on all the configured channels", + "preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.", + "preview_chart_threshold_label": "Threshold", + "placeholder_label_key_pair": "Click here to enter a label (key value pairs)", + "button_yes": "Yes", + "button_no": "No", + "remove_label_confirm": "This action will remove all the labels. Do you want to proceed?", + "remove_label_success": "Labels cleared", + "alert_form_step1": "Step 1 - Define the metric", + "alert_form_step2": "Step 2 - Define Alert Conditions", + "alert_form_step3": "Step 3 - Alert Configuration", + "metric_query_max_limit": "Can not create query. You can create maximum of 5 queries", + "confirm_save_title": "Save Changes", + "confirm_save_content_part1": "Your alert built with", + "confirm_save_content_part2": "query will be saved. Press OK to confirm.", + "unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin", + "rule_created": "Rule created successfully", + "rule_edited": "Rule edited successfully", + "expression_missing": "expression is missing in {{where}}", + "metricname_missing": "metric name is missing in {{where}}", + "condition_required": "at least one metric condition is required", + "alertname_required": "alert name is required", + "promql_required": "promql expression is required when query format is set to PromQL", + "chquery_required": "query is required when query format is set to ClickHouse", + "button_savechanges": "Save Rule", + "button_createrule": "Create Rule", + "button_returntorules": "Return to rules", + "button_cancelchanges": "Cancel", + "button_discard": "Discard", + "text_condition1": "Send a notification when", + "text_condition2": "the threshold", + "text_condition3": "during the last", + "option_5min": "5 mins", + "option_10min": "10 mins", + "option_15min": "15 mins", + "option_60min": "60 mins", + "option_4hours": "4 hours", + "option_24hours": "24 hours", + "field_threshold": "Alert Threshold", + "option_allthetimes": "all the times", + "option_atleastonce": "at least once", + "option_onaverage": "on average", + "option_intotal": "in total", + "option_above": "above", + "option_below": "below", + "option_equal": "is equal to", + "option_notequal": "not equal to", + "button_query": "Query", + "button_formula": "Formula", + "tab_qb": "Query Builder", + "tab_promql": "PromQL", + "tab_chquery": "ClickHouse Query", + "title_confirm": "Confirm", + "button_ok": "Yes", + "button_cancel": "No", + "field_promql_expr": "PromQL Expression", + "field_alert_name": "Alert Name", + "field_notification_channel": "Notification Channel", + "field_alert_desc": "Alert Description", + "field_labels": "Labels", + "field_severity": "Severity", + "option_critical": "Critical", + "option_error": "Error", + "option_warning": "Warning", + "option_info": "Info", + "user_guide_headline": "Steps to create an Alert", + "user_guide_qb_step1": "Step 1 - Define the metric", + "user_guide_qb_step1a": "Choose a metric which you want to create an alert on", + "user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed", + "user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric", + "user_guide_qb_step1d": "Create a formula based on Queries if needed", + "user_guide_qb_step2": "Step 2 - Define Alert Conditions", + "user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value", + "user_guide_qb_step2b": "Enter the Alert threshold", + "user_guide_qb_step3": "Step 3 -Alert Configuration", + "user_guide_qb_step3a": "Set alert severity, name and descriptions", + "user_guide_qb_step3b": "Add tags to the alert in the Label field if needed", + "user_guide_pql_step1": "Step 1 - Define the metric", + "user_guide_pql_step1a": "Write a PromQL query for the metric", + "user_guide_pql_step1b": "Format the legends based on labels you want to highlight", + "user_guide_pql_step2": "Step 2 - Define Alert Conditions", + "user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value", + "user_guide_pql_step2b": "Enter the Alert threshold", + "user_guide_pql_step3": "Step 3 -Alert Configuration", + "user_guide_pql_step3a": "Set alert severity, name and descriptions", + "user_guide_pql_step3b": "Add tags to the alert in the Label field if needed", + "user_guide_ch_step1": "Step 1 - Define the metric", + "user_guide_ch_step1a": "Write a Clickhouse query for alert evaluation. Follow <0>this tutorial to learn about query format and supported vars.", + "user_guide_ch_step1b": "Format the legends based on labels you want to highlight in the preview chart", + "user_guide_ch_step2": "Step 2 - Define Alert Conditions", + "user_guide_ch_step2a": "Select the threshold type and whether you want to alert above/below a value", + "user_guide_ch_step2b": "Enter the Alert threshold", + "user_guide_ch_step3": "Step 3 -Alert Configuration", + "user_guide_ch_step3a": "Set alert severity, name and descriptions", + "user_guide_ch_step3b": "Add tags to the alert in the Label field if needed", + "user_tooltip_more_help": "More details on how to create alerts", + "choose_alert_type": "Choose a type for the alert", + "metric_based_alert": "Metric based Alert", + "metric_based_alert_desc": "Send a notification when a condition occurs in the metric data", + "log_based_alert": "Log-based Alert", + "log_based_alert_desc": "Send a notification when a condition occurs in the logs data.", + "traces_based_alert": "Trace-based Alert", + "traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.", + "exceptions_based_alert": "Exceptions-based Alert", + "exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.", + "field_unit": "Threshold unit", + "text_alert_on_absent": "Send a notification if data is missing for", + "text_for": "minutes", + "selected_query_placeholder": "Select query" +} diff --git a/frontend/public/locales/en-GB/channels.json b/frontend/public/locales/en-GB/channels.json new file mode 100644 index 0000000..027501f --- /dev/null +++ b/frontend/public/locales/en-GB/channels.json @@ -0,0 +1,58 @@ +{ + "channel_delete_unexp_error": "Something went wrong", + "channel_delete_success": "Channel Deleted Successfully", + "column_channel_name": "Name", + "column_channel_type": "Type", + "column_channel_action": "Action", + "column_channel_edit": "Edit", + "button_new_channel": "New Alert Channel", + "tooltip_notification_channels": "More details on how to setting notification channels", + "sending_channels_note": "The alerts will be sent to all the configured channels.", + "loading_channels_message": "Loading Channels..", + "page_title_create": "New Notification Channels", + "page_title_edit": "Edit Notification Channels", + "button_save_channel": "Save", + "button_test_channel": "Test", + "button_return": "Back", + "field_channel_name": "Name", + "field_channel_type": "Type", + "field_webhook_url": "Webhook URL", + "field_slack_recipient": "Recipient", + "field_slack_title": "Title", + "field_slack_description": "Description", + "field_webhook_username": "User Name (optional)", + "field_webhook_password": "Password (optional)", + "field_pager_routing_key": "Routing Key", + "field_pager_description": "Description", + "field_pager_severity": "Severity", + "field_pager_details": "Additional Information", + "field_pager_component": "Component", + "field_pager_group": "Group", + "field_pager_class": "Class", + "field_pager_client": "Client", + "field_pager_client_url": "Client URL", + "placeholder_slack_description": "Description", + "placeholder_pager_description": "Description", + "help_pager_client": "Shows up as event source in Pagerduty", + "help_pager_client_url": "Shows up as event source link in Pagerduty", + "help_pager_class": "The class/type of the event", + "help_pager_details": "Specify a key-value format (must be a valid json)", + "help_pager_group": "A cluster or grouping of sources", + "help_pager_component": "The part or component of the affected system that is broke", + "help_pager_severity": "Severity of the incident, must be one of: must be one of the following: 'critical', 'warning', 'error' or 'info'", + "help_webhook_username": "Leave empty for bearer auth or when authentication is not necessary.", + "help_webhook_password": "Specify a password or bearer token", + "help_pager_description": "Shows up as description in pagerduty", + "channel_creation_done": "Successfully created the channel", + "channel_creation_failed": "An unexpected error occurred while creating this channel", + "channel_edit_done": "Channels Edited Successfully", + "channel_edit_failed": "An unexpected error occurred while updating this channel", + "selected_channel_invalid": "Channel type selected is invalid", + "username_no_password": "A Password must be provided with user name", + "test_unsupported": "Sorry, this channel type does not support test yet", + "channel_test_done": "An alert has been sent to this channel", + "channel_test_failed": "Failed to send a test message to this channel, please confirm that the parameters are set correctly", + "channel_test_unexpected": "An unexpected error occurred while sending a message to this channel, please try again", + "webhook_url_required": "Webhook URL is mandatory", + "slack_channel_help": "Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace)" +} \ No newline at end of file diff --git a/frontend/public/locales/en-GB/common.json b/frontend/public/locales/en-GB/common.json new file mode 100644 index 0000000..f167aec --- /dev/null +++ b/frontend/public/locales/en-GB/common.json @@ -0,0 +1,10 @@ +{ + "something_went_wrong": "Something went wrong", + "already_logged_in": "Already Logged In", + "success": "Success", + "cancel": "Cancel", + "share": "Share", + "save": "Save", + "edit": "Edit", + "logged_in": "Logged In" +} diff --git a/frontend/public/locales/en-GB/dashboard.json b/frontend/public/locales/en-GB/dashboard.json new file mode 100644 index 0000000..bc7969d --- /dev/null +++ b/frontend/public/locales/en-GB/dashboard.json @@ -0,0 +1,29 @@ +{ + "create_dashboard": "Create Dashboard", + "import_json": "Import JSON", + "import_grafana_json": "Import Grafana JSON", + "copy_to_clipboard": "Copy To ClipBoard", + "download_json": "Download JSON", + "view_json": "View JSON", + "export_dashboard": "Export this dashboard.", + "upload_json_file": "Upload JSON file", + "paste_json_below": "Paste JSON below", + "error_upload_json": "Invalid JSON", + "load_json": "Load JSON", + "import_dashboard_by_pasting": "Import dashboard by pasting JSON or importing JSON file", + "error_loading_json": "Error loading JSON file", + "empty_json_not_allowed": "Empty JSON is not allowed", + "new_dashboard_title": "Sample Title", + "layout_saved_successfully": "Layout saved successfully", + "add_panel": "Add Panel", + "save_layout": "Save Layout", + "variable_updated_successfully": "Variable updated successfully", + "error_while_updating_variable": "Error while updating variable", + "dashboard_has_been_updated": "Dashboard has been updated", + "do_you_want_to_refresh_the_dashboard": "Do you want to refresh the dashboard?", + "delete_dashboard_success": "{{name}} dashboard deleted successfully", + "dashboard_unsave_changes": "There are unsaved changes in the Query builder, please stage and run the query or the changes will be lost. Press OK to discard.", + "dashboard_save_changes": "Your graph built with {{queryTag}} query will be saved. Press OK to confirm.", + "your_graph_build_with": "Your graph built with", + "dashboard_ok_confirm": "query will be saved. Press OK to confirm." +} diff --git a/frontend/public/locales/en-GB/errorDetails.json b/frontend/public/locales/en-GB/errorDetails.json new file mode 100644 index 0000000..f29f499 --- /dev/null +++ b/frontend/public/locales/en-GB/errorDetails.json @@ -0,0 +1,7 @@ +{ + "see_trace_graph": "See what happened before and after this error in a trace graph", + "see_error_in_trace_graph": "See the error in trace graph", + "stack_trace": "Stacktrace", + "older": "Older", + "newer": "Newer" +} diff --git a/frontend/public/locales/en-GB/explorer.json b/frontend/public/locales/en-GB/explorer.json new file mode 100644 index 0000000..b4ffa61 --- /dev/null +++ b/frontend/public/locales/en-GB/explorer.json @@ -0,0 +1,3 @@ +{ + "name_of_the_view": "Name of the view" +} \ No newline at end of file diff --git a/frontend/public/locales/en-GB/generalSettings.json b/frontend/public/locales/en-GB/generalSettings.json new file mode 100644 index 0000000..1519873 --- /dev/null +++ b/frontend/public/locales/en-GB/generalSettings.json @@ -0,0 +1,21 @@ +{ + "total_retention_period": "Total Retention Period", + "move_to_s3": "Move to S3\n(should be lower than total retention period)", + "status_message": { + "success": "Your last call to change retention period to {{total_retention}} {{s3_part}} was successful.", + "failed": "Your last call to change retention period to {{total_retention}} {{s3_part}} failed. Please try again.", + "pending": "Your last call to change retention period to {{total_retention}} {{s3_part}} is pending. This may take some time.", + "s3_part": "and S3 to {{s3_retention}}" + }, + "retention_save_button": { + "pending": "Updating {{name}} retention period", + "success": "Save" + }, + "retention_request_race_condition": "Your request to change retention period has failed, as another request is still in process.", + "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 {{name}}." +} diff --git a/frontend/public/locales/en-GB/licenses.json b/frontend/public/locales/en-GB/licenses.json new file mode 100644 index 0000000..ed7c3ee --- /dev/null +++ b/frontend/public/locales/en-GB/licenses.json @@ -0,0 +1,13 @@ +{ + "column_license_key": "License Key", + "column_valid_from": "Valid From", + "column_valid_until": "Valid Until", + "column_license_status": "Status", + "button_apply": "Apply", + "placeholder_license_key": "Enter a License Key", + "tab_current_license": "Current License", + "tab_license_history": "History", + "loading_licenses": "Loading licenses...", + "enter_license_key": "Please enter a license key", + "license_applied": "License applied successfully" +} diff --git a/frontend/public/locales/en-GB/login.json b/frontend/public/locales/en-GB/login.json new file mode 100644 index 0000000..84c9e1d --- /dev/null +++ b/frontend/public/locales/en-GB/login.json @@ -0,0 +1,22 @@ +{ + "label_email": "Email", + "placeholder_email": "name@yourcompany.com", + "label_password": "Password", + "button_initiate_login": "Next", + "button_login": "Login", + "login_page_title": "Login with SigNoz", + "login_with_sso": "Login with SSO", + "login_with_pwd": "Login with password", + "forgot_password": "Forgot password?", + "create_an_account": "Create an account", + "prompt_if_admin": "If you are admin,", + "prompt_create_account": "If you are setting up SigNoz for the first time,", + "prompt_no_account": "Don't have an account? Contact your admin to send you an invite link.", + "prompt_forgot_password": "Ask your admin to reset your password and send you a new invite link", + "prompt_on_sso_error": "Are you trying to resolve SSO configuration issue?", + "unexpected_error": "Sorry, something went wrong", + "failed_to_login": "sorry, failed to login", + "invalid_email": "Please enter a valid email address", + "invalid_account": "This account does not exist. To create a new account, contact your admin to get an invite link", + "invalid_config": "Invalid configuration detected, please contact your administrator" +} \ No newline at end of file diff --git a/frontend/public/locales/en-GB/logs.json b/frontend/public/locales/en-GB/logs.json new file mode 100644 index 0000000..804f66f --- /dev/null +++ b/frontend/public/locales/en-GB/logs.json @@ -0,0 +1 @@ +{ "fetching_log_lines": "Fetching log lines" } diff --git a/frontend/public/locales/en-GB/organizationsettings.json b/frontend/public/locales/en-GB/organizationsettings.json new file mode 100644 index 0000000..deae966 --- /dev/null +++ b/frontend/public/locales/en-GB/organizationsettings.json @@ -0,0 +1,19 @@ +{ + "display_name": "Display Name", + "signoz": "SigNoz", + "email_address": "Email address", + "name_optional": "Name (optional)", + "role": "Role", + "email_placeholder": "john@signoz.io", + "name_placeholder": "John", + "add_another_team_member": "Add another team member", + "invite_team_members": "Invite team members", + "invite_members": "Invite Members", + "pending_invites": "Pending Invites", + "authenticated_domains": "Authenticated Domains", + "delete_domain_message": "Are you sure you want to delete this domain?", + "delete_domain": "Delete Domain", + "add_domain": "Add Domains", + "saml_settings": "Your SAML settings have been saved, please login from incognito window to confirm that it has been set up correctly", + "invite_link_share_manually": "After inviting members, please copy the invite link and send them the link manually" +} diff --git a/frontend/public/locales/en-GB/routes.json b/frontend/public/locales/en-GB/routes.json new file mode 100644 index 0000000..ede3f46 --- /dev/null +++ b/frontend/public/locales/en-GB/routes.json @@ -0,0 +1,15 @@ +{ + "general": "General", + "alert_channels": "Alert Channels", + "organization_settings": "Organization Settings", + "ingestion_settings": "Ingestion Settings", + "api_keys": "Access Tokens", + "my_settings": "My Settings", + "overview_metrics": "Overview Metrics", + "dbcall_metrics": "Database Calls", + "external_metrics": "External Calls", + "pipeline": "Pipeline", + "pipelines": "Pipelines", + "archives": "Archives", + "logs_to_metrics": "Logs To Metrics" +} diff --git a/frontend/public/locales/en-GB/rules.json b/frontend/public/locales/en-GB/rules.json new file mode 100644 index 0000000..9d55a0b --- /dev/null +++ b/frontend/public/locales/en-GB/rules.json @@ -0,0 +1,86 @@ +{ + "preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.", + "preview_chart_threshold_label": "Threshold", + "placeholder_label_key_pair": "Click here to enter a label (key value pairs)", + "button_yes": "Yes", + "button_no": "No", + "remove_label_confirm": "This action will remove all the labels. Do you want to proceed?", + "remove_label_success": "Labels cleared", + "alert_form_step1": "Step 1 - Define the metric", + "alert_form_step2": "Step 2 - Define Alert Conditions", + "alert_form_step3": "Step 3 - Alert Configuration", + "metric_query_max_limit": "Can not create query. You can create maximum of 5 queries", + "confirm_save_title": "Save Changes", + "confirm_save_content_part1": "Your alert built with", + "confirm_save_content_part2": "query will be saved. Press OK to confirm.", + "unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin", + "rule_created": "Rule created successfully", + "rule_edited": "Rule edited successfully", + "expression_missing": "expression is missing in {{where}}", + "metricname_missing": "metric name is missing in {{where}}", + "condition_required": "at least one metric condition is required", + "alertname_required": "alert name is required", + "promql_required": "promql expression is required when query format is set to PromQL", + "button_savechanges": "Save Rule", + "button_createrule": "Create Rule", + "button_returntorules": "Return to rules", + "button_cancelchanges": "Cancel", + "button_discard": "Discard", + "text_condition1": "Send a notification when", + "text_condition2": "the threshold", + "text_condition3": "during the last", + "option_5min": "5 mins", + "option_10min": "10 mins", + "option_15min": "15 mins", + "option_60min": "60 mins", + "option_4hours": "4 hours", + "option_24hours": "24 hours", + "field_threshold": "Alert Threshold", + "option_allthetimes": "all the times", + "option_atleastonce": "at least once", + "option_onaverage": "on average", + "option_intotal": "in total", + "option_above": "above", + "option_below": "below", + "option_equal": "is equal to", + "option_notequal": "not equal to", + "button_query": "Query", + "button_formula": "Formula", + "tab_qb": "Query Builder", + "tab_promql": "PromQL", + "title_confirm": "Confirm", + "button_ok": "Yes", + "button_cancel": "No", + "field_promql_expr": "PromQL Expression", + "field_alert_name": "Alert Name", + "field_alert_desc": "Alert Description", + "field_notification_channel": "Notification Channel", + "field_labels": "Labels", + "field_severity": "Severity", + "option_critical": "Critical", + "option_error": "Error", + "option_warning": "Warning", + "option_info": "Info", + "user_guide_headline": "Steps to create an Alert", + "user_guide_qb_step1": "Step 1 - Define the metric", + "user_guide_qb_step1a": "Choose a metric which you want to create an alert on", + "user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed", + "user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric", + "user_guide_qb_step1d": "Create a formula based on Queries if needed", + "user_guide_qb_step2": "Step 2 - Define Alert Conditions", + "user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value", + "user_guide_qb_step2b": "Enter the Alert threshold", + "user_guide_qb_step3": "Step 3 -Alert Configuration", + "user_guide_qb_step3a": "Set alert severity, name and descriptions", + "user_guide_qb_step3b": "Add tags to the alert in the Label field if needed", + "user_guide_pql_step1": "Step 1 - Define the metric", + "user_guide_pql_step1a": "Write a PromQL query for the metric", + "user_guide_pql_step1b": "Format the legends based on labels you want to highlight", + "user_guide_pql_step2": "Step 2 - Define Alert Conditions", + "user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value", + "user_guide_pql_step2b": "Enter the Alert threshold", + "user_guide_pql_step3": "Step 3 -Alert Configuration", + "user_guide_pql_step3a": "Set alert severity, name and descriptions", + "user_guide_pql_step3b": "Add tags to the alert in the Label field if needed", + "user_tooltip_more_help": "More details on how to create alerts" +} diff --git a/frontend/public/locales/en-GB/services.json b/frontend/public/locales/en-GB/services.json new file mode 100644 index 0000000..4c49847 --- /dev/null +++ b/frontend/public/locales/en-GB/services.json @@ -0,0 +1,3 @@ +{ + "rps_over_100": "You are sending data at more than 100 RPS, your ingestion may be rate limited. Please reach out to us via Intercom support." +} diff --git a/frontend/public/locales/en-GB/settings.json b/frontend/public/locales/en-GB/settings.json new file mode 100644 index 0000000..b5041b3 --- /dev/null +++ b/frontend/public/locales/en-GB/settings.json @@ -0,0 +1,5 @@ +{ + "current_password": "Current Password", + "new_password": "New Password", + "change_password": "Change Password" +} diff --git a/frontend/public/locales/en-GB/signup.json b/frontend/public/locales/en-GB/signup.json new file mode 100644 index 0000000..9e0d586 --- /dev/null +++ b/frontend/public/locales/en-GB/signup.json @@ -0,0 +1,18 @@ +{ + "label_email": "Email", + "placeholder_email": "name@yourcompany.com", + "label_password": "Password", + "label_confirm_password": "Confirm Password", + "label_firstname": "First Name", + "placeholder_firstname": "Your Name", + "label_orgname": "Organization Name", + "placeholder_orgname": "Your Company", + "prompt_keepme_posted": "Keep me updated on new SigNoz features", + "prompt_anonymise": "Anonymise my usage data. We collect data to measure product usage", + "failed_confirm_password": "Passwords don’t match. Please try again", + "unexpected_error": "Something went wrong", + "failed_to_initiate_login": "Signup completed but failed to initiate login", + "token_required": "Invite token is required for signup, please request one from your admin", + "button_get_started": "Get Started", + "prompt_admin_warning": "This will create an admin account. If you are not an admin, please ask your admin for an invite link" +} \ No newline at end of file diff --git a/frontend/public/locales/en-GB/titles.json b/frontend/public/locales/en-GB/titles.json new file mode 100644 index 0000000..0eb98e9 --- /dev/null +++ b/frontend/public/locales/en-GB/titles.json @@ -0,0 +1,42 @@ +{ + "SIGN_UP": "SigNoz | Sign Up", + "LOGIN": "SigNoz | Login", + "GET_STARTED": "SigNoz | Get Started", + "SERVICE_METRICS": "SigNoz | Service Metrics", + "SERVICE_MAP": "SigNoz | Service Map", + "TRACE": "SigNoz | Trace", + "TRACE_DETAIL": "SigNoz | Trace Detail", + "TRACES_EXPLORER": "SigNoz | Traces Explorer", + "SETTINGS": "SigNoz | Settings", + "USAGE_EXPLORER": "SigNoz | Usage Explorer", + "APPLICATION": "SigNoz | Home", + "BILLING": "SigNoz | Billing", + "ALL_DASHBOARD": "SigNoz | All Dashboards", + "DASHBOARD": "SigNoz | Dashboard", + "DASHBOARD_WIDGET": "SigNoz | Dashboard Widget", + "EDIT_ALERTS": "SigNoz | Edit Alerts", + "LIST_ALL_ALERT": "SigNoz | All Alerts", + "ALERTS_NEW": "SigNoz | New Alert", + "ALL_CHANNELS": "SigNoz | All Channels", + "CHANNELS_NEW": "SigNoz | New Channel", + "CHANNELS_EDIT": "SigNoz | Edit Channel", + "ALL_ERROR": "SigNoz | All Errors", + "ERROR_DETAIL": "SigNoz | Error Detail", + "VERSION": "SigNoz | Version", + "MY_SETTINGS": "SigNoz | My Settings", + "ORG_SETTINGS": "SigNoz | Organization Settings", + "INGESTION_SETTINGS": "SigNoz | Ingestion Settings", + "API_KEYS": "SigNoz | Access Tokens", + "SOMETHING_WENT_WRONG": "SigNoz | Something Went Wrong", + "UN_AUTHORIZED": "SigNoz | Unauthorized", + "NOT_FOUND": "SigNoz | Page Not Found", + "LOGS": "SigNoz | Logs", + "LOGS_EXPLORER": "SigNoz | Logs Explorer", + "LIVE_LOGS": "SigNoz | Live Logs", + "HOME_PAGE": "Open source Observability Platform | SigNoz", + "PASSWORD_RESET": "SigNoz | Password Reset", + "LIST_LICENSES": "SigNoz | List of Licenses", + "WORKSPACE_LOCKED": "SigNoz | Workspace Locked", + "SUPPORT": "SigNoz | Support", + "DEFAULT": "Open source Observability Platform | SigNoz" +} diff --git a/frontend/public/locales/en-GB/trace.json b/frontend/public/locales/en-GB/trace.json new file mode 100644 index 0000000..c1e5519 --- /dev/null +++ b/frontend/public/locales/en-GB/trace.json @@ -0,0 +1,11 @@ +{ + "options_menu": { + "options": "Options", + "format": "Format", + "raw": "Raw", + "default": "Default", + "column": "Column", + "maxLines": "Max lines per Row", + "addColumn": "Add a column" + } +} diff --git a/frontend/public/locales/en-GB/traceDetails.json b/frontend/public/locales/en-GB/traceDetails.json new file mode 100644 index 0000000..bb61ff2 --- /dev/null +++ b/frontend/public/locales/en-GB/traceDetails.json @@ -0,0 +1,3 @@ +{ + "search_tags": "Search Tag Names" +} diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json new file mode 100644 index 0000000..c1bd296 --- /dev/null +++ b/frontend/public/locales/en-GB/translation.json @@ -0,0 +1,19 @@ +{ + "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 losing out 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", + "all_errors": "All Exceptions", + "index_fields": "Index Fields", + "pipelines": "Pipelines" + } +} diff --git a/frontend/public/locales/en/alerts.json b/frontend/public/locales/en/alerts.json new file mode 100644 index 0000000..0349568 --- /dev/null +++ b/frontend/public/locales/en/alerts.json @@ -0,0 +1,117 @@ +{ + "target_missing": "Please enter a threshold to proceed", + "rule_test_fired": "Test notification sent successfully", + "no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.", + "button_testrule": "Test Notification", + "label_channel_select": "Notification Channels", + "placeholder_channel_select": "select one or more channels", + "channel_select_tooltip": "Leave empty to send this alert on all the configured channels", + "preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.", + "preview_chart_threshold_label": "Threshold", + "placeholder_label_key_pair": "Click here to enter a label (key value pairs)", + "button_yes": "Yes", + "button_no": "No", + "remove_label_confirm": "This action will remove all the labels. Do you want to proceed?", + "remove_label_success": "Labels cleared", + "alert_form_step1": "Step 1 - Define the metric", + "alert_form_step2": "Step 2 - Define Alert Conditions", + "alert_form_step3": "Step 3 - Alert Configuration", + "metric_query_max_limit": "Can not create query. You can create maximum of 5 queries", + "confirm_save_title": "Save Changes", + "confirm_save_content_part1": "Your alert built with", + "confirm_save_content_part2": "query will be saved. Press OK to confirm.", + "unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin", + "rule_created": "Rule created successfully", + "rule_edited": "Rule edited successfully", + "expression_missing": "expression is missing in {{where}}", + "metricname_missing": "metric name is missing in {{where}}", + "condition_required": "at least one metric condition is required", + "alertname_required": "alert name is required", + "promql_required": "promql expression is required when query format is set to PromQL", + "chquery_required": "query is required when query format is set to ClickHouse", + "button_savechanges": "Save Rule", + "button_createrule": "Create Rule", + "button_returntorules": "Return to rules", + "button_cancelchanges": "Cancel", + "button_discard": "Discard", + "text_condition1": "Send a notification when", + "text_condition2": "the threshold", + "text_condition3": "during the last", + "option_5min": "5 mins", + "option_10min": "10 mins", + "option_15min": "15 mins", + "option_60min": "60 mins", + "option_4hours": "4 hours", + "option_24hours": "24 hours", + "field_threshold": "Alert Threshold", + "option_allthetimes": "all the times", + "option_atleastonce": "at least once", + "option_onaverage": "on average", + "option_intotal": "in total", + "option_above": "above", + "option_below": "below", + "option_equal": "is equal to", + "option_notequal": "not equal to", + "button_query": "Query", + "button_formula": "Formula", + "tab_qb": "Query Builder", + "tab_promql": "PromQL", + "tab_chquery": "ClickHouse Query", + "title_confirm": "Confirm", + "button_ok": "Yes", + "button_cancel": "No", + "field_promql_expr": "PromQL Expression", + "field_alert_name": "Alert Name", + "field_alert_desc": "Alert Description", + "field_notification_channel": "Notification Channel", + "field_labels": "Labels", + "field_severity": "Severity", + "option_critical": "Critical", + "option_error": "Error", + "option_warning": "Warning", + "option_info": "Info", + "user_guide_headline": "Steps to create an Alert", + "user_guide_qb_step1": "Step 1 - Define the metric", + "user_guide_qb_step1a": "Choose a metric which you want to create an alert on", + "user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed", + "user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric", + "user_guide_qb_step1d": "Create a formula based on Queries if needed", + "user_guide_qb_step2": "Step 2 - Define Alert Conditions", + "user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value", + "user_guide_qb_step2b": "Enter the Alert threshold", + "user_guide_qb_step3": "Step 3 -Alert Configuration", + "user_guide_qb_step3a": "Set alert severity, name and descriptions", + "user_guide_qb_step3b": "Add tags to the alert in the Label field if needed", + "user_guide_pql_step1": "Step 1 - Define the metric", + "user_guide_pql_step1a": "Write a PromQL query for the metric", + "user_guide_pql_step1b": "Format the legends based on labels you want to highlight", + "user_guide_pql_step2": "Step 2 - Define Alert Conditions", + "user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value", + "user_guide_pql_step2b": "Enter the Alert threshold", + "user_guide_pql_step3": "Step 3 -Alert Configuration", + "user_guide_pql_step3a": "Set alert severity, name and descriptions", + "user_guide_pql_step3b": "Add tags to the alert in the Label field if needed", + "user_guide_ch_step1": "Step 1 - Define the metric", + "user_guide_ch_step1a": "Write a Clickhouse query for alert evaluation. Follow <0>this tutorial to learn about query format and supported vars.", + "user_guide_ch_step1b": "Format the legends based on labels you want to highlight in the preview chart", + "user_guide_ch_step2": "Step 2 - Define Alert Conditions", + "user_guide_ch_step2a": "Select the threshold type and whether you want to alert above/below a value", + "user_guide_ch_step2b": "Enter the Alert threshold", + "user_guide_ch_step3": "Step 3 -Alert Configuration", + "user_guide_ch_step3a": "Set alert severity, name and descriptions", + "user_guide_ch_step3b": "Add tags to the alert in the Label field if needed", + "user_tooltip_more_help": "More details on how to create alerts", + "choose_alert_type": "Choose a type for the alert", + "metric_based_alert": "Metric based Alert", + "metric_based_alert_desc": "Send a notification when a condition occurs in the metric data", + "log_based_alert": "Log-based Alert", + "log_based_alert_desc": "Send a notification when a condition occurs in the logs data.", + "traces_based_alert": "Trace-based Alert", + "traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.", + "exceptions_based_alert": "Exceptions-based Alert", + "exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.", + "field_unit": "Threshold unit", + "text_alert_on_absent": "Send a notification if data is missing for", + "text_for": "minutes", + "selected_query_placeholder": "Select query" +} diff --git a/frontend/public/locales/en/apiKeys.json b/frontend/public/locales/en/apiKeys.json new file mode 100644 index 0000000..fb86610 --- /dev/null +++ b/frontend/public/locales/en/apiKeys.json @@ -0,0 +1,3 @@ +{ + "delete_confirm_message": "Are you sure you want to delete {{keyName}} token? Deleting a token is irreversible and cannot be undone." +} diff --git a/frontend/public/locales/en/channels.json b/frontend/public/locales/en/channels.json new file mode 100644 index 0000000..9ab31d6 --- /dev/null +++ b/frontend/public/locales/en/channels.json @@ -0,0 +1,74 @@ +{ + "channel_delete_unexp_error": "Something went wrong", + "channel_delete_success": "Channel Deleted Successfully", + "column_channel_name": "Name", + "column_channel_type": "Type", + "column_channel_action": "Action", + "column_channel_edit": "Edit", + "button_new_channel": "New Alert Channel", + "tooltip_notification_channels": "More details on how to setting notification channels", + "sending_channels_note": "The alerts will be sent to all the configured channels.", + "loading_channels_message": "Loading Channels..", + "page_title_create": "New Notification Channels", + "page_title_edit": "Edit Notification Channels", + "button_save_channel": "Save", + "button_test_channel": "Test", + "button_return": "Back", + "field_channel_name": "Name", + "field_channel_type": "Type", + "field_webhook_url": "Webhook URL", + "field_slack_recipient": "Recipient", + "field_slack_title": "Title", + "field_slack_description": "Description", + "field_opsgenie_api_key": "API Key", + "field_opsgenie_description": "Description", + "placeholder_opsgenie_description": "Description", + "help_email_to": "Email address(es) to send alerts to (comma separated)", + "field_email_to": "To", + "placeholder_email_to": "To", + "help_email_html": "Send email in html format", + "field_email_html": "Email body template", + "placeholder_email_html": "Email body template", + "field_webhook_username": "User Name (optional)", + "field_webhook_password": "Password (optional)", + "field_pager_routing_key": "Routing Key", + "field_pager_description": "Description", + "field_pager_severity": "Severity", + "field_pager_details": "Additional Information", + "field_pager_component": "Component", + "field_pager_group": "Group", + "field_pager_class": "Class", + "field_pager_client": "Client", + "field_pager_client_url": "Client URL", + "field_opsgenie_message": "Message", + "field_opsgenie_priority": "Priority", + "placeholder_slack_description": "Description", + "placeholder_pager_description": "Description", + "placeholder_opsgenie_message": "Message", + "placeholder_opsgenie_priority": "Priority", + "help_pager_client": "Shows up as event source in Pagerduty", + "help_pager_client_url": "Shows up as event source link in Pagerduty", + "help_pager_class": "The class/type of the event", + "help_pager_details": "Specify a key-value format (must be a valid json)", + "help_pager_group": "A cluster or grouping of sources", + "help_pager_component": "The part or component of the affected system that is broke", + "help_pager_severity": "Severity of the incident, must be one of: must be one of the following: 'critical', 'warning', 'error' or 'info'", + "help_webhook_username": "Leave empty for bearer auth or when authentication is not necessary.", + "help_webhook_password": "Specify a password or bearer token", + "help_pager_description": "Shows up as description in pagerduty", + "help_opsgenie_message": "Shows up as message in opsgenie", + "help_opsgenie_priority": "Priority of the incident", + "help_opsgenie_description": "Shows up as description in opsgenie", + "channel_creation_done": "Successfully created the channel", + "channel_creation_failed": "An unexpected error occurred while creating this channel", + "channel_edit_done": "Channels Edited Successfully", + "channel_edit_failed": "An unexpected error occurred while updating this channel", + "selected_channel_invalid": "Channel type selected is invalid", + "username_no_password": "A Password must be provided with user name", + "test_unsupported": "Sorry, this channel type does not support test yet", + "channel_test_done": "An alert has been sent to this channel", + "channel_test_failed": "Failed to send a test message to this channel, please confirm that the parameters are set correctly", + "channel_test_unexpected": "An unexpected error occurred while sending a message to this channel, please try again", + "webhook_url_required": "Webhook URL is mandatory", + "slack_channel_help": "Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace)" +} \ No newline at end of file diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json new file mode 100644 index 0000000..f167aec --- /dev/null +++ b/frontend/public/locales/en/common.json @@ -0,0 +1,10 @@ +{ + "something_went_wrong": "Something went wrong", + "already_logged_in": "Already Logged In", + "success": "Success", + "cancel": "Cancel", + "share": "Share", + "save": "Save", + "edit": "Edit", + "logged_in": "Logged In" +} diff --git a/frontend/public/locales/en/dashboard.json b/frontend/public/locales/en/dashboard.json new file mode 100644 index 0000000..9c0529c --- /dev/null +++ b/frontend/public/locales/en/dashboard.json @@ -0,0 +1,32 @@ +{ + "create_dashboard": "Create Dashboard", + "import_json": "Import JSON", + "import_grafana_json": "Import Grafana JSON", + "copy_to_clipboard": "Copy To ClipBoard", + "download_json": "Download JSON", + "view_json": "View JSON", + "export_dashboard": "Export this dashboard.", + "upload_json_file": "Upload JSON file", + "paste_json_below": "Paste JSON below", + "error_upload_json": "Invalid JSON", + "load_json": "Load JSON", + "import_dashboard_by_pasting": "Import dashboard by pasting JSON or importing JSON file", + "error_loading_json": "Error loading JSON file", + "empty_json_not_allowed": "Empty JSON is not allowed", + "new_dashboard_title": "Sample Title", + "layout_saved_successfully": "Layout saved successfully", + "add_panel": "Add Panel", + "save_layout": "Save Layout", + "full_view": "Full Screen View", + "variable_updated_successfully": "Variable updated successfully", + "error_while_updating_variable": "Error while updating variable", + "dashboard_has_been_updated": "Dashboard has been updated", + "do_you_want_to_refresh_the_dashboard": "Do you want to refresh the dashboard?", + "locked_dashboard_delete_tooltip_admin_author": "Dashboard is locked. Please unlock the dashboard to enable delete.", + "locked_dashboard_delete_tooltip_editor": "Dashboard is locked. Please contact admin to delete the dashboard.", + "delete_dashboard_success": "{{name}} dashboard deleted successfully", + "dashboard_unsave_changes": "There are unsaved changes in the Query builder, please stage and run the query or the changes will be lost. Press OK to discard.", + "dashboard_save_changes": "Your graph built with {{queryTag}} query will be saved. Press OK to confirm.", + "your_graph_build_with": "Your graph built with", + "dashboard_ok_confirm": "query will be saved. Press OK to confirm." +} diff --git a/frontend/public/locales/en/errorDetails.json b/frontend/public/locales/en/errorDetails.json new file mode 100644 index 0000000..da68ed4 --- /dev/null +++ b/frontend/public/locales/en/errorDetails.json @@ -0,0 +1,9 @@ +{ + "see_trace_graph": "See what happened before and after this error in a trace graph", + "see_error_in_trace_graph": "See the error in trace graph", + "stack_trace": "Stacktrace", + "older": "Older", + "newer": "Newer", + "something_went_wrong": "Oops !!! Something went wrong", + "contact_if_issue_exists": "Don't worry, our team is here to help. Please contact support if the issue persists." +} diff --git a/frontend/public/locales/en/explorer.json b/frontend/public/locales/en/explorer.json new file mode 100644 index 0000000..65e4fc2 --- /dev/null +++ b/frontend/public/locales/en/explorer.json @@ -0,0 +1,4 @@ +{ + "name_of_the_view": "Name of the view", + "delete_confirm_message": "Are you sure you want to delete {{viewName}} view? Deleting a view is irreversible and cannot be undone." +} \ No newline at end of file diff --git a/frontend/public/locales/en/generalSettings.json b/frontend/public/locales/en/generalSettings.json new file mode 100644 index 0000000..1519873 --- /dev/null +++ b/frontend/public/locales/en/generalSettings.json @@ -0,0 +1,21 @@ +{ + "total_retention_period": "Total Retention Period", + "move_to_s3": "Move to S3\n(should be lower than total retention period)", + "status_message": { + "success": "Your last call to change retention period to {{total_retention}} {{s3_part}} was successful.", + "failed": "Your last call to change retention period to {{total_retention}} {{s3_part}} failed. Please try again.", + "pending": "Your last call to change retention period to {{total_retention}} {{s3_part}} is pending. This may take some time.", + "s3_part": "and S3 to {{s3_retention}}" + }, + "retention_save_button": { + "pending": "Updating {{name}} retention period", + "success": "Save" + }, + "retention_request_race_condition": "Your request to change retention period has failed, as another request is still in process.", + "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 {{name}}." +} diff --git a/frontend/public/locales/en/licenses.json b/frontend/public/locales/en/licenses.json new file mode 100644 index 0000000..ed7c3ee --- /dev/null +++ b/frontend/public/locales/en/licenses.json @@ -0,0 +1,13 @@ +{ + "column_license_key": "License Key", + "column_valid_from": "Valid From", + "column_valid_until": "Valid Until", + "column_license_status": "Status", + "button_apply": "Apply", + "placeholder_license_key": "Enter a License Key", + "tab_current_license": "Current License", + "tab_license_history": "History", + "loading_licenses": "Loading licenses...", + "enter_license_key": "Please enter a license key", + "license_applied": "License applied successfully" +} diff --git a/frontend/public/locales/en/login.json b/frontend/public/locales/en/login.json new file mode 100644 index 0000000..84c9e1d --- /dev/null +++ b/frontend/public/locales/en/login.json @@ -0,0 +1,22 @@ +{ + "label_email": "Email", + "placeholder_email": "name@yourcompany.com", + "label_password": "Password", + "button_initiate_login": "Next", + "button_login": "Login", + "login_page_title": "Login with SigNoz", + "login_with_sso": "Login with SSO", + "login_with_pwd": "Login with password", + "forgot_password": "Forgot password?", + "create_an_account": "Create an account", + "prompt_if_admin": "If you are admin,", + "prompt_create_account": "If you are setting up SigNoz for the first time,", + "prompt_no_account": "Don't have an account? Contact your admin to send you an invite link.", + "prompt_forgot_password": "Ask your admin to reset your password and send you a new invite link", + "prompt_on_sso_error": "Are you trying to resolve SSO configuration issue?", + "unexpected_error": "Sorry, something went wrong", + "failed_to_login": "sorry, failed to login", + "invalid_email": "Please enter a valid email address", + "invalid_account": "This account does not exist. To create a new account, contact your admin to get an invite link", + "invalid_config": "Invalid configuration detected, please contact your administrator" +} \ No newline at end of file diff --git a/frontend/public/locales/en/logs.json b/frontend/public/locales/en/logs.json new file mode 100644 index 0000000..804f66f --- /dev/null +++ b/frontend/public/locales/en/logs.json @@ -0,0 +1 @@ +{ "fetching_log_lines": "Fetching log lines" } diff --git a/frontend/public/locales/en/organizationsettings.json b/frontend/public/locales/en/organizationsettings.json new file mode 100644 index 0000000..deae966 --- /dev/null +++ b/frontend/public/locales/en/organizationsettings.json @@ -0,0 +1,19 @@ +{ + "display_name": "Display Name", + "signoz": "SigNoz", + "email_address": "Email address", + "name_optional": "Name (optional)", + "role": "Role", + "email_placeholder": "john@signoz.io", + "name_placeholder": "John", + "add_another_team_member": "Add another team member", + "invite_team_members": "Invite team members", + "invite_members": "Invite Members", + "pending_invites": "Pending Invites", + "authenticated_domains": "Authenticated Domains", + "delete_domain_message": "Are you sure you want to delete this domain?", + "delete_domain": "Delete Domain", + "add_domain": "Add Domains", + "saml_settings": "Your SAML settings have been saved, please login from incognito window to confirm that it has been set up correctly", + "invite_link_share_manually": "After inviting members, please copy the invite link and send them the link manually" +} diff --git a/frontend/public/locales/en/pipeline.json b/frontend/public/locales/en/pipeline.json new file mode 100644 index 0000000..d4b95bb --- /dev/null +++ b/frontend/public/locales/en/pipeline.json @@ -0,0 +1,46 @@ +{ + "delete": "Delete", + "filter": "Filter", + "update": "Update", + "create": "Create", + "reorder": "Reorder", + "cancel": "Cancel", + "learn_more": "Learn more about pipelines", + "reorder_pipeline": "Do you want to reorder pipeline?", + "reorder_pipeline_description": "Logs are processed sequentially in processors and pipelines. Reordering it may change how data is processed by them.", + "delete_pipeline": "Do you want to delete pipeline", + "delete_pipeline_description": "Logs are processed sequentially in processors and pipelines. Deleting a pipeline may change content of data processed by other pipelines & processors", + "add_new_pipeline": "Add a New Pipeline", + "new_pipeline": "New Pipeline", + "enter_edit_mode": "Enter Edit Mode", + "save_configuration": "Save Configuration", + "edit_pipeline": "Edit Pipeline", + "create_pipeline": "Create New Pipeline", + "add_new_processor": "Add Processor", + "edit_processor": "Edit Processor", + "create_processor": "Create New Processor", + "processor_type": "Select Processor Type", + "reorder_processor": "Do you want to reorder processor?", + "reorder_processor_description": "Logs are processed sequentially in processors. Reordering it may change how data is processed by them.", + "delete_processor": "Do you want to delete processor", + "delete_processor_description": "Logs are processed sequentially in processors. Deleting a processor may change content of data processed by other processors", + "search_pipeline_placeholder": "Filter Pipelines", + "pipeline_name_placeholder": "Name", + "pipeline_filter_placeholder": "Filter for selecting logs to be processed by this pipeline. Example: service_name = billing", + "pipeline_tags_placeholder": "Tags", + "pipeline_description_placeholder": "Enter description for your pipeline", + "processor_name_placeholder": "Name", + "processor_regex_placeholder": "Regex", + "processor_parsefrom_placeholder": "Parse From", + "processor_parseto_placeholder": "Parse To", + "processor_onerror_placeholder": "on Error", + "processor_pattern_placeholder": "Pattern", + "processor_field_placeholder": "Field", + "processor_value_placeholder": "Value", + "processor_description_placeholder": "example rule: %{word:first}", + "processor_trace_id_placeholder": "Parse Trace ID from", + "processor_span_id_placeholder": "Parse Span ID from", + "processor_trace_flags_placeholder": "Parse Trace flags from", + "processor_from_placeholder": "From", + "processor_to_placeholder": "To" +} diff --git a/frontend/public/locales/en/routes.json b/frontend/public/locales/en/routes.json new file mode 100644 index 0000000..ede3f46 --- /dev/null +++ b/frontend/public/locales/en/routes.json @@ -0,0 +1,15 @@ +{ + "general": "General", + "alert_channels": "Alert Channels", + "organization_settings": "Organization Settings", + "ingestion_settings": "Ingestion Settings", + "api_keys": "Access Tokens", + "my_settings": "My Settings", + "overview_metrics": "Overview Metrics", + "dbcall_metrics": "Database Calls", + "external_metrics": "External Calls", + "pipeline": "Pipeline", + "pipelines": "Pipelines", + "archives": "Archives", + "logs_to_metrics": "Logs To Metrics" +} diff --git a/frontend/public/locales/en/rules.json b/frontend/public/locales/en/rules.json new file mode 100644 index 0000000..9d55a0b --- /dev/null +++ b/frontend/public/locales/en/rules.json @@ -0,0 +1,86 @@ +{ + "preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.", + "preview_chart_threshold_label": "Threshold", + "placeholder_label_key_pair": "Click here to enter a label (key value pairs)", + "button_yes": "Yes", + "button_no": "No", + "remove_label_confirm": "This action will remove all the labels. Do you want to proceed?", + "remove_label_success": "Labels cleared", + "alert_form_step1": "Step 1 - Define the metric", + "alert_form_step2": "Step 2 - Define Alert Conditions", + "alert_form_step3": "Step 3 - Alert Configuration", + "metric_query_max_limit": "Can not create query. You can create maximum of 5 queries", + "confirm_save_title": "Save Changes", + "confirm_save_content_part1": "Your alert built with", + "confirm_save_content_part2": "query will be saved. Press OK to confirm.", + "unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin", + "rule_created": "Rule created successfully", + "rule_edited": "Rule edited successfully", + "expression_missing": "expression is missing in {{where}}", + "metricname_missing": "metric name is missing in {{where}}", + "condition_required": "at least one metric condition is required", + "alertname_required": "alert name is required", + "promql_required": "promql expression is required when query format is set to PromQL", + "button_savechanges": "Save Rule", + "button_createrule": "Create Rule", + "button_returntorules": "Return to rules", + "button_cancelchanges": "Cancel", + "button_discard": "Discard", + "text_condition1": "Send a notification when", + "text_condition2": "the threshold", + "text_condition3": "during the last", + "option_5min": "5 mins", + "option_10min": "10 mins", + "option_15min": "15 mins", + "option_60min": "60 mins", + "option_4hours": "4 hours", + "option_24hours": "24 hours", + "field_threshold": "Alert Threshold", + "option_allthetimes": "all the times", + "option_atleastonce": "at least once", + "option_onaverage": "on average", + "option_intotal": "in total", + "option_above": "above", + "option_below": "below", + "option_equal": "is equal to", + "option_notequal": "not equal to", + "button_query": "Query", + "button_formula": "Formula", + "tab_qb": "Query Builder", + "tab_promql": "PromQL", + "title_confirm": "Confirm", + "button_ok": "Yes", + "button_cancel": "No", + "field_promql_expr": "PromQL Expression", + "field_alert_name": "Alert Name", + "field_alert_desc": "Alert Description", + "field_notification_channel": "Notification Channel", + "field_labels": "Labels", + "field_severity": "Severity", + "option_critical": "Critical", + "option_error": "Error", + "option_warning": "Warning", + "option_info": "Info", + "user_guide_headline": "Steps to create an Alert", + "user_guide_qb_step1": "Step 1 - Define the metric", + "user_guide_qb_step1a": "Choose a metric which you want to create an alert on", + "user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed", + "user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric", + "user_guide_qb_step1d": "Create a formula based on Queries if needed", + "user_guide_qb_step2": "Step 2 - Define Alert Conditions", + "user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value", + "user_guide_qb_step2b": "Enter the Alert threshold", + "user_guide_qb_step3": "Step 3 -Alert Configuration", + "user_guide_qb_step3a": "Set alert severity, name and descriptions", + "user_guide_qb_step3b": "Add tags to the alert in the Label field if needed", + "user_guide_pql_step1": "Step 1 - Define the metric", + "user_guide_pql_step1a": "Write a PromQL query for the metric", + "user_guide_pql_step1b": "Format the legends based on labels you want to highlight", + "user_guide_pql_step2": "Step 2 - Define Alert Conditions", + "user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value", + "user_guide_pql_step2b": "Enter the Alert threshold", + "user_guide_pql_step3": "Step 3 -Alert Configuration", + "user_guide_pql_step3a": "Set alert severity, name and descriptions", + "user_guide_pql_step3b": "Add tags to the alert in the Label field if needed", + "user_tooltip_more_help": "More details on how to create alerts" +} diff --git a/frontend/public/locales/en/services.json b/frontend/public/locales/en/services.json new file mode 100644 index 0000000..4c49847 --- /dev/null +++ b/frontend/public/locales/en/services.json @@ -0,0 +1,3 @@ +{ + "rps_over_100": "You are sending data at more than 100 RPS, your ingestion may be rate limited. Please reach out to us via Intercom support." +} diff --git a/frontend/public/locales/en/settings.json b/frontend/public/locales/en/settings.json new file mode 100644 index 0000000..94a4f71 --- /dev/null +++ b/frontend/public/locales/en/settings.json @@ -0,0 +1,6 @@ +{ + "current_password": "Current Password", + "new_password": "New Password", + "change_password": "Change Password", + "input_password": "input password" +} diff --git a/frontend/public/locales/en/signup.json b/frontend/public/locales/en/signup.json new file mode 100644 index 0000000..9e0d586 --- /dev/null +++ b/frontend/public/locales/en/signup.json @@ -0,0 +1,18 @@ +{ + "label_email": "Email", + "placeholder_email": "name@yourcompany.com", + "label_password": "Password", + "label_confirm_password": "Confirm Password", + "label_firstname": "First Name", + "placeholder_firstname": "Your Name", + "label_orgname": "Organization Name", + "placeholder_orgname": "Your Company", + "prompt_keepme_posted": "Keep me updated on new SigNoz features", + "prompt_anonymise": "Anonymise my usage data. We collect data to measure product usage", + "failed_confirm_password": "Passwords don’t match. Please try again", + "unexpected_error": "Something went wrong", + "failed_to_initiate_login": "Signup completed but failed to initiate login", + "token_required": "Invite token is required for signup, please request one from your admin", + "button_get_started": "Get Started", + "prompt_admin_warning": "This will create an admin account. If you are not an admin, please ask your admin for an invite link" +} \ No newline at end of file diff --git a/frontend/public/locales/en/titles.json b/frontend/public/locales/en/titles.json new file mode 100644 index 0000000..e707c99 --- /dev/null +++ b/frontend/public/locales/en/titles.json @@ -0,0 +1,52 @@ +{ + "SIGN_UP": "SigNoz | Sign Up", + "LOGIN": "SigNoz | Login", + "SERVICE_METRICS": "SigNoz | Service Metrics", + "SERVICE_MAP": "SigNoz | Service Map", + "GET_STARTED": "SigNoz | Get Started", + "GET_STARTED_APPLICATION_MONITORING": "SigNoz | Get Started | APM", + "GET_STARTED_LOGS_MANAGEMENT": "SigNoz | Get Started | Logs", + "GET_STARTED_INFRASTRUCTURE_MONITORING": "SigNoz | Get Started | Infrastructure", + "GET_STARTED_AWS_MONITORING": "SigNoz | Get Started | AWS", + "TRACE": "SigNoz | Trace", + "TRACE_DETAIL": "SigNoz | Trace Detail", + "TRACES_EXPLORER": "SigNoz | Traces Explorer", + "SETTINGS": "SigNoz | Settings", + "USAGE_EXPLORER": "SigNoz | Usage Explorer", + "APPLICATION": "SigNoz | Home", + "BILLING": "SigNoz | Billing", + "ALL_DASHBOARD": "SigNoz | All Dashboards", + "DASHBOARD": "SigNoz | Dashboard", + "DASHBOARD_WIDGET": "SigNoz | Dashboard Widget", + "EDIT_ALERTS": "SigNoz | Edit Alerts", + "LIST_ALL_ALERT": "SigNoz | All Alerts", + "ALERTS_NEW": "SigNoz | New Alert", + "ALL_CHANNELS": "SigNoz | All Channels", + "CHANNELS_NEW": "SigNoz | New Channel", + "CHANNELS_EDIT": "SigNoz | Edit Channel", + "ALL_ERROR": "SigNoz | All Errors", + "ERROR_DETAIL": "SigNoz | Error Detail", + "VERSION": "SigNoz | Version", + "MY_SETTINGS": "SigNoz | My Settings", + "ORG_SETTINGS": "SigNoz | Organization Settings", + "INGESTION_SETTINGS": "SigNoz | Ingestion Settings", + "API_KEYS": "SigNoz | Access Tokens", + "SOMETHING_WENT_WRONG": "SigNoz | Something Went Wrong", + "UN_AUTHORIZED": "SigNoz | Unauthorized", + "NOT_FOUND": "SigNoz | Page Not Found", + "LOGS": "SigNoz | Logs", + "LOGS_EXPLORER": "SigNoz | Logs Explorer", + "OLD_LOGS_EXPLORER": "SigNoz | Old Logs Explorer", + "LIVE_LOGS": "SigNoz | Live Logs", + "LOGS_PIPELINES": "SigNoz | Logs Pipelines", + "HOME_PAGE": "Open source Observability Platform | SigNoz", + "PASSWORD_RESET": "SigNoz | Password Reset", + "LIST_LICENSES": "SigNoz | List of Licenses", + "WORKSPACE_LOCKED": "SigNoz | Workspace Locked", + "SUPPORT": "SigNoz | Support", + "LOGS_SAVE_VIEWS": "SigNoz | Logs Saved Views", + "TRACES_SAVE_VIEWS": "SigNoz | Traces Saved Views", + "DEFAULT": "Open source Observability Platform | SigNoz", + "SHORTCUTS": "SigNoz | Shortcuts", + "INTEGRATIONS_INSTALLED": "SigNoz | Integrations" +} diff --git a/frontend/public/locales/en/trace.json b/frontend/public/locales/en/trace.json new file mode 100644 index 0000000..c1e5519 --- /dev/null +++ b/frontend/public/locales/en/trace.json @@ -0,0 +1,11 @@ +{ + "options_menu": { + "options": "Options", + "format": "Format", + "raw": "Raw", + "default": "Default", + "column": "Column", + "maxLines": "Max lines per Row", + "addColumn": "Add a column" + } +} diff --git a/frontend/public/locales/en/traceDetails.json b/frontend/public/locales/en/traceDetails.json new file mode 100644 index 0000000..bb61ff2 --- /dev/null +++ b/frontend/public/locales/en/traceDetails.json @@ -0,0 +1,3 @@ +{ + "search_tags": "Search Tag Names" +} diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json new file mode 100644 index 0000000..08b795d --- /dev/null +++ b/frontend/public/locales/en/translation.json @@ -0,0 +1,19 @@ +{ + "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 missing out 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", + "all_errors": "All Exceptions", + "index_fields": "Index Fields", + "pipelines": "Pipelines" + } +} diff --git a/frontend/public/locales/en/valueGraph.json b/frontend/public/locales/en/valueGraph.json new file mode 100644 index 0000000..76aa50e --- /dev/null +++ b/frontend/public/locales/en/valueGraph.json @@ -0,0 +1,3 @@ +{ + "this_value_satisfies_multiple_thresholds": "This value satisfies multiple thresholds." +} \ No newline at end of file diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json new file mode 100644 index 0000000..7c86642 --- /dev/null +++ b/frontend/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/frontend/public/robots.txt b/frontend/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/frontend/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/frontend/public/signoz-signup.svg b/frontend/public/signoz-signup.svg new file mode 100644 index 0000000..67c45b1 --- /dev/null +++ b/frontend/public/signoz-signup.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/public/signoz.svg b/frontend/public/signoz.svg new file mode 100644 index 0000000..cdfe945 --- /dev/null +++ b/frontend/public/signoz.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/scripts/typecheck-staged.sh b/frontend/scripts/typecheck-staged.sh new file mode 100644 index 0000000..0990e81 --- /dev/null +++ b/frontend/scripts/typecheck-staged.sh @@ -0,0 +1,25 @@ +files=""; + +# lint-staged will pass all files in $1 $2 $3 etc. iterate and concat. +for var in "$@" +do + files="$files \"$var\"," +done + +# create temporary tsconfig which includes only passed files +str="{ + \"extends\": \"./tsconfig.json\", + \"include\": [ \"src/typings/**/*.ts\",\"src/**/*.d.ts\", \"./babel.config.js\", \"./jest.config.ts\", \"./.eslintrc.js\",\"./__mocks__\",\"./conf/default.conf\",\"./public\",\"./tests\",\"./playwright.config.ts\",\"./commitlint.config.ts\",\"./webpack.config.js\",\"./webpack.config.prod.js\",\"./jest.setup.ts\",\"./**/*.d.ts\",$files] +}" +echo $str > tsconfig.tmp + +# run typecheck using temp config +tsc -p ./tsconfig.tmp + +# capture exit code of tsc +code=$? + +# delete temp config +rm ./tsconfig.tmp + +exit $code diff --git a/frontend/sonar-project.properties b/frontend/sonar-project.properties new file mode 100644 index 0000000..c8676c9 --- /dev/null +++ b/frontend/sonar-project.properties @@ -0,0 +1,6 @@ +sonar.organization=signoz +sonar.projectKey=SigNoz_signoz + +# relative paths to source directories. More details and properties are described +# in https://sonarcloud.io/documentation/project-administration/narrowing-the-focus/ +sonar.sources=./src \ No newline at end of file diff --git a/frontend/src/AppRoutes/Private.tsx b/frontend/src/AppRoutes/Private.tsx new file mode 100644 index 0000000..669def6 --- /dev/null +++ b/frontend/src/AppRoutes/Private.tsx @@ -0,0 +1,231 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import getLocalStorageApi from 'api/browser/localstorage/get'; +import loginApi from 'api/user/login'; +import { Logout } from 'api/utils'; +import Spinner from 'components/Spinner'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import ROUTES from 'constants/routes'; +import useLicense from 'hooks/useLicense'; +import { useNotifications } from 'hooks/useNotifications'; +import history from 'lib/history'; +import { ReactChild, useEffect, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; +import { matchPath, Redirect, useLocation } from 'react-router-dom'; +import { Dispatch } from 'redux'; +import { AppState } from 'store/reducers'; +import { getInitialUserTokenRefreshToken } from 'store/utils'; +import AppActions from 'types/actions'; +import { UPDATE_USER_IS_FETCH } from 'types/actions/app'; +import AppReducer from 'types/reducer/app'; +import { routePermission } from 'utils/permission'; + +import routes, { + LIST_LICENSES, + oldNewRoutesMapping, + oldRoutes, +} from './routes'; +import afterLogin from './utils'; + +function PrivateRoute({ children }: PrivateRouteProps): JSX.Element { + const location = useLocation(); + const { pathname } = location; + + const mapRoutes = useMemo( + () => + new Map( + [...routes, LIST_LICENSES].map((e) => { + const currentPath = matchPath(pathname, { + path: e.path, + }); + return [currentPath === null ? null : 'current', e]; + }), + ), + [pathname], + ); + + const { + data: licensesData, + isFetching: isFetchingLicensesData, + } = useLicense(); + + const { + isUserFetching, + isUserFetchingError, + isLoggedIn: isLoggedInState, + } = useSelector((state) => state.app); + + const { t } = useTranslation(['common']); + const localStorageUserAuthToken = getInitialUserTokenRefreshToken(); + + const dispatch = useDispatch>(); + + const { notifications } = useNotifications(); + + const currentRoute = mapRoutes.get('current'); + + const isOldRoute = oldRoutes.indexOf(pathname) > -1; + + const isLocalStorageLoggedIn = + getLocalStorageApi(LOCALSTORAGE.IS_LOGGED_IN) === 'true'; + + const navigateToLoginIfNotLoggedIn = (isLoggedIn = isLoggedInState): void => { + dispatch({ + type: UPDATE_USER_IS_FETCH, + payload: { + isUserFetching: false, + }, + }); + + if (!isLoggedIn) { + history.push(ROUTES.LOGIN); + } + }; + + const handleUserLoginIfTokenPresent = async ( + key: keyof typeof ROUTES, + ): Promise => { + if (localStorageUserAuthToken?.refreshJwt) { + // localstorage token is present + + // renew web access token + const response = await loginApi({ + refreshToken: localStorageUserAuthToken?.refreshJwt, + }); + + if (response.statusCode === 200) { + const route = routePermission[key]; + + // get all resource and put it over redux + const userResponse = await afterLogin( + response.payload.userId, + response.payload.accessJwt, + response.payload.refreshJwt, + ); + + if ( + userResponse && + route && + route.find((e) => e === userResponse.payload.role) === undefined + ) { + history.push(ROUTES.UN_AUTHORIZED); + } + } else { + Logout(); + + notifications.error({ + message: response.error || t('something_went_wrong'), + }); + } + } + }; + + const handlePrivateRoutes = async ( + key: keyof typeof ROUTES, + ): Promise => { + if ( + localStorageUserAuthToken && + localStorageUserAuthToken.refreshJwt && + isUserFetching + ) { + handleUserLoginIfTokenPresent(key); + } else { + // user does have localstorage values + + navigateToLoginIfNotLoggedIn(isLocalStorageLoggedIn); + } + }; + + const navigateToWorkSpaceBlocked = (route: any): void => { + const { path } = route; + + if (path && path !== ROUTES.WORKSPACE_LOCKED) { + history.push(ROUTES.WORKSPACE_LOCKED); + + dispatch({ + type: UPDATE_USER_IS_FETCH, + payload: { + isUserFetching: false, + }, + }); + } + }; + + useEffect(() => { + if (!isFetchingLicensesData) { + const shouldBlockWorkspace = licensesData?.payload?.workSpaceBlock; + + if (shouldBlockWorkspace) { + navigateToWorkSpaceBlocked(currentRoute); + } + } + }, [isFetchingLicensesData]); + + // eslint-disable-next-line sonarjs/cognitive-complexity + useEffect(() => { + (async (): Promise => { + try { + if (isOldRoute) { + const redirectUrl = oldNewRoutesMapping[pathname]; + + const newLocation = { + ...location, + pathname: redirectUrl, + }; + history.replace(newLocation); + } + + if (currentRoute) { + const { isPrivate, key } = currentRoute; + + if (isPrivate && key !== String(ROUTES.WORKSPACE_LOCKED)) { + handlePrivateRoutes(key); + } else { + // no need to fetch the user and make user fetching false + + if (getLocalStorageApi(LOCALSTORAGE.IS_LOGGED_IN) === 'true') { + history.push(ROUTES.APPLICATION); + } + dispatch({ + type: UPDATE_USER_IS_FETCH, + payload: { + isUserFetching: false, + }, + }); + } + } else if (pathname === ROUTES.HOME_PAGE) { + // routing to application page over root page + if (isLoggedInState) { + history.push(ROUTES.APPLICATION); + } else { + navigateToLoginIfNotLoggedIn(); + } + } else { + // not found + navigateToLoginIfNotLoggedIn(isLocalStorageLoggedIn); + } + } catch (error) { + // something went wrong + history.push(ROUTES.SOMETHING_WENT_WRONG); + } + })(); + }, [dispatch, isLoggedInState, currentRoute, licensesData]); + + if (isUserFetchingError) { + return ; + } + + if (isUserFetching) { + return ; + } + + // NOTE: disabling this rule as there is no need to have div + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>{children}; +} + +interface PrivateRouteProps { + children: ReactChild; +} + +export default PrivateRoute; diff --git a/frontend/src/AppRoutes/index.tsx b/frontend/src/AppRoutes/index.tsx new file mode 100644 index 0000000..f1927c9 --- /dev/null +++ b/frontend/src/AppRoutes/index.tsx @@ -0,0 +1,209 @@ +import { ConfigProvider } from 'antd'; +import getLocalStorageApi from 'api/browser/localstorage/get'; +import setLocalStorageApi from 'api/browser/localstorage/set'; +import NotFound from 'components/NotFound'; +import Spinner from 'components/Spinner'; +import { FeatureKeys } from 'constants/features'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import ROUTES from 'constants/routes'; +import AppLayout from 'container/AppLayout'; +import useAnalytics from 'hooks/analytics/useAnalytics'; +import { KeyboardHotkeysProvider } from 'hooks/hotkeys/useKeyboardHotkeys'; +import { useThemeConfig } from 'hooks/useDarkMode'; +import useGetFeatureFlag from 'hooks/useGetFeatureFlag'; +import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense'; +import { NotificationProvider } from 'hooks/useNotifications'; +import { ResourceProvider } from 'hooks/useResourceAttribute'; +import history from 'lib/history'; +import { identity, pickBy } from 'lodash-es'; +import { DashboardProvider } from 'providers/Dashboard/Dashboard'; +import { QueryBuilderProvider } from 'providers/QueryBuilder'; +import { Suspense, useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Route, Router, Switch } from 'react-router-dom'; +import { Dispatch } from 'redux'; +import { AppState } from 'store/reducers'; +import AppActions from 'types/actions'; +import { UPDATE_FEATURE_FLAG_RESPONSE } from 'types/actions/app'; +import AppReducer, { User } from 'types/reducer/app'; +import { extractDomain, isCloudUser, isEECloudUser } from 'utils/app'; + +import PrivateRoute from './Private'; +import defaultRoutes, { + AppRoutes, + LIST_LICENSES, + SUPPORT_ROUTE, +} from './routes'; + +function App(): JSX.Element { + const themeConfig = useThemeConfig(); + const { data } = useLicense(); + const [routes, setRoutes] = useState(defaultRoutes); + const { role, isLoggedIn: isLoggedInState, user, org } = useSelector< + AppState, + AppReducer + >((state) => state.app); + + const dispatch = useDispatch>(); + + const { trackPageView } = useAnalytics(); + + const { hostname, pathname } = window.location; + + const isCloudUserVal = isCloudUser(); + + const featureResponse = useGetFeatureFlag((allFlags) => { + const isOnboardingEnabled = + allFlags.find((flag) => flag.name === FeatureKeys.ONBOARDING)?.active || + false; + + const isChatSupportEnabled = + allFlags.find((flag) => flag.name === FeatureKeys.CHAT_SUPPORT)?.active || + false; + + dispatch({ + type: UPDATE_FEATURE_FLAG_RESPONSE, + payload: { + featureFlag: allFlags, + refetch: featureResponse.refetch, + }, + }); + + if (!isOnboardingEnabled || !isCloudUserVal) { + const newRoutes = routes.filter( + (route) => route?.path !== ROUTES.GET_STARTED, + ); + + setRoutes(newRoutes); + } + + if (isLoggedInState && isChatSupportEnabled) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + window.Intercom('boot', { + app_id: process.env.INTERCOM_APP_ID, + email: user?.email || '', + name: user?.name || '', + }); + } + }); + + const isOnBasicPlan = + data?.payload?.licenses?.some( + (license) => + license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN, + ) || data?.payload?.licenses === null; + + const enableAnalytics = (user: User): void => { + const orgName = + org && Array.isArray(org) && org.length > 0 ? org[0].name : ''; + + const { name, email } = user; + + const identifyPayload = { + email, + name, + company_name: orgName, + role, + source: 'signoz-ui', + }; + + const sanitizedIdentifyPayload = pickBy(identifyPayload, identity); + + const domain = extractDomain(email); + + const hostNameParts = hostname.split('.'); + + const groupTraits = { + name: orgName, + tenant_id: hostNameParts[0], + data_region: hostNameParts[1], + tenant_url: hostname, + company_domain: domain, + source: 'signoz-ui', + }; + + window.analytics.identify(email, sanitizedIdentifyPayload); + + window.analytics.group(domain, groupTraits); + + window.clarity('identify', email, name); + }; + + useEffect(() => { + const isIdentifiedUser = getLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER); + + if ( + isLoggedInState && + user && + user.userId && + user.email && + !isIdentifiedUser + ) { + setLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER, 'true'); + + if (isCloudUserVal) { + enableAnalytics(user); + } + } + + if (isOnBasicPlan || (isLoggedInState && role && role !== 'ADMIN')) { + const newRoutes = routes.filter((route) => route?.path !== ROUTES.BILLING); + setRoutes(newRoutes); + } + + if (isCloudUserVal || isEECloudUser()) { + const newRoutes = [...routes, SUPPORT_ROUTE]; + + setRoutes(newRoutes); + } else { + const newRoutes = [...routes, LIST_LICENSES]; + + setRoutes(newRoutes); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isLoggedInState, isOnBasicPlan, user]); + + useEffect(() => { + trackPageView(pathname); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pathname]); + + return ( + + + + + + + + + + }> + + {routes.map(({ path, component, exact }) => ( + + ))} + + + + + + + + + + + + + + ); +} + +export default App; diff --git a/frontend/src/AppRoutes/pageComponents.ts b/frontend/src/AppRoutes/pageComponents.ts new file mode 100644 index 0000000..bea07a7 --- /dev/null +++ b/frontend/src/AppRoutes/pageComponents.ts @@ -0,0 +1,207 @@ +import Loadable from 'components/Loadable'; + +export const ServicesTablePage = Loadable( + () => import(/* webpackChunkName: "ServicesTablePage" */ 'pages/Services'), +); + +export const ServiceMetricsPage = Loadable( + () => + import( + /* webpackChunkName: "ServiceMetricsPage" */ 'pages/MetricsApplication' + ), +); + +export const ServiceMapPage = Loadable( + () => import(/* webpackChunkName: "ServiceMapPage" */ 'modules/Servicemap'), +); + +export const LogsSaveViews = Loadable( + () => import(/* webpackChunkName: "LogsSaveViews" */ 'pages/LogsModulePage'), // TODO: Add a wrapper so that the same component can be used in traces +); + +export const TracesExplorer = Loadable( + () => + import( + /* webpackChunkName: "Traces Explorer Page" */ 'pages/TracesModulePage' + ), +); + +export const TracesSaveViews = Loadable( + () => + import(/* webpackChunkName: "Traces Save Views" */ 'pages/TracesModulePage'), +); + +export const TraceFilter = Loadable( + () => import(/* webpackChunkName: "Trace Filter Page" */ 'pages/Trace'), +); + +export const TraceDetail = Loadable( + () => import(/* webpackChunkName: "TraceDetail Page" */ 'pages/TraceDetail'), +); + +export const UsageExplorerPage = Loadable( + () => import(/* webpackChunkName: "UsageExplorerPage" */ 'modules/Usage'), +); + +export const SignupPage = Loadable( + () => import(/* webpackChunkName: "SignupPage" */ 'pages/SignUp'), +); + +export const SettingsPage = Loadable( + () => import(/* webpackChunkName: "SettingsPage" */ 'pages/Settings'), +); + +export const GettingStarted = Loadable( + () => import(/* webpackChunkName: "GettingStarted" */ 'pages/GettingStarted'), +); + +export const Onboarding = Loadable( + () => import(/* webpackChunkName: "Onboarding" */ 'pages/OnboardingPage'), +); + +export const DashboardPage = Loadable( + () => + import(/* webpackChunkName: "DashboardPage" */ 'pages/DashboardsListPage'), +); + +export const NewDashboardPage = Loadable( + () => import(/* webpackChunkName: "New DashboardPage" */ 'pages/NewDashboard'), +); + +export const DashboardWidget = Loadable( + () => + import(/* webpackChunkName: "DashboardWidgetPage" */ 'pages/DashboardWidget'), +); + +export const EditRulesPage = Loadable( + () => import(/* webpackChunkName: "Alerts Edit Page" */ 'pages/EditRules'), +); + +export const ListAllALertsPage = Loadable( + () => import(/* webpackChunkName: "All Alerts Page" */ 'pages/AlertList'), +); + +export const CreateNewAlerts = Loadable( + () => import(/* webpackChunkName: "Create Alerts" */ 'pages/CreateAlert'), +); + +export const CreateAlertChannelAlerts = Loadable( + () => + import(/* webpackChunkName: "Create Channels" */ 'pages/AlertChannelCreate'), +); + +export const EditAlertChannelsAlerts = Loadable( + () => import(/* webpackChunkName: "Edit Channels" */ 'pages/ChannelsEdit'), +); + +export const AllAlertChannels = Loadable( + () => import(/* webpackChunkName: "All Channels" */ 'pages/Settings'), +); + +export const AllErrors = Loadable( + /* webpackChunkName: "All Exceptions" */ () => import('pages/AllErrors'), +); + +export const ErrorDetails = Loadable( + () => import(/* webpackChunkName: "Error Details" */ 'pages/ErrorDetails'), +); + +export const StatusPage = Loadable( + () => import(/* webpackChunkName: "All Status" */ 'pages/Status'), +); + +export const OrganizationSettings = Loadable( + () => import(/* webpackChunkName: "All Settings" */ 'pages/Settings'), +); + +export const IngestionSettings = Loadable( + () => import(/* webpackChunkName: "Ingestion Settings" */ 'pages/Settings'), +); + +export const APIKeys = Loadable( + () => import(/* webpackChunkName: "All Settings" */ 'pages/Settings'), +); + +export const MySettings = Loadable( + () => import(/* webpackChunkName: "All MySettings" */ 'pages/MySettings'), +); + +export const Logs = Loadable( + () => import(/* webpackChunkName: "Logs" */ 'pages/LogsModulePage'), +); + +export const LogsExplorer = Loadable( + () => import(/* webpackChunkName: "Logs Explorer" */ 'pages/LogsModulePage'), +); + +export const OldLogsExplorer = Loadable( + () => import(/* webpackChunkName: "Logs Explorer" */ 'pages/Logs'), +); + +export const LiveLogs = Loadable( + () => import(/* webpackChunkName: "Live Logs" */ 'pages/LiveLogs'), +); + +export const PipelinePage = Loadable( + () => import(/* webpackChunkName: "Pipelines" */ 'pages/LogsModulePage'), +); + +export const Login = Loadable( + () => import(/* webpackChunkName: "Login" */ 'pages/Login'), +); + +export const UnAuthorized = Loadable( + () => import(/* webpackChunkName: "UnAuthorized" */ 'pages/UnAuthorized'), +); + +export const PasswordReset = Loadable( + () => import(/* webpackChunkName: "ResetPassword" */ 'pages/ResetPassword'), +); + +export const SomethingWentWrong = Loadable( + () => + import( + /* webpackChunkName: "SomethingWentWrong" */ 'pages/SomethingWentWrong' + ), +); + +export const LicensePage = Loadable( + () => import(/* webpackChunkName: "All Channels" */ 'pages/License'), +); + +export const LogsIndexToFields = Loadable( + () => + import(/* webpackChunkName: "LogsIndexToFields Page" */ 'pages/LogsSettings'), +); + +export const BillingPage = Loadable( + () => import(/* webpackChunkName: "BillingPage" */ 'pages/Billing'), +); + +export const SupportPage = Loadable( + () => import(/* webpackChunkName: "SupportPage" */ 'pages/Support'), +); + +export const WorkspaceBlocked = Loadable( + () => + import(/* webpackChunkName: "WorkspaceLocked" */ 'pages/WorkspaceLocked'), +); + +export const ShortcutsPage = Loadable( + () => import(/* webpackChunkName: "ShortcutsPage" */ 'pages/Shortcuts'), +); + +export const InstalledIntegrations = Loadable( + () => + import( + /* webpackChunkName: "InstalledIntegrations" */ 'pages/IntegrationsModulePage' + ), +); + +export const IntegrationsMarketPlace = Loadable( + // eslint-disable-next-line sonarjs/no-identical-functions + () => + import( + /* webpackChunkName: "IntegrationsMarketPlace" */ 'pages/IntegrationsModulePage' + ), +); diff --git a/frontend/src/AppRoutes/routes.ts b/frontend/src/AppRoutes/routes.ts new file mode 100644 index 0000000..360c74d --- /dev/null +++ b/frontend/src/AppRoutes/routes.ts @@ -0,0 +1,400 @@ +import ROUTES from 'constants/routes'; +import { RouteProps } from 'react-router-dom'; + +import { + AllAlertChannels, + AllErrors, + APIKeys, + BillingPage, + CreateAlertChannelAlerts, + CreateNewAlerts, + DashboardPage, + DashboardWidget, + EditAlertChannelsAlerts, + EditRulesPage, + ErrorDetails, + IngestionSettings, + InstalledIntegrations, + IntegrationsMarketPlace, + LicensePage, + ListAllALertsPage, + LiveLogs, + Login, + Logs, + LogsExplorer, + LogsIndexToFields, + LogsSaveViews, + MySettings, + NewDashboardPage, + OldLogsExplorer, + Onboarding, + OrganizationSettings, + PasswordReset, + PipelinePage, + ServiceMapPage, + ServiceMetricsPage, + ServicesTablePage, + SettingsPage, + ShortcutsPage, + SignupPage, + SomethingWentWrong, + StatusPage, + SupportPage, + TraceDetail, + TraceFilter, + TracesExplorer, + TracesSaveViews, + UnAuthorized, + UsageExplorerPage, + WorkspaceBlocked, +} from './pageComponents'; + +const routes: AppRoutes[] = [ + { + component: SignupPage, + path: ROUTES.SIGN_UP, + exact: true, + isPrivate: false, + key: 'SIGN_UP', + }, + { + path: ROUTES.GET_STARTED, + exact: false, + component: Onboarding, + isPrivate: true, + key: 'GET_STARTED', + }, + { + component: LogsIndexToFields, + path: ROUTES.LOGS_INDEX_FIELDS, + exact: true, + isPrivate: true, + key: 'LOGS_INDEX_FIELDS', + }, + { + component: ServicesTablePage, + path: ROUTES.APPLICATION, + exact: true, + isPrivate: true, + key: 'APPLICATION', + }, + { + path: ROUTES.SERVICE_METRICS, + exact: true, + component: ServiceMetricsPage, + isPrivate: true, + key: 'SERVICE_METRICS', + }, + { + path: ROUTES.SERVICE_MAP, + component: ServiceMapPage, + isPrivate: true, + exact: true, + key: 'SERVICE_MAP', + }, + { + path: ROUTES.LOGS_SAVE_VIEWS, + component: LogsSaveViews, + isPrivate: true, + exact: true, + key: 'LOGS_SAVE_VIEWS', + }, + { + path: ROUTES.TRACE_DETAIL, + exact: true, + component: TraceDetail, + isPrivate: true, + key: 'TRACE_DETAIL', + }, + { + path: ROUTES.SETTINGS, + exact: true, + component: SettingsPage, + isPrivate: true, + key: 'SETTINGS', + }, + { + path: ROUTES.USAGE_EXPLORER, + exact: true, + component: UsageExplorerPage, + isPrivate: true, + key: 'USAGE_EXPLORER', + }, + { + path: ROUTES.ALL_DASHBOARD, + exact: true, + component: DashboardPage, + isPrivate: true, + key: 'ALL_DASHBOARD', + }, + { + path: ROUTES.DASHBOARD, + exact: true, + component: NewDashboardPage, + isPrivate: true, + key: 'DASHBOARD', + }, + { + path: ROUTES.DASHBOARD_WIDGET, + exact: true, + component: DashboardWidget, + isPrivate: true, + key: 'DASHBOARD_WIDGET', + }, + { + path: ROUTES.EDIT_ALERTS, + exact: true, + component: EditRulesPage, + isPrivate: true, + key: 'EDIT_ALERTS', + }, + { + path: ROUTES.LIST_ALL_ALERT, + exact: true, + component: ListAllALertsPage, + isPrivate: true, + key: 'LIST_ALL_ALERT', + }, + { + path: ROUTES.ALERTS_NEW, + exact: true, + component: CreateNewAlerts, + isPrivate: true, + key: 'ALERTS_NEW', + }, + { + path: ROUTES.TRACE, + exact: true, + component: TraceFilter, + isPrivate: true, + key: 'TRACE', + }, + { + path: ROUTES.TRACES_EXPLORER, + exact: true, + component: TracesExplorer, + isPrivate: true, + key: 'TRACES_EXPLORER', + }, + { + path: ROUTES.TRACES_SAVE_VIEWS, + exact: true, + component: TracesSaveViews, + isPrivate: true, + key: 'TRACES_SAVE_VIEWS', + }, + { + path: ROUTES.CHANNELS_NEW, + exact: true, + component: CreateAlertChannelAlerts, + isPrivate: true, + key: 'CHANNELS_NEW', + }, + { + path: ROUTES.CHANNELS_EDIT, + exact: true, + component: EditAlertChannelsAlerts, + isPrivate: true, + key: 'CHANNELS_EDIT', + }, + { + path: ROUTES.ALL_CHANNELS, + exact: true, + component: AllAlertChannels, + isPrivate: true, + key: 'ALL_CHANNELS', + }, + { + path: ROUTES.ALL_ERROR, + exact: true, + isPrivate: true, + component: AllErrors, + key: 'ALL_ERROR', + }, + { + path: ROUTES.ERROR_DETAIL, + exact: true, + component: ErrorDetails, + isPrivate: true, + key: 'ERROR_DETAIL', + }, + { + path: ROUTES.VERSION, + exact: true, + component: StatusPage, + isPrivate: true, + key: 'VERSION', + }, + { + path: ROUTES.ORG_SETTINGS, + exact: true, + component: OrganizationSettings, + isPrivate: true, + key: 'ORG_SETTINGS', + }, + { + path: ROUTES.INGESTION_SETTINGS, + exact: true, + component: IngestionSettings, + isPrivate: true, + key: 'INGESTION_SETTINGS', + }, + { + path: ROUTES.API_KEYS, + exact: true, + component: APIKeys, + isPrivate: true, + key: 'API_KEYS', + }, + { + path: ROUTES.MY_SETTINGS, + exact: true, + component: MySettings, + isPrivate: true, + key: 'MY_SETTINGS', + }, + { + path: ROUTES.LOGS, + exact: true, + component: Logs, + key: 'LOGS', + isPrivate: true, + }, + { + path: ROUTES.LOGS_EXPLORER, + exact: true, + component: LogsExplorer, + key: 'LOGS_EXPLORER', + isPrivate: true, + }, + { + path: ROUTES.OLD_LOGS_EXPLORER, + exact: true, + component: OldLogsExplorer, + key: 'OLD_LOGS_EXPLORER', + isPrivate: true, + }, + { + path: ROUTES.LIVE_LOGS, + exact: true, + component: LiveLogs, + key: 'LIVE_LOGS', + isPrivate: true, + }, + { + path: ROUTES.LOGS_PIPELINES, + exact: true, + component: PipelinePage, + key: 'LOGS_PIPELINES', + isPrivate: true, + }, + { + path: ROUTES.LOGIN, + exact: true, + component: Login, + isPrivate: false, + key: 'LOGIN', + }, + { + path: ROUTES.UN_AUTHORIZED, + exact: true, + component: UnAuthorized, + key: 'UN_AUTHORIZED', + isPrivate: true, + }, + { + path: ROUTES.PASSWORD_RESET, + exact: true, + component: PasswordReset, + key: 'PASSWORD_RESET', + isPrivate: false, + }, + { + path: ROUTES.SOMETHING_WENT_WRONG, + exact: true, + component: SomethingWentWrong, + key: 'SOMETHING_WENT_WRONG', + isPrivate: false, + }, + { + path: ROUTES.BILLING, + exact: true, + component: BillingPage, + key: 'BILLING', + isPrivate: true, + }, + { + path: ROUTES.WORKSPACE_LOCKED, + exact: true, + component: WorkspaceBlocked, + isPrivate: true, + key: 'WORKSPACE_LOCKED', + }, + { + path: ROUTES.SHORTCUTS, + exact: true, + component: ShortcutsPage, + isPrivate: true, + key: 'SHORTCUTS', + }, + { + path: ROUTES.INTEGRATIONS_INSTALLED, + exact: true, + component: InstalledIntegrations, + isPrivate: true, + key: 'INTEGRATIONS_INSTALLED', + }, + { + path: ROUTES.INTEGRATIONS_MARKETPLACE, + exact: true, + component: IntegrationsMarketPlace, + isPrivate: true, + key: 'INTEGRATIONS_MARKETPLACE', + }, +]; + +export const SUPPORT_ROUTE: AppRoutes = { + path: ROUTES.SUPPORT, + exact: true, + component: SupportPage, + key: 'SUPPORT', + isPrivate: true, +}; + +export const LIST_LICENSES: AppRoutes = { + path: ROUTES.LIST_LICENSES, + exact: true, + component: LicensePage, + isPrivate: true, + key: 'LIST_LICENSES', +}; + +export const oldRoutes = [ + '/pipelines', + '/logs/old-logs-explorer', + '/logs-explorer', + '/logs-explorer/live', + '/logs-save-views', + '/traces-save-views', + '/settings/api-keys', +]; + +export const oldNewRoutesMapping: Record = { + '/pipelines': '/logs/pipelines', + '/logs/old-logs-explorer': '/logs/old-logs-explorer', + '/logs-explorer': '/logs/logs-explorer', + '/logs-explorer/live': '/logs/logs-explorer/live', + '/logs-save-views': '/logs/saved-views', + '/traces-save-views': '/traces/saved-views', + '/settings/api-keys': '/settings/access-tokens', +}; + +export interface AppRoutes { + component: RouteProps['component']; + path: RouteProps['path']; + exact: RouteProps['exact']; + isPrivate: boolean; + key: keyof typeof ROUTES; +} + +export default routes; diff --git a/frontend/src/AppRoutes/utils.ts b/frontend/src/AppRoutes/utils.ts new file mode 100644 index 0000000..68df507 --- /dev/null +++ b/frontend/src/AppRoutes/utils.ts @@ -0,0 +1,92 @@ +import getLocalStorageApi from 'api/browser/localstorage/get'; +import setLocalStorageApi from 'api/browser/localstorage/set'; +import getUserApi from 'api/user/getUser'; +import { Logout } from 'api/utils'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import store from 'store'; +import AppActions from 'types/actions'; +import { + LOGGED_IN, + UPDATE_USER, + UPDATE_USER_ACCESS_REFRESH_ACCESS_TOKEN, + UPDATE_USER_IS_FETCH, +} from 'types/actions/app'; +import { SuccessResponse } from 'types/api'; +import { PayloadProps } from 'types/api/user/getUser'; + +const afterLogin = async ( + userId: string, + authToken: string, + refreshToken: string, +): Promise | undefined> => { + setLocalStorageApi(LOCALSTORAGE.AUTH_TOKEN, authToken); + setLocalStorageApi(LOCALSTORAGE.REFRESH_AUTH_TOKEN, refreshToken); + + store.dispatch({ + type: UPDATE_USER_ACCESS_REFRESH_ACCESS_TOKEN, + payload: { + accessJwt: authToken, + refreshJwt: refreshToken, + }, + }); + + const [getUserResponse] = await Promise.all([ + getUserApi({ + userId, + token: authToken, + }), + ]); + + if (getUserResponse.statusCode === 200 && getUserResponse.payload) { + store.dispatch({ + type: LOGGED_IN, + payload: { + isLoggedIn: true, + }, + }); + + const { payload } = getUserResponse; + + store.dispatch({ + type: UPDATE_USER, + payload: { + ROLE: payload.role, + email: payload.email, + name: payload.name, + orgName: payload.organization, + profilePictureURL: payload.profilePictureURL, + userId: payload.id, + orgId: payload.orgId, + userFlags: payload.flags, + }, + }); + + const isLoggedInLocalStorage = getLocalStorageApi(LOCALSTORAGE.IS_LOGGED_IN); + + if (isLoggedInLocalStorage === null) { + setLocalStorageApi(LOCALSTORAGE.IS_LOGGED_IN, 'true'); + } + + store.dispatch({ + type: UPDATE_USER_IS_FETCH, + payload: { + isUserFetching: false, + }, + }); + + return getUserResponse; + } + + store.dispatch({ + type: UPDATE_USER_IS_FETCH, + payload: { + isUserFetching: false, + }, + }); + + Logout(); + + return undefined; +}; + +export default afterLogin; diff --git a/frontend/src/ReactI18/index.tsx b/frontend/src/ReactI18/index.tsx new file mode 100644 index 0000000..a2d89e3 --- /dev/null +++ b/frontend/src/ReactI18/index.tsx @@ -0,0 +1,35 @@ +import i18n from 'i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; +import Backend from 'i18next-http-backend'; +import { initReactI18next } from 'react-i18next'; + +import cacheBursting from '../../i18n-translations-hash.json'; + +i18n + // load translation using http -> see /public/locales + .use(Backend) + // detect user language + .use(LanguageDetector) + // pass the i18n instance to react-i18next. + .use(initReactI18next) + // init i18next + .init({ + debug: false, + fallbackLng: 'en', + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, + backend: { + loadPath: (language, namespace) => { + const ns = namespace[0]; + const pathkey = `/${language}/${ns}`; + const hash = cacheBursting[pathkey as keyof typeof cacheBursting] || ''; + return `/locales/${language}/${namespace}.json?h=${hash}`; + }, + }, + react: { + useSuspense: false, + }, + }); + +export default i18n; diff --git a/frontend/src/api/APIKeys/createAPIKey.ts b/frontend/src/api/APIKeys/createAPIKey.ts new file mode 100644 index 0000000..2b219a0 --- /dev/null +++ b/frontend/src/api/APIKeys/createAPIKey.ts @@ -0,0 +1,26 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { APIKeyProps, CreateAPIKeyProps } from 'types/api/pat/types'; + +const createAPIKey = async ( + props: CreateAPIKeyProps, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/pats', { + ...props, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default createAPIKey; diff --git a/frontend/src/api/APIKeys/deleteAPIKey.ts b/frontend/src/api/APIKeys/deleteAPIKey.ts new file mode 100644 index 0000000..03b8d59 --- /dev/null +++ b/frontend/src/api/APIKeys/deleteAPIKey.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 { AllAPIKeyProps } from 'types/api/pat/types'; + +const deleteAPIKey = async ( + id: string, +): Promise | ErrorResponse> => { + try { + const response = await axios.delete(`/pats/${id}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default deleteAPIKey; diff --git a/frontend/src/api/APIKeys/getAPIKey.ts b/frontend/src/api/APIKeys/getAPIKey.ts new file mode 100644 index 0000000..c0410d8 --- /dev/null +++ b/frontend/src/api/APIKeys/getAPIKey.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, Props } from 'types/api/alerts/get'; + +const get = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`/pats/${props.id}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default get; diff --git a/frontend/src/api/APIKeys/getAllAPIKeys.ts b/frontend/src/api/APIKeys/getAllAPIKeys.ts new file mode 100644 index 0000000..488d9dc --- /dev/null +++ b/frontend/src/api/APIKeys/getAllAPIKeys.ts @@ -0,0 +1,6 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; +import { AllAPIKeyProps } from 'types/api/pat/types'; + +export const getAllAPIKeys = (): Promise> => + axios.get(`/pats`); diff --git a/frontend/src/api/APIKeys/updateAPIKey.ts b/frontend/src/api/APIKeys/updateAPIKey.ts new file mode 100644 index 0000000..38d2022 --- /dev/null +++ b/frontend/src/api/APIKeys/updateAPIKey.ts @@ -0,0 +1,26 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, UpdateAPIKeyProps } from 'types/api/pat/types'; + +const updateAPIKey = async ( + props: UpdateAPIKeyProps, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/pats/${props.id}`, { + ...props.data, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default updateAPIKey; diff --git a/frontend/src/api/ErrorResponseHandler.ts b/frontend/src/api/ErrorResponseHandler.ts new file mode 100644 index 0000000..3f28ff4 --- /dev/null +++ b/frontend/src/api/ErrorResponseHandler.ts @@ -0,0 +1,63 @@ +import { AxiosError, AxiosResponse } from 'axios'; +import { ErrorResponse } from 'types/api'; +import { ErrorStatusCode } from 'types/common'; + +export function ErrorResponseHandler(error: AxiosError): ErrorResponse { + const { response, request } = error; + if (response) { + // client received an error response (5xx, 4xx) + // making the error status code as standard Error Status Code + const statusCode = response.status as ErrorStatusCode; + + if (statusCode >= 400 && statusCode < 500) { + const { data } = response as AxiosResponse; + + if (statusCode === 404) { + return { + statusCode, + payload: null, + error: data.errorType, + message: null, + }; + } + + const { errors, error } = data; + + const errorMessage = + Array.isArray(errors) && errors.length >= 1 ? errors[0].msg : error; + + return { + statusCode, + payload: null, + error: errorMessage, + message: null, + }; + } + + return { + statusCode, + payload: null, + error: 'Something went wrong', + message: null, + }; + } + if (request) { + // client never received a response, or request never left + console.error('client never received a response, or request never left'); + + return { + statusCode: 500, + payload: null, + error: 'Something went wrong', + message: null, + }; + } + // anything else + console.error('any'); + return { + statusCode: 500, + payload: null, + error: String(error), + message: null, + }; +} diff --git a/frontend/src/api/Integrations/getAllIntegrations.ts b/frontend/src/api/Integrations/getAllIntegrations.ts new file mode 100644 index 0000000..8aec6ef --- /dev/null +++ b/frontend/src/api/Integrations/getAllIntegrations.ts @@ -0,0 +1,7 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; +import { AllIntegrationsProps } from 'types/api/integrations/types'; + +export const getAllIntegrations = (): Promise< + AxiosResponse +> => axios.get(`/integrations`); diff --git a/frontend/src/api/Integrations/getIntegration.ts b/frontend/src/api/Integrations/getIntegration.ts new file mode 100644 index 0000000..84fb696 --- /dev/null +++ b/frontend/src/api/Integrations/getIntegration.ts @@ -0,0 +1,11 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; +import { + GetIntegrationPayloadProps, + GetIntegrationProps, +} from 'types/api/integrations/types'; + +export const getIntegration = ( + props: GetIntegrationPayloadProps, +): Promise> => + axios.get(`/integrations/${props.integrationId}`); diff --git a/frontend/src/api/Integrations/getIntegrationStatus.ts b/frontend/src/api/Integrations/getIntegrationStatus.ts new file mode 100644 index 0000000..fbfbca2 --- /dev/null +++ b/frontend/src/api/Integrations/getIntegrationStatus.ts @@ -0,0 +1,11 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; +import { + GetIntegrationPayloadProps, + GetIntegrationStatusProps, +} from 'types/api/integrations/types'; + +export const getIntegrationStatus = ( + props: GetIntegrationPayloadProps, +): Promise> => + axios.get(`/integrations/${props.integrationId}/connection_status`); diff --git a/frontend/src/api/Integrations/installIntegration.ts b/frontend/src/api/Integrations/installIntegration.ts new file mode 100644 index 0000000..609ec00 --- /dev/null +++ b/frontend/src/api/Integrations/installIntegration.ts @@ -0,0 +1,31 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { + InstalledIntegrationsSuccessResponse, + InstallIntegrationKeyProps, +} from 'types/api/integrations/types'; + +const installIntegration = async ( + props: InstallIntegrationKeyProps, +): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.post('/integrations/install', { + ...props, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default installIntegration; diff --git a/frontend/src/api/Integrations/uninstallIntegration.ts b/frontend/src/api/Integrations/uninstallIntegration.ts new file mode 100644 index 0000000..f2a9760 --- /dev/null +++ b/frontend/src/api/Integrations/uninstallIntegration.ts @@ -0,0 +1,31 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { + UninstallIntegrationProps, + UninstallIntegrationSuccessResponse, +} from 'types/api/integrations/types'; + +const unInstallIntegration = async ( + props: UninstallIntegrationProps, +): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.post('/integrations/uninstall', { + ...props, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default unInstallIntegration; diff --git a/frontend/src/api/SAML/deleteDomain.ts b/frontend/src/api/SAML/deleteDomain.ts new file mode 100644 index 0000000..50c2b51 --- /dev/null +++ b/frontend/src/api/SAML/deleteDomain.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, Props } from 'types/api/SAML/deleteDomain'; + +const deleteDomain = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.delete(`/domains/${props.id}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default deleteDomain; diff --git a/frontend/src/api/SAML/listAllDomain.ts b/frontend/src/api/SAML/listAllDomain.ts new file mode 100644 index 0000000..dea73e4 --- /dev/null +++ b/frontend/src/api/SAML/listAllDomain.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, Props } from 'types/api/SAML/listDomain'; + +const listAllDomain = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`orgs/${props.orgId}/domains`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default listAllDomain; diff --git a/frontend/src/api/SAML/postDomain.ts b/frontend/src/api/SAML/postDomain.ts new file mode 100644 index 0000000..34a8ecd --- /dev/null +++ b/frontend/src/api/SAML/postDomain.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, Props } from 'types/api/SAML/postDomain'; + +const postDomain = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/domains`, props); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default postDomain; diff --git a/frontend/src/api/SAML/updateDomain.ts b/frontend/src/api/SAML/updateDomain.ts new file mode 100644 index 0000000..0c4cce8 --- /dev/null +++ b/frontend/src/api/SAML/updateDomain.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, Props } from 'types/api/SAML/updateDomain'; + +const updateDomain = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/domains/${props.id}`, props); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default updateDomain; diff --git a/frontend/src/api/alerts/create.ts b/frontend/src/api/alerts/create.ts new file mode 100644 index 0000000..cad7917 --- /dev/null +++ b/frontend/src/api/alerts/create.ts @@ -0,0 +1,26 @@ +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/alerts/create'; + +const create = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/rules', { + ...props.data, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default create; diff --git a/frontend/src/api/alerts/delete.ts b/frontend/src/api/alerts/delete.ts new file mode 100644 index 0000000..278e3e2 --- /dev/null +++ b/frontend/src/api/alerts/delete.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, Props } from 'types/api/alerts/delete'; + +const deleteAlerts = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.delete(`/rules/${props.id}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data.rules, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default deleteAlerts; diff --git a/frontend/src/api/alerts/get.ts b/frontend/src/api/alerts/get.ts new file mode 100644 index 0000000..0437f8d --- /dev/null +++ b/frontend/src/api/alerts/get.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, Props } from 'types/api/alerts/get'; + +const get = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`/rules/${props.id}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default get; diff --git a/frontend/src/api/alerts/getAll.ts b/frontend/src/api/alerts/getAll.ts new file mode 100644 index 0000000..e6b1fdb --- /dev/null +++ b/frontend/src/api/alerts/getAll.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/alerts/getAll'; + +const getAll = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get('/rules'); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data.rules, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getAll; diff --git a/frontend/src/api/alerts/getGroup.ts b/frontend/src/api/alerts/getGroup.ts new file mode 100644 index 0000000..f5e7247 --- /dev/null +++ b/frontend/src/api/alerts/getGroup.ts @@ -0,0 +1,29 @@ +import { AxiosAlertManagerInstance } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import convertObjectIntoParams from 'lib/query/convertObjectIntoParams'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/alerts/getGroups'; + +const getGroups = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const queryParams = convertObjectIntoParams(props); + + const response = await AxiosAlertManagerInstance.get( + `/alerts/groups?${queryParams}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getGroups; diff --git a/frontend/src/api/alerts/getTriggered.ts b/frontend/src/api/alerts/getTriggered.ts new file mode 100644 index 0000000..6955cc3 --- /dev/null +++ b/frontend/src/api/alerts/getTriggered.ts @@ -0,0 +1,29 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import convertObjectIntoParams from 'lib/query/convertObjectIntoParams'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/alerts/getTriggered'; + +const getTriggered = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const queryParams = convertObjectIntoParams(props); + + const response = await axios.get(`/alerts?${queryParams}`); + + const amData = JSON.parse(response.data.data); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: amData.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getTriggered; diff --git a/frontend/src/api/alerts/patch.ts b/frontend/src/api/alerts/patch.ts new file mode 100644 index 0000000..920b53a --- /dev/null +++ b/frontend/src/api/alerts/patch.ts @@ -0,0 +1,26 @@ +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/alerts/patch'; + +const patch = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.patch(`/rules/${props.id}`, { + ...props.data, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default patch; diff --git a/frontend/src/api/alerts/put.ts b/frontend/src/api/alerts/put.ts new file mode 100644 index 0000000..b8c34e9 --- /dev/null +++ b/frontend/src/api/alerts/put.ts @@ -0,0 +1,26 @@ +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/alerts/save'; + +const put = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/rules/${props.id}`, { + ...props.data, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default put; diff --git a/frontend/src/api/alerts/save.ts b/frontend/src/api/alerts/save.ts new file mode 100644 index 0000000..229f0ae --- /dev/null +++ b/frontend/src/api/alerts/save.ts @@ -0,0 +1,17 @@ +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/alerts/save'; + +import create from './create'; +import put from './put'; + +const save = async ( + props: Props, +): Promise | ErrorResponse> => { + if (props.id && props.id > 0) { + return put({ ...props }); + } + + return create({ ...props }); +}; + +export default save; diff --git a/frontend/src/api/alerts/testAlert.ts b/frontend/src/api/alerts/testAlert.ts new file mode 100644 index 0000000..a30e977 --- /dev/null +++ b/frontend/src/api/alerts/testAlert.ts @@ -0,0 +1,26 @@ +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/alerts/testAlert'; + +const testAlert = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/testRule', { + ...props.data, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default testAlert; diff --git a/frontend/src/api/apiV1.ts b/frontend/src/api/apiV1.ts new file mode 100644 index 0000000..4fba137 --- /dev/null +++ b/frontend/src/api/apiV1.ts @@ -0,0 +1,8 @@ +const apiV1 = '/api/v1/'; + +export const apiV2 = '/api/v2/'; +export const apiV3 = '/api/v3/'; +export const apiV4 = '/api/v4/'; +export const apiAlertManager = '/api/alertmanager'; + +export default apiV1; diff --git a/frontend/src/api/billing/checkout.ts b/frontend/src/api/billing/checkout.ts new file mode 100644 index 0000000..e6c7640 --- /dev/null +++ b/frontend/src/api/billing/checkout.ts @@ -0,0 +1,31 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { + CheckoutRequestPayloadProps, + CheckoutSuccessPayloadProps, +} from 'types/api/billing/checkout'; + +const updateCreditCardApi = async ( + props: CheckoutRequestPayloadProps, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/checkout', { + licenseKey: props.licenseKey, + successURL: props.successURL, + cancelURL: props.cancelURL, // temp + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default updateCreditCardApi; diff --git a/frontend/src/api/billing/getUsage.ts b/frontend/src/api/billing/getUsage.ts new file mode 100644 index 0000000..1cb5be5 --- /dev/null +++ b/frontend/src/api/billing/getUsage.ts @@ -0,0 +1,35 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; + +export interface UsageResponsePayloadProps { + billingPeriodStart: Date; + billingPeriodEnd: Date; + details: { + total: number; + baseFee: number; + breakdown: []; + billTotal: number; + }; + discount: number; +} + +const getUsage = async ( + licenseKey: string, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`/billing?licenseKey=${licenseKey}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getUsage; diff --git a/frontend/src/api/billing/manage.ts b/frontend/src/api/billing/manage.ts new file mode 100644 index 0000000..dca561b --- /dev/null +++ b/frontend/src/api/billing/manage.ts @@ -0,0 +1,30 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { + CheckoutRequestPayloadProps, + CheckoutSuccessPayloadProps, +} from 'types/api/billing/checkout'; + +const manageCreditCardApi = async ( + props: CheckoutRequestPayloadProps, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/portal', { + licenseKey: props.licenseKey, + returnURL: props.successURL, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default manageCreditCardApi; diff --git a/frontend/src/api/browser/localstorage/get.ts b/frontend/src/api/browser/localstorage/get.ts new file mode 100644 index 0000000..026c7a7 --- /dev/null +++ b/frontend/src/api/browser/localstorage/get.ts @@ -0,0 +1,9 @@ +const get = (key: string): string | null => { + try { + return localStorage.getItem(key); + } catch (e) { + return ''; + } +}; + +export default get; diff --git a/frontend/src/api/browser/localstorage/remove.ts b/frontend/src/api/browser/localstorage/remove.ts new file mode 100644 index 0000000..b1c5c40 --- /dev/null +++ b/frontend/src/api/browser/localstorage/remove.ts @@ -0,0 +1,10 @@ +const remove = (key: string): boolean => { + try { + window.localStorage.removeItem(key); + return true; + } catch (e) { + return false; + } +}; + +export default remove; diff --git a/frontend/src/api/browser/localstorage/set.ts b/frontend/src/api/browser/localstorage/set.ts new file mode 100644 index 0000000..1ce9f31 --- /dev/null +++ b/frontend/src/api/browser/localstorage/set.ts @@ -0,0 +1,10 @@ +const set = (key: string, value: string): boolean => { + try { + localStorage.setItem(key, value); + return true; + } catch (e) { + return false; + } +}; + +export default set; diff --git a/frontend/src/api/channels/createEmail.ts b/frontend/src/api/channels/createEmail.ts new file mode 100644 index 0000000..cde74b9 --- /dev/null +++ b/frontend/src/api/channels/createEmail.ts @@ -0,0 +1,34 @@ +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/channels/createEmail'; + +const create = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/channels', { + name: props.name, + email_configs: [ + { + send_resolved: true, + to: props.to, + html: props.html, + headers: props.headers, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default create; diff --git a/frontend/src/api/channels/createMsTeams.ts b/frontend/src/api/channels/createMsTeams.ts new file mode 100644 index 0000000..9e06e27 --- /dev/null +++ b/frontend/src/api/channels/createMsTeams.ts @@ -0,0 +1,34 @@ +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/channels/createMsTeams'; + +const create = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/channels', { + name: props.name, + msteams_configs: [ + { + send_resolved: true, + webhook_url: props.webhook_url, + title: props.title, + text: props.text, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default create; diff --git a/frontend/src/api/channels/createOpsgenie.ts b/frontend/src/api/channels/createOpsgenie.ts new file mode 100644 index 0000000..4cf60f9 --- /dev/null +++ b/frontend/src/api/channels/createOpsgenie.ts @@ -0,0 +1,37 @@ +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/channels/createOpsgenie'; + +const create = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/channels', { + name: props.name, + opsgenie_configs: [ + { + api_key: props.api_key, + description: props.description, + priority: props.priority, + message: props.message, + details: { + ...props.detailsArray, + }, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default create; diff --git a/frontend/src/api/channels/createPager.ts b/frontend/src/api/channels/createPager.ts new file mode 100644 index 0000000..2747768 --- /dev/null +++ b/frontend/src/api/channels/createPager.ts @@ -0,0 +1,42 @@ +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/channels/createPager'; + +const create = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/channels', { + name: props.name, + pagerduty_configs: [ + { + send_resolved: true, + routing_key: props.routing_key, + client: props.client, + client_url: props.client_url, + description: props.description, + severity: props.severity, + class: props.class, + component: props.component, + group: props.group, + details: { + ...props.detailsArray, + }, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default create; diff --git a/frontend/src/api/channels/createSlack.ts b/frontend/src/api/channels/createSlack.ts new file mode 100644 index 0000000..f9e430f --- /dev/null +++ b/frontend/src/api/channels/createSlack.ts @@ -0,0 +1,35 @@ +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/channels/createSlack'; + +const create = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/channels', { + name: props.name, + slack_configs: [ + { + send_resolved: true, + api_url: props.api_url, + channel: props.channel, + title: props.title, + text: props.text, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default create; diff --git a/frontend/src/api/channels/createWebhook.ts b/frontend/src/api/channels/createWebhook.ts new file mode 100644 index 0000000..9c3c52c --- /dev/null +++ b/frontend/src/api/channels/createWebhook.ts @@ -0,0 +1,51 @@ +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/channels/createWebhook'; + +const create = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + let httpConfig = {}; + + if (props.username !== '' && props.password !== '') { + httpConfig = { + basic_auth: { + username: props.username, + password: props.password, + }, + }; + } else if (props.username === '' && props.password !== '') { + httpConfig = { + authorization: { + type: 'bearer', + credentials: props.password, + }, + }; + } + + const response = await axios.post('/channels', { + name: props.name, + webhook_configs: [ + { + send_resolved: true, + url: props.api_url, + http_config: httpConfig, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default create; diff --git a/frontend/src/api/channels/delete.ts b/frontend/src/api/channels/delete.ts new file mode 100644 index 0000000..a5366af --- /dev/null +++ b/frontend/src/api/channels/delete.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, Props } from 'types/api/channels/delete'; + +const deleteChannel = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.delete(`/channels/${props.id}`); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default deleteChannel; diff --git a/frontend/src/api/channels/editEmail.ts b/frontend/src/api/channels/editEmail.ts new file mode 100644 index 0000000..f20e5eb --- /dev/null +++ b/frontend/src/api/channels/editEmail.ts @@ -0,0 +1,34 @@ +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/channels/editEmail'; + +const editEmail = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/channels/${props.id}`, { + name: props.name, + email_configs: [ + { + send_resolved: true, + to: props.to, + html: props.html, + headers: props.headers, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default editEmail; diff --git a/frontend/src/api/channels/editMsTeams.ts b/frontend/src/api/channels/editMsTeams.ts new file mode 100644 index 0000000..ee6bd30 --- /dev/null +++ b/frontend/src/api/channels/editMsTeams.ts @@ -0,0 +1,34 @@ +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/channels/editMsTeams'; + +const editMsTeams = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/channels/${props.id}`, { + name: props.name, + msteams_configs: [ + { + send_resolved: true, + webhook_url: props.webhook_url, + title: props.title, + text: props.text, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default editMsTeams; diff --git a/frontend/src/api/channels/editOpsgenie.ts b/frontend/src/api/channels/editOpsgenie.ts new file mode 100644 index 0000000..71f830f --- /dev/null +++ b/frontend/src/api/channels/editOpsgenie.ts @@ -0,0 +1,38 @@ +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/channels/editOpsgenie'; + +const editOpsgenie = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/channels/${props.id}`, { + name: props.name, + opsgenie_configs: [ + { + send_resolved: true, + api_key: props.api_key, + description: props.description, + priority: props.priority, + message: props.message, + details: { + ...props.detailsArray, + }, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default editOpsgenie; diff --git a/frontend/src/api/channels/editPager.ts b/frontend/src/api/channels/editPager.ts new file mode 100644 index 0000000..a31d73d --- /dev/null +++ b/frontend/src/api/channels/editPager.ts @@ -0,0 +1,42 @@ +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/channels/editPager'; + +const editPager = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/channels/${props.id}`, { + name: props.name, + pagerduty_configs: [ + { + send_resolved: true, + routing_key: props.routing_key, + client: props.client, + client_url: props.client_url, + description: props.description, + severity: props.severity, + class: props.class, + component: props.component, + group: props.group, + details: { + ...props.detailsArray, + }, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default editPager; diff --git a/frontend/src/api/channels/editSlack.ts b/frontend/src/api/channels/editSlack.ts new file mode 100644 index 0000000..9a34f41 --- /dev/null +++ b/frontend/src/api/channels/editSlack.ts @@ -0,0 +1,35 @@ +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/channels/editSlack'; + +const editSlack = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/channels/${props.id}`, { + name: props.name, + slack_configs: [ + { + send_resolved: true, + api_url: props.api_url, + channel: props.channel, + title: props.title, + text: props.text, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default editSlack; diff --git a/frontend/src/api/channels/editWebhook.ts b/frontend/src/api/channels/editWebhook.ts new file mode 100644 index 0000000..a574633 --- /dev/null +++ b/frontend/src/api/channels/editWebhook.ts @@ -0,0 +1,50 @@ +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/channels/editWebhook'; + +const editWebhook = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + let httpConfig = {}; + if (props.username !== '' && props.password !== '') { + httpConfig = { + basic_auth: { + username: props.username, + password: props.password, + }, + }; + } else if (props.username === '' && props.password !== '') { + httpConfig = { + authorization: { + type: 'bearer', + credentials: props.password, + }, + }; + } + + const response = await axios.put(`/channels/${props.id}`, { + name: props.name, + webhook_configs: [ + { + send_resolved: true, + url: props.api_url, + http_config: httpConfig, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default editWebhook; diff --git a/frontend/src/api/channels/get.ts b/frontend/src/api/channels/get.ts new file mode 100644 index 0000000..39c40ec --- /dev/null +++ b/frontend/src/api/channels/get.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, Props } from 'types/api/channels/get'; + +const get = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`/channels/${props.id}`); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default get; diff --git a/frontend/src/api/channels/getAll.ts b/frontend/src/api/channels/getAll.ts new file mode 100644 index 0000000..11b530a --- /dev/null +++ b/frontend/src/api/channels/getAll.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/channels/getAll'; + +const getAll = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get('/channels'); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getAll; diff --git a/frontend/src/api/channels/testEmail.ts b/frontend/src/api/channels/testEmail.ts new file mode 100644 index 0000000..825836a --- /dev/null +++ b/frontend/src/api/channels/testEmail.ts @@ -0,0 +1,34 @@ +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/channels/createEmail'; + +const testEmail = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/testChannel', { + name: props.name, + email_configs: [ + { + send_resolved: true, + to: props.to, + html: props.html, + headers: props.headers, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default testEmail; diff --git a/frontend/src/api/channels/testMsTeams.ts b/frontend/src/api/channels/testMsTeams.ts new file mode 100644 index 0000000..3b4fc21 --- /dev/null +++ b/frontend/src/api/channels/testMsTeams.ts @@ -0,0 +1,34 @@ +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/channels/createMsTeams'; + +const testMsTeams = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/testChannel', { + name: props.name, + msteams_configs: [ + { + send_resolved: true, + webhook_url: props.webhook_url, + title: props.title, + text: props.text, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default testMsTeams; diff --git a/frontend/src/api/channels/testOpsgenie.ts b/frontend/src/api/channels/testOpsgenie.ts new file mode 100644 index 0000000..780a443 --- /dev/null +++ b/frontend/src/api/channels/testOpsgenie.ts @@ -0,0 +1,37 @@ +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/channels/createOpsgenie'; + +const testOpsgenie = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/testChannel', { + name: props.name, + opsgenie_configs: [ + { + api_key: props.api_key, + description: props.description, + priority: props.priority, + message: props.message, + details: { + ...props.detailsArray, + }, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default testOpsgenie; diff --git a/frontend/src/api/channels/testPager.ts b/frontend/src/api/channels/testPager.ts new file mode 100644 index 0000000..7174046 --- /dev/null +++ b/frontend/src/api/channels/testPager.ts @@ -0,0 +1,42 @@ +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/channels/createPager'; + +const testPager = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/testChannel', { + name: props.name, + pagerduty_configs: [ + { + send_resolved: true, + routing_key: props.routing_key, + client: props.client, + client_url: props.client_url, + description: props.description, + severity: props.severity, + class: props.class, + component: props.component, + group: props.group, + details: { + ...props.detailsArray, + }, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default testPager; diff --git a/frontend/src/api/channels/testSlack.ts b/frontend/src/api/channels/testSlack.ts new file mode 100644 index 0000000..a2b4b1f --- /dev/null +++ b/frontend/src/api/channels/testSlack.ts @@ -0,0 +1,35 @@ +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/channels/createSlack'; + +const testSlack = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/testChannel', { + name: props.name, + slack_configs: [ + { + send_resolved: true, + api_url: props.api_url, + channel: props.channel, + title: props.title, + text: props.text, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default testSlack; diff --git a/frontend/src/api/channels/testWebhook.ts b/frontend/src/api/channels/testWebhook.ts new file mode 100644 index 0000000..4b915e9 --- /dev/null +++ b/frontend/src/api/channels/testWebhook.ts @@ -0,0 +1,51 @@ +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/channels/createWebhook'; + +const testWebhook = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + let httpConfig = {}; + + if (props.username !== '' && props.password !== '') { + httpConfig = { + basic_auth: { + username: props.username, + password: props.password, + }, + }; + } else if (props.username === '' && props.password !== '') { + httpConfig = { + authorization: { + type: 'bearer', + credentials: props.password, + }, + }; + } + + const response = await axios.post('/testChannel', { + name: props.name, + webhook_configs: [ + { + send_resolved: true, + url: props.api_url, + http_config: httpConfig, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default testWebhook; diff --git a/frontend/src/api/dashboard/create.ts b/frontend/src/api/dashboard/create.ts new file mode 100644 index 0000000..bf5458a --- /dev/null +++ b/frontend/src/api/dashboard/create.ts @@ -0,0 +1,27 @@ +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/dashboard/create'; + +const createDashboard = async ( + props: Props, +): Promise | ErrorResponse> => { + const url = props.uploadedGrafana ? '/dashboards/grafana' : '/dashboards'; + try { + const response = await axios.post(url, { + ...props, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default createDashboard; diff --git a/frontend/src/api/dashboard/delete.ts b/frontend/src/api/dashboard/delete.ts new file mode 100644 index 0000000..8faf711 --- /dev/null +++ b/frontend/src/api/dashboard/delete.ts @@ -0,0 +1,9 @@ +import axios from 'api'; +import { PayloadProps, Props } from 'types/api/dashboard/delete'; + +const deleteDashboard = (props: Props): Promise => + axios + .delete(`/dashboards/${props.uuid}`) + .then((response) => response.data); + +export default deleteDashboard; diff --git a/frontend/src/api/dashboard/get.ts b/frontend/src/api/dashboard/get.ts new file mode 100644 index 0000000..01e04c6 --- /dev/null +++ b/frontend/src/api/dashboard/get.ts @@ -0,0 +1,11 @@ +import axios from 'api'; +import { ApiResponse } from 'types/api'; +import { Props } from 'types/api/dashboard/get'; +import { Dashboard } from 'types/api/dashboard/getAll'; + +const getDashboard = (props: Props): Promise => + axios + .get>(`/dashboards/${props.uuid}`) + .then((res) => res.data.data); + +export default getDashboard; diff --git a/frontend/src/api/dashboard/getAll.ts b/frontend/src/api/dashboard/getAll.ts new file mode 100644 index 0000000..aafe44b --- /dev/null +++ b/frontend/src/api/dashboard/getAll.ts @@ -0,0 +1,8 @@ +import axios from 'api'; +import { ApiResponse } from 'types/api'; +import { Dashboard } from 'types/api/dashboard/getAll'; + +export const getAllDashboardList = (): Promise => + axios + .get>('/dashboards') + .then((res) => res.data.data); diff --git a/frontend/src/api/dashboard/lockDashboard.ts b/frontend/src/api/dashboard/lockDashboard.ts new file mode 100644 index 0000000..3393de8 --- /dev/null +++ b/frontend/src/api/dashboard/lockDashboard.ts @@ -0,0 +1,11 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; + +interface LockDashboardProps { + uuid: string; +} + +const lockDashboard = (props: LockDashboardProps): Promise => + axios.put(`/dashboards/${props.uuid}/lock`); + +export default lockDashboard; diff --git a/frontend/src/api/dashboard/queryRangeFormat.ts b/frontend/src/api/dashboard/queryRangeFormat.ts new file mode 100644 index 0000000..02e020b --- /dev/null +++ b/frontend/src/api/dashboard/queryRangeFormat.ts @@ -0,0 +1,15 @@ +import { ApiV3Instance as axios } from 'api'; +import { ApiResponse } from 'types/api'; +import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery'; +import { QueryRangePayload } from 'types/api/metrics/getQueryRange'; + +interface IQueryRangeFormat { + compositeQuery: ICompositeMetricQuery; +} + +export const getQueryRangeFormat = ( + props?: Partial, +): Promise => + axios + .post>('/query_range/format', props) + .then((res) => res.data.data); diff --git a/frontend/src/api/dashboard/unlockDashboard.ts b/frontend/src/api/dashboard/unlockDashboard.ts new file mode 100644 index 0000000..fd4ffbe --- /dev/null +++ b/frontend/src/api/dashboard/unlockDashboard.ts @@ -0,0 +1,11 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; + +interface UnlockDashboardProps { + uuid: string; +} + +const unlockDashboard = (props: UnlockDashboardProps): Promise => + axios.put(`/dashboards/${props.uuid}/unlock`); + +export default unlockDashboard; diff --git a/frontend/src/api/dashboard/update.ts b/frontend/src/api/dashboard/update.ts new file mode 100644 index 0000000..db53508 --- /dev/null +++ b/frontend/src/api/dashboard/update.ts @@ -0,0 +1,26 @@ +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/dashboard/update'; + +const updateDashboard = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/dashboards/${props.uuid}`, { + ...props.data, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default updateDashboard; diff --git a/frontend/src/api/dashboard/variables/dashboardVariablesQuery.ts b/frontend/src/api/dashboard/variables/dashboardVariablesQuery.ts new file mode 100644 index 0000000..8605ce7 --- /dev/null +++ b/frontend/src/api/dashboard/variables/dashboardVariablesQuery.ts @@ -0,0 +1,30 @@ +import { ApiV2Instance as axios } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { + Props, + VariableResponseProps, +} from 'types/api/dashboard/variables/query'; + +const dashboardVariablesQuery = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/variables/query`, props); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + const formattedError = ErrorResponseHandler(error as AxiosError); + + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw { message: 'Error fetching data', details: formattedError }; + } +}; + +export default dashboardVariablesQuery; diff --git a/frontend/src/api/disks/getDisks.ts b/frontend/src/api/disks/getDisks.ts new file mode 100644 index 0000000..9dced1b --- /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/dynamicConfigs/getDynamicConfigs.ts b/frontend/src/api/dynamicConfigs/getDynamicConfigs.ts new file mode 100644 index 0000000..149c113 --- /dev/null +++ b/frontend/src/api/dynamicConfigs/getDynamicConfigs.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/dynamicConfigs/getDynamicConfigs'; + +const getDynamicConfigs = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get(`/configs`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getDynamicConfigs; diff --git a/frontend/src/api/errors/getAll.ts b/frontend/src/api/errors/getAll.ts new file mode 100644 index 0000000..8d6793e --- /dev/null +++ b/frontend/src/api/errors/getAll.ts @@ -0,0 +1,34 @@ +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/errors/getAll'; + +const getAll = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/listErrors`, { + start: `${props.start}`, + end: `${props.end}`, + order: props.order, + orderParam: props.orderParam, + limit: props.limit, + offset: props.offset, + exceptionType: props.exceptionType, + serviceName: props.serviceName, + tags: props.tags, + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getAll; diff --git a/frontend/src/api/errors/getByErrorTypeAndService.ts b/frontend/src/api/errors/getByErrorTypeAndService.ts new file mode 100644 index 0000000..c9a710f --- /dev/null +++ b/frontend/src/api/errors/getByErrorTypeAndService.ts @@ -0,0 +1,29 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import createQueryParams from 'lib/createQueryParams'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/errors/getByErrorTypeAndService'; + +const getByErrorType = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/errorFromGroupID?${createQueryParams({ + ...props, + })}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.message, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getByErrorType; diff --git a/frontend/src/api/errors/getById.ts b/frontend/src/api/errors/getById.ts new file mode 100644 index 0000000..ab0bae3 --- /dev/null +++ b/frontend/src/api/errors/getById.ts @@ -0,0 +1,29 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import createQueryParams from 'lib/createQueryParams'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/errors/getByErrorId'; + +const getById = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/errorFromErrorID?${createQueryParams({ + ...props, + })}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.message, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getById; diff --git a/frontend/src/api/errors/getErrorCounts.ts b/frontend/src/api/errors/getErrorCounts.ts new file mode 100644 index 0000000..977eeb2 --- /dev/null +++ b/frontend/src/api/errors/getErrorCounts.ts @@ -0,0 +1,30 @@ +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/errors/getErrorCounts'; + +const getErrorCounts = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/countErrors`, { + start: `${props.start}`, + end: `${props.end}`, + exceptionType: props.exceptionType, + serviceName: props.serviceName, + tags: props.tags, + }); + + return { + statusCode: 200, + error: null, + message: response.data.message, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getErrorCounts; diff --git a/frontend/src/api/errors/getNextPrevId.ts b/frontend/src/api/errors/getNextPrevId.ts new file mode 100644 index 0000000..07798c5 --- /dev/null +++ b/frontend/src/api/errors/getNextPrevId.ts @@ -0,0 +1,29 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import createQueryParams from 'lib/createQueryParams'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/errors/getNextPrevId'; + +const getErrorCounts = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/nextPrevErrorIDs?${createQueryParams({ + ...props, + })}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.message, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getErrorCounts; diff --git a/frontend/src/api/features/getFeatureFlags.ts b/frontend/src/api/features/getFeatureFlags.ts new file mode 100644 index 0000000..2ce37b9 --- /dev/null +++ b/frontend/src/api/features/getFeatureFlags.ts @@ -0,0 +1,10 @@ +import axios from 'api'; +import { ApiResponse } from 'types/api'; +import { FeatureFlagProps } from 'types/api/features/getFeaturesFlags'; + +const getFeaturesFlags = (): Promise => + axios + .get>(`/featureFlags`) + .then((response) => response.data.data); + +export default getFeaturesFlags; diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts new file mode 100644 index 0000000..92a0636 --- /dev/null +++ b/frontend/src/api/index.ts @@ -0,0 +1,144 @@ +/* eslint-disable sonarjs/cognitive-complexity */ +/* eslint-disable no-param-reassign */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import getLocalStorageApi from 'api/browser/localstorage/get'; +import loginApi from 'api/user/login'; +import afterLogin from 'AppRoutes/utils'; +import axios, { AxiosResponse, InternalAxiosRequestConfig } from 'axios'; +import { ENVIRONMENT } from 'constants/env'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import store from 'store'; + +import apiV1, { apiAlertManager, apiV2, apiV3, apiV4 } from './apiV1'; +import { Logout } from './utils'; + +const interceptorsResponse = ( + value: AxiosResponse, +): Promise> => Promise.resolve(value); + +const interceptorsRequestResponse = ( + value: InternalAxiosRequestConfig, +): InternalAxiosRequestConfig => { + const token = + store.getState().app.user?.accessJwt || + getLocalStorageApi(LOCALSTORAGE.AUTH_TOKEN) || + ''; + + if (value && value.headers) { + value.headers.Authorization = token ? `Bearer ${token}` : ''; + } + + return value; +}; + +const interceptorRejected = async ( + value: AxiosResponse, +): Promise> => { + try { + if (axios.isAxiosError(value) && value.response) { + const { response } = value; + // reject the refresh token error + if (response.status === 401 && response.config.url !== '/login') { + const response = await loginApi({ + refreshToken: store.getState().app.user?.refreshJwt, + }); + + if (response.statusCode === 200) { + const user = await afterLogin( + response.payload.userId, + response.payload.accessJwt, + response.payload.refreshJwt, + ); + + if (user) { + const reResponse = await axios( + `${value.config.baseURL}${value.config.url?.substring(1)}`, + { + method: value.config.method, + headers: { + ...value.config.headers, + Authorization: `Bearer ${response.payload.accessJwt}`, + }, + data: { + ...JSON.parse(value.config.data || '{}'), + }, + }, + ); + + if (reResponse.status === 200) { + return await Promise.resolve(reResponse); + } + Logout(); + + return await Promise.reject(reResponse); + } + Logout(); + + return await Promise.reject(value); + } + Logout(); + } + + // when refresh token is expired + if (response.status === 401 && response.config.url === '/login') { + Logout(); + } + } + return await Promise.reject(value); + } catch (error) { + return await Promise.reject(error); + } +}; + +const instance = axios.create({ + baseURL: `${ENVIRONMENT.baseURL}${apiV1}`, +}); + +instance.interceptors.request.use(interceptorsRequestResponse); +instance.interceptors.response.use(interceptorsResponse, interceptorRejected); + +export const AxiosAlertManagerInstance = axios.create({ + baseURL: `${ENVIRONMENT.baseURL}${apiAlertManager}`, +}); + +export const ApiV2Instance = axios.create({ + baseURL: `${ENVIRONMENT.baseURL}${apiV2}`, +}); +ApiV2Instance.interceptors.response.use( + interceptorsResponse, + interceptorRejected, +); +ApiV2Instance.interceptors.request.use(interceptorsRequestResponse); + +// axios V3 +export const ApiV3Instance = axios.create({ + baseURL: `${ENVIRONMENT.baseURL}${apiV3}`, +}); + +ApiV3Instance.interceptors.response.use( + interceptorsResponse, + interceptorRejected, +); +ApiV3Instance.interceptors.request.use(interceptorsRequestResponse); +// + +// axios V4 +export const ApiV4Instance = axios.create({ + baseURL: `${ENVIRONMENT.baseURL}${apiV4}`, +}); + +ApiV4Instance.interceptors.response.use( + interceptorsResponse, + interceptorRejected, +); +ApiV4Instance.interceptors.request.use(interceptorsRequestResponse); +// + +AxiosAlertManagerInstance.interceptors.response.use( + interceptorsResponse, + interceptorRejected, +); +AxiosAlertManagerInstance.interceptors.request.use(interceptorsRequestResponse); + +export { apiV1 }; +export default instance; diff --git a/frontend/src/api/licenses/apply.ts b/frontend/src/api/licenses/apply.ts new file mode 100644 index 0000000..d0ac852 --- /dev/null +++ b/frontend/src/api/licenses/apply.ts @@ -0,0 +1,26 @@ +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/licenses/apply'; + +const apply = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/licenses', { + key: props.key, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default apply; diff --git a/frontend/src/api/licenses/getAll.ts b/frontend/src/api/licenses/getAll.ts new file mode 100644 index 0000000..4782be3 --- /dev/null +++ b/frontend/src/api/licenses/getAll.ts @@ -0,0 +1,24 @@ +import { ApiV2Instance as axios } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps } from 'types/api/licenses/getAll'; + +const getAll = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get('/licenses'); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getAll; diff --git a/frontend/src/api/logs/AddToSelectedField.ts b/frontend/src/api/logs/AddToSelectedField.ts new file mode 100644 index 0000000..b2672f7 --- /dev/null +++ b/frontend/src/api/logs/AddToSelectedField.ts @@ -0,0 +1,23 @@ +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/logs/addToSelectedFields'; + +const addToSelectedFields = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const data = await axios.post(`/logs/fields`, props); + return { + statusCode: 200, + error: null, + message: '', + payload: data.data, + }; + } catch (error) { + return Promise.reject(ErrorResponseHandler(error as AxiosError)); + } +}; + +export default addToSelectedFields; diff --git a/frontend/src/api/logs/GetLogs.ts b/frontend/src/api/logs/GetLogs.ts new file mode 100644 index 0000000..411e2e3 --- /dev/null +++ b/frontend/src/api/logs/GetLogs.ts @@ -0,0 +1,26 @@ +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/logs/getLogs'; + +const GetLogs = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const data = await axios.get(`/logs`, { + params: props, + }); + + return { + statusCode: 200, + error: null, + message: '', + payload: data.data.results, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default GetLogs; diff --git a/frontend/src/api/logs/GetLogsAggregate.ts b/frontend/src/api/logs/GetLogsAggregate.ts new file mode 100644 index 0000000..3385999 --- /dev/null +++ b/frontend/src/api/logs/GetLogsAggregate.ts @@ -0,0 +1,26 @@ +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/logs/getLogsAggregate'; + +const GetLogsAggregate = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const data = await axios.get(`/logs/aggregate`, { + params: props, + }); + + return { + statusCode: 200, + error: null, + message: '', + payload: data.data.items, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default GetLogsAggregate; diff --git a/frontend/src/api/logs/GetSearchFields.ts b/frontend/src/api/logs/GetSearchFields.ts new file mode 100644 index 0000000..ce3188b --- /dev/null +++ b/frontend/src/api/logs/GetSearchFields.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/logs/getSearchFields'; + +const GetSearchFields = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const data = await axios.get(`/logs/fields`); + + return { + statusCode: 200, + error: null, + message: '', + payload: data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default GetSearchFields; diff --git a/frontend/src/api/logs/RemoveFromSelectedField.ts b/frontend/src/api/logs/RemoveFromSelectedField.ts new file mode 100644 index 0000000..f417565 --- /dev/null +++ b/frontend/src/api/logs/RemoveFromSelectedField.ts @@ -0,0 +1,23 @@ +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/logs/addToSelectedFields'; + +const removeSelectedField = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const data = await axios.post(`/logs/fields`, props); + return { + statusCode: 200, + error: null, + message: '', + payload: data.data, + }; + } catch (error) { + return Promise.reject(ErrorResponseHandler(error as AxiosError)); + } +}; + +export default removeSelectedField; diff --git a/frontend/src/api/logs/livetail.ts b/frontend/src/api/logs/livetail.ts new file mode 100644 index 0000000..150f63d --- /dev/null +++ b/frontend/src/api/logs/livetail.ts @@ -0,0 +1,19 @@ +import apiV1 from 'api/apiV1'; +import getLocalStorageKey from 'api/browser/localstorage/get'; +import { ENVIRONMENT } from 'constants/env'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import { EventSourcePolyfill } from 'event-source-polyfill'; + +// 10 min in ms +const TIMEOUT_IN_MS = 10 * 60 * 1000; + +export const LiveTail = (queryParams: string): EventSourcePolyfill => + new EventSourcePolyfill( + `${ENVIRONMENT.baseURL}${apiV1}logs/tail?${queryParams}`, + { + headers: { + Authorization: `Bearer ${getLocalStorageKey(LOCALSTORAGE.AUTH_TOKEN)}`, + }, + heartbeatTimeout: TIMEOUT_IN_MS, + }, + ); diff --git a/frontend/src/api/metrics/ApDex/apDexSettings.ts b/frontend/src/api/metrics/ApDex/apDexSettings.ts new file mode 100644 index 0000000..e3d69c9 --- /dev/null +++ b/frontend/src/api/metrics/ApDex/apDexSettings.ts @@ -0,0 +1,16 @@ +import axios from 'api'; +import { + ApDexPayloadAndSettingsProps, + SetApDexPayloadProps, +} from 'types/api/metrics/getApDex'; + +export const setApDexSettings = async ({ + servicename, + threshold, + excludeStatusCode, +}: ApDexPayloadAndSettingsProps): Promise => + axios.post('/settings/apdex', { + servicename, + threshold, + excludeStatusCode, + }); diff --git a/frontend/src/api/metrics/ApDex/getApDexSettings.ts b/frontend/src/api/metrics/ApDex/getApDexSettings.ts new file mode 100644 index 0000000..4dcb96c --- /dev/null +++ b/frontend/src/api/metrics/ApDex/getApDexSettings.ts @@ -0,0 +1,8 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; +import { ApDexPayloadAndSettingsProps } from 'types/api/metrics/getApDex'; + +export const getApDexSettings = ( + servicename: string, +): Promise> => + axios.get(`/settings/apdex?services=${servicename}`); diff --git a/frontend/src/api/metrics/ApDex/getMetricMeta.ts b/frontend/src/api/metrics/ApDex/getMetricMeta.ts new file mode 100644 index 0000000..e304573 --- /dev/null +++ b/frontend/src/api/metrics/ApDex/getMetricMeta.ts @@ -0,0 +1,9 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; +import { MetricMetaProps } from 'types/api/metrics/getApDex'; + +export const getMetricMeta = ( + metricName: string, + servicename: string, +): Promise> => + axios.get(`/metric_meta?metricName=${metricName}&serviceName=${servicename}`); diff --git a/frontend/src/api/metrics/getDBOverView.ts b/frontend/src/api/metrics/getDBOverView.ts new file mode 100644 index 0000000..7afd56d --- /dev/null +++ b/frontend/src/api/metrics/getDBOverView.ts @@ -0,0 +1,26 @@ +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/metrics/getDBOverview'; + +const getDBOverView = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/service/dbOverview?&start=${props.start}&end=${props.end}&service=${props.service}&step=${props.step}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getDBOverView; diff --git a/frontend/src/api/metrics/getExternalAverageDuration.ts b/frontend/src/api/metrics/getExternalAverageDuration.ts new file mode 100644 index 0000000..51be375 --- /dev/null +++ b/frontend/src/api/metrics/getExternalAverageDuration.ts @@ -0,0 +1,29 @@ +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/metrics/getExternalAverageDuration'; + +const getExternalAverageDuration = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/service/externalAvgDuration?&start=${props.start}&end=${props.end}&service=${props.service}&step=${props.step}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getExternalAverageDuration; diff --git a/frontend/src/api/metrics/getExternalError.ts b/frontend/src/api/metrics/getExternalError.ts new file mode 100644 index 0000000..3587639 --- /dev/null +++ b/frontend/src/api/metrics/getExternalError.ts @@ -0,0 +1,26 @@ +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/metrics/getExternalError'; + +const getExternalError = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/service/externalErrors?&start=${props.start}&end=${props.end}&service=${props.service}&step=${props.step}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getExternalError; diff --git a/frontend/src/api/metrics/getExternalService.ts b/frontend/src/api/metrics/getExternalService.ts new file mode 100644 index 0000000..de9bf65 --- /dev/null +++ b/frontend/src/api/metrics/getExternalService.ts @@ -0,0 +1,26 @@ +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/metrics/getExternalService'; + +const getExternalService = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/service/external?&start=${props.start}&end=${props.end}&service=${props.service}&step=${props.step}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getExternalService; diff --git a/frontend/src/api/metrics/getMetricName.ts b/frontend/src/api/metrics/getMetricName.ts new file mode 100644 index 0000000..f3bff5a --- /dev/null +++ b/frontend/src/api/metrics/getMetricName.ts @@ -0,0 +1,27 @@ +import { ApiV2Instance as axios } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { + MetricNameProps, + MetricNamesPayloadProps, +} from 'types/api/metrics/getMetricName'; + +export const getMetricName = async ( + props: MetricNameProps, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/metrics/autocomplete/list?match=${props || ''}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; diff --git a/frontend/src/api/metrics/getQueryRange.ts b/frontend/src/api/metrics/getQueryRange.ts new file mode 100644 index 0000000..40deb02 --- /dev/null +++ b/frontend/src/api/metrics/getQueryRange.ts @@ -0,0 +1,41 @@ +import { ApiV3Instance, ApiV4Instance } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ENTITY_VERSION_V4 } from 'constants/app'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { + MetricRangePayloadV3, + QueryRangePayload, +} from 'types/api/metrics/getQueryRange'; + +export const getMetricsQueryRange = async ( + props: QueryRangePayload, + version: string, + signal: AbortSignal, +): Promise | ErrorResponse> => { + try { + if (version && version === ENTITY_VERSION_V4) { + const response = await ApiV4Instance.post('/query_range', props, { signal }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + params: props, + }; + } + + const response = await ApiV3Instance.post('/query_range', props, { signal }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + params: props, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; diff --git a/frontend/src/api/metrics/getResourceAttributes.ts b/frontend/src/api/metrics/getResourceAttributes.ts new file mode 100644 index 0000000..66524bf --- /dev/null +++ b/frontend/src/api/metrics/getResourceAttributes.ts @@ -0,0 +1,50 @@ +import { ApiV2Instance as axios } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { + TagKeyProps, + TagKeysPayloadProps, + TagValueProps, + TagValuesPayloadProps, +} from 'types/api/metrics/getResourceAttributes'; + +export const getResourceAttributesTagKeys = async ( + props: TagKeyProps, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/metrics/autocomplete/tagKey?metricName=${props.metricName}${ + props.match ? `&match=${props.match}` : '' + }`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export const getResourceAttributesTagValues = async ( + props: TagValueProps, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/metrics/autocomplete/tagValue?metricName=${props.metricName}&tagKey=${props.tagKey}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; diff --git a/frontend/src/api/metrics/getService.ts b/frontend/src/api/metrics/getService.ts new file mode 100644 index 0000000..731da11 --- /dev/null +++ b/frontend/src/api/metrics/getService.ts @@ -0,0 +1,13 @@ +import axios from 'api'; +import { PayloadProps, Props } from 'types/api/metrics/getService'; + +const getService = async (props: Props): Promise => { + const response = await axios.post(`/services`, { + start: `${props.start}`, + end: `${props.end}`, + tags: props.selectedTags, + }); + return response.data; +}; + +export default getService; diff --git a/frontend/src/api/metrics/getServiceOverview.ts b/frontend/src/api/metrics/getServiceOverview.ts new file mode 100644 index 0000000..47febaa --- /dev/null +++ b/frontend/src/api/metrics/getServiceOverview.ts @@ -0,0 +1,16 @@ +import axios from 'api'; +import { PayloadProps, Props } from 'types/api/metrics/getServiceOverview'; + +const getServiceOverview = async (props: Props): Promise => { + const response = await axios.post(`/service/overview`, { + start: `${props.start}`, + end: `${props.end}`, + service: props.service, + step: props.step, + tags: props.selectedTags, + }); + + return response.data; +}; + +export default getServiceOverview; diff --git a/frontend/src/api/metrics/getTopLevelOperations.ts b/frontend/src/api/metrics/getTopLevelOperations.ts new file mode 100644 index 0000000..2f5a202 --- /dev/null +++ b/frontend/src/api/metrics/getTopLevelOperations.ts @@ -0,0 +1,12 @@ +import axios from 'api'; + +const getTopLevelOperations = async (): Promise => { + const response = await axios.post(`/service/top_level_operations`); + return response.data; +}; + +export type ServiceDataProps = { + [serviceName: string]: string[]; +}; + +export default getTopLevelOperations; diff --git a/frontend/src/api/metrics/getTopOperations.ts b/frontend/src/api/metrics/getTopOperations.ts new file mode 100644 index 0000000..9c85602 --- /dev/null +++ b/frontend/src/api/metrics/getTopOperations.ts @@ -0,0 +1,15 @@ +import axios from 'api'; +import { PayloadProps, Props } from 'types/api/metrics/getTopOperations'; + +const getTopOperations = async (props: Props): Promise => { + const response = await axios.post(`/service/top_operations`, { + start: `${props.start}`, + end: `${props.end}`, + service: props.service, + tags: props.selectedTags, + }); + + return response.data; +}; + +export default getTopOperations; diff --git a/frontend/src/api/pipeline/get.ts b/frontend/src/api/pipeline/get.ts new file mode 100644 index 0000000..ff4dd7f --- /dev/null +++ b/frontend/src/api/pipeline/get.ts @@ -0,0 +1,25 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { Pipeline } from 'types/api/pipeline/def'; +import { Props } from 'types/api/pipeline/get'; + +const get = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`/logs/pipelines/${props.version}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response?.data?.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default get; diff --git a/frontend/src/api/pipeline/post.ts b/frontend/src/api/pipeline/post.ts new file mode 100644 index 0000000..c2e7ca2 --- /dev/null +++ b/frontend/src/api/pipeline/post.ts @@ -0,0 +1,25 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { Pipeline } from 'types/api/pipeline/def'; +import { Props } from 'types/api/pipeline/post'; + +const post = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/logs/pipelines', props.data); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default post; diff --git a/frontend/src/api/pipeline/preview.ts b/frontend/src/api/pipeline/preview.ts new file mode 100644 index 0000000..a349ecd --- /dev/null +++ b/frontend/src/api/pipeline/preview.ts @@ -0,0 +1,21 @@ +import axios from 'api'; +import { ILog } from 'types/api/logs/log'; +import { PipelineData } from 'types/api/pipeline/def'; + +export interface PipelineSimulationRequest { + logs: ILog[]; + pipelines: PipelineData[]; +} + +export interface PipelineSimulationResponse { + logs: ILog[]; +} + +const simulatePipelineProcessing = async ( + requestBody: PipelineSimulationRequest, +): Promise => + axios + .post('/logs/pipelines/preview', requestBody) + .then((res) => res.data.data); + +export default simulatePipelineProcessing; diff --git a/frontend/src/api/queryBuilder/getAggregateAttribute.ts b/frontend/src/api/queryBuilder/getAggregateAttribute.ts new file mode 100644 index 0000000..e493bb4 --- /dev/null +++ b/frontend/src/api/queryBuilder/getAggregateAttribute.ts @@ -0,0 +1,49 @@ +import { ApiV3Instance } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError, AxiosResponse } from 'axios'; +import { baseAutoCompleteIdKeysOrder } from 'constants/queryBuilder'; +import { createIdFromObjectFields } from 'lib/createIdFromObjectFields'; +import createQueryParams from 'lib/createQueryParams'; +// ** Helpers +import { ErrorResponse, SuccessResponse } from 'types/api'; +// ** Types +import { IGetAggregateAttributePayload } from 'types/api/queryBuilder/getAggregatorAttribute'; +import { + BaseAutocompleteData, + IQueryAutocompleteResponse, +} from 'types/api/queryBuilder/queryAutocompleteResponse'; + +export const getAggregateAttribute = async ({ + aggregateOperator, + searchText, + dataSource, +}: IGetAggregateAttributePayload): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response: AxiosResponse<{ + data: IQueryAutocompleteResponse; + }> = await ApiV3Instance.get( + `autocomplete/aggregate_attributes?${createQueryParams({ + aggregateOperator, + searchText, + dataSource, + })}`, + ); + + const payload: BaseAutocompleteData[] = + response.data.data.attributeKeys?.map(({ id: _, ...item }) => ({ + ...item, + id: createIdFromObjectFields(item, baseAutoCompleteIdKeysOrder), + })) || []; + + return { + statusCode: 200, + error: null, + message: response.statusText, + payload: { attributeKeys: payload }, + }; + } catch (e) { + return ErrorResponseHandler(e as AxiosError); + } +}; diff --git a/frontend/src/api/queryBuilder/getAttributeKeys.ts b/frontend/src/api/queryBuilder/getAttributeKeys.ts new file mode 100644 index 0000000..99edc63 --- /dev/null +++ b/frontend/src/api/queryBuilder/getAttributeKeys.ts @@ -0,0 +1,51 @@ +import { ApiV3Instance } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError, AxiosResponse } from 'axios'; +import { baseAutoCompleteIdKeysOrder } from 'constants/queryBuilder'; +import { createIdFromObjectFields } from 'lib/createIdFromObjectFields'; +import createQueryParams from 'lib/createQueryParams'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +// ** Types +import { IGetAttributeKeysPayload } from 'types/api/queryBuilder/getAttributeKeys'; +import { + BaseAutocompleteData, + IQueryAutocompleteResponse, +} from 'types/api/queryBuilder/queryAutocompleteResponse'; + +export const getAggregateKeys = async ({ + aggregateOperator, + searchText, + dataSource, + aggregateAttribute, + tagType, +}: IGetAttributeKeysPayload): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response: AxiosResponse<{ + data: IQueryAutocompleteResponse; + }> = await ApiV3Instance.get( + `autocomplete/attribute_keys?${createQueryParams({ + aggregateOperator, + searchText, + dataSource, + aggregateAttribute, + })}&tagType=${tagType}`, + ); + + const payload: BaseAutocompleteData[] = + response.data.data.attributeKeys?.map(({ id: _, ...item }) => ({ + ...item, + id: createIdFromObjectFields(item, baseAutoCompleteIdKeysOrder), + })) || []; + + return { + statusCode: 200, + error: null, + message: response.statusText, + payload: { attributeKeys: payload }, + }; + } catch (e) { + return ErrorResponseHandler(e as AxiosError); + } +}; diff --git a/frontend/src/api/queryBuilder/getAttributesValues.ts b/frontend/src/api/queryBuilder/getAttributesValues.ts new file mode 100644 index 0000000..216da1e --- /dev/null +++ b/frontend/src/api/queryBuilder/getAttributesValues.ts @@ -0,0 +1,42 @@ +import { ApiV3Instance } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import createQueryParams from 'lib/createQueryParams'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { + IAttributeValuesResponse, + IGetAttributeValuesPayload, +} from 'types/api/queryBuilder/getAttributesValues'; + +export const getAttributesValues = async ({ + aggregateOperator, + dataSource, + aggregateAttribute, + attributeKey, + filterAttributeKeyDataType, + tagType, + searchText, +}: IGetAttributeValuesPayload): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await ApiV3Instance.get( + `/autocomplete/attribute_values?${createQueryParams({ + aggregateOperator, + dataSource, + aggregateAttribute, + attributeKey, + searchText, + })}&filterAttributeKeyDataType=${filterAttributeKeyDataType}&tagType=${tagType}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; diff --git a/frontend/src/api/saveView/deleteView.ts b/frontend/src/api/saveView/deleteView.ts new file mode 100644 index 0000000..e58e731 --- /dev/null +++ b/frontend/src/api/saveView/deleteView.ts @@ -0,0 +1,5 @@ +import axios from 'api'; +import { DeleteViewPayloadProps } from 'types/api/saveViews/types'; + +export const deleteView = (uuid: string): Promise => + axios.delete(`explorer/views/${uuid}`); diff --git a/frontend/src/api/saveView/getAllViews.ts b/frontend/src/api/saveView/getAllViews.ts new file mode 100644 index 0000000..bdafb96 --- /dev/null +++ b/frontend/src/api/saveView/getAllViews.ts @@ -0,0 +1,9 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; +import { AllViewsProps } from 'types/api/saveViews/types'; +import { DataSource } from 'types/common/queryBuilder'; + +export const getAllViews = ( + sourcepage: DataSource, +): Promise> => + axios.get(`explorer/views?sourcePage=${sourcepage}`); diff --git a/frontend/src/api/saveView/saveView.ts b/frontend/src/api/saveView/saveView.ts new file mode 100644 index 0000000..a0c7ba5 --- /dev/null +++ b/frontend/src/api/saveView/saveView.ts @@ -0,0 +1,16 @@ +import axios from 'api'; +import { AxiosResponse } from 'axios'; +import { SaveViewPayloadProps, SaveViewProps } from 'types/api/saveViews/types'; + +export const saveView = ({ + compositeQuery, + sourcePage, + viewName, + extraData, +}: SaveViewProps): Promise> => + axios.post('explorer/views', { + name: viewName, + sourcePage, + compositeQuery, + extraData, + }); diff --git a/frontend/src/api/saveView/updateView.ts b/frontend/src/api/saveView/updateView.ts new file mode 100644 index 0000000..6ee745f --- /dev/null +++ b/frontend/src/api/saveView/updateView.ts @@ -0,0 +1,19 @@ +import axios from 'api'; +import { + UpdateViewPayloadProps, + UpdateViewProps, +} from 'types/api/saveViews/types'; + +export const updateView = ({ + compositeQuery, + viewName, + extraData, + sourcePage, + viewKey, +}: UpdateViewProps): Promise => + axios.put(`explorer/views/${viewKey}`, { + name: viewName, + compositeQuery, + extraData, + sourcePage, + }); diff --git a/frontend/src/api/settings/getIngestionData.ts b/frontend/src/api/settings/getIngestionData.ts new file mode 100644 index 0000000..c35b964 --- /dev/null +++ b/frontend/src/api/settings/getIngestionData.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 { IngestionResponseProps } from 'types/api/settings/ingestion'; + +const getIngestionData = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get(`/settings/ingestion_key`); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getIngestionData; diff --git a/frontend/src/api/settings/getRetention.ts b/frontend/src/api/settings/getRetention.ts new file mode 100644 index 0000000..d19ab1a --- /dev/null +++ b/frontend/src/api/settings/getRetention.ts @@ -0,0 +1,26 @@ +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/settings/getRetention'; + +const getRetention = async ( + props: T, +): Promise> | ErrorResponse> => { + try { + const response = await axios.get>( + `/settings/ttl?type=${props}`, + ); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getRetention; diff --git a/frontend/src/api/settings/setRetention.ts b/frontend/src/api/settings/setRetention.ts new file mode 100644 index 0000000..481760b --- /dev/null +++ b/frontend/src/api/settings/setRetention.ts @@ -0,0 +1,30 @@ +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/settings/setRetention'; + +const setRetention = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post( + `/settings/ttl?duration=${props.totalDuration}&type=${props.type}${ + props.coldStorage + ? `&coldStorage=${props.coldStorage}&toColdDuration=${props.toColdDuration}` + : '' + }`, + ); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default setRetention; diff --git a/frontend/src/api/trace/getFilters.ts b/frontend/src/api/trace/getFilters.ts new file mode 100644 index 0000000..7eeb940 --- /dev/null +++ b/frontend/src/api/trace/getFilters.ts @@ -0,0 +1,49 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import omitBy from 'lodash-es/omitBy'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/trace/getFilters'; + +const getFilters = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const duration = + omitBy(props.other, (_, key) => !key.startsWith('duration')) || []; + + const nonDuration = omitBy(props.other, (_, key) => + key.startsWith('duration'), + ); + + const exclude: string[] = []; + + props.isFilterExclude.forEach((value, key) => { + if (value) { + exclude.push(key); + } + }); + + const response = await axios.post(`/getSpanFilters`, { + start: props.start, + end: props.end, + getFilters: props.getFilters, + ...nonDuration, + maxDuration: String((duration.duration || [])[0] || ''), + minDuration: String((duration.duration || [])[1] || ''), + exclude, + spanKind: props.spanKind, + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getFilters; diff --git a/frontend/src/api/trace/getSpans.ts b/frontend/src/api/trace/getSpans.ts new file mode 100644 index 0000000..261b265 --- /dev/null +++ b/frontend/src/api/trace/getSpans.ts @@ -0,0 +1,62 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import omitBy from 'lodash-es/omitBy'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/trace/getSpans'; + +const getSpans = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const updatedSelectedTags = props.selectedTags.map((e) => ({ + Key: `${e.Key}.(string)`, + Operator: e.Operator, + StringValues: e.StringValues, + NumberValues: e.NumberValues, + BoolValues: e.BoolValues, + })); + + const exclude: string[] = []; + + props.isFilterExclude.forEach((value, key) => { + if (value) { + exclude.push(key); + } + }); + + const other = Object.fromEntries(props.selectedFilter); + + const duration = omitBy(other, (_, key) => !key.startsWith('duration')) || []; + + const nonDuration = omitBy(other, (_, key) => key.startsWith('duration')); + + const response = await axios.post( + `/getFilteredSpans/aggregates`, + { + start: String(props.start), + end: String(props.end), + function: props.function, + groupBy: props.groupBy === 'none' ? '' : props.groupBy, + step: props.step, + tags: updatedSelectedTags, + ...nonDuration, + maxDuration: String((duration.duration || [])[0] || ''), + minDuration: String((duration.duration || [])[1] || ''), + exclude, + spanKind: props.spanKind, + }, + ); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getSpans; diff --git a/frontend/src/api/trace/getSpansAggregate.ts b/frontend/src/api/trace/getSpansAggregate.ts new file mode 100644 index 0000000..7f24560 --- /dev/null +++ b/frontend/src/api/trace/getSpansAggregate.ts @@ -0,0 +1,65 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import omitBy from 'lodash-es/omitBy'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/trace/getSpanAggregate'; +import { TraceFilterEnum } from 'types/reducer/trace'; + +const getSpanAggregate = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const preProps = { + start: String(props.start), + end: String(props.end), + limit: props.limit, + offset: props.offset, + order: props.order, + orderParam: props.orderParam, + }; + + const exclude: TraceFilterEnum[] = []; + + props.isFilterExclude.forEach((value, key) => { + if (value) { + exclude.push(key); + } + }); + + const updatedSelectedTags = props.selectedTags.map((e) => ({ + Key: `${e.Key}.(string)`, + Operator: e.Operator, + StringValues: e.StringValues, + NumberValues: e.NumberValues, + BoolValues: e.BoolValues, + })); + + const other = Object.fromEntries(props.selectedFilter); + + const duration = omitBy(other, (_, key) => !key.startsWith('duration')) || []; + + const nonDuration = omitBy(other, (_, key) => key.startsWith('duration')); + + const response = await axios.post(`/getFilteredSpans`, { + ...preProps, + tags: updatedSelectedTags, + ...nonDuration, + maxDuration: String((duration.duration || [])[0] || ''), + minDuration: String((duration.duration || [])[1] || ''), + exclude, + spanKind: props.spanKind, + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getSpanAggregate; diff --git a/frontend/src/api/trace/getTagFilter.ts b/frontend/src/api/trace/getTagFilter.ts new file mode 100644 index 0000000..2f53ab5 --- /dev/null +++ b/frontend/src/api/trace/getTagFilter.ts @@ -0,0 +1,49 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { omitBy } from 'lodash-es'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/trace/getTagFilters'; +import { TraceFilterEnum } from 'types/reducer/trace'; + +const getTagFilters = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const duration = + omitBy(props.other, (_, key) => !key.startsWith('duration')) || []; + + const exclude: TraceFilterEnum[] = []; + + props.isFilterExclude.forEach((value, key) => { + if (value) { + exclude.push(key); + } + }); + + const nonDuration = omitBy(props.other, (_, key) => + key.startsWith('duration'), + ); + + const response = await axios.post(`/getTagFilters`, { + start: String(props.start), + end: String(props.end), + ...nonDuration, + maxDuration: String((duration.duration || [])[0] || ''), + minDuration: String((duration.duration || [])[1] || ''), + exclude, + spanKind: props.spanKind, + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getTagFilters; diff --git a/frontend/src/api/trace/getTagValue.ts b/frontend/src/api/trace/getTagValue.ts new file mode 100644 index 0000000..2519c7a --- /dev/null +++ b/frontend/src/api/trace/getTagValue.ts @@ -0,0 +1,31 @@ +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: { + Key: props.tagKey.Key, + Type: props.tagKey.Type, + }, + spanKind: props.spanKind, + }); + 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/trace/getTraceItem.ts b/frontend/src/api/trace/getTraceItem.ts new file mode 100644 index 0000000..054c809 --- /dev/null +++ b/frontend/src/api/trace/getTraceItem.ts @@ -0,0 +1,32 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { formUrlParams } from 'container/TraceDetail/utils'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { GetTraceItemProps, PayloadProps } from 'types/api/trace/getTraceItem'; + +const getTraceItem = async ( + props: GetTraceItemProps, +): Promise | ErrorResponse> => { + try { + const response = await axios.request({ + url: `/traces/${props.id}${formUrlParams({ + spanId: props.spanId, + levelUp: props.levelUp, + levelDown: props.levelDown, + })}`, + method: 'get', + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getTraceItem; diff --git a/frontend/src/api/user/changeMyPassword.ts b/frontend/src/api/user/changeMyPassword.ts new file mode 100644 index 0000000..cdca2cd --- /dev/null +++ b/frontend/src/api/user/changeMyPassword.ts @@ -0,0 +1,26 @@ +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/user/changeMyPassword'; + +const changeMyPassword = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/changePassword/${props.userId}`, { + ...props, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default changeMyPassword; diff --git a/frontend/src/api/user/deleteInvite.ts b/frontend/src/api/user/deleteInvite.ts new file mode 100644 index 0000000..16233ef --- /dev/null +++ b/frontend/src/api/user/deleteInvite.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, Props } from 'types/api/user/deleteInvite'; + +const deleteInvite = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.delete(`/invite/${props.email}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default deleteInvite; diff --git a/frontend/src/api/user/deleteUser.ts b/frontend/src/api/user/deleteUser.ts new file mode 100644 index 0000000..4eb2694 --- /dev/null +++ b/frontend/src/api/user/deleteUser.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, Props } from 'types/api/user/deleteUser'; + +const deleteUser = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.delete(`/user/${props.userId}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default deleteUser; diff --git a/frontend/src/api/user/editOrg.ts b/frontend/src/api/user/editOrg.ts new file mode 100644 index 0000000..da980ac --- /dev/null +++ b/frontend/src/api/user/editOrg.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/user/editOrg'; + +const editOrg = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/org/${props.orgId}`, { + name: props.name, + isAnonymous: props.isAnonymous, + hasOptedUpdates: props.hasOptedUpdates, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default editOrg; diff --git a/frontend/src/api/user/editUser.ts b/frontend/src/api/user/editUser.ts new file mode 100644 index 0000000..88f7c40 --- /dev/null +++ b/frontend/src/api/user/editUser.ts @@ -0,0 +1,26 @@ +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/user/editUser'; + +const editUser = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/user/${props.userId}`, { + Name: props.name, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default editUser; diff --git a/frontend/src/api/user/getInviteDetails.ts b/frontend/src/api/user/getInviteDetails.ts new file mode 100644 index 0000000..22d49cf --- /dev/null +++ b/frontend/src/api/user/getInviteDetails.ts @@ -0,0 +1,26 @@ +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/user/getInviteDetails'; + +const getInviteDetails = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/invite/${props.inviteId}?ref=${window.location.href}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getInviteDetails; diff --git a/frontend/src/api/user/getLatestVersion.ts b/frontend/src/api/user/getLatestVersion.ts new file mode 100644 index 0000000..28a72f7 --- /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/api/user/getOrgUser.ts b/frontend/src/api/user/getOrgUser.ts new file mode 100644 index 0000000..8956adc --- /dev/null +++ b/frontend/src/api/user/getOrgUser.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, Props } from 'types/api/user/getOrgMembers'; + +const getOrgUser = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`/orgUsers/${props.orgId}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getOrgUser; diff --git a/frontend/src/api/user/getOrganization.ts b/frontend/src/api/user/getOrganization.ts new file mode 100644 index 0000000..dfda5e4 --- /dev/null +++ b/frontend/src/api/user/getOrganization.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 } from 'types/api/user/getOrganization'; + +const getOrganization = async ( + token?: string, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`/org`, { + headers: { + Authorization: `bearer ${token}`, + }, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getOrganization; diff --git a/frontend/src/api/user/getPendingInvites.ts b/frontend/src/api/user/getPendingInvites.ts new file mode 100644 index 0000000..947b7bf --- /dev/null +++ b/frontend/src/api/user/getPendingInvites.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/user/getPendingInvites'; + +const getPendingInvites = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get(`/invite`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getPendingInvites; diff --git a/frontend/src/api/user/getPreference.ts b/frontend/src/api/user/getPreference.ts new file mode 100644 index 0000000..b284eab --- /dev/null +++ b/frontend/src/api/user/getPreference.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/user/getUserPreference'; + +const getPreference = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get(`/userPreferences`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getPreference; diff --git a/frontend/src/api/user/getResetPasswordToken.ts b/frontend/src/api/user/getResetPasswordToken.ts new file mode 100644 index 0000000..845826e --- /dev/null +++ b/frontend/src/api/user/getResetPasswordToken.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, Props } from 'types/api/user/getResetPasswordToken'; + +const getResetPasswordToken = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`/getResetPasswordToken/${props.userId}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getResetPasswordToken; diff --git a/frontend/src/api/user/getRoles.ts b/frontend/src/api/user/getRoles.ts new file mode 100644 index 0000000..0602a0a --- /dev/null +++ b/frontend/src/api/user/getRoles.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/user/getUserRole'; + +const getRoles = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`/rbac/role/${props.userId}`, { + headers: { + Authorization: `bearer ${props.token}`, + }, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getRoles; diff --git a/frontend/src/api/user/getUser.ts b/frontend/src/api/user/getUser.ts new file mode 100644 index 0000000..6bedb78 --- /dev/null +++ b/frontend/src/api/user/getUser.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/user/getUser'; + +const getUser = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get(`/user/${props.userId}`, { + headers: { + Authorization: `bearer ${props.token}`, + }, + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getUser; diff --git a/frontend/src/api/user/getVersion.ts b/frontend/src/api/user/getVersion.ts new file mode 100644 index 0000000..0f3e7f8 --- /dev/null +++ b/frontend/src/api/user/getVersion.ts @@ -0,0 +1,25 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { getVersion } from 'constants/api'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps } from 'types/api/user/getVersion'; + +const getVersionApi = async (): Promise< + SuccessResponse | ErrorResponse +> => { + try { + const response = await axios.get(`/${getVersion}`); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getVersionApi; diff --git a/frontend/src/api/user/login.ts b/frontend/src/api/user/login.ts new file mode 100644 index 0000000..4eff883 --- /dev/null +++ b/frontend/src/api/user/login.ts @@ -0,0 +1,26 @@ +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/user/login'; + +const login = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/login`, { + ...props, + }); + + return { + statusCode: 200, + error: null, + message: response.statusText, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default login; diff --git a/frontend/src/api/user/loginPrecheck.ts b/frontend/src/api/user/loginPrecheck.ts new file mode 100644 index 0000000..c0cdc3d --- /dev/null +++ b/frontend/src/api/user/loginPrecheck.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/user/loginPrecheck'; + +const loginPrecheck = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/loginPrecheck?email=${encodeURIComponent( + props.email, + )}&ref=${encodeURIComponent(window.location.href)}`, + ); + + return { + statusCode: 200, + error: null, + message: response.statusText, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default loginPrecheck; diff --git a/frontend/src/api/user/resetPassword.ts b/frontend/src/api/user/resetPassword.ts new file mode 100644 index 0000000..eb6d275 --- /dev/null +++ b/frontend/src/api/user/resetPassword.ts @@ -0,0 +1,26 @@ +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/user/resetPassword'; + +const resetPassword = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/resetPassword`, { + ...props, + }); + + return { + statusCode: 200, + error: null, + message: response.statusText, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default resetPassword; diff --git a/frontend/src/api/user/sendInvite.ts b/frontend/src/api/user/sendInvite.ts new file mode 100644 index 0000000..9835588 --- /dev/null +++ b/frontend/src/api/user/sendInvite.ts @@ -0,0 +1,26 @@ +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/user/setInvite'; + +const sendInvite = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/invite`, { + ...props, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default sendInvite; diff --git a/frontend/src/api/user/setFlags.ts b/frontend/src/api/user/setFlags.ts new file mode 100644 index 0000000..0ae9b18 --- /dev/null +++ b/frontend/src/api/user/setFlags.ts @@ -0,0 +1,26 @@ +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/user/setFlags'; + +const setFlags = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.patch(`/user/${props.userId}/flags`, { + ...props.flags, + }); + + return { + statusCode: 200, + error: null, + message: response.data?.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default setFlags; diff --git a/frontend/src/api/user/signup.ts b/frontend/src/api/user/signup.ts new file mode 100644 index 0000000..fcb483d --- /dev/null +++ b/frontend/src/api/user/signup.ts @@ -0,0 +1,26 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps } from 'types/api/user/loginPrecheck'; +import { Props } from 'types/api/user/signup'; + +const signup = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.post(`/register`, { + ...props, + }); + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data?.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default signup; diff --git a/frontend/src/api/user/updateRole.ts b/frontend/src/api/user/updateRole.ts new file mode 100644 index 0000000..5d82a3d --- /dev/null +++ b/frontend/src/api/user/updateRole.ts @@ -0,0 +1,26 @@ +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/user/updateRole'; + +const updateRole = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/rbac/role/${props.userId}`, { + group_name: props.group_name, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default updateRole; diff --git a/frontend/src/api/userFeedback/sendFeedback.ts b/frontend/src/api/userFeedback/sendFeedback.ts new file mode 100644 index 0000000..abf8113 --- /dev/null +++ b/frontend/src/api/userFeedback/sendFeedback.ts @@ -0,0 +1,21 @@ +import axios from 'api'; +import { Props } from 'types/api/userFeedback/sendResponse'; + +const sendFeedback = async (props: Props): Promise => { + const response = await axios.post( + '/feedback', + { + email: props.email, + message: props.message, + }, + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }, + ); + + return response.status; +}; + +export default sendFeedback; diff --git a/frontend/src/api/utils.ts b/frontend/src/api/utils.ts new file mode 100644 index 0000000..bd81719 --- /dev/null +++ b/frontend/src/api/utils.ts @@ -0,0 +1,76 @@ +import deleteLocalStorageKey from 'api/browser/localstorage/remove'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import ROUTES from 'constants/routes'; +import history from 'lib/history'; +import store from 'store'; +import { + LOGGED_IN, + UPDATE_ORG, + UPDATE_USER, + UPDATE_USER_ACCESS_REFRESH_ACCESS_TOKEN, + UPDATE_USER_ORG_ROLE, +} from 'types/actions/app'; + +export const Logout = (): void => { + deleteLocalStorageKey(LOCALSTORAGE.AUTH_TOKEN); + deleteLocalStorageKey(LOCALSTORAGE.IS_LOGGED_IN); + deleteLocalStorageKey(LOCALSTORAGE.IS_IDENTIFIED_USER); + deleteLocalStorageKey(LOCALSTORAGE.REFRESH_AUTH_TOKEN); + deleteLocalStorageKey(LOCALSTORAGE.LOGGED_IN_USER_EMAIL); + deleteLocalStorageKey(LOCALSTORAGE.LOGGED_IN_USER_NAME); + deleteLocalStorageKey(LOCALSTORAGE.CHAT_SUPPORT); + + store.dispatch({ + type: LOGGED_IN, + payload: { + isLoggedIn: false, + }, + }); + + store.dispatch({ + type: UPDATE_USER_ORG_ROLE, + payload: { + org: null, + role: null, + }, + }); + + store.dispatch({ + type: UPDATE_USER, + payload: { + ROLE: 'VIEWER', + email: '', + name: '', + orgId: '', + orgName: '', + profilePictureURL: '', + userId: '', + userFlags: {}, + }, + }); + + store.dispatch({ + type: UPDATE_USER_ACCESS_REFRESH_ACCESS_TOKEN, + payload: { + accessJwt: '', + refreshJwt: '', + }, + }); + + store.dispatch({ + type: UPDATE_ORG, + payload: { + org: [], + }, + }); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (window && window.Intercom) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + window.Intercom('shutdown'); + } + + history.push(ROUTES.LOGIN); +}; diff --git a/frontend/src/api/widgets/getQuery.ts b/frontend/src/api/widgets/getQuery.ts new file mode 100644 index 0000000..a706db3 --- /dev/null +++ b/frontend/src/api/widgets/getQuery.ts @@ -0,0 +1,26 @@ +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/widgets/getQuery'; + +const getQuery = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const response = await axios.get( + `/query_range?query=${props.query}&start=${props.start}&end=${props.end}&step=${props.step}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getQuery; diff --git a/frontend/src/assets/Dashboard/BarIcon.tsx b/frontend/src/assets/Dashboard/BarIcon.tsx new file mode 100644 index 0000000..b8e6b3c --- /dev/null +++ b/frontend/src/assets/Dashboard/BarIcon.tsx @@ -0,0 +1,41 @@ +import { CSSProperties } from 'react'; + +function BarIcon({ + fillColor, +}: { + fillColor: CSSProperties['color']; +}): JSX.Element { + return ( + + + + + + ); +} + +export default BarIcon; diff --git a/frontend/src/assets/Dashboard/List.tsx b/frontend/src/assets/Dashboard/List.tsx new file mode 100644 index 0000000..1c4d1d0 --- /dev/null +++ b/frontend/src/assets/Dashboard/List.tsx @@ -0,0 +1,30 @@ +import { CSSProperties } from 'react'; + +function ListIcon({ + fillColor, +}: { + fillColor: CSSProperties['color']; +}): JSX.Element { + return ( + + + + + + + + + ); +} + +export default ListIcon; diff --git a/frontend/src/assets/Dashboard/Table.tsx b/frontend/src/assets/Dashboard/Table.tsx new file mode 100644 index 0000000..60effdb --- /dev/null +++ b/frontend/src/assets/Dashboard/Table.tsx @@ -0,0 +1,48 @@ +import { CSSProperties } from 'react'; + +function TableIcon({ + fillColor, +}: { + fillColor: CSSProperties['color']; +}): JSX.Element { + return ( + + + + + + + ); +} + +export default TableIcon; diff --git a/frontend/src/assets/Dashboard/TimeSeries.tsx b/frontend/src/assets/Dashboard/TimeSeries.tsx new file mode 100644 index 0000000..afa9b5f --- /dev/null +++ b/frontend/src/assets/Dashboard/TimeSeries.tsx @@ -0,0 +1,69 @@ +import { CSSProperties } from 'react'; + +function TimeSeries({ + fillColor, +}: { + fillColor: CSSProperties['color']; +}): JSX.Element { + return ( + + + + + + + + + + ); +} + +export default TimeSeries; diff --git a/frontend/src/assets/Dashboard/Value.tsx b/frontend/src/assets/Dashboard/Value.tsx new file mode 100644 index 0000000..39ef8d9 --- /dev/null +++ b/frontend/src/assets/Dashboard/Value.tsx @@ -0,0 +1,32 @@ +import { CSSProperties } from 'react'; + +function Value({ + fillColor, +}: { + fillColor: CSSProperties['color']; +}): JSX.Element { + return ( + + + + + ); +} + +export default Value; diff --git a/frontend/src/assets/NotFound.tsx b/frontend/src/assets/NotFound.tsx new file mode 100644 index 0000000..b8bf4d0 --- /dev/null +++ b/frontend/src/assets/NotFound.tsx @@ -0,0 +1,14 @@ +function NotFound(): JSX.Element { + return ( + not-found + ); +} + +export default NotFound; diff --git a/frontend/src/assets/SomethingWentWrong.tsx b/frontend/src/assets/SomethingWentWrong.tsx new file mode 100644 index 0000000..e6b0d30 --- /dev/null +++ b/frontend/src/assets/SomethingWentWrong.tsx @@ -0,0 +1,468 @@ +function SomethingWentWrong(): JSX.Element { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default SomethingWentWrong; diff --git a/frontend/src/assets/UnAuthorized.tsx b/frontend/src/assets/UnAuthorized.tsx new file mode 100644 index 0000000..93cfb48 --- /dev/null +++ b/frontend/src/assets/UnAuthorized.tsx @@ -0,0 +1,26 @@ +function UnAuthorized(): JSX.Element { + return ( + + + + + + ); +} + +export default UnAuthorized; diff --git a/frontend/src/components/CustomTimePicker/CustomTimePicker.styles.scss b/frontend/src/components/CustomTimePicker/CustomTimePicker.styles.scss new file mode 100644 index 0000000..14f80a9 --- /dev/null +++ b/frontend/src/components/CustomTimePicker/CustomTimePicker.styles.scss @@ -0,0 +1,121 @@ +.custom-time-picker { + display: flex; + flex-direction: column; +} + +.time-options-container { + .time-options-item { + margin: 2px 0; + padding: 8px; + border-radius: 2px; + + &.active { + background-color: rgba($color: #000000, $alpha: 0.2); + + &:hover { + cursor: pointer; + background-color: rgba($color: #000000, $alpha: 0.3); + } + } + + &:hover { + cursor: pointer; + background-color: rgba($color: #000000, $alpha: 0.3); + } + } +} + +.time-selection-dropdown-content { + min-width: 172px; + width: 100%; +} + +.timeSelection-input { + display: flex; + gap: 8px; + height: 33px; + align-items: center; + padding: 4px 8px; + padding-left: 0px !important; + + &.custom-time { + input:not(:focus) { + min-width: 240px; + } + } + + input::placeholder { + color: white; + } + + input:focus::placeholder { + color: rgba($color: #ffffff, $alpha: 0.4); + } +} + +.valid-format-error { + margin-top: 4px; + color: var(--bg-cherry-400) !important; + font-size: 13px !important; + font-weight: 400 !important; +} + +.info-text { + display: flex; + align-items: center; + justify-content: center; + padding: 4px; + cursor: default; + color: var(--bg-vanilla-400, #c0c1c3) !important; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: normal; + letter-spacing: 0.14px; +} + +.info-text:hover { + &.ant-btn-text { + background-color: unset !important; + } +} + +.lightMode { + .time-options-container { + .time-options-item { + &.active { + background-color: rgba($color: #ffffff, $alpha: 0.2); + + &:hover { + cursor: pointer; + background-color: rgba($color: #ffffff, $alpha: 0.3); + } + } + + &:hover { + cursor: pointer; + background-color: rgba($color: #ffffff, $alpha: 0.3); + } + } + } + + .timeSelection-input { + display: flex; + gap: 8px; + align-items: center; + padding: 4px 8px; + padding-left: 0px !important; + + input::placeholder { + color: var(---bg-ink-300); + } + + input:focus::placeholder { + color: rgba($color: #000000, $alpha: 0.4); + } + } + + .info-text { + color: var(--bg-slate-400) !important; + } +} diff --git a/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx b/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx new file mode 100644 index 0000000..a29f018 --- /dev/null +++ b/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx @@ -0,0 +1,323 @@ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +import './CustomTimePicker.styles.scss'; + +import { Input, Popover, Tooltip, Typography } from 'antd'; +import cx from 'classnames'; +import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal'; +import { Options } from 'container/TopNav/DateTimeSelection/config'; +import { + FixedDurationSuggestionOptions, + RelativeDurationSuggestionOptions, +} from 'container/TopNav/DateTimeSelectionV2/config'; +import dayjs from 'dayjs'; +import { defaultTo, noop } from 'lodash-es'; +import debounce from 'lodash-es/debounce'; +import { CheckCircle, ChevronDown, Clock } from 'lucide-react'; +import { + ChangeEvent, + Dispatch, + SetStateAction, + useEffect, + useState, +} from 'react'; +import { useLocation } from 'react-router-dom'; +import { popupContainer } from 'utils/selectPopupContainer'; + +import CustomTimePickerPopoverContent from './CustomTimePickerPopoverContent'; + +const maxAllowedMinTimeInMonths = 6; + +interface CustomTimePickerProps { + onSelect: (value: string) => void; + onError: (value: boolean) => void; + selectedValue: string; + selectedTime: string; + onValidCustomDateChange: ([t1, t2]: any[]) => void; + open: boolean; + setOpen: Dispatch>; + items: any[]; + newPopover?: boolean; + customDateTimeVisible?: boolean; + setCustomDTPickerVisible?: Dispatch>; + onCustomDateHandler?: (dateTimeRange: DateTimeRangeType) => void; + handleGoLive?: () => void; +} + +function CustomTimePicker({ + onSelect, + onError, + items, + selectedValue, + selectedTime, + open, + setOpen, + onValidCustomDateChange, + newPopover, + customDateTimeVisible, + setCustomDTPickerVisible, + onCustomDateHandler, + handleGoLive, +}: CustomTimePickerProps): JSX.Element { + const [ + selectedTimePlaceholderValue, + setSelectedTimePlaceholderValue, + ] = useState('Select / Enter Time Range'); + + const [inputValue, setInputValue] = useState(''); + const [inputStatus, setInputStatus] = useState<'' | 'error' | 'success'>(''); + const [inputErrorMessage, setInputErrorMessage] = useState( + null, + ); + const location = useLocation(); + const [isInputFocused, setIsInputFocused] = useState(false); + + const getSelectedTimeRangeLabel = ( + selectedTime: string, + selectedTimeValue: string, + ): string => { + if (selectedTime === 'custom') { + return selectedTimeValue; + } + + for (let index = 0; index < Options.length; index++) { + if (Options[index].value === selectedTime) { + return Options[index].label; + } + } + for ( + let index = 0; + index < RelativeDurationSuggestionOptions.length; + index++ + ) { + if (RelativeDurationSuggestionOptions[index].value === selectedTime) { + return RelativeDurationSuggestionOptions[index].label; + } + } + for (let index = 0; index < FixedDurationSuggestionOptions.length; index++) { + if (FixedDurationSuggestionOptions[index].value === selectedTime) { + return FixedDurationSuggestionOptions[index].label; + } + } + + return ''; + }; + + useEffect(() => { + const value = getSelectedTimeRangeLabel(selectedTime, selectedValue); + + setSelectedTimePlaceholderValue(value); + }, [selectedTime, selectedValue]); + + const hide = (): void => { + setOpen(false); + }; + + const handleOpenChange = (newOpen: boolean): void => { + setOpen(newOpen); + if (!newOpen) { + setCustomDTPickerVisible?.(false); + } + }; + + const debouncedHandleInputChange = debounce((inputValue): void => { + const isValidFormat = /^(\d+)([mhdw])$/.test(inputValue); + if (isValidFormat) { + setInputStatus('success'); + onError(false); + setInputErrorMessage(null); + + const match = inputValue.match(/^(\d+)([mhdw])$/); + + const value = parseInt(match[1], 10); + const unit = match[2]; + + const currentTime = dayjs(); + const maxAllowedMinTime = currentTime.subtract( + maxAllowedMinTimeInMonths, + 'month', + ); + let minTime = null; + + switch (unit) { + case 'm': + minTime = currentTime.subtract(value, 'minute'); + break; + + case 'h': + minTime = currentTime.subtract(value, 'hour'); + break; + case 'd': + minTime = currentTime.subtract(value, 'day'); + break; + case 'w': + minTime = currentTime.subtract(value, 'week'); + break; + default: + break; + } + + if (minTime && (!minTime.isValid() || minTime < maxAllowedMinTime)) { + setInputStatus('error'); + onError(true); + setInputErrorMessage('Please enter time less than 6 months'); + } else { + onValidCustomDateChange([minTime, currentTime]); + } + } else { + setInputStatus('error'); + onError(true); + setInputErrorMessage(null); + } + }, 300); + + const handleInputChange = (event: ChangeEvent): void => { + const inputValue = event.target.value; + + if (inputValue.length > 0) { + setOpen(false); + } else { + setOpen(true); + } + + setInputValue(inputValue); + + // Call the debounced function with the input value + debouncedHandleInputChange(inputValue); + }; + + const handleSelect = (label: string, value: string): void => { + onSelect(value); + setSelectedTimePlaceholderValue(label); + setInputStatus(''); + onError(false); + setInputErrorMessage(null); + setInputValue(''); + if (value !== 'custom') { + hide(); + } + }; + + const content = ( +

    + ); + + const handleFocus = (): void => { + setIsInputFocused(true); + }; + + const handleBlur = (): void => { + setIsInputFocused(false); + }; + + // this is required as TopNav component wraps the components and we need to clear the state on path change + useEffect(() => { + setInputStatus(''); + onError(false); + setInputErrorMessage(null); + setInputValue(''); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [location.pathname]); + + return ( +
    + + ) : ( + content + ) + } + arrow={false} + trigger="hover" + open={open} + onOpenChange={handleOpenChange} + style={{ + padding: 0, + }} + > + + ) : ( + + + + ) + } + suffix={ + { + setOpen(!open); + }} + /> + } + /> + + + {inputStatus === 'error' && inputErrorMessage && ( + + {inputErrorMessage} + + )} +
    + ); +} + +export default CustomTimePicker; + +CustomTimePicker.defaultProps = { + newPopover: false, + customDateTimeVisible: false, + setCustomDTPickerVisible: noop, + onCustomDateHandler: noop, + handleGoLive: noop, +}; diff --git a/frontend/src/components/CustomTimePicker/CustomTimePickerPopoverContent.tsx b/frontend/src/components/CustomTimePicker/CustomTimePickerPopoverContent.tsx new file mode 100644 index 0000000..4a41bec --- /dev/null +++ b/frontend/src/components/CustomTimePicker/CustomTimePickerPopoverContent.tsx @@ -0,0 +1,118 @@ +import './CustomTimePicker.styles.scss'; + +import { Button } from 'antd'; +import cx from 'classnames'; +import ROUTES from 'constants/routes'; +import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal'; +import { + LexicalContext, + Option, + RelativeDurationSuggestionOptions, +} from 'container/TopNav/DateTimeSelectionV2/config'; +import { Dispatch, SetStateAction, useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; + +import RangePickerModal from './RangePickerModal'; + +interface CustomTimePickerPopoverContentProps { + options: any[]; + setIsOpen: Dispatch>; + customDateTimeVisible: boolean; + setCustomDTPickerVisible: Dispatch>; + onCustomDateHandler: ( + dateTimeRange: DateTimeRangeType, + lexicalContext?: LexicalContext, + ) => void; + onSelectHandler: (label: string, value: string) => void; + handleGoLive: () => void; + selectedTime: string; +} + +function CustomTimePickerPopoverContent({ + options, + setIsOpen, + customDateTimeVisible, + setCustomDTPickerVisible, + onCustomDateHandler, + onSelectHandler, + handleGoLive, + selectedTime, +}: CustomTimePickerPopoverContentProps): JSX.Element { + const { pathname } = useLocation(); + + const isLogsExplorerPage = useMemo(() => pathname === ROUTES.LOGS_EXPLORER, [ + pathname, + ]); + + function getTimeChips(options: Option[]): JSX.Element { + return ( +
    + {options.map((option) => ( + + ))} +
    + ); + } + + return ( +
    +
    + {isLogsExplorerPage && ( + + )} + {options.map((option) => ( + + ))} +
    +
    + {selectedTime === 'custom' || customDateTimeVisible ? ( + + ) : ( +
    +
    RELATIVE TIMES
    +
    {getTimeChips(RelativeDurationSuggestionOptions)}
    +
    + )} +
    +
    + ); +} + +export default CustomTimePickerPopoverContent; diff --git a/frontend/src/components/CustomTimePicker/RangePickerModal.styles.scss b/frontend/src/components/CustomTimePicker/RangePickerModal.styles.scss new file mode 100644 index 0000000..58ebe06 --- /dev/null +++ b/frontend/src/components/CustomTimePicker/RangePickerModal.styles.scss @@ -0,0 +1,4 @@ +.custom-date-picker { + display: flex; + flex-direction: column; +} diff --git a/frontend/src/components/CustomTimePicker/RangePickerModal.tsx b/frontend/src/components/CustomTimePicker/RangePickerModal.tsx new file mode 100644 index 0000000..24ba0e2 --- /dev/null +++ b/frontend/src/components/CustomTimePicker/RangePickerModal.tsx @@ -0,0 +1,68 @@ +import './RangePickerModal.styles.scss'; + +import { DatePicker } from 'antd'; +import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal'; +import { LexicalContext } from 'container/TopNav/DateTimeSelectionV2/config'; +import dayjs, { Dayjs } from 'dayjs'; +import { Dispatch, SetStateAction } from 'react'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { GlobalReducer } from 'types/reducer/globalTime'; + +interface RangePickerModalProps { + setCustomDTPickerVisible: Dispatch>; + setIsOpen: Dispatch>; + onCustomDateHandler: ( + dateTimeRange: DateTimeRangeType, + lexicalContext?: LexicalContext | undefined, + ) => void; + selectedTime: string; +} + +function RangePickerModal(props: RangePickerModalProps): JSX.Element { + const { + setCustomDTPickerVisible, + setIsOpen, + onCustomDateHandler, + selectedTime, + } = props; + const { RangePicker } = DatePicker; + const { maxTime, minTime } = useSelector( + (state) => state.globalTime, + ); + + const disabledDate = (current: Dayjs): boolean => { + const currentDay = dayjs(current); + return currentDay.isAfter(dayjs()); + }; + + const onPopoverClose = (visible: boolean): void => { + if (!visible) { + setCustomDTPickerVisible(false); + } + setIsOpen(visible); + }; + + const onModalOkHandler = (date_time: any): void => { + if (date_time?.[1]) { + onPopoverClose(false); + } + onCustomDateHandler(date_time, LexicalContext.CUSTOM_DATE_PICKER); + }; + return ( +
    + +
    + ); +} + +export default RangePickerModal; diff --git a/frontend/src/components/DraggableTableRow/index.tsx b/frontend/src/components/DraggableTableRow/index.tsx new file mode 100644 index 0000000..7c4a483 --- /dev/null +++ b/frontend/src/components/DraggableTableRow/index.tsx @@ -0,0 +1,54 @@ +import React, { useCallback, useRef } from 'react'; +import { useDrag, useDrop } from 'react-dnd'; + +import { dragHandler, dropHandler } from './utils'; + +const type = 'DraggableTableRow'; + +function DraggableTableRow({ + index, + moveRow, + className, + style, + ...restProps +}: DraggableTableRowProps): JSX.Element { + const ref = useRef(null); + + const handleDrop = useCallback( + (item: { index: number }) => { + if (moveRow) moveRow(item.index, index); + }, + [moveRow, index], + ); + + const [, drop] = useDrop({ + accept: type, + collect: dropHandler, + drop: handleDrop, + }); + + const [, drag] = useDrag({ + type, + item: { index }, + collect: dragHandler, + }); + drop(drag(ref)); + + return ( + + ); +} + +interface DraggableTableRowProps + extends React.HTMLAttributes { + index: number; + moveRow: (dragIndex: number, hoverIndex: number) => void; +} + +export default DraggableTableRow; diff --git a/frontend/src/components/DraggableTableRow/tests/DraggableTableRow.test.tsx b/frontend/src/components/DraggableTableRow/tests/DraggableTableRow.test.tsx new file mode 100644 index 0000000..f938a19 --- /dev/null +++ b/frontend/src/components/DraggableTableRow/tests/DraggableTableRow.test.tsx @@ -0,0 +1,38 @@ +import { render } from '@testing-library/react'; +import { Table } from 'antd'; +import { matchMedia } from 'container/PipelinePage/tests/AddNewPipeline.test'; +import { I18nextProvider } from 'react-i18next'; +import { Provider } from 'react-redux'; +import i18n from 'ReactI18'; +import store from 'store'; + +import DraggableTableRow from '..'; + +beforeAll(() => { + matchMedia(); +}); + +jest.mock('react-dnd', () => ({ + useDrop: jest.fn().mockImplementation(() => [jest.fn(), jest.fn(), jest.fn()]), + useDrag: jest.fn().mockImplementation(() => [jest.fn(), jest.fn(), jest.fn()]), +})); + +describe('DraggableTableRow Snapshot test', () => { + it('should render DraggableTableRow', async () => { + const { asFragment } = render( + + + + + , + ); + expect(asFragment()).toMatchSnapshot(); + }); +}); diff --git a/frontend/src/components/DraggableTableRow/tests/__snapshots__/DraggableTableRow.test.tsx.snap b/frontend/src/components/DraggableTableRow/tests/__snapshots__/DraggableTableRow.test.tsx.snap new file mode 100644 index 0000000..984d943 --- /dev/null +++ b/frontend/src/components/DraggableTableRow/tests/__snapshots__/DraggableTableRow.test.tsx.snap @@ -0,0 +1,103 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DraggableTableRow Snapshot test should render DraggableTableRow 1`] = ` + +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + +
    +
    +
    +
    + + + + + + + + + +
    +
    + No data +
    +
    +
    + + + + + + + +`; + +exports[`PipelinePage container test should render AddNewPipeline section 1`] = ``; diff --git a/frontend/src/components/DraggableTableRow/tests/utils.test.ts b/frontend/src/components/DraggableTableRow/tests/utils.test.ts new file mode 100644 index 0000000..8085494 --- /dev/null +++ b/frontend/src/components/DraggableTableRow/tests/utils.test.ts @@ -0,0 +1,44 @@ +import { dragHandler, dropHandler } from '../utils'; + +jest.mock('react-dnd', () => ({ + useDrop: jest.fn().mockImplementation(() => [jest.fn(), jest.fn(), jest.fn()]), + useDrag: jest.fn().mockImplementation(() => [jest.fn(), jest.fn(), jest.fn()]), +})); + +describe('Utils testing of DraggableTableRow component', () => { + test('Should dropHandler return true', () => { + const monitor = { + isOver: jest.fn().mockReturnValueOnce(true), + } as never; + const dropDataTruthy = dropHandler(monitor); + + expect(dropDataTruthy).toEqual({ isOver: true }); + }); + + test('Should dropHandler return false', () => { + const monitor = { + isOver: jest.fn().mockReturnValueOnce(false), + } as never; + const dropDataFalsy = dropHandler(monitor); + + expect(dropDataFalsy).toEqual({ isOver: false }); + }); + + test('Should dragHandler return true', () => { + const monitor = { + isDragging: jest.fn().mockReturnValueOnce(true), + } as never; + const dragDataTruthy = dragHandler(monitor); + + expect(dragDataTruthy).toEqual({ isDragging: true }); + }); + + test('Should dragHandler return false', () => { + const monitor = { + isDragging: jest.fn().mockReturnValueOnce(false), + } as never; + const dragDataFalsy = dragHandler(monitor); + + expect(dragDataFalsy).toEqual({ isDragging: false }); + }); +}); diff --git a/frontend/src/components/DraggableTableRow/utils.ts b/frontend/src/components/DraggableTableRow/utils.ts new file mode 100644 index 0000000..475145f --- /dev/null +++ b/frontend/src/components/DraggableTableRow/utils.ts @@ -0,0 +1,15 @@ +import { DragSourceMonitor, DropTargetMonitor } from 'react-dnd'; + +export function dropHandler(monitor: DropTargetMonitor): { isOver: boolean } { + return { + isOver: monitor.isOver(), + }; +} + +export function dragHandler( + monitor: DragSourceMonitor, +): { isDragging: boolean } { + return { + isDragging: monitor.isDragging(), + }; +} diff --git a/frontend/src/components/DropDown/DropDown.styles.scss b/frontend/src/components/DropDown/DropDown.styles.scss new file mode 100644 index 0000000..232b349 --- /dev/null +++ b/frontend/src/components/DropDown/DropDown.styles.scss @@ -0,0 +1,11 @@ +.dropdown-button { + color: #fff; +} + +.dropdown-button--dark { + color: #000; +} + +.dropdown-icon { + font-size: 1.2rem; +} \ No newline at end of file diff --git a/frontend/src/components/DropDown/DropDown.tsx b/frontend/src/components/DropDown/DropDown.tsx new file mode 100644 index 0000000..c29fbdd --- /dev/null +++ b/frontend/src/components/DropDown/DropDown.tsx @@ -0,0 +1,30 @@ +import './DropDown.styles.scss'; + +import { EllipsisOutlined } from '@ant-design/icons'; +import { Button, Dropdown, MenuProps } from 'antd'; +import { useIsDarkMode } from 'hooks/useDarkMode'; + +function DropDown({ element }: { element: JSX.Element[] }): JSX.Element { + const isDarkMode = useIsDarkMode(); + + const items: MenuProps['items'] = element.map( + (e: JSX.Element, index: number) => ({ + label: e, + key: index, + }), + ); + + return ( + + + + ); +} + +export default DropDown; diff --git a/frontend/src/components/Editor/Editor.test.tsx b/frontend/src/components/Editor/Editor.test.tsx new file mode 100644 index 0000000..ec9e505 --- /dev/null +++ b/frontend/src/components/Editor/Editor.test.tsx @@ -0,0 +1,59 @@ +import { render, screen } from '@testing-library/react'; +import { useIsDarkMode } from 'hooks/useDarkMode'; + +import Editor from './index'; + +jest.mock('hooks/useDarkMode', () => ({ + useIsDarkMode: jest.fn(), +})); + +describe('Editor', () => { + it('renders correctly with default props', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it('renders correctly with custom props', () => { + const customProps = { + value: 'test', + language: 'javascript', + readOnly: true, + height: '50vh', + options: { minimap: { enabled: false } }, + }; + const { container } = render( + , + ); + expect(container).toMatchSnapshot(); + }); + + it('renders with dark mode theme', () => { + (useIsDarkMode as jest.Mock).mockImplementation(() => true); + + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); + + it('renders with light mode theme', () => { + (useIsDarkMode as jest.Mock).mockImplementation(() => false); + + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); + + it('displays "Loading..." message initially', () => { + const { rerender } = render(); + + expect(screen.getByText('Loading...')).toBeInTheDocument(); + + rerender(); + }); +}); diff --git a/frontend/src/components/Editor/__snapshots__/Editor.test.tsx.snap b/frontend/src/components/Editor/__snapshots__/Editor.test.tsx.snap new file mode 100644 index 0000000..1670ced --- /dev/null +++ b/frontend/src/components/Editor/__snapshots__/Editor.test.tsx.snap @@ -0,0 +1,69 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Editor renders correctly with custom props 1`] = ` +
    +
    +
    + Loading... +
    +
    +
    +
    +`; + +exports[`Editor renders correctly with default props 1`] = ` +
    +
    +
    + Loading... +
    +
    +
    +
    +`; + +exports[`Editor renders with dark mode theme 1`] = ` +
    +
    +
    + Loading... +
    +
    +
    +
    +`; + +exports[`Editor renders with light mode theme 1`] = ` +
    +
    +
    + Loading... +
    +
    +
    +
    +`; diff --git a/frontend/src/components/Editor/index.tsx b/frontend/src/components/Editor/index.tsx new file mode 100644 index 0000000..5f70d92 --- /dev/null +++ b/frontend/src/components/Editor/index.tsx @@ -0,0 +1,56 @@ +import MEditor, { EditorProps } from '@monaco-editor/react'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { useMemo } from 'react'; + +function Editor({ + value, + language, + onChange, + readOnly, + height, + options, +}: MEditorProps): JSX.Element { + const isDarkMode = useIsDarkMode(); + + const onChangeHandler = (newValue?: string): void => { + if (readOnly) return; + + if (typeof newValue === 'string' && onChange) onChange(newValue); + }; + + const editorOptions = useMemo( + () => ({ fontSize: 16, automaticLayout: true, readOnly, ...options }), + [options, readOnly], + ); + + return ( + + ); +} + +interface MEditorProps { + value: string; + language?: string; + onChange?: (value: string) => void; + readOnly?: boolean; + height?: string; + options?: EditorProps['options']; +} + +Editor.defaultProps = { + language: 'yaml', + readOnly: false, + height: '40vh', + options: {}, + onChange: (): void => {}, +}; + +export default Editor; diff --git a/frontend/src/components/ExplorerCard/ExplorerCard.tsx b/frontend/src/components/ExplorerCard/ExplorerCard.tsx new file mode 100644 index 0000000..73d7a24 --- /dev/null +++ b/frontend/src/components/ExplorerCard/ExplorerCard.tsx @@ -0,0 +1,252 @@ +import { + DeleteOutlined, + MoreOutlined, + SaveOutlined, + ShareAltOutlined, +} from '@ant-design/icons'; +import { + Button, + Col, + Dropdown, + MenuProps, + Popover, + Row, + Select, + Space, + Typography, +} from 'antd'; +import axios from 'axios'; +import TextToolTip from 'components/TextToolTip'; +import { SOMETHING_WENT_WRONG } from 'constants/api'; +import { QueryParams } from 'constants/query'; +import { useGetSearchQueryParam } from 'hooks/queryBuilder/useGetSearchQueryParam'; +import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; +import { useDeleteView } from 'hooks/saveViews/useDeleteView'; +import { useGetAllViews } from 'hooks/saveViews/useGetAllViews'; +import { useUpdateView } from 'hooks/saveViews/useUpdateView'; +import useErrorNotification from 'hooks/useErrorNotification'; +import { useNotifications } from 'hooks/useNotifications'; +import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery'; +import { useState } from 'react'; +import { useCopyToClipboard } from 'react-use'; +import { popupContainer } from 'utils/selectPopupContainer'; + +import { ExploreHeaderToolTip, SaveButtonText } from './constants'; +import MenuItemGenerator from './MenuItemGenerator'; +import SaveViewWithName from './SaveViewWithName'; +import { + DropDownOverlay, + ExplorerCardHeadContainer, + OffSetCol, +} from './styles'; +import { ExplorerCardProps } from './types'; +import { deleteViewHandler } from './utils'; + +function ExplorerCard({ + sourcepage, + children, +}: ExplorerCardProps): JSX.Element { + const [isOpen, setIsOpen] = useState(false); + const [, setCopyUrl] = useCopyToClipboard(); + const { notifications } = useNotifications(); + + const onCopyUrlHandler = (): void => { + setCopyUrl(window.location.href); + notifications.success({ + message: 'Copied to clipboard', + }); + }; + + const { + currentQuery, + panelType, + redirectWithQueryBuilderData, + updateAllQueriesOperators, + isStagedQueryUpdated, + } = useQueryBuilder(); + + const { + data: viewsData, + isLoading, + error, + isRefetching, + refetch: refetchAllView, + } = useGetAllViews(sourcepage); + + useErrorNotification(error); + + const handleOpenChange = (newOpen = false): void => { + setIsOpen(newOpen); + }; + + const viewName = useGetSearchQueryParam(QueryParams.viewName) || ''; + + const viewKey = useGetSearchQueryParam(QueryParams.viewKey) || ''; + + const isQueryUpdated = isStagedQueryUpdated(viewsData?.data?.data, viewKey); + + const { mutateAsync: updateViewAsync } = useUpdateView({ + compositeQuery: mapCompositeQueryFromQuery(currentQuery, panelType), + viewKey, + extraData: '', + sourcePage: sourcepage, + viewName, + }); + + const { mutateAsync: deleteViewAsync } = useDeleteView(viewKey); + + const showErrorNotification = (err: Error): void => { + notifications.error({ + message: axios.isAxiosError(err) ? err.message : SOMETHING_WENT_WRONG, + }); + }; + + const onDeleteHandler = (): void => + deleteViewHandler({ + deleteViewAsync, + notifications, + panelType, + redirectWithQueryBuilderData, + refetchAllView, + viewId: viewKey, + viewKey, + updateAllQueriesOperators, + sourcePage: sourcepage, + }); + + const onUpdateQueryHandler = (): void => { + updateViewAsync( + { + compositeQuery: mapCompositeQueryFromQuery(currentQuery, panelType), + viewKey, + extraData: '', + sourcePage: sourcepage, + viewName, + }, + { + onSuccess: () => { + notifications.success({ + message: 'View Updated Successfully', + }); + refetchAllView(); + }, + onError: (err) => { + showErrorNotification(err); + }, + }, + ); + }; + + const moreOptionMenu: MenuProps = { + items: [ + { + key: 'delete', + label: Delete, + onClick: onDeleteHandler, + icon: , + }, + ], + }; + + const saveButtonType = isQueryUpdated ? 'default' : 'primary'; + const saveButtonIcon = isQueryUpdated ? null : ; + + const showSaveView = false; + + return ( + <> + {showSaveView && ( + + + + + Query Builder + + + + + + {viewsData?.data.data && viewsData?.data.data.length && ( + + + + )} + {isQueryUpdated && ( + + )} + + } + showArrow={false} + open={isOpen} + onOpenChange={handleOpenChange} + > + + + + {viewKey && ( + + + + )} + + + + + )} + +
    {children}
    + + ); +} + +export default ExplorerCard; diff --git a/frontend/src/components/ExplorerCard/MenuItemGenerator.tsx b/frontend/src/components/ExplorerCard/MenuItemGenerator.tsx new file mode 100644 index 0000000..c908e70 --- /dev/null +++ b/frontend/src/components/ExplorerCard/MenuItemGenerator.tsx @@ -0,0 +1,103 @@ +import { DeleteOutlined } from '@ant-design/icons'; +import { Col, Row, Tooltip, Typography } from 'antd'; +import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; +import { useDeleteView } from 'hooks/saveViews/useDeleteView'; +import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange'; +import { useNotifications } from 'hooks/useNotifications'; +import { MouseEvent, useCallback } from 'react'; + +import { MenuItemContainer } from './styles'; +import { MenuItemLabelGeneratorProps } from './types'; +import { + deleteViewHandler, + getViewDetailsUsingViewKey, + trimViewName, +} from './utils'; + +function MenuItemGenerator({ + viewName, + viewKey, + createdBy, + uuid, + viewData, + refetchAllView, + sourcePage, +}: MenuItemLabelGeneratorProps): JSX.Element { + const { + panelType, + redirectWithQueryBuilderData, + updateAllQueriesOperators, + } = useQueryBuilder(); + const { handleExplorerTabChange } = useHandleExplorerTabChange(); + + const { notifications } = useNotifications(); + + const { mutateAsync: deleteViewAsync } = useDeleteView(uuid); + + const onDeleteHandler = (event: MouseEvent): void => { + event.stopPropagation(); + deleteViewHandler({ + deleteViewAsync, + notifications, + panelType, + redirectWithQueryBuilderData, + refetchAllView, + viewId: uuid, + viewKey, + updateAllQueriesOperators, + sourcePage, + }); + }; + + const onMenuItemSelectHandler = useCallback( + ({ key }: { key: string }): void => { + const currentViewDetails = getViewDetailsUsingViewKey(key, viewData); + if (!currentViewDetails) return; + const { + query, + name, + uuid, + panelType: currentPanelType, + } = currentViewDetails; + + handleExplorerTabChange(currentPanelType, { + query, + name, + uuid, + }); + }, + [viewData, handleExplorerTabChange], + ); + + const onLabelClickHandler = (): void => { + onMenuItemSelectHandler({ + key: uuid, + }); + }; + + const newViewName = trimViewName(viewName); + + return ( + + + + + + {newViewName} + + + + Created by {createdBy} + + + + + + + + + + ); +} + +export default MenuItemGenerator; diff --git a/frontend/src/components/ExplorerCard/SaveViewWithName.tsx b/frontend/src/components/ExplorerCard/SaveViewWithName.tsx new file mode 100644 index 0000000..cb5457c --- /dev/null +++ b/frontend/src/components/ExplorerCard/SaveViewWithName.tsx @@ -0,0 +1,80 @@ +import { Card, Form, Input, Typography } from 'antd'; +import { PANEL_TYPES } from 'constants/queryBuilder'; +import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; +import { useSaveView } from 'hooks/saveViews/useSaveView'; +import { useNotifications } from 'hooks/useNotifications'; +import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery'; +import { useTranslation } from 'react-i18next'; + +import { SaveButton } from './styles'; +import { SaveViewFormProps, SaveViewWithNameProps } from './types'; +import { saveViewHandler } from './utils'; + +function SaveViewWithName({ + sourcePage, + handlePopOverClose, + refetchAllView, +}: SaveViewWithNameProps): JSX.Element { + const [form] = Form.useForm(); + const { t } = useTranslation(['explorer']); + const { + currentQuery, + panelType, + redirectWithQueryBuilderData, + } = useQueryBuilder(); + const { notifications } = useNotifications(); + const compositeQuery = mapCompositeQueryFromQuery(currentQuery, panelType); + + const { isLoading, mutateAsync: saveViewAsync } = useSaveView({ + viewName: form.getFieldValue('viewName'), + compositeQuery, + sourcePage, + extraData: '', + }); + + const onSaveHandler = (): void => { + saveViewHandler({ + compositeQuery, + handlePopOverClose, + extraData: '', + notifications, + panelType: panelType || PANEL_TYPES.LIST, + redirectWithQueryBuilderData, + refetchAllView, + saveViewAsync, + sourcePage, + viewName: form.getFieldValue('viewName'), + form, + }); + }; + + return ( + + {t('name_of_the_view')} +
    + + + + + Save + +
    +
    + ); +} + +export default SaveViewWithName; diff --git a/frontend/src/components/ExplorerCard/__mock__/viewData.ts b/frontend/src/components/ExplorerCard/__mock__/viewData.ts new file mode 100644 index 0000000..5423cc9 --- /dev/null +++ b/frontend/src/components/ExplorerCard/__mock__/viewData.ts @@ -0,0 +1,32 @@ +import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery'; +import { ViewProps } from 'types/api/saveViews/types'; +import { DataSource } from 'types/common/queryBuilder'; + +export const viewMockData: ViewProps[] = [ + { + uuid: 'view1', + name: 'View 1', + createdBy: 'User 1', + category: 'category 1', + compositeQuery: {} as ICompositeMetricQuery, + createdAt: '2021-07-07T06:31:00.000Z', + updatedAt: '2021-07-07T06:33:00.000Z', + extraData: '', + sourcePage: DataSource.TRACES, + tags: [], + updatedBy: 'User 1', + }, + { + uuid: 'view2', + name: 'View 2', + createdBy: 'User 2', + category: 'category 2', + compositeQuery: {} as ICompositeMetricQuery, + createdAt: '2021-07-07T06:30:00.000Z', + updatedAt: '2021-07-07T06:30:00.000Z', + extraData: '', + sourcePage: DataSource.TRACES, + tags: [], + updatedBy: 'User 2', + }, +]; diff --git a/frontend/src/components/ExplorerCard/constants.ts b/frontend/src/components/ExplorerCard/constants.ts new file mode 100644 index 0000000..ee06168 --- /dev/null +++ b/frontend/src/components/ExplorerCard/constants.ts @@ -0,0 +1,14 @@ +import { QueryParams } from 'constants/query'; + +export const ExploreHeaderToolTip = { + url: + 'https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=new-query-builder', + text: 'More details on how to use query builder', +}; + +export const SaveButtonText = { + SAVE_AS_NEW_VIEW: 'Save as new view', + SAVE_VIEW: 'Save view', +}; + +export type QuerySearchParamNames = QueryParams.viewName | QueryParams.viewKey; diff --git a/frontend/src/components/ExplorerCard/styles.ts b/frontend/src/components/ExplorerCard/styles.ts new file mode 100644 index 0000000..56c3d38 --- /dev/null +++ b/frontend/src/components/ExplorerCard/styles.ts @@ -0,0 +1,29 @@ +import { Button, Card, Col } from 'antd'; +import styled, { CSSProperties } from 'styled-components'; + +export const ExplorerCardHeadContainer = styled(Card)` + margin: 1rem 0; + padding: 0; +`; + +export const OffSetCol = styled(Col)` + text-align: right; +`; + +export const SaveButton = styled(Button)` + &&& { + margin: 1rem 0; + width: 5rem; + } +`; + +export const DropDownOverlay: CSSProperties = { + maxHeight: '20rem', + overflowY: 'auto', + width: '20rem', + padding: 0, +}; + +export const MenuItemContainer = styled(Card)` + padding: 0; +`; diff --git a/frontend/src/components/ExplorerCard/test/ExplorerCard.test.tsx b/frontend/src/components/ExplorerCard/test/ExplorerCard.test.tsx new file mode 100644 index 0000000..1fdb29d --- /dev/null +++ b/frontend/src/components/ExplorerCard/test/ExplorerCard.test.tsx @@ -0,0 +1,60 @@ +import { render, screen } from '@testing-library/react'; +import ROUTES from 'constants/routes'; +import MockQueryClientProvider from 'providers/test/MockQueryClientProvider'; +import { DataSource } from 'types/common/queryBuilder'; + +import { viewMockData } from '../__mock__/viewData'; +import ExplorerCard from '../ExplorerCard'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: (): { pathname: string } => ({ + pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.TRACES_EXPLORER}/`, + }), +})); + +jest.mock('hooks/queryBuilder/useGetPanelTypesQueryParam', () => ({ + useGetPanelTypesQueryParam: jest.fn(() => 'mockedPanelType'), +})); + +jest.mock('hooks/saveViews/useGetAllViews', () => ({ + useGetAllViews: jest.fn(() => ({ + data: { data: { data: viewMockData } }, + isLoading: false, + error: null, + isRefetching: false, + refetch: jest.fn(), + })), +})); + +jest.mock('hooks/saveViews/useUpdateView', () => ({ + useUpdateView: jest.fn(() => ({ + mutateAsync: jest.fn(), + })), +})); + +jest.mock('hooks/saveViews/useDeleteView', () => ({ + useDeleteView: jest.fn(() => ({ + mutateAsync: jest.fn(), + })), +})); + +describe('ExplorerCard', () => { + it('renders a card with a title and a description', () => { + render( + + child + , + ); + expect(screen.queryByText('Query Builder')).not.toBeInTheDocument(); + }); + + it('renders a save view button', () => { + render( + + child + , + ); + expect(screen.queryByText('Save view')).not.toBeInTheDocument(); + }); +}); diff --git a/frontend/src/components/ExplorerCard/test/MenuItemGenerator.test.tsx b/frontend/src/components/ExplorerCard/test/MenuItemGenerator.test.tsx new file mode 100644 index 0000000..c869024 --- /dev/null +++ b/frontend/src/components/ExplorerCard/test/MenuItemGenerator.test.tsx @@ -0,0 +1,62 @@ +import { render, screen } from '@testing-library/react'; +import ROUTES from 'constants/routes'; +import MockQueryClientProvider from 'providers/test/MockQueryClientProvider'; +import { DataSource } from 'types/common/queryBuilder'; + +import { viewMockData } from '../__mock__/viewData'; +import MenuItemGenerator from '../MenuItemGenerator'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: (): { pathname: string } => ({ + pathname: `${process.env.FRONTEND_API_ENDPOINT}${ROUTES.APPLICATION}/`, + }), +})); + +jest.mock('antd/es/form/Form', () => ({ + useForm: jest.fn().mockReturnValue({ + onFinish: jest.fn(), + }), +})); + +describe('MenuItemGenerator', () => { + it('should render MenuItemGenerator component', () => { + const screen = render( + + + , + ); + + expect(screen.getByText(viewMockData[0].name)).toBeInTheDocument(); + }); + + it('should call onMenuItemSelectHandler on click of MenuItemGenerator', () => { + render( + + + , + ); + + const spanElement = screen.getByRole('img', { + name: 'delete', + }); + + expect(spanElement).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/components/ExplorerCard/test/SaveViewWithName.test.tsx b/frontend/src/components/ExplorerCard/test/SaveViewWithName.test.tsx new file mode 100644 index 0000000..8e6664a --- /dev/null +++ b/frontend/src/components/ExplorerCard/test/SaveViewWithName.test.tsx @@ -0,0 +1,63 @@ +import { fireEvent, render } from '@testing-library/react'; +import ROUTES from 'constants/routes'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import { DataSource } from 'types/common/queryBuilder'; + +import SaveViewWithName from '../SaveViewWithName'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: (): { pathname: string } => ({ + pathname: `${process.env.FRONTEND_API_ENDPOINT}${ROUTES.APPLICATION}/`, + }), +})); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + }, + }, +}); + +jest.mock('hooks/queryBuilder/useGetPanelTypesQueryParam', () => ({ + useGetPanelTypesQueryParam: jest.fn(() => 'mockedPanelType'), +})); + +jest.mock('hooks/saveViews/useSaveView', () => ({ + useSaveView: jest.fn(() => ({ + mutateAsync: jest.fn(), + })), +})); + +describe('SaveViewWithName', () => { + it('should render SaveViewWithName component', () => { + const screen = render( + + + , + ); + + expect(screen.getByText('Save')).toBeInTheDocument(); + }); + + it('should call saveViewAsync on click of Save button', () => { + const screen = render( + + + , + ); + + fireEvent.click(screen.getByText('Save')); + + expect(screen.getByText('Save')).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/components/ExplorerCard/types.ts b/frontend/src/components/ExplorerCard/types.ts new file mode 100644 index 0000000..9f4eed3 --- /dev/null +++ b/frontend/src/components/ExplorerCard/types.ts @@ -0,0 +1,84 @@ +import { FormInstance } from 'antd'; +import { NotificationInstance } from 'antd/es/notification/interface'; +import { AxiosResponse } from 'axios'; +import { PANEL_TYPES } from 'constants/queryBuilder'; +import { UseMutateAsyncFunction } from 'react-query'; +import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; +import { + DeleteViewPayloadProps, + SaveViewPayloadProps, + SaveViewProps, + ViewProps, +} from 'types/api/saveViews/types'; +import { DataSource, QueryBuilderContextType } from 'types/common/queryBuilder'; + +export interface ExplorerCardProps { + sourcepage: DataSource; + children: React.ReactNode; +} + +export type GetViewDetailsUsingViewKey = ( + viewKey: string, + data: ViewProps[] | undefined, +) => + | { query: Query; name: string; uuid: string; panelType: PANEL_TYPES } + | undefined; + +export interface IsQueryUpdatedInViewProps { + viewKey: string; + data: ViewProps[] | undefined; + stagedQuery: Query | null; + currentPanelType: PANEL_TYPES | null; +} + +export interface SaveViewWithNameProps { + sourcePage: ExplorerCardProps['sourcepage']; + handlePopOverClose: VoidFunction; + refetchAllView: VoidFunction; +} + +export interface SaveViewFormProps { + viewName: string; +} + +export interface MenuItemLabelGeneratorProps { + viewName: string; + viewKey: string; + createdBy: string; + uuid: string; + viewData: ViewProps[]; + refetchAllView: VoidFunction; + sourcePage: ExplorerCardProps['sourcepage']; +} + +export interface SaveViewHandlerProps { + viewName: string; + compositeQuery: ICompositeMetricQuery; + sourcePage: ExplorerCardProps['sourcepage']; + extraData: string; + panelType: PANEL_TYPES | null; + notifications: NotificationInstance; + refetchAllView: SaveViewWithNameProps['refetchAllView']; + saveViewAsync: UseMutateAsyncFunction< + AxiosResponse, + Error, + SaveViewProps, + SaveViewPayloadProps + >; + handlePopOverClose: SaveViewWithNameProps['handlePopOverClose']; + redirectWithQueryBuilderData: QueryBuilderContextType['redirectWithQueryBuilderData']; + form: FormInstance; +} + +export interface DeleteViewHandlerProps { + deleteViewAsync: UseMutateAsyncFunction; + refetchAllView: MenuItemLabelGeneratorProps['refetchAllView']; + redirectWithQueryBuilderData: QueryBuilderContextType['redirectWithQueryBuilderData']; + notifications: NotificationInstance; + panelType: PANEL_TYPES | null; + viewKey: string; + viewId: string; + updateAllQueriesOperators: QueryBuilderContextType['updateAllQueriesOperators']; + sourcePage: ExplorerCardProps['sourcepage']; +} diff --git a/frontend/src/components/ExplorerCard/utils.ts b/frontend/src/components/ExplorerCard/utils.ts new file mode 100644 index 0000000..48af06b --- /dev/null +++ b/frontend/src/components/ExplorerCard/utils.ts @@ -0,0 +1,195 @@ +import { NotificationInstance } from 'antd/es/notification/interface'; +import axios from 'axios'; +import { SOMETHING_WENT_WRONG } from 'constants/api'; +import { QueryParams } from 'constants/query'; +import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; +import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi'; +import isEqual from 'lodash-es/isEqual'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; + +import { + DeleteViewHandlerProps, + GetViewDetailsUsingViewKey, + IsQueryUpdatedInViewProps, + SaveViewHandlerProps, +} from './types'; + +export const showErrorNotification = ( + notifications: NotificationInstance, + err: Error, +): void => { + notifications.error({ + message: axios.isAxiosError(err) ? err.message : SOMETHING_WENT_WRONG, + }); +}; + +export const getViewDetailsUsingViewKey: GetViewDetailsUsingViewKey = ( + viewKey, + data, +) => { + const selectedView = data?.find((view) => view.uuid === viewKey); + if (selectedView) { + const { compositeQuery, name, uuid } = selectedView; + const query = mapQueryDataFromApi(compositeQuery); + return { query, name, uuid, panelType: compositeQuery.panelType }; + } + return undefined; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const omitIdFromQuery = (query: Query | null): any => ({ + ...query, + builder: { + ...query?.builder, + queryData: query?.builder.queryData.map((queryData) => { + const { id, ...rest } = queryData.aggregateAttribute; + const newAggregateAttribute = rest; + const newGroupByAttributes = queryData.groupBy.map((groupByAttribute) => { + const { id, ...rest } = groupByAttribute; + return rest; + }); + const newItems = queryData.filters.items.map((item) => { + const { id, ...newItem } = item; + if (item.key) { + const { id, ...rest } = item.key; + return { + ...newItem, + key: rest, + }; + } + return newItem; + }); + return { + ...queryData, + aggregateAttribute: newAggregateAttribute, + groupBy: newGroupByAttributes, + filters: { + ...queryData.filters, + items: newItems, + }, + limit: queryData.limit ? queryData.limit : 0, + offset: queryData.offset ? queryData.offset : 0, + pageSize: queryData.pageSize ? queryData.pageSize : 0, + }; + }), + }, +}); + +export const isQueryUpdatedInView = ({ + viewKey, + data, + stagedQuery, + currentPanelType, +}: IsQueryUpdatedInViewProps): boolean => { + const currentViewDetails = getViewDetailsUsingViewKey(viewKey, data); + if (!currentViewDetails) { + return false; + } + const { query, panelType } = currentViewDetails; + + // Omitting id from aggregateAttribute and groupBy + const updatedCurrentQuery = omitIdFromQuery(stagedQuery); + + if ( + updatedCurrentQuery?.builder === undefined || + updatedCurrentQuery.clickhouse_sql === undefined || + updatedCurrentQuery.promql === undefined + ) { + return false; + } + + return ( + panelType !== currentPanelType || + !isEqual(query.builder, updatedCurrentQuery?.builder) || + !isEqual(query.clickhouse_sql, updatedCurrentQuery?.clickhouse_sql) || + !isEqual(query.promql, updatedCurrentQuery?.promql) + ); +}; + +export const saveViewHandler = ({ + saveViewAsync, + refetchAllView, + notifications, + handlePopOverClose, + viewName, + compositeQuery, + sourcePage, + extraData, + redirectWithQueryBuilderData, + panelType, + form, +}: SaveViewHandlerProps): void => { + saveViewAsync( + { + viewName, + compositeQuery, + sourcePage, + extraData, + }, + { + onSuccess: (data) => { + refetchAllView(); + redirectWithQueryBuilderData(mapQueryDataFromApi(compositeQuery), { + [QueryParams.panelTypes]: panelType, + [QueryParams.viewName]: viewName, + [QueryParams.viewKey]: data.data.data, + }); + notifications.success({ + message: 'View Saved Successfully', + }); + }, + onError: (err) => { + showErrorNotification(notifications, err); + }, + onSettled: () => { + handlePopOverClose(); + form.resetFields(); + }, + }, + ); +}; + +export const deleteViewHandler = ({ + deleteViewAsync, + refetchAllView, + redirectWithQueryBuilderData, + notifications, + panelType, + viewKey, + viewId, + updateAllQueriesOperators, + sourcePage, +}: DeleteViewHandlerProps): void => { + deleteViewAsync(viewKey, { + onSuccess: () => { + if (viewId === viewKey) { + redirectWithQueryBuilderData( + updateAllQueriesOperators( + initialQueriesMap[sourcePage], + panelType || PANEL_TYPES.LIST, + sourcePage, + ), + { + [QueryParams.viewName]: '', + [QueryParams.panelTypes]: panelType, + [QueryParams.viewKey]: '', + }, + ); + } + notifications.success({ + message: 'View Deleted Successfully', + }); + refetchAllView(); + }, + onError: (err) => { + showErrorNotification(notifications, err); + }, + }); +}; + +export const trimViewName = (viewName: string): string => { + if (viewName.length > 20) { + return `${viewName.substring(0, 20)}...`; + } + return viewName; +}; diff --git a/frontend/src/components/Graph/Plugin/DragSelect.ts b/frontend/src/components/Graph/Plugin/DragSelect.ts new file mode 100644 index 0000000..400b870 --- /dev/null +++ b/frontend/src/components/Graph/Plugin/DragSelect.ts @@ -0,0 +1,321 @@ +import { Chart, ChartTypeRegistry, Plugin } from 'chart.js'; +import { getRelativePosition } from 'chart.js/helpers'; + +// utils +import { ChartEventHandler, mergeDefaultOptions } from './utils'; + +export const dragSelectPluginId = 'drag-select-plugin'; + +type ChartDragHandlers = { + mousedown: ChartEventHandler; + mousemove: ChartEventHandler; + mouseup: ChartEventHandler; + globalMouseup: () => void; +}; + +export type DragSelectPluginOptions = { + color?: string; + onSelect?: (startValueX: number, endValueX: number) => void; +}; + +const defaultDragSelectPluginOptions: Required = { + color: 'rgba(0, 0, 0, 0.5)', + onSelect: () => {}, +}; + +export function createDragSelectPluginOptions( + isEnabled: boolean, + onSelect?: (start: number, end: number) => void, + color?: string, +): DragSelectPluginOptions | false { + if (!isEnabled) { + return false; + } + + return { + onSelect, + color, + }; +} + +function createMousedownHandler( + chart: Chart, + dragData: DragSelectData, +): ChartEventHandler { + return (ev): void => { + const { left, right } = chart.chartArea; + + let { x: startDragPositionX } = getRelativePosition(ev, chart); + + if (left > startDragPositionX) { + startDragPositionX = left; + } + + if (right < startDragPositionX) { + startDragPositionX = right; + } + + const startValuePositionX = chart.scales.x.getValueForPixel( + startDragPositionX, + ); + + dragData.onDragStart(startDragPositionX, startValuePositionX); + }; +} + +function createMousemoveHandler( + chart: Chart, + dragData: DragSelectData, +): ChartEventHandler { + return (ev): void => { + if (!dragData.isMouseDown) { + return; + } + + const { left, right } = chart.chartArea; + + let { x: dragPositionX } = getRelativePosition(ev, chart); + + if (left > dragPositionX) { + dragPositionX = left; + } + + if (right < dragPositionX) { + dragPositionX = right; + } + + const valuePositionX = chart.scales.x.getValueForPixel(dragPositionX); + + dragData.onDrag(dragPositionX, valuePositionX); + chart.update('none'); + }; +} + +function createMouseupHandler( + chart: Chart, + options: DragSelectPluginOptions, + dragData: DragSelectData, +): ChartEventHandler { + return (ev): void => { + const { left, right } = chart.chartArea; + + let { x: endRelativePostionX } = getRelativePosition(ev, chart); + + if (left > endRelativePostionX) { + endRelativePostionX = left; + } + + if (right < endRelativePostionX) { + endRelativePostionX = right; + } + + const endValuePositionX = chart.scales.x.getValueForPixel( + endRelativePostionX, + ); + + dragData.onDragEnd(endRelativePostionX, endValuePositionX); + + chart.update('none'); + + if ( + typeof options.onSelect === 'function' && + typeof dragData.startValuePositionX === 'number' && + typeof dragData.endValuePositionX === 'number' + ) { + const start = Math.min( + dragData.startValuePositionX, + dragData.endValuePositionX, + ); + const end = Math.max( + dragData.startValuePositionX, + dragData.endValuePositionX, + ); + + options.onSelect(start, end); + } + }; +} + +function createGlobalMouseupHandler( + options: DragSelectPluginOptions, + dragData: DragSelectData, +): () => void { + return (): void => { + const { isDragging, endRelativePixelPositionX, endValuePositionX } = dragData; + + if (!isDragging) { + return; + } + + dragData.onDragEnd( + endRelativePixelPositionX as number, + endValuePositionX as number, + ); + + if ( + typeof options.onSelect === 'function' && + typeof dragData.startValuePositionX === 'number' && + typeof dragData.endValuePositionX === 'number' + ) { + const start = Math.min( + dragData.startValuePositionX, + dragData.endValuePositionX, + ); + const end = Math.max( + dragData.startValuePositionX, + dragData.endValuePositionX, + ); + + options.onSelect(start, end); + } + }; +} + +class DragSelectData { + public isDragging = false; + + public isMouseDown = false; + + public startRelativePixelPositionX: number | null = null; + + public startValuePositionX: number | null | undefined = null; + + public endRelativePixelPositionX: number | null = null; + + public endValuePositionX: number | null | undefined = null; + + public initialize(): void { + this.isDragging = false; + this.isMouseDown = false; + this.startRelativePixelPositionX = null; + this.startValuePositionX = null; + this.endRelativePixelPositionX = null; + this.endValuePositionX = null; + } + + public onDragStart( + startRelativePixelPositionX: number, + startValuePositionX: number | undefined, + ): void { + this.isDragging = false; + this.isMouseDown = true; + this.startRelativePixelPositionX = startRelativePixelPositionX; + this.startValuePositionX = startValuePositionX; + this.endRelativePixelPositionX = null; + this.endValuePositionX = null; + } + + public onDrag( + endRelativePixelPositionX: number, + endValuePositionX: number | undefined, + ): void { + this.isDragging = true; + this.endRelativePixelPositionX = endRelativePixelPositionX; + this.endValuePositionX = endValuePositionX; + } + + public onDragEnd( + endRelativePixelPositionX: number, + endValuePositionX: number | undefined, + ): void { + if (!this.isDragging) { + this.initialize(); + return; + } + + this.isDragging = false; + this.isMouseDown = false; + this.endRelativePixelPositionX = endRelativePixelPositionX; + this.endValuePositionX = endValuePositionX; + } +} + +export const createDragSelectPlugin = (): Plugin< + keyof ChartTypeRegistry, + DragSelectPluginOptions +> => { + const dragData = new DragSelectData(); + let pluginOptions: Required; + + const handlers: ChartDragHandlers = { + mousedown: () => {}, + mousemove: () => {}, + mouseup: () => {}, + globalMouseup: () => {}, + }; + + const dragSelectPlugin: Plugin< + keyof ChartTypeRegistry, + DragSelectPluginOptions + > = { + id: dragSelectPluginId, + start: (chart: Chart, _, passedOptions) => { + pluginOptions = mergeDefaultOptions( + passedOptions, + defaultDragSelectPluginOptions, + ); + + const { canvas } = chart; + + dragData.initialize(); + + const mousedownHandler = createMousedownHandler(chart, dragData); + const mousemoveHandler = createMousemoveHandler(chart, dragData); + const mouseupHandler = createMouseupHandler(chart, pluginOptions, dragData); + const globalMouseupHandler = createGlobalMouseupHandler( + pluginOptions, + dragData, + ); + + canvas.addEventListener('mousedown', mousedownHandler, { passive: true }); + canvas.addEventListener('mousemove', mousemoveHandler, { passive: true }); + canvas.addEventListener('mouseup', mouseupHandler, { passive: true }); + document.addEventListener('mouseup', globalMouseupHandler, { + passive: true, + }); + + handlers.mousedown = mousedownHandler; + handlers.mousemove = mousemoveHandler; + handlers.mouseup = mouseupHandler; + handlers.globalMouseup = globalMouseupHandler; + }, + beforeDestroy: (chart: Chart) => { + const { canvas } = chart; + + if (!canvas) { + return; + } + + canvas.removeEventListener('mousedown', handlers.mousedown); + canvas.removeEventListener('mousemove', handlers.mousemove); + canvas.removeEventListener('mouseup', handlers.mouseup); + document.removeEventListener('mouseup', handlers.globalMouseup); + }, + afterDatasetsDraw: (chart: Chart) => { + const { + startRelativePixelPositionX, + endRelativePixelPositionX, + isDragging, + } = dragData; + + if (startRelativePixelPositionX && endRelativePixelPositionX && isDragging) { + const left = Math.min( + startRelativePixelPositionX, + endRelativePixelPositionX, + ); + const right = Math.max( + startRelativePixelPositionX, + endRelativePixelPositionX, + ); + const top = chart.chartArea.top - 5; + const bottom = chart.chartArea.bottom + 5; + + /* eslint-disable-next-line no-param-reassign */ + chart.ctx.fillStyle = pluginOptions.color; + chart.ctx.fillRect(left, top, right - left, bottom - top); + } + }, + }; + + return dragSelectPlugin; +}; diff --git a/frontend/src/components/Graph/Plugin/EmptyGraph.ts b/frontend/src/components/Graph/Plugin/EmptyGraph.ts new file mode 100644 index 0000000..ab008ca --- /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', width / 2, height / 2); + ctx.restore(); + }, +}; diff --git a/frontend/src/components/Graph/Plugin/IntersectionCursor.ts b/frontend/src/components/Graph/Plugin/IntersectionCursor.ts new file mode 100644 index 0000000..a3d9a4d --- /dev/null +++ b/frontend/src/components/Graph/Plugin/IntersectionCursor.ts @@ -0,0 +1,164 @@ +import { Chart, ChartEvent, ChartTypeRegistry, Plugin } from 'chart.js'; +import { getRelativePosition } from 'chart.js/helpers'; + +// utils +import { ChartEventHandler, mergeDefaultOptions } from './utils'; + +export const intersectionCursorPluginId = 'intersection-cursor-plugin'; + +export type IntersectionCursorPluginOptions = { + color?: string; + dashSize?: number; + gapSize?: number; +}; + +export const defaultIntersectionCursorPluginOptions: Required = { + color: 'white', + dashSize: 3, + gapSize: 3, +}; + +export function createIntersectionCursorPluginOptions( + isEnabled: boolean, + color?: string, + dashSize?: number, + gapSize?: number, +): IntersectionCursorPluginOptions | false { + if (!isEnabled) { + return false; + } + + return { + color, + dashSize, + gapSize, + }; +} + +function createMousemoveHandler( + chart: Chart, + cursorData: IntersectionCursorData, +): ChartEventHandler { + return (ev: ChartEvent | MouseEvent): void => { + const { left, right, top, bottom } = chart.chartArea; + + let { x, y } = getRelativePosition(ev, chart); + + if (left > x) { + x = left; + } + + if (right < x) { + x = right; + } + + if (y < top) { + y = top; + } + + if (y > bottom) { + y = bottom; + } + + cursorData.onMouseMove(x, y); + }; +} + +function createMouseoutHandler( + cursorData: IntersectionCursorData, +): ChartEventHandler { + return (): void => { + cursorData.onMouseOut(); + }; +} + +class IntersectionCursorData { + public positionX: number | null | undefined; + + public positionY: number | null | undefined; + + public initialize(): void { + this.positionX = null; + this.positionY = null; + } + + public onMouseMove(x: number | undefined, y: number | undefined): void { + this.positionX = x; + this.positionY = y; + } + + public onMouseOut(): void { + this.positionX = null; + this.positionY = null; + } +} + +export const createIntersectionCursorPlugin = (): Plugin< + keyof ChartTypeRegistry, + IntersectionCursorPluginOptions +> => { + const cursorData = new IntersectionCursorData(); + let pluginOptions: Required; + + let mousemoveHandler: (ev: ChartEvent | MouseEvent) => void; + let mouseoutHandler: (ev: ChartEvent | MouseEvent) => void; + + const intersectionCursorPlugin: Plugin< + keyof ChartTypeRegistry, + IntersectionCursorPluginOptions + > = { + id: intersectionCursorPluginId, + start: (chart: Chart, _, passedOptions) => { + const { canvas } = chart; + + cursorData.initialize(); + pluginOptions = mergeDefaultOptions( + passedOptions, + defaultIntersectionCursorPluginOptions, + ); + + mousemoveHandler = createMousemoveHandler(chart, cursorData); + mouseoutHandler = createMouseoutHandler(cursorData); + + canvas.addEventListener('mousemove', mousemoveHandler, { passive: true }); + canvas.addEventListener('mouseout', mouseoutHandler, { passive: true }); + }, + beforeDestroy: (chart: Chart) => { + const { canvas } = chart; + + if (!canvas) { + return; + } + + canvas.removeEventListener('mousemove', mousemoveHandler); + canvas.removeEventListener('mouseout', mouseoutHandler); + }, + afterDatasetsDraw: (chart: Chart) => { + const { positionX, positionY } = cursorData; + + const lineDashData = [pluginOptions.dashSize, pluginOptions.gapSize]; + + if (typeof positionX === 'number' && typeof positionY === 'number') { + const { top, bottom, left, right } = chart.chartArea; + + chart.ctx.beginPath(); + /* eslint-disable-next-line no-param-reassign */ + chart.ctx.strokeStyle = pluginOptions.color; + chart.ctx.setLineDash(lineDashData); + chart.ctx.moveTo(left, positionY); + chart.ctx.lineTo(right, positionY); + chart.ctx.stroke(); + + chart.ctx.beginPath(); + chart.ctx.setLineDash(lineDashData); + /* eslint-disable-next-line no-param-reassign */ + chart.ctx.strokeStyle = pluginOptions.color; + chart.ctx.moveTo(positionX, top); + chart.ctx.lineTo(positionX, bottom); + chart.ctx.stroke(); + } + }, + }; + + return intersectionCursorPlugin; +}; diff --git a/frontend/src/components/Graph/Plugin/Legend.ts b/frontend/src/components/Graph/Plugin/Legend.ts new file mode 100644 index 0000000..809e0d1 --- /dev/null +++ b/frontend/src/components/Graph/Plugin/Legend.ts @@ -0,0 +1,114 @@ +import { Chart, ChartType, Plugin } from 'chart.js'; +import { Events } from 'constants/events'; +import { colors } from 'lib/getRandomColor'; +import { get } from 'lodash-es'; +import { eventEmitter } from 'utils/getEventEmitter'; + +const getOrCreateLegendList = ( + chart: Chart, + id: string, + isLonger: boolean, +): HTMLUListElement => { + const legendContainer = document.getElementById(id); + let listContainer = legendContainer?.querySelector('ul'); + + if (!listContainer) { + listContainer = document.createElement('ul'); + listContainer.style.display = 'flex'; + // listContainer.style.flexDirection = isLonger ? 'column' : 'row'; + listContainer.style.margin = '0'; + listContainer.style.padding = '0'; + listContainer.style.overflowY = 'scroll'; + listContainer.style.justifyContent = isLonger ? 'start' : 'center'; + listContainer.style.alignItems = isLonger ? 'start' : 'center'; + listContainer.style.minHeight = '2rem'; + listContainer.style.height = '100%'; + listContainer.style.flexWrap = 'wrap'; + listContainer.style.justifyContent = 'center'; + listContainer.style.fontSize = '0.75rem'; + legendContainer?.appendChild(listContainer); + } + + return listContainer; +}; + +export const legend = (id: string, isLonger: boolean): Plugin => ({ + id: 'htmlLegend', + afterUpdate(chart): void { + const ul = getOrCreateLegendList(chart, id || 'legend', isLonger); + + // Remove old legend items + while (ul.firstChild) { + ul.firstChild.remove(); + } + + // Reuse the built-in legendItems generator + const items = get(chart, [ + 'options', + 'plugins', + 'legend', + 'labels', + 'generateLabels', + ]) + ? get(chart, ['options', 'plugins', 'legend', 'labels', 'generateLabels'])( + chart, + ) + : null; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + items?.forEach((item: Record, index: number) => { + const li = document.createElement('li'); + li.style.alignItems = 'center'; + li.style.cursor = 'pointer'; + li.style.display = 'flex'; + li.style.marginLeft = '10px'; + // li.style.marginTop = '5px'; + + li.onclick = (): void => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const { type } = chart.config; + if (type === 'pie' || type === 'doughnut') { + // Pie and doughnut charts only have a single dataset and visibility is per item + chart.toggleDataVisibility(index); + } else { + chart.setDatasetVisibility( + item.datasetIndex, + !chart.isDatasetVisible(item.datasetIndex), + ); + eventEmitter.emit(Events.UPDATE_GRAPH_MANAGER_TABLE, { + name: id, + index: item.datasetIndex, + }); + } + chart.update(); + }; + + // Color box + const boxSpan = document.createElement('span'); + boxSpan.style.background = `${item.strokeStyle}` || `${colors[0]}`; + boxSpan.style.borderColor = `${item?.strokeStyle}`; + boxSpan.style.borderWidth = `${item.lineWidth}px`; + boxSpan.style.display = 'inline-block'; + boxSpan.style.minHeight = '0.75rem'; + boxSpan.style.marginRight = '0.5rem'; + boxSpan.style.minWidth = '0.75rem'; + boxSpan.style.borderRadius = '50%'; + + if (item.text) { + // Text + const textContainer = document.createElement('span'); + textContainer.style.margin = '0'; + textContainer.style.padding = '0'; + textContainer.style.textDecoration = item.hidden ? 'line-through' : ''; + + const text = document.createTextNode(item.text); + textContainer.appendChild(text); + + li.appendChild(boxSpan); + li.appendChild(textContainer); + ul.appendChild(li); + } + }); + }, +}); diff --git a/frontend/src/components/Graph/Plugin/Tooltip.ts b/frontend/src/components/Graph/Plugin/Tooltip.ts new file mode 100644 index 0000000..0a37694 --- /dev/null +++ b/frontend/src/components/Graph/Plugin/Tooltip.ts @@ -0,0 +1,46 @@ +import { + ActiveElement, + ChartTypeRegistry, + Point, + TooltipModel, + TooltipXAlignment, + TooltipYAlignment, +} from 'chart.js'; + +export function TooltipPosition( + this: TooltipModel, + _: readonly ActiveElement[], + eventPosition: Point, +): ITooltipPosition { + const { + chartArea: { width }, + scales: { x, y }, + } = this.chart; + + const valueForPixelOnX = Number(x.getValueForPixel(eventPosition.x)); + const valueForPixelonY = Number(y.getValueForPixel(eventPosition.y)); + + const rightmostWidth = this.width + x.getPixelForValue(valueForPixelOnX) + 20; + + if (rightmostWidth > width) { + return { + x: x.getPixelForValue(valueForPixelOnX) - 20, + y: y.getPixelForValue(valueForPixelonY) + 10, + xAlign: 'right', + yAlign: 'top', + }; + } + return { + x: x.getPixelForValue(valueForPixelOnX) + 20, + y: y.getPixelForValue(valueForPixelonY) + 10, + xAlign: 'left', + yAlign: 'top', + }; +} + +interface ITooltipPosition { + x: number; + y: number; + xAlign: TooltipXAlignment; + yAlign: TooltipYAlignment; +} diff --git a/frontend/src/components/Graph/Plugin/index.ts b/frontend/src/components/Graph/Plugin/index.ts new file mode 100644 index 0000000..6adb3cc --- /dev/null +++ b/frontend/src/components/Graph/Plugin/index.ts @@ -0,0 +1 @@ +export * from './Legend'; diff --git a/frontend/src/components/Graph/Plugin/utils.ts b/frontend/src/components/Graph/Plugin/utils.ts new file mode 100644 index 0000000..4260e9e --- /dev/null +++ b/frontend/src/components/Graph/Plugin/utils.ts @@ -0,0 +1,20 @@ +import { ChartEvent } from 'chart.js'; + +export type ChartEventHandler = (ev: ChartEvent | MouseEvent) => void; + +export function mergeDefaultOptions>( + options: T, + defaultOptions: Required, +): Required { + const sanitizedOptions = { ...options }; + Object.keys(options).forEach((key) => { + if (sanitizedOptions[key as keyof T] === undefined) { + delete sanitizedOptions[key as keyof T]; + } + }); + + return { + ...defaultOptions, + ...sanitizedOptions, + }; +} diff --git a/frontend/src/components/Graph/__tests__/xAxisConfig.test.ts b/frontend/src/components/Graph/__tests__/xAxisConfig.test.ts new file mode 100644 index 0000000..b26a243 --- /dev/null +++ b/frontend/src/components/Graph/__tests__/xAxisConfig.test.ts @@ -0,0 +1,74 @@ +import dayjs from 'dayjs'; + +import { convertTimeRange, TIME_UNITS } from '../xAxisConfig'; + +describe('xAxisConfig for Chart', () => { + describe('convertTimeRange', () => { + it('should return relevant time units for given range', () => { + { + const start = dayjs(); + const end = start.add(10, 'millisecond'); + + expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual( + TIME_UNITS.millisecond, + ); + } + { + const start = dayjs(); + const end = start.add(10, 'second'); + + expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual( + TIME_UNITS.second, + ); + } + { + const start = dayjs(); + const end = start.add(10, 'minute'); + + expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual( + TIME_UNITS.minute, + ); + } + { + const start = dayjs(); + const end = start.add(10, 'hour'); + + expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual( + TIME_UNITS.hour, + ); + } + { + const start = dayjs(); + const end = start.add(10, 'day'); + + expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual( + TIME_UNITS.day, + ); + } + { + const start = dayjs(); + const end = start.add(10, 'week'); + + expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual( + TIME_UNITS.week, + ); + } + { + const start = dayjs(); + const end = start.add(10, 'month'); + + expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual( + TIME_UNITS.month, + ); + } + { + const start = dayjs(); + const end = start.add(10, 'year'); + + expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual( + TIME_UNITS.year, + ); + } + }); + }); +}); diff --git a/frontend/src/components/Graph/hasData.ts b/frontend/src/components/Graph/hasData.ts new file mode 100644 index 0000000..5ba968b --- /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) { + hasData = true; + break; + } + } + } catch (error) { + console.error(error); + } + + return hasData; +}; diff --git a/frontend/src/components/Graph/helpers.ts b/frontend/src/components/Graph/helpers.ts new file mode 100644 index 0000000..c097dcb --- /dev/null +++ b/frontend/src/components/Graph/helpers.ts @@ -0,0 +1,8 @@ +import { themeColors } from 'constants/theme'; + +export const getAxisLabelColor = (currentTheme: string): string => { + if (currentTheme === 'light') { + return themeColors.black; + } + return themeColors.whiteCream; +}; diff --git a/frontend/src/components/Graph/index.tsx b/frontend/src/components/Graph/index.tsx new file mode 100644 index 0000000..0065f6b --- /dev/null +++ b/frontend/src/components/Graph/index.tsx @@ -0,0 +1,209 @@ +import { + BarController, + BarElement, + CategoryScale, + Chart, + Decimation, + Filler, + Legend, + LinearScale, + LineController, + LineElement, + PointElement, + SubTitle, + TimeScale, + TimeSeriesScale, + Title, + Tooltip, +} from 'chart.js'; +import annotationPlugin from 'chartjs-plugin-annotation'; +import { generateGridTitle } from 'container/GridPanelSwitch/utils'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import isEqual from 'lodash-es/isEqual'; +import { + forwardRef, + memo, + useCallback, + useEffect, + useImperativeHandle, + useMemo, + useRef, +} from 'react'; + +import { hasData } from './hasData'; +import { legend } from './Plugin'; +import { createDragSelectPlugin } from './Plugin/DragSelect'; +import { emptyGraph } from './Plugin/EmptyGraph'; +import { createIntersectionCursorPlugin } from './Plugin/IntersectionCursor'; +import { TooltipPosition as TooltipPositionHandler } from './Plugin/Tooltip'; +import { LegendsContainer } from './styles'; +import { CustomChartOptions, GraphProps, ToggleGraphProps } from './types'; +import { getGraphOptions, toggleGraph } from './utils'; +import { useXAxisTimeUnit } from './xAxisConfig'; + +Chart.register( + LineElement, + PointElement, + LineController, + CategoryScale, + LinearScale, + TimeScale, + TimeSeriesScale, + Decimation, + Filler, + Legend, + Title, + Tooltip, + SubTitle, + BarController, + BarElement, + annotationPlugin, +); + +Tooltip.positioners.custom = TooltipPositionHandler; + +const Graph = forwardRef( + ( + { + animate = true, + data, + type, + title, + isStacked, + onClickHandler, + name, + yAxisUnit = 'short', + forceReRender, + staticLine, + containerHeight, + onDragSelect, + dragSelectColor, + }, + ref, + ): JSX.Element => { + const nearestDatasetIndex = useRef(null); + const chartRef = useRef(null); + const isDarkMode = useIsDarkMode(); + const gridTitle = useMemo(() => generateGridTitle(title), [title]); + + const currentTheme = isDarkMode ? 'dark' : 'light'; + const xAxisTimeUnit = useXAxisTimeUnit(data); // Computes the relevant time unit for x axis by analyzing the time stamp data + + const lineChartRef = useRef(); + + useImperativeHandle( + ref, + (): ToggleGraphProps => ({ + toggleGraph(graphIndex: number, isVisible: boolean): void { + toggleGraph(graphIndex, isVisible, lineChartRef); + }, + }), + ); + + const getGridColor = useCallback(() => { + if (currentTheme === undefined) { + return 'rgba(231,233,237,0.1)'; + } + + if (currentTheme === 'dark') { + return 'rgba(231,233,237,0.1)'; + } + + return 'rgba(231,233,237,0.8)'; + }, [currentTheme]); + + const buildChart = useCallback(() => { + if (lineChartRef.current !== undefined) { + lineChartRef.current.destroy(); + } + + if (chartRef.current !== null) { + const options: CustomChartOptions = getGraphOptions( + animate, + staticLine, + gridTitle, + nearestDatasetIndex, + yAxisUnit, + onDragSelect, + dragSelectColor, + currentTheme, + getGridColor, + xAxisTimeUnit, + isStacked, + onClickHandler, + data, + ); + + const chartHasData = hasData(data); + const chartPlugins = []; + + if (chartHasData) { + chartPlugins.push(createIntersectionCursorPlugin()); + chartPlugins.push(createDragSelectPlugin()); + } else { + chartPlugins.push(emptyGraph); + } + + chartPlugins.push(legend(name, data.datasets.length > 3)); + + lineChartRef.current = new Chart(chartRef.current, { + type, + data, + options, + plugins: chartPlugins, + }); + } + }, [ + animate, + staticLine, + gridTitle, + yAxisUnit, + onDragSelect, + dragSelectColor, + currentTheme, + getGridColor, + xAxisTimeUnit, + isStacked, + onClickHandler, + data, + name, + type, + ]); + + useEffect(() => { + buildChart(); + }, [buildChart, forceReRender]); + + return ( +
    + + +
    + ); + }, +); + +declare module 'chart.js' { + interface TooltipPositionerMap { + custom: TooltipPositionerFunction; + } +} + +Graph.defaultProps = { + animate: undefined, + title: undefined, + isStacked: undefined, + onClickHandler: undefined, + yAxisUnit: undefined, + forceReRender: undefined, + staticLine: undefined, + containerHeight: '90%', + onDragSelect: undefined, + dragSelectColor: undefined, +}; + +Graph.displayName = 'Graph'; + +export default memo(Graph, (prevProps, nextProps) => + isEqual(prevProps.data, nextProps.data), +); diff --git a/frontend/src/components/Graph/styles.ts b/frontend/src/components/Graph/styles.ts new file mode 100644 index 0000000..371aa9d --- /dev/null +++ b/frontend/src/components/Graph/styles.ts @@ -0,0 +1,28 @@ +import { themeColors } from 'constants/theme'; +import styled from 'styled-components'; + +export const LegendsContainer = styled.div` + height: 10%; + + * { + ::-webkit-scrollbar { + width: 0.3rem; + } + ::-webkit-scrollbar:horizontal { + height: 0.3rem; + } + ::-webkit-scrollbar-track { + background: transparent; + } + ::-webkit-scrollbar-thumb { + background: ${themeColors.royalGrey}; + border-radius: 0.625rem; + } + ::-webkit-scrollbar-thumb:hover { + background: ${themeColors.matterhornGrey}; + } + ::-webkit-scrollbar-corner { + background: transparent; + } + } +`; diff --git a/frontend/src/components/Graph/types.ts b/frontend/src/components/Graph/types.ts new file mode 100644 index 0000000..4dd1d5b --- /dev/null +++ b/frontend/src/components/Graph/types.ts @@ -0,0 +1,78 @@ +import { + ActiveElement, + Chart, + ChartData, + ChartEvent, + ChartOptions, + ChartType, + TimeUnit, +} from 'chart.js'; +import { ForwardedRef, ReactNode } from 'react'; + +import { + dragSelectPluginId, + DragSelectPluginOptions, +} from './Plugin/DragSelect'; +import { + intersectionCursorPluginId, + IntersectionCursorPluginOptions, +} from './Plugin/IntersectionCursor'; + +export interface StaticLineProps { + yMin: number | undefined; + yMax: number | undefined; + borderColor: string; + borderWidth: number; + lineText: string; + textColor: string; +} + +export type GraphOnClickHandler = ( + event: ChartEvent, + elements: ActiveElement[], + chart: Chart, + data: ChartData, +) => void; + +export type ToggleGraphProps = { + toggleGraph(graphIndex: number, isVisible: boolean, reference?: string): void; +}; + +export type CustomChartOptions = ChartOptions & { + plugins: { + [dragSelectPluginId]: DragSelectPluginOptions | false; + [intersectionCursorPluginId]: IntersectionCursorPluginOptions | false; + }; +}; + +export interface GraphProps { + animate?: boolean; + type: ChartType; + data: Chart['data']; + title?: ReactNode; + isStacked?: boolean; + onClickHandler?: GraphOnClickHandler; + name: string; + yAxisUnit?: string; + forceReRender?: boolean | null | number; + staticLine?: StaticLineProps | undefined; + containerHeight?: string | number; + onDragSelect?: (start: number, end: number) => void; + dragSelectColor?: string; + ref?: ForwardedRef; +} + +export interface IAxisTimeUintConfig { + unitName: TimeUnit; + multiplier: number; +} + +export interface IAxisTimeConfig { + unitName: TimeUnit; + stepSize: number; +} + +export interface ITimeRange { + minTime: number | null; + maxTime: number | null; +} diff --git a/frontend/src/components/Graph/utils.ts b/frontend/src/components/Graph/utils.ts new file mode 100644 index 0000000..db30b6a --- /dev/null +++ b/frontend/src/components/Graph/utils.ts @@ -0,0 +1,223 @@ +import { Chart, ChartConfiguration, ChartData, Color } from 'chart.js'; +import * as chartjsAdapter from 'chartjs-adapter-date-fns'; +import dayjs from 'dayjs'; +import { MutableRefObject } from 'react'; + +import { getAxisLabelColor } from './helpers'; +import { + createDragSelectPluginOptions, + dragSelectPluginId, +} from './Plugin/DragSelect'; +import { + createIntersectionCursorPluginOptions, + intersectionCursorPluginId, +} from './Plugin/IntersectionCursor'; +import { + CustomChartOptions, + GraphOnClickHandler, + IAxisTimeConfig, + StaticLineProps, +} from './types'; +import { getToolTipValue, getYAxisFormattedValue } from './yAxisConfig'; + +export const toggleGraph = ( + graphIndex: number, + isVisible: boolean, + lineChartRef: MutableRefObject, +): void => { + if (lineChartRef && lineChartRef.current) { + const { type } = lineChartRef.current?.config as ChartConfiguration; + if (type === 'pie' || type === 'doughnut') { + lineChartRef.current?.toggleDataVisibility(graphIndex); + } else { + lineChartRef.current?.setDatasetVisibility(graphIndex, isVisible); + } + lineChartRef.current?.update(); + } +}; + +export const getGraphOptions = ( + animate: boolean, + staticLine: StaticLineProps | undefined, + title: string | undefined, + nearestDatasetIndex: MutableRefObject, + yAxisUnit: string, + onDragSelect: ((start: number, end: number) => void) | undefined, + dragSelectColor: string | undefined, + currentTheme: 'dark' | 'light', + getGridColor: () => 'rgba(231,233,237,0.1)' | 'rgba(231,233,237,0.8)', + xAxisTimeUnit: IAxisTimeConfig, + isStacked: boolean | undefined, + onClickHandler: GraphOnClickHandler | undefined, + data: ChartData, + // eslint-disable-next-line sonarjs/cognitive-complexity +): CustomChartOptions => ({ + animation: { + duration: animate ? 200 : 0, + }, + responsive: true, + maintainAspectRatio: false, + interaction: { + mode: 'index', + intersect: false, + }, + plugins: { + annotation: staticLine + ? { + annotations: [ + { + type: 'line', + yMin: staticLine.yMin, + yMax: staticLine.yMax, + borderColor: staticLine.borderColor, + borderWidth: staticLine.borderWidth, + label: { + content: staticLine.lineText, + enabled: true, + font: { + size: 10, + }, + borderWidth: 0, + position: 'start', + backgroundColor: 'transparent', + color: staticLine.textColor, + }, + }, + ], + } + : undefined, + title: { + display: title !== undefined, + text: title, + }, + legend: { + display: false, + }, + tooltip: { + callbacks: { + title(context): string | string[] { + const date = dayjs(context[0].parsed.x); + return date.format('MMM DD, YYYY, HH:mm:ss'); + }, + label(context): string | string[] { + let label = context.dataset.label || ''; + + if (label) { + label += ': '; + } + if (context.parsed.y !== null) { + label += getToolTipValue(context.parsed.y.toString(), yAxisUnit); + } + + return label; + }, + labelTextColor(labelData): Color { + if (labelData.datasetIndex === nearestDatasetIndex.current) { + return 'rgba(255, 255, 255, 1)'; + } + + return 'rgba(255, 255, 255, 0.75)'; + }, + }, + position: 'custom', + itemSort(item1, item2): number { + return item2.parsed.y - item1.parsed.y; + }, + }, + [dragSelectPluginId]: createDragSelectPluginOptions( + !!onDragSelect, + onDragSelect, + dragSelectColor, + ), + [intersectionCursorPluginId]: createIntersectionCursorPluginOptions( + !!onDragSelect, + currentTheme === 'dark' ? 'white' : 'black', + ), + }, + layout: { + padding: 0, + }, + scales: { + x: { + grid: { + display: true, + color: getGridColor(), + drawTicks: true, + }, + adapters: { + date: chartjsAdapter, + }, + time: { + unit: xAxisTimeUnit?.unitName || 'minute', + stepSize: xAxisTimeUnit?.stepSize || 1, + displayFormats: { + millisecond: 'HH:mm:ss', + second: 'HH:mm:ss', + minute: 'HH:mm', + hour: 'MM/dd HH:mm', + day: 'MM/dd', + week: 'MM/dd', + month: 'yy-MM', + year: 'yy', + }, + }, + type: 'time', + ticks: { color: getAxisLabelColor(currentTheme) }, + }, + y: { + display: true, + grid: { + display: true, + color: getGridColor(), + }, + ticks: { + color: getAxisLabelColor(currentTheme), + // Include a dollar sign in the ticks + callback(value): string { + return getYAxisFormattedValue(value.toString(), yAxisUnit); + }, + }, + }, + stacked: { + display: isStacked === undefined ? false : 'auto', + }, + }, + elements: { + line: { + tension: 0, + cubicInterpolationMode: 'monotone', + }, + point: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + hoverBackgroundColor: (ctx: any): string => { + if (ctx?.element?.options?.borderColor) { + return ctx.element.options.borderColor; + } + return 'rgba(0,0,0,0.1)'; + }, + hoverRadius: 5, + }, + }, + onClick: (event, element, chart): void => { + if (onClickHandler) { + onClickHandler(event, element, chart, data); + } + }, + onHover: (event, _, chart): void => { + if (event.native) { + const interactions = chart.getElementsAtEventForMode( + event.native, + 'nearest', + { + intersect: false, + }, + true, + ); + + if (interactions[0]) { + // eslint-disable-next-line no-param-reassign + nearestDatasetIndex.current = interactions[0].datasetIndex; + } + } + }, +}); diff --git a/frontend/src/components/Graph/xAxisConfig.ts b/frontend/src/components/Graph/xAxisConfig.ts new file mode 100644 index 0000000..3fa0b00 --- /dev/null +++ b/frontend/src/components/Graph/xAxisConfig.ts @@ -0,0 +1,140 @@ +import { Chart, TimeUnit } from 'chart.js'; +import { useMemo } from 'react'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { GlobalReducer } from 'types/reducer/globalTime'; + +import { IAxisTimeConfig, IAxisTimeUintConfig, ITimeRange } from './types'; + +export const TIME_UNITS: Record = { + millisecond: 'millisecond', + second: 'second', + minute: 'minute', + hour: 'hour', + day: 'day', + week: 'week', + month: 'month', + year: 'year', + quarter: 'quarter', +}; + +const TIME_UNITS_CONFIG: IAxisTimeUintConfig[] = [ + { + unitName: TIME_UNITS.millisecond, + multiplier: 1, + }, + { + unitName: TIME_UNITS.second, + multiplier: 1 / 1e3, + }, + { + unitName: TIME_UNITS.minute, + multiplier: 1 / (1e3 * 60), + }, + { + unitName: TIME_UNITS.hour, + multiplier: 1 / (1e3 * 60 * 60), + }, + { + unitName: TIME_UNITS.day, + multiplier: 1 / (1e3 * 60 * 60 * 24), + }, + { + unitName: TIME_UNITS.week, + multiplier: 1 / (1e3 * 60 * 60 * 24 * 7), + }, + { + unitName: TIME_UNITS.month, + multiplier: 1 / (1e3 * 60 * 60 * 24 * 30), + }, + { + unitName: TIME_UNITS.year, + multiplier: 1 / (1e3 * 60 * 60 * 24 * 365), + }, +]; + +/** + * Finds the relevant time unit based on the input time stamps (in ms) + */ +export const convertTimeRange = ( + start: number, + end: number, +): IAxisTimeConfig => { + const MIN_INTERVALS = 6; + const range = end - start; + let relevantTimeUnit = TIME_UNITS_CONFIG[1]; + let stepSize = 1; + try { + for (let idx = TIME_UNITS_CONFIG.length - 1; idx >= 0; idx -= 1) { + const timeUnit = TIME_UNITS_CONFIG[idx]; + const units = range * timeUnit.multiplier; + const steps = units / MIN_INTERVALS; + if (steps >= 1) { + relevantTimeUnit = timeUnit; + stepSize = steps; + break; + } + } + } catch (error) { + console.error(error); + } + + return { + unitName: relevantTimeUnit.unitName, + stepSize: Math.floor(stepSize) || 1, + }; +}; + +/** + * Accepts Chart.js data's data-structure and returns the relevant time unit for the axis based on the range of the data. + */ +export const useXAxisTimeUnit = (data: Chart['data']): IAxisTimeConfig => { + // Local time is the time range inferred from the input chart data. + let localTime: ITimeRange | null; + try { + let minTime = Number.POSITIVE_INFINITY; + let maxTime = Number.NEGATIVE_INFINITY; + data?.labels?.forEach((timeStamp: unknown): void => { + const getTimeStamp = (time: Date | number): Date | number | string => { + if (time instanceof Date) { + return Date.parse(time.toString()); + } + + return time; + }; + const time = getTimeStamp(timeStamp as Date | number); + + minTime = Math.min(parseInt(time.toString(), 10), minTime); + maxTime = Math.max(parseInt(time.toString(), 10), maxTime); + }); + + localTime = { + minTime: minTime === Number.POSITIVE_INFINITY ? null : minTime, + maxTime: maxTime === Number.NEGATIVE_INFINITY ? null : maxTime, + }; + } catch (error) { + localTime = null; + console.error(error); + } + + // Global time is the time selected from the global time selector menu. + const globalTime = useSelector( + (state) => state.globalTime, + ); + + // Use local time if valid else use the global time range + const { maxTime, minTime } = useMemo(() => { + if (localTime && localTime.maxTime && localTime.minTime) { + return { + minTime: localTime.minTime, + maxTime: localTime.maxTime, + }; + } + return { + minTime: globalTime.minTime / 1e6, + maxTime: globalTime.maxTime / 1e6, + }; + }, [globalTime, localTime]); + + return convertTimeRange(minTime, maxTime); +}; diff --git a/frontend/src/components/Graph/yAxisConfig.ts b/frontend/src/components/Graph/yAxisConfig.ts new file mode 100644 index 0000000..a5eca12 --- /dev/null +++ b/frontend/src/components/Graph/yAxisConfig.ts @@ -0,0 +1,58 @@ +import { formattedValueToString, getValueFormat } from '@grafana/data'; + +export const getYAxisFormattedValue = ( + value: string, + format: string, +): string => { + let decimalPrecision: number | undefined; + const parsedValue = getValueFormat(format)( + parseFloat(value), + undefined, + undefined, + undefined, + ); + try { + const decimalSplitted = parsedValue.text.split('.'); + if (decimalSplitted.length === 1) { + decimalPrecision = 0; + } else { + const decimalDigits = decimalSplitted[1].split(''); + decimalPrecision = decimalDigits.length; + let nonZeroCtr = 0; + for (let idx = 0; idx < decimalDigits.length; idx += 1) { + if (decimalDigits[idx] !== '0') { + nonZeroCtr += 1; + if (nonZeroCtr >= 2) { + decimalPrecision = idx + 1; + } + } else if (nonZeroCtr) { + decimalPrecision = idx; + break; + } + } + } + + return formattedValueToString( + getValueFormat(format)( + parseFloat(value), + decimalPrecision, + undefined, + undefined, + ), + ); + } catch (error) { + console.error(error); + } + return `${parseFloat(value)}`; +}; + +export const getToolTipValue = (value: string, format?: string): string => { + try { + return formattedValueToString( + getValueFormat(format)(parseFloat(value), undefined, undefined, undefined), + ); + } catch (error) { + console.error(error); + } + return `${value}`; +}; diff --git a/frontend/src/components/Input/index.tsx b/frontend/src/components/Input/index.tsx new file mode 100644 index 0000000..18a84f6 --- /dev/null +++ b/frontend/src/components/Input/index.tsx @@ -0,0 +1,71 @@ +import { Form, Input, InputProps, InputRef } from 'antd'; +import { + ChangeEventHandler, + FocusEventHandler, + KeyboardEventHandler, + LegacyRef, + ReactNode, + Ref, +} from 'react'; + +function InputComponent({ + value, + type = 'text', + onChangeHandler, + placeholder, + ref, + size = 'small', + onBlurHandler, + onPressEnterHandler, + label, + labelOnTop, + addonBefore, + ...props +}: InputComponentProps): JSX.Element { + return ( + + } + size={size} + addonBefore={addonBefore} + onBlur={onBlurHandler} + onPressEnter={onPressEnterHandler} + // eslint-disable-next-line react/jsx-props-no-spreading + {...props} + /> + + ); +} + +interface InputComponentProps extends InputProps { + value: InputProps['value']; + type?: InputProps['type']; + onChangeHandler?: ChangeEventHandler; + placeholder?: InputProps['placeholder']; + ref?: LegacyRef; + size?: InputProps['size']; + onBlurHandler?: FocusEventHandler; + onPressEnterHandler?: KeyboardEventHandler; + label?: string; + labelOnTop?: boolean; + addonBefore?: ReactNode; +} + +InputComponent.defaultProps = { + type: undefined, + onChangeHandler: undefined, + placeholder: undefined, + ref: undefined, + size: undefined, + onBlurHandler: undefined, + onPressEnterHandler: undefined, + label: undefined, + labelOnTop: undefined, + addonBefore: undefined, +}; + +export default InputComponent; diff --git a/frontend/src/components/Loadable/Loadable.test.tsx b/frontend/src/components/Loadable/Loadable.test.tsx new file mode 100644 index 0000000..2d2a217 --- /dev/null +++ b/frontend/src/components/Loadable/Loadable.test.tsx @@ -0,0 +1,49 @@ +import { + render, + screen, + waitForElementToBeRemoved, +} from '@testing-library/react'; +import React, { ComponentType, Suspense } from 'react'; + +import Loadable from './index'; + +// Sample component to be loaded lazily +function SampleComponent(): JSX.Element { + return
    Sample Component
    ; +} + +const loadSampleComponent = (): Promise<{ + default: ComponentType; +}> => + new Promise<{ default: ComponentType }>((resolve) => { + setTimeout(() => { + resolve({ default: SampleComponent }); + }, 500); + }); + +describe('Loadable', () => { + it('should render the lazily loaded component', async () => { + const LoadableSampleComponent = Loadable(loadSampleComponent); + + const { container } = render( + Loading...}> + + , + ); + + expect(screen.getByText('Loading...')).toBeInTheDocument(); + await waitForElementToBeRemoved(() => screen.queryByText('Loading...')); + + expect(container.querySelector('div')).toHaveTextContent('Sample Component'); + }); + + it('should call lazy with the provided import path', () => { + const reactLazySpy = jest.spyOn(React, 'lazy'); + Loadable(loadSampleComponent); + + expect(reactLazySpy).toHaveBeenCalledTimes(1); + expect(reactLazySpy).toHaveBeenCalledWith(expect.any(Function)); + + reactLazySpy.mockRestore(); + }); +}); diff --git a/frontend/src/components/Loadable/index.tsx b/frontend/src/components/Loadable/index.tsx new file mode 100644 index 0000000..5cffc57 --- /dev/null +++ b/frontend/src/components/Loadable/index.tsx @@ -0,0 +1,15 @@ +import { ComponentType, lazy, LazyExoticComponent } from 'react'; + +function Loadable(importPath: { + (): LoadableProps; +}): LazyExoticComponent { + return lazy(() => importPath()); +} + +type LazyComponent = ComponentType>; + +type LoadableProps = Promise<{ + default: LazyComponent; +}>; + +export default Loadable; diff --git a/frontend/src/components/LogDetail/LogDetail.interfaces.ts b/frontend/src/components/LogDetail/LogDetail.interfaces.ts new file mode 100644 index 0000000..399e1df --- /dev/null +++ b/frontend/src/components/LogDetail/LogDetail.interfaces.ts @@ -0,0 +1,14 @@ +import { DrawerProps } from 'antd'; +import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC'; +import { ActionItemProps } from 'container/LogDetailedView/ActionItem'; +import { ILog } from 'types/api/logs/log'; + +import { VIEWS } from './constants'; + +export type LogDetailProps = { + log: ILog | null; + selectedTab: VIEWS; + isListViewPanel?: boolean; +} & Pick & + Partial> & + Pick; diff --git a/frontend/src/components/LogDetail/LogDetails.styles.scss b/frontend/src/components/LogDetail/LogDetails.styles.scss new file mode 100644 index 0000000..c8ac0be --- /dev/null +++ b/frontend/src/components/LogDetail/LogDetails.styles.scss @@ -0,0 +1,232 @@ +.log-detail-drawer { + border-left: 1px solid var(--bg-slate-500); + background: var(--bg-ink-400); + box-shadow: -4px 10px 16px 2px rgba(0, 0, 0, 0.2); + + .ant-drawer-header { + padding: 8px 16px; + border-bottom: none; + + align-items: stretch; + + border-bottom: 1px solid var(--bg-slate-500); + background: var(--bg-ink-400); + } + + .ant-drawer-close { + margin-inline-end: 0px; + } + + .ant-drawer-body { + display: flex; + flex-direction: column; + padding: 16px; + } + + .title { + color: var(--text-vanilla-400); + font-family: Inter; + font-size: var(--font-size-sm); + font-style: normal; + font-weight: var(--font-weight-normal); + line-height: 20px; /* 142.857% */ + letter-spacing: -0.07px; + } + + .radio-button { + display: flex; + align-items: center; + justify-content: center; + padding-top: var(--padding-1); + border: 1px solid var(--bg-slate-400); + background: var(--bg-ink-300); + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1); + } + + .log-detail-drawer__log { + width: 100%; + display: flex; + align-items: center; + gap: 4px; + position: relative; + + .log-body { + font-family: 'SF Mono'; + font-family: 'Space Mono', monospace; + + font-size: var(--font-size-sm); + font-weight: var(--font-weight-normal); + line-height: 18px; + letter-spacing: -0.07px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + color: var(--text-vanilla-400); + opacity: 0.6; + } + + .log-type-indicator { + height: 24px; + border: 2px solid var(--bg-slate-400); + border-radius: 5px; + margin-left: 0; + + &.INFO { + border-color: #1d212d; + } + + &.WARNING { + border-color: #ffcd56; + } + + &.ERROR { + border-color: #e5484d; + } + } + + .log-overflow-shadow { + background: linear-gradient(270deg, #121317 10.4%, rgba(18, 19, 23, 0) 100%); + + width: 196px; + position: absolute; + right: 0; + } + } + + .tabs-and-search { + display: flex; + justify-content: space-between; + align-items: center; + margin: 16px 0; + + .action-btn { + border-radius: 2px; + border: 1px solid var(--bg-slate-400); + background: var(--bg-ink-300); + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1); + display: flex; + align-items: center; + justify-content: center; + } + + .json-action-btn { + display: flex; + gap: 8px; + } + } + + .views-tabs { + color: var(--text-vanilla-400); + + .view-title { + display: flex; + gap: var(--margin-2); + align-items: center; + justify-content: center; + font-size: var(--font-size-xs); + font-style: normal; + font-weight: var(--font-weight-normal); + } + + .tab { + border: 1px solid var(--bg-slate-400); + width: 114px; + } + + .tab::before { + background: var(--bg-slate-400); + } + + .selected_view { + background: var(--bg-slate-300); + color: var(--text-vanilla-100); + border: 1px solid var(--bg-slate-400); + } + + .selected_view::before { + background: var(--bg-slate-400); + } + } + + .search-input { + margin-top: var(--margin-2); + border: 1px solid var(--bg-slate-400); + height: 46px; + padding: var(--padding-1) var(--padding-2); + box-shadow: none; + border-radius: 0; + } + + .ant-drawer-close { + padding: 0px; + } +} + +.lightMode { + .ant-drawer-header { + border-bottom: 1px solid var(--bg-vanilla-400); + background: var(--bg-vanilla-100); + } + + .log-detail-drawer { + .title { + color: var(--text-ink-300); + } + + .log-detail-drawer__log { + .log-overflow-shadow { + background: linear-gradient( + 270deg, + var(--bg-vanilla-100) 10.4%, + rgba(255, 255, 255, 0) 100% + ); + } + + .log-type-indicator { + border: 2px solid var(--bg-vanilla-400); + } + + .ant-typography { + color: var(--text-ink-300); + background: transparent; + } + } + + .radio-button { + border: 1px solid var(--bg-vanilla-400); + background: var(--bg-vanilla-100); + color: var(--text-ink-300); + } + + .views-tabs { + .tab { + background: var(--bg-vanilla-100); + } + + .selected_view { + background: var(--bg-vanilla-300); + border: 1px solid var(--bg-slate-300); + color: var(--text-ink-400); + } + + .selected_view::before { + background: var(--bg-vanilla-300); + border-left: 1px solid var(--bg-slate-300); + } + } + + .tabs-and-search { + .action-btn { + border: 1px solid var(--bg-vanilla-400); + background: var(--bg-vanilla-100); + color: var(--text-ink-300); + } + } + + .search-input { + border: 1px solid var(--bg-vanilla-200); + background: var(--bg-vanilla-100); + color: var(--text-ink-300); + } + } +} diff --git a/frontend/src/components/LogDetail/QueryBuilderSearchWrapper.styles.scss b/frontend/src/components/LogDetail/QueryBuilderSearchWrapper.styles.scss new file mode 100644 index 0000000..e3da355 --- /dev/null +++ b/frontend/src/components/LogDetail/QueryBuilderSearchWrapper.styles.scss @@ -0,0 +1,10 @@ +.query-builder-search-wrapper { + margin-top: 10px; + height: 46px; + border: 1px solid var(--bg-slate-400); + border-bottom: none; + + .ant-select-selector { + border: none !important; + } +} \ No newline at end of file diff --git a/frontend/src/components/LogDetail/QueryBuilderSearchWrapper.tsx b/frontend/src/components/LogDetail/QueryBuilderSearchWrapper.tsx new file mode 100644 index 0000000..72d18b7 --- /dev/null +++ b/frontend/src/components/LogDetail/QueryBuilderSearchWrapper.tsx @@ -0,0 +1,77 @@ +import './QueryBuilderSearchWrapper.styles.scss'; + +import useInitialQuery from 'container/LogsExplorerContext/useInitialQuery'; +import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch'; +import { Dispatch, SetStateAction, useEffect } from 'react'; +import { ILog } from 'types/api/logs/log'; +import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData'; + +function QueryBuilderSearchWrapper({ + log, + filters, + contextQuery, + isEdit, + suffixIcon, + setFilters, + setContextQuery, +}: QueryBuilderSearchWraperProps): JSX.Element { + const initialContextQuery = useInitialQuery(log); + + useEffect(() => { + setContextQuery(initialContextQuery); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleSearch = (tagFilters: TagFilter): void => { + const tagFiltersLength = tagFilters.items.length; + + if ( + (!tagFiltersLength && (!filters || !filters.items.length)) || + tagFiltersLength === filters?.items.length || + !contextQuery + ) + return; + + const nextQuery: Query = { + ...contextQuery, + builder: { + ...contextQuery.builder, + queryData: contextQuery.builder.queryData.map((item) => ({ + ...item, + filters: tagFilters, + })), + }, + }; + + setFilters({ ...tagFilters }); + setContextQuery({ ...nextQuery }); + }; + + // eslint-disable-next-line react/jsx-no-useless-fragment + if (!contextQuery || !isEdit) return <>; + + return ( + + ); +} + +interface QueryBuilderSearchWraperProps { + log: ILog; + isEdit: boolean; + contextQuery: Query | undefined; + setContextQuery: Dispatch>; + filters: TagFilter | null; + setFilters: Dispatch>; + suffixIcon?: React.ReactNode; +} + +QueryBuilderSearchWrapper.defaultProps = { + suffixIcon: undefined, +}; + +export default QueryBuilderSearchWrapper; diff --git a/frontend/src/components/LogDetail/constants.ts b/frontend/src/components/LogDetail/constants.ts new file mode 100644 index 0000000..92199d4 --- /dev/null +++ b/frontend/src/components/LogDetail/constants.ts @@ -0,0 +1,7 @@ +export const VIEW_TYPES = { + OVERVIEW: 'OVERVIEW', + JSON: 'JSON', + CONTEXT: 'CONTEXT', +} as const; + +export type VIEWS = typeof VIEW_TYPES[keyof typeof VIEW_TYPES]; diff --git a/frontend/src/components/LogDetail/index.tsx b/frontend/src/components/LogDetail/index.tsx new file mode 100644 index 0000000..0794ead --- /dev/null +++ b/frontend/src/components/LogDetail/index.tsx @@ -0,0 +1,211 @@ +/* eslint-disable sonarjs/cognitive-complexity */ +import './LogDetails.styles.scss'; + +import { Color, Spacing } from '@signozhq/design-tokens'; +import { Button, Divider, Drawer, Radio, Tooltip, Typography } from 'antd'; +import { RadioChangeEvent } from 'antd/lib'; +import cx from 'classnames'; +import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator'; +import ContextView from 'container/LogDetailedView/ContextView/ContextView'; +import JSONView from 'container/LogDetailedView/JsonView'; +import Overview from 'container/LogDetailedView/Overview'; +import { aggregateAttributesResourcesToString } from 'container/LogDetailedView/utils'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { useNotifications } from 'hooks/useNotifications'; +import { + Braces, + Copy, + Filter, + HardHat, + Table, + TextSelect, + X, +} from 'lucide-react'; +import { useState } from 'react'; +import { useCopyToClipboard } from 'react-use'; +import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData'; + +import { VIEW_TYPES, VIEWS } from './constants'; +import { LogDetailProps } from './LogDetail.interfaces'; +import QueryBuilderSearchWrapper from './QueryBuilderSearchWrapper'; + +function LogDetail({ + log, + onClose, + onAddToQuery, + onClickActionItem, + selectedTab, + isListViewPanel = false, +}: LogDetailProps): JSX.Element { + const [, copyToClipboard] = useCopyToClipboard(); + const [selectedView, setSelectedView] = useState(selectedTab); + + const [isFilterVisibile, setIsFilterVisible] = useState(false); + + const [contextQuery, setContextQuery] = useState(); + const [filters, setFilters] = useState(null); + const [isEdit, setIsEdit] = useState(false); + + const isDarkMode = useIsDarkMode(); + + const { notifications } = useNotifications(); + + const LogJsonData = log ? aggregateAttributesResourcesToString(log) : ''; + + const handleModeChange = (e: RadioChangeEvent): void => { + setSelectedView(e.target.value); + setIsEdit(false); + setIsFilterVisible(false); + }; + + const handleFilterVisible = (): void => { + setIsFilterVisible(!isFilterVisibile); + setIsEdit(!isEdit); + }; + + const drawerCloseHandler = ( + e: React.MouseEvent | React.KeyboardEvent, + ): void => { + if (onClose) { + onClose(e); + } + }; + + const handleJSONCopy = (): void => { + copyToClipboard(LogJsonData); + notifications.success({ + message: 'Copied to clipboard', + }); + }; + + if (!log) { + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>; + } + + const logType = log?.attributes_string?.log_level || LogType.INFO; + + return ( + + + Log details + + } + placement="right" + // closable + onClose={drawerCloseHandler} + open={log !== null} + style={{ + overscrollBehavior: 'contain', + background: isDarkMode ? Color.BG_INK_400 : Color.BG_VANILLA_100, + }} + className="log-detail-drawer" + destroyOnClose + closeIcon={} + > +
    + + + {log?.body} + + +
     
    +
    + +
    + + +
    + + Overview + + + +
    + + JSON +
    +
    + +
    + + Context +
    +
    + + + {selectedView === VIEW_TYPES.JSON && ( +
    +
    + )} + + {selectedView === VIEW_TYPES.CONTEXT && ( +
    + ); +} + +export default LogsTableView; diff --git a/frontend/src/components/Logs/TableView/styles.ts b/frontend/src/components/Logs/TableView/styles.ts new file mode 100644 index 0000000..9213021 --- /dev/null +++ b/frontend/src/components/Logs/TableView/styles.ts @@ -0,0 +1,23 @@ +import styled from 'styled-components'; + +interface TableBodyContentProps { + linesPerRow: number; + isDarkMode?: boolean; +} + +export const TableBodyContent = styled.div` + margin-bottom: 0; + color: ${(props): string => + props.isDarkMode ? 'var(--bg-vanilla-400, #c0c1c3)' : 'var(--bg-slate-400)'}; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 18px; /* 128.571% */ + letter-spacing: -0.07px; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: ${(props): number => props.linesPerRow}; + line-clamp: ${(props): number => props.linesPerRow}; + -webkit-box-orient: vertical; +`; diff --git a/frontend/src/components/Logs/TableView/types.ts b/frontend/src/components/Logs/TableView/types.ts new file mode 100644 index 0000000..36a796a --- /dev/null +++ b/frontend/src/components/Logs/TableView/types.ts @@ -0,0 +1,35 @@ +import { ColumnsType, ColumnType } from 'antd/es/table'; +import { IField } from 'types/api/logs/fields'; +import { ILog } from 'types/api/logs/log'; + +export type ColumnTypeRender = ReturnType< + NonNullable['render']> +>; + +export type LogsTableViewProps = { + logs: ILog[]; + fields: IField[]; + linesPerRow: number; + onClickExpand?: (log: ILog) => void; +}; + +export type UseTableViewResult = { + columns: ColumnsType>; + dataSource: Record[]; +}; + +export type UseTableViewProps = { + appendTo?: 'center' | 'end'; + onOpenLogsContext?: (log: ILog) => void; + onClickExpand?: (log: ILog) => void; + activeLog?: ILog | null; + activeLogIndex?: number; + activeContextLog?: ILog | null; + isListViewPanel?: boolean; +} & LogsTableViewProps; + +export type ActionsColumnProps = { + logId: string; + logs: ILog[]; + onOpenLogsContext?: (log: ILog) => void; +}; diff --git a/frontend/src/components/Logs/TableView/useTableView.styles.scss b/frontend/src/components/Logs/TableView/useTableView.styles.scss new file mode 100644 index 0000000..3723ecc --- /dev/null +++ b/frontend/src/components/Logs/TableView/useTableView.styles.scss @@ -0,0 +1,27 @@ +.text { + color: var(--bg-vanilla-400); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 18px; /* 128.571% */ + letter-spacing: -0.07px; +} + +.table-timestamp { + display: flex; + align-items: center; + + .ant-typography { + margin-bottom: 0; + } + + .log-state-indicator { + padding: 0px; + } +} + +.lightMode { + .text { + color: var(--bg-slate-400); + } +} diff --git a/frontend/src/components/Logs/TableView/useTableView.tsx b/frontend/src/components/Logs/TableView/useTableView.tsx new file mode 100644 index 0000000..259e046 --- /dev/null +++ b/frontend/src/components/Logs/TableView/useTableView.tsx @@ -0,0 +1,131 @@ +import './useTableView.styles.scss'; + +import Convert from 'ansi-to-html'; +import { Typography } from 'antd'; +import { ColumnsType } from 'antd/es/table'; +import dayjs from 'dayjs'; +import dompurify from 'dompurify'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { FlatLogData } from 'lib/logs/flatLogData'; +import { useMemo } from 'react'; + +import LogStateIndicator from '../LogStateIndicator/LogStateIndicator'; +import { getLogIndicatorTypeForTable } from '../LogStateIndicator/utils'; +import { + defaultListViewPanelStyle, + defaultTableStyle, + getDefaultCellStyle, +} from './config'; +import { TableBodyContent } from './styles'; +import { + ColumnTypeRender, + UseTableViewProps, + UseTableViewResult, +} from './types'; + +const convert = new Convert(); + +export const useTableView = (props: UseTableViewProps): UseTableViewResult => { + const { + logs, + fields, + linesPerRow, + appendTo = 'center', + activeContextLog, + activeLog, + isListViewPanel, + } = props; + + const isDarkMode = useIsDarkMode(); + + const flattenLogData = useMemo(() => logs.map((log) => FlatLogData(log)), [ + logs, + ]); + + const columns: ColumnsType> = useMemo(() => { + const fieldColumns: ColumnsType> = fields + .filter((e) => e.name !== 'id') + .map(({ name }) => ({ + title: name, + dataIndex: name, + key: name, + render: (field): ColumnTypeRender> => ({ + props: { + style: isListViewPanel + ? defaultListViewPanelStyle + : getDefaultCellStyle(isDarkMode), + }, + children: ( + + {field} + + ), + }), + })); + + if (isListViewPanel) { + return [...fieldColumns]; + } + + return [ + { + title: 'timestamp', + dataIndex: 'timestamp', + key: 'timestamp', + // https://github.com/ant-design/ant-design/discussions/36886 + render: (field, item): ColumnTypeRender> => { + const date = + typeof field === 'string' + ? dayjs(field).format() + : dayjs(field / 1e6).format(); + return { + children: ( +
    + + + {date} + +
    + ), + }; + }, + }, + ...(appendTo === 'center' ? fieldColumns : []), + { + title: 'body', + dataIndex: 'body', + key: 'body', + render: (field): ColumnTypeRender> => ({ + props: { + style: defaultTableStyle, + }, + children: ( + + ), + }), + }, + ...(appendTo === 'end' ? fieldColumns : []), + ]; + }, [ + fields, + isListViewPanel, + appendTo, + isDarkMode, + linesPerRow, + activeLog?.id, + activeContextLog?.id, + ]); + + return { columns, dataSource: flattenLogData }; +}; diff --git a/frontend/src/components/Logs/styles.ts b/frontend/src/components/Logs/styles.ts new file mode 100644 index 0000000..53f3119 --- /dev/null +++ b/frontend/src/components/Logs/styles.ts @@ -0,0 +1,8 @@ +import { Button } from 'antd'; +import styled from 'styled-components'; + +export const ButtonContainer = styled(Button)` + &&& { + padding-left: 0; + } +`; diff --git a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss new file mode 100644 index 0000000..efd668f --- /dev/null +++ b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss @@ -0,0 +1,396 @@ +.nested-menu-container { + z-index: 2; + position: absolute; + right: -2px; + margin: 6px 0; + width: 160px; + + border-radius: 4px; + + border: 1px solid var(--bg-slate-400, #1d212d); + background: linear-gradient( + 139deg, + rgba(18, 19, 23, 0.8) 0%, + rgba(18, 19, 23, 0.9) 98.68% + ); + + box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2); + backdrop-filter: blur(20px); + + .menu-container { + padding: 12px; + + .title { + font-family: Inter; + font-size: 11px; + font-weight: 600; + line-height: 18px; + letter-spacing: 0.08em; + text-align: left; + color: var(--bg-slate-200, #52575c); + } + + .menu-items { + display: flex; + gap: 8px; + flex-direction: column; + margin-top: 12px; + } + + .item { + font-family: Inter; + font-size: 13px; + font-weight: 400; + line-height: 17px; + letter-spacing: 0.01em; + text-align: left; + + .item-label { + display: flex; + color: var(--bg-vanilla-400, #c0c1c3); + justify-content: space-between; + align-items: center; + } + + cursor: pointer; + } + } + + .horizontal-line { + height: 1px; + background: #1d212d; + } + + .max-lines-per-row { + padding: 12px; + + .title { + color: var(--bg-slate-200, #52575c); + font-family: Inter; + font-size: 11px; + font-style: normal; + font-weight: 600; + line-height: 18px; /* 163.636% */ + letter-spacing: 0.88px; + text-transform: uppercase; + + margin-bottom: 12px; + + display: flex; + justify-content: space-between; + align-items: center; + + .lucide { + color: var(--bg-vanilla-400, #c0c1c3); + cursor: pointer; + } + } + + .max-lines-per-row-input { + display: flex; + + .ant-input-number-handler-wrap { + display: none; + } + + .ant-input-number { + min-width: 36px; + width: auto; + border-right: none; + border-left: none; + border-top: 1px solid var(--bg-slate-400); + border-bottom: 1px solid var(--bg-slate-400); + text-align: center; + height: 26px; + border-radius: 0; + + &:active, + &:focus { + border: none; + box-shadow: none; + } + } + + .ant-input-number-focused { + box-shadow: none !important; + } + + .ant-input-number-input-wrap { + input { + text-align: center; + font-size: 13px; + + &:active, + &:focus { + border: none; + } + } + + &:active, + &:focus { + border: none; + } + } + + .periscope-btn { + box-shadow: none; + padding: 6px 12px; + height: 26px; + + border-radius: 0px 1px 1px 0px; + background: var(--bg-ink-300, #16181d); + } + } + } + + .selected-item-content-container { + .add-new-column-header { + padding: 8px; + } + + .title { + color: var(--bg-slate-200, #52575c); + font-family: Inter; + font-size: 11px; + font-style: normal; + font-weight: 600; + line-height: 18px; /* 163.636% */ + letter-spacing: 0.88px; + text-transform: uppercase; + + margin-bottom: 12px; + + display: flex; + justify-content: space-between; + align-items: center; + + .lucide { + color: var(--bg-vanilla-400, #c0c1c3); + cursor: pointer; + } + } + + .horizontal-line { + height: 1px; + background: #1d212d; + } + + .loading-container { + margin: 12px 0; + } + + .item-content { + padding: 12px; + + .column-format, + .column-format-new-options { + display: flex; + gap: 12px; + flex-direction: column; + margin-top: 12px; + + .column-name { + color: var(--bg-vanilla-400, #c0c1c3); + font-family: Inter; + font-size: 13px; + font-style: normal; + font-weight: 400; + line-height: 20px; /* 142.857% */ + letter-spacing: -0.07px; + + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; + + cursor: pointer; + + .name { + flex: 1; + overflow: hidden; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + cursor: pointer; + } + + .delete-btn { + display: none; + flex: 0 0 16px; + cursor: pointer; + } + + &:hover { + .delete-btn { + display: block; + } + } + } + + overflow-x: hidden; + + &::-webkit-scrollbar { + height: 1rem; + width: 0.2rem; + } + } + + .column-format { + max-height: 150px; + overflow: auto; + overflow-x: hidden; + } + + .column-format-new-options { + max-height: 150px; + overflow-y: auto; + overflow-x: hidden; + } + + .column-divider { + margin: 12px 0; + border-top: 2px solid var(--bg-slate-400); + } + } + } + + &.active { + .nested-menu-container { + backdrop-filter: blur(18px); + + .item { + .item-label { + color: var(--bg-vanilla-400); + } + } + } + + .selected-item-content-container { + width: 110%; + margin-left: -5%; + + border-radius: 4px; + border: 1px solid var(--bg-slate-400); + background: linear-gradient( + 139deg, + rgba(18, 19, 23, 0.8) 0%, + rgba(18, 19, 23, 0.9) 98.68% + ); + + box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2); + backdrop-filter: blur(20px); + + .column-format { + margin-top: 0px; + } + } + } +} + +.lightMode { + .nested-menu-container { + border: 1px solid var(--bg-vanilla-300); + background: linear-gradient( + 139deg, + rgba(255, 255, 255, 0.8) 0%, + rgba(255, 255, 255, 0.9) 98.68% + ); + + box-shadow: 4px 10px 16px 2px rgba(255, 255, 255, 0.2); + + .horizontal-line { + background: var(--bg-vanilla-300); + } + + .item-content { + .column-divider { + border-top: 2px solid var(--bg-vanilla-300); + } + } + + .max-lines-per-row { + .title { + color: var(--bg-ink-200); + + .lucide { + color: var(--bg-ink-300); + } + } + + .max-lines-per-row-input { + border: 1px solid var(--bg-vanilla-300); + + .periscope-btn { + background: var(--bg-vanilla-300); + } + } + } + + .menu-container { + .title { + color: var(--bg-ink-200); + } + + .item { + .item-label { + color: var(--bg-ink-400); + } + } + } + + .selected-item-content-container { + .title { + color: var(--bg-ink-200); + + .lucide { + color: var(--bg-ink-300); + } + } + + .horizontal-line { + background: var(--bg-vanilla-300); + } + + .item-content { + .max-lines-per-row-input { + border: 1px solid var(--bg-vanilla-300); + + .periscope-btn { + background: var(--bg-vanilla-300); + } + } + + .column-format, + .column-format-new-options { + .column-name { + color: var(--bg-ink-300); + } + } + } + } + + &.active { + .nested-menu-container { + backdrop-filter: blur(18px); + + .item { + .item-label { + color: var(--bg-ink-300); + } + } + } + + .selected-item-content-container { + border: 1px solid var(--bg-vanilla-300); + background: linear-gradient( + 139deg, + rgba(255, 255, 255, 0.8) 0%, + rgba(255, 255, 255, 0.9) 98.68% + ); + + box-shadow: 4px 10px 16px 2px rgba(255, 255, 255, 0.2); + } + } + } +} diff --git a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx new file mode 100644 index 0000000..558a97c --- /dev/null +++ b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx @@ -0,0 +1,244 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +import './LogsFormatOptionsMenu.styles.scss'; + +import { Divider, Input, InputNumber, Tooltip } from 'antd'; +import cx from 'classnames'; +import { LogViewMode } from 'container/LogsTable'; +import { OptionsMenuConfig } from 'container/OptionsMenu/types'; +import useDebouncedFn from 'hooks/useDebouncedFunction'; +import { Check, Minus, Plus, X } from 'lucide-react'; +import { useCallback, useEffect, useState } from 'react'; + +interface LogsFormatOptionsMenuProps { + title: string; + items: any; + selectedOptionFormat: any; + config: OptionsMenuConfig; +} + +export default function LogsFormatOptionsMenu({ + title, + items, + selectedOptionFormat, + config, +}: LogsFormatOptionsMenuProps): JSX.Element { + const { maxLines, format, addColumn } = config; + const [selectedItem, setSelectedItem] = useState(selectedOptionFormat); + const maxLinesNumber = (maxLines?.value as number) || 1; + const [maxLinesPerRow, setMaxLinesPerRow] = useState(maxLinesNumber); + + const [addNewColumn, setAddNewColumn] = useState(false); + + const onChange = useCallback( + (key: LogViewMode) => { + if (!format) return; + + format.onChange(key); + }, + [format], + ); + + const handleMenuItemClick = (key: LogViewMode): void => { + setSelectedItem(key); + onChange(key); + setAddNewColumn(false); + }; + + const incrementMaxLinesPerRow = (): void => { + if (maxLinesPerRow < 10) { + setMaxLinesPerRow(maxLinesPerRow + 1); + } + }; + + const decrementMaxLinesPerRow = (): void => { + if (maxLinesPerRow > 1) { + setMaxLinesPerRow(maxLinesPerRow - 1); + } + }; + + const handleSearchValueChange = useDebouncedFn((event): void => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const value = event?.target?.value || ''; + + if (addColumn && addColumn?.onSearch) { + addColumn?.onSearch(value); + } + }, 300); + + const handleToggleAddNewColumn = (): void => { + setAddNewColumn(!addNewColumn); + }; + + const handleLinesPerRowChange = (maxLinesPerRow: number | null): void => { + if ( + maxLinesPerRow && + Number.isInteger(maxLinesNumber) && + maxLinesPerRow > 1 + ) { + setMaxLinesPerRow(maxLinesPerRow); + } + }; + + useEffect(() => { + if (maxLinesPerRow && config && config.maxLines?.onChange) { + config.maxLines.onChange(maxLinesPerRow); + } + }, [maxLinesPerRow]); + + return ( +
    { + // this is to restrict click events to propogate to parent + event.stopPropagation(); + }} + > +
    +
    {title}
    + +
    + {items.map( + (item: any): JSX.Element => ( +
    handleMenuItemClick(item.key)} + > +
    + {item.label} + + {selectedItem === item.key && } +
    +
    + ), + )} +
    +
    + + {selectedItem && ( + <> + {selectedItem === 'raw' && ( + <> +
    +
    +
    max lines per row
    +
    + + + +
    +
    + + )} + +
    + {!addNewColumn &&
    } + + {addNewColumn && ( +
    +
    + {' '} + columns + {' '} +
    + + +
    + )} + +
    + {!addNewColumn && ( +
    + columns + {' '} +
    + )} + +
    + {addColumn?.value?.map(({ key, id }) => ( +
    +
    + + {key} + +
    + addColumn.onRemove(id as string)} + /> +
    + ))} +
    + + {addColumn?.isFetching && ( +
    Loading ...
    + )} + + {addNewColumn && + addColumn && + addColumn.value.length > 0 && + addColumn.options && + addColumn?.options?.length > 0 && ( + + )} + + {addNewColumn && ( +
    + {addColumn?.options?.map(({ label, value }) => ( +
    { + eve.stopPropagation(); + + if (addColumn && addColumn?.onSelect) { + addColumn?.onSelect(value, { label, disabled: false }); + } + }} + > +
    + + {label} + +
    +
    + ))} +
    + )} +
    +
    + + )} +
    + ); +} diff --git a/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.scss b/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.scss new file mode 100644 index 0000000..2d2166f --- /dev/null +++ b/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.scss @@ -0,0 +1,36 @@ +.code-snippet-container { + position: relative; + // background-color: rgb(43, 43, 43); + background-color: #111a2c; + border-color: #111a2c; +} + +.code-copy-btn { + position: absolute; + top: 8px; + right: 8px; + display: flex; + justify-content: flex-end; + align-items: center; + + button { + cursor: pointer; + + background-color: rgba($color: #1d1d1d, $alpha: 0.7); + color: white; + border: none; + padding: 8px; + border-radius: 3px; + transition: all 0.1s; + + &:hover { + background-color: rgba($color: #1d1d1d, $alpha: 1); + } + } + + &.copied { + button { + background-color: rgba($color: #52c41a, $alpha: 1); + } + } +} diff --git a/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx b/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx new file mode 100644 index 0000000..e23882c --- /dev/null +++ b/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx @@ -0,0 +1,32 @@ +import './CodeCopyBtn.scss'; + +import { CheckOutlined, CopyOutlined } from '@ant-design/icons'; +import cx from 'classnames'; +import { useState } from 'react'; + +export default function CodeCopyBtn({ + children, +}: { + children: React.ReactNode; +}): JSX.Element { + const [isSnippetCopied, setIsSnippetCopied] = useState(false); + + const handleClick = (): void => { + if (children && Array.isArray(children)) { + setIsSnippetCopied(true); + navigator.clipboard.writeText(children[0].props.children[0]).finally(() => { + setTimeout(() => { + setIsSnippetCopied(false); + }, 1000); + }); + } + }; + + return ( +
    + +
    + ); +} diff --git a/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx b/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx new file mode 100644 index 0000000..20be067 --- /dev/null +++ b/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx @@ -0,0 +1,109 @@ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable react/jsx-props-no-spreading */ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ + +import ReactMarkdown from 'react-markdown'; +import { CodeProps } from 'react-markdown/lib/ast-to-react'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { a11yDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; +import rehypeRaw from 'rehype-raw'; + +import CodeCopyBtn from './CodeCopyBtn/CodeCopyBtn'; + +interface LinkProps { + href: string; + children: React.ReactElement; +} + +function Pre({ children }: { children: React.ReactNode }): JSX.Element { + return ( +
    +			{children}
    +			{children}
    +		
    + ); +} + +function Code({ + node, + inline, + className = 'blog-code', + children, + ...props +}: CodeProps): JSX.Element { + const match = /language-(\w+)/.exec(className || ''); + return !inline && match ? ( + + {String(children).replace(/\n$/, '')} + + ) : ( + + {children} + + ); +} + +function Link({ href, children }: LinkProps): JSX.Element { + return ( + + {children} + + ); +} + +const interpolateMarkdown = ( + markdownContent: any, + variables: { [s: string]: unknown } | ArrayLike, +) => { + let interpolatedContent = markdownContent; + + const variableEntries = Object.entries(variables); + + // Loop through variables and replace placeholders with values + for (const [key, value] of variableEntries) { + const placeholder = `{{${key}}}`; + const regex = new RegExp(placeholder, 'g'); + interpolatedContent = interpolatedContent.replace(regex, value); + } + + return interpolatedContent; +}; + +function CustomTag({ color }: { color: string }): JSX.Element { + return

    This is custom element

    ; +} + +function MarkdownRenderer({ + markdownContent, + variables, +}: { + markdownContent: any; + variables: any; +}): JSX.Element { + const interpolatedMarkdown = interpolateMarkdown(markdownContent, variables); + + return ( + + {interpolatedMarkdown} + + ); +} + +export { Code, Link, MarkdownRenderer, Pre }; diff --git a/frontend/src/components/MessageTip/MessageTip.test.tsx b/frontend/src/components/MessageTip/MessageTip.test.tsx new file mode 100644 index 0000000..1c050c0 --- /dev/null +++ b/frontend/src/components/MessageTip/MessageTip.test.tsx @@ -0,0 +1,47 @@ +import { render, screen } from '@testing-library/react'; + +import MessageTip from './index'; + +describe('MessageTip', () => { + it('should not render when show prop is false', () => { + render( + Close} + />, + ); + + const messageTip = screen.queryByRole('alert'); + + expect(messageTip).toBeNull(); + }); + + it('should render with the provided message and action', () => { + const message = 'Test Message'; + const action = ; + + render(); + + const messageTip = screen.getByRole('alert'); + const messageText = screen.getByText(message); + const actionButton = screen.getByRole('button', { name: 'Close' }); + + expect(messageTip).toBeInTheDocument(); + expect(messageText).toBeInTheDocument(); + expect(actionButton).toBeInTheDocument(); + }); + + // taken from antd docs + // https://github.com/ant-design/ant-design/blob/master/components/alert/__tests__/index.test.tsx + it('custom action', () => { + const { container } = render( + Close} + />, + ); + expect(container.firstChild).toMatchSnapshot(); + }); +}); diff --git a/frontend/src/components/MessageTip/__snapshots__/MessageTip.test.tsx.snap b/frontend/src/components/MessageTip/__snapshots__/MessageTip.test.tsx.snap new file mode 100644 index 0000000..8d671bb --- /dev/null +++ b/frontend/src/components/MessageTip/__snapshots__/MessageTip.test.tsx.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageTip custom action 1`] = ` +.c0 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + + +`; diff --git a/frontend/src/components/MessageTip/index.tsx b/frontend/src/components/MessageTip/index.tsx new file mode 100644 index 0000000..23c7403 --- /dev/null +++ b/frontend/src/components/MessageTip/index.tsx @@ -0,0 +1,27 @@ +import { ReactNode } from 'react'; + +import { StyledAlert } from './styles'; + +interface MessageTipProps { + show?: boolean; + message: ReactNode | string; + action: ReactNode | undefined; +} + +function MessageTip({ + show, + message, + action, +}: MessageTipProps): JSX.Element | null { + if (!show) return null; + + return ( + + ); +} + +MessageTip.defaultProps = { + show: false, +}; + +export default MessageTip; diff --git a/frontend/src/components/MessageTip/styles.ts b/frontend/src/components/MessageTip/styles.ts new file mode 100644 index 0000000..2bf5740 --- /dev/null +++ b/frontend/src/components/MessageTip/styles.ts @@ -0,0 +1,6 @@ +import { Alert } from 'antd'; +import styled from 'styled-components'; + +export const StyledAlert = styled(Alert)` + align-items: center; +`; diff --git a/frontend/src/components/Modal.tsx b/frontend/src/components/Modal.tsx new file mode 100644 index 0000000..a8f8bb6 --- /dev/null +++ b/frontend/src/components/Modal.tsx @@ -0,0 +1,36 @@ +import { Modal, ModalProps as Props } from 'antd'; +import { ReactElement } from 'react'; + +function CustomModal({ + title, + children, + isModalVisible, + footer, + closable = true, +}: ModalProps): JSX.Element { + return ( + + {children} + + ); +} + +interface ModalProps { + isModalVisible: boolean; + closable?: boolean; + footer?: Props['footer']; + title: string; + children: ReactElement; +} + +CustomModal.defaultProps = { + closable: undefined, + footer: undefined, +}; + +export default CustomModal; diff --git a/frontend/src/components/NotFound/NotFound.test.tsx b/frontend/src/components/NotFound/NotFound.test.tsx new file mode 100644 index 0000000..f72596e --- /dev/null +++ b/frontend/src/components/NotFound/NotFound.test.tsx @@ -0,0 +1,19 @@ +import { render } from '@testing-library/react'; +import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router-dom'; +import store from 'store'; + +import NotFound from './index'; + +describe('Not Found page test', () => { + it('should render Not Found page without errors', () => { + const { asFragment } = render( + + + + + , + ); + expect(asFragment()).toMatchSnapshot(); + }); +}); diff --git a/frontend/src/components/NotFound/__snapshots__/NotFound.test.tsx.snap b/frontend/src/components/NotFound/__snapshots__/NotFound.test.tsx.snap new file mode 100644 index 0000000..5415d86 --- /dev/null +++ b/frontend/src/components/NotFound/__snapshots__/NotFound.test.tsx.snap @@ -0,0 +1,130 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Not Found page test should render Not Found page without errors 1`] = ` + + .c3 { + border: 2px solid #2f80ed; + box-sizing: border-box; + border-radius: 10px; + width: 400px; + background: inherit; + font-style: normal; + font-weight: normal; + font-size: 24px; + line-height: 20px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + padding-top: 14px; + padding-bottom: 14px; + color: #2f80ed; +} + +.c0 { + min-height: 80vh; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c2 { + font-style: normal; + font-weight: 300; + font-size: 18px; + line-height: 20px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + text-align: center; + color: #828282; + text-align: center; + margin: 0; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c1 { + min-height: 50px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + margin-bottom: 30px; + margin-top: 20px; +} + +
    + not-found +
    +

    + Ah, seems like we reached a dead end! +

    +

    + Page Not Found +

    +
    + + Return To Services Page + +
    +
    +`; diff --git a/frontend/src/components/NotFound/constant.ts b/frontend/src/components/NotFound/constant.ts new file mode 100644 index 0000000..b23534f --- /dev/null +++ b/frontend/src/components/NotFound/constant.ts @@ -0,0 +1 @@ +export const defaultText = 'Ah, seems like we reached a dead end!'; diff --git a/frontend/src/components/NotFound/index.tsx b/frontend/src/components/NotFound/index.tsx new file mode 100644 index 0000000..5af3c46 --- /dev/null +++ b/frontend/src/components/NotFound/index.tsx @@ -0,0 +1,53 @@ +import getLocalStorageKey from 'api/browser/localstorage/get'; +import NotFoundImage from 'assets/NotFound'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import ROUTES from 'constants/routes'; +import { useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import { Dispatch } from 'redux'; +import AppActions from 'types/actions'; +import { LOGGED_IN } from 'types/actions/app'; + +import { defaultText } from './constant'; +import { Button, Container, Text, TextContainer } from './styles'; + +function NotFound({ text = defaultText }: Props): JSX.Element { + const dispatch = useDispatch>(); + const isLoggedIn = getLocalStorageKey(LOCALSTORAGE.IS_LOGGED_IN); + + const onClickHandler = useCallback(() => { + if (isLoggedIn) { + dispatch({ + type: LOGGED_IN, + payload: { + isLoggedIn: true, + }, + }); + } + }, [dispatch, isLoggedIn]); + + return ( + + + + + {text} + Page Not Found + + + + + ); +} + +interface Props { + text?: string; +} + +NotFound.defaultProps = { + text: defaultText, +}; + +export default NotFound; diff --git a/frontend/src/components/NotFound/styles.ts b/frontend/src/components/NotFound/styles.ts new file mode 100644 index 0000000..812fba7 --- /dev/null +++ b/frontend/src/components/NotFound/styles.ts @@ -0,0 +1,60 @@ +import { Link } from 'react-router-dom'; +import styled from 'styled-components'; + +export const Button = styled(Link)` + border: 2px solid #2f80ed; + box-sizing: border-box; + border-radius: 10px; + width: 400px; + + background: inherit; + + font-style: normal; + font-weight: normal; + font-size: 24px; + line-height: 20px; + + display: flex; + align-items: center; + justify-content: center; + padding-top: 14px; + padding-bottom: 14px; + + color: #2f80ed; +`; + +export const Container = styled.div` + min-height: 80vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +`; + +export const Text = styled.p` + font-style: normal; + font-weight: 300; + font-size: 18px; + line-height: 20px; + + display: flex; + align-items: center; + text-align: center; + + color: #828282; + text-align: center; + margin: 0; + + display: flex; + justify-content: center; + align-items: center; +`; + +export const TextContainer = styled.div` + min-height: 50px; + display: flex; + justify-content: space-between; + flex-direction: column; + margin-bottom: 30px; + margin-top: 20px; +`; diff --git a/frontend/src/components/ReleaseNote/ReleaseNoteProps.ts b/frontend/src/components/ReleaseNote/ReleaseNoteProps.ts new file mode 100644 index 0000000..f240759 --- /dev/null +++ b/frontend/src/components/ReleaseNote/ReleaseNoteProps.ts @@ -0,0 +1,4 @@ +export default interface ReleaseNoteProps { + path?: string; + release?: string; +} diff --git a/frontend/src/components/ReleaseNote/Releases/ReleaseNote0120.tsx b/frontend/src/components/ReleaseNote/Releases/ReleaseNote0120.tsx new file mode 100644 index 0000000..249147f --- /dev/null +++ b/frontend/src/components/ReleaseNote/Releases/ReleaseNote0120.tsx @@ -0,0 +1,73 @@ +import { Button, Space } from 'antd'; +import setFlags from 'api/user/setFlags'; +import MessageTip from 'components/MessageTip'; +import { useCallback } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Dispatch } from 'redux'; +import { AppState } from 'store/reducers'; +import AppActions from 'types/actions'; +import { UPDATE_USER_FLAG } from 'types/actions/app'; +import { UserFlags } from 'types/api/user/setFlags'; +import AppReducer from 'types/reducer/app'; + +import ReleaseNoteProps from '../ReleaseNoteProps'; + +export default function ReleaseNote0120({ + release, +}: ReleaseNoteProps): JSX.Element | null { + const { user } = useSelector((state) => state.app); + + const dispatch = useDispatch>(); + + const handleDontShow = useCallback(async (): Promise => { + const flags: UserFlags = { ReleaseNote0120Hide: 'Y' }; + + try { + dispatch({ + type: UPDATE_USER_FLAG, + payload: { + flags, + }, + }); + if (!user) { + // no user is set, so escape the routine + return; + } + + const response = await setFlags({ userId: user?.userId, flags }); + + if (response.statusCode !== 200) { + console.log('failed to complete do not show status', response.error); + } + } catch (e) { + // here we do not nothing as the cost of error is minor, + // the user can switch the do no show option again in the further. + console.log('unexpected error: failed to complete do not show status', e); + } + }, [dispatch, user]); + + return ( + + You are using {release} of SigNoz. We have introduced distributed setup in + v0.12.0 release. If you use or plan to use clickhouse queries in dashboard + or alerts, you might want to read about querying the new distributed tables{' '} + + here + +
    + } + action={ + + + + } + /> + ); +} diff --git a/frontend/src/components/ReleaseNote/index.tsx b/frontend/src/components/ReleaseNote/index.tsx new file mode 100644 index 0000000..bf788e6 --- /dev/null +++ b/frontend/src/components/ReleaseNote/index.tsx @@ -0,0 +1,66 @@ +import ReleaseNoteProps from 'components/ReleaseNote/ReleaseNoteProps'; +import ReleaseNote0120 from 'components/ReleaseNote/Releases/ReleaseNote0120'; +import ROUTES from 'constants/routes'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { UserFlags } from 'types/api/user/setFlags'; +import AppReducer from 'types/reducer/app'; + +interface ComponentMapType { + match: ( + path: string | undefined, + version: string, + userFlags: UserFlags | null, + ) => boolean; + component: ({ path, release }: ReleaseNoteProps) => JSX.Element | null; +} + +const allComponentMap: ComponentMapType[] = [ + { + match: ( + path: string | undefined, + version: string, + userFlags: UserFlags | null, + ): boolean => { + if (!path) { + return false; + } + const allowedPaths: string[] = [ + ROUTES.LIST_ALL_ALERT, + ROUTES.APPLICATION, + ROUTES.ALL_DASHBOARD, + ]; + + return ( + userFlags?.ReleaseNote0120Hide !== 'Y' && + allowedPaths.includes(path) && + version.startsWith('v0.12') + ); + }, + component: ReleaseNote0120, + }, +]; + +// ReleaseNote prints release specific warnings and notes that +// user needs to be aware of before using the upgraded version. +function ReleaseNote({ path }: ReleaseNoteProps): JSX.Element | null { + const { userFlags, currentVersion } = useSelector( + (state) => state.app, + ); + + const c = allComponentMap.find((item) => + item.match(path, currentVersion, userFlags), + ); + + if (!c) { + return null; + } + + return ; +} + +ReleaseNote.defaultProps = { + path: '', +}; + +export default ReleaseNote; diff --git a/frontend/src/components/ResizeTable/DynamicColumnTable.syles.scss b/frontend/src/components/ResizeTable/DynamicColumnTable.syles.scss new file mode 100644 index 0000000..31026f4 --- /dev/null +++ b/frontend/src/components/ResizeTable/DynamicColumnTable.syles.scss @@ -0,0 +1,31 @@ +.DynamicColumnTable { + display: flex; + flex-direction: column; + width: 100%; + + .dynamicColumnTable-button { + align-self: flex-end; + margin: 10px 0; + + &.filter-btn { + display: flex; + align-items: center; + justify-content: center; + } + } +} + +.dynamicColumnsTable-items { + display: flex; + width: 10.625rem; + justify-content: space-between; + align-items: center; +} + +@media (max-width: 768px) { + .dynamicColumnsTable-items { + flex-direction: column; + width: auto; + text-align: center; + } +} diff --git a/frontend/src/components/ResizeTable/DynamicColumnTable.tsx b/frontend/src/components/ResizeTable/DynamicColumnTable.tsx new file mode 100644 index 0000000..4015172 --- /dev/null +++ b/frontend/src/components/ResizeTable/DynamicColumnTable.tsx @@ -0,0 +1,113 @@ +/* eslint-disable react/jsx-props-no-spreading */ +import './DynamicColumnTable.syles.scss'; + +import { Button, Dropdown, MenuProps, Switch } from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import { SlidersHorizontal } from 'lucide-react'; +import { memo, useEffect, useState } from 'react'; +import { popupContainer } from 'utils/selectPopupContainer'; + +import ResizeTable from './ResizeTable'; +import { DynamicColumnTableProps } from './types'; +import { + getNewColumnData, + getVisibleColumns, + setVisibleColumns, +} from './utils'; + +function DynamicColumnTable({ + tablesource, + columns, + dynamicColumns, + onDragColumn, + ...restProps +}: DynamicColumnTableProps): JSX.Element { + const [columnsData, setColumnsData] = useState( + columns, + ); + + useEffect(() => { + setColumnsData(columns); + const visibleColumns = getVisibleColumns({ + tablesource, + columnsData: columns, + dynamicColumns, + }); + setColumnsData((prevColumns) => + prevColumns + ? [ + ...prevColumns.slice(0, prevColumns.length - 1), + ...visibleColumns, + prevColumns[prevColumns.length - 1], + ] + : undefined, + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [columns, dynamicColumns]); + + const onToggleHandler = (index: number) => ( + checked: boolean, + event: React.MouseEvent, + ): void => { + event.stopPropagation(); + setVisibleColumns({ + tablesource, + dynamicColumns, + index, + checked, + }); + setColumnsData((prevColumns) => + getNewColumnData({ + checked, + index, + prevColumns, + dynamicColumns, + }), + ); + }; + + const items: MenuProps['items'] = + dynamicColumns?.map((column, index) => ({ + label: ( +
    +
    {column.title?.toString()}
    + c.key === column.key) !== -1} + onChange={onToggleHandler(index)} + /> +
    + ), + key: index, + type: 'checkbox', + })) || []; + + return ( +
    + {dynamicColumns && ( + +
    + ); +} + +DynamicColumnTable.defaultProps = { + onDragColumn: undefined, +}; + +export default memo(DynamicColumnTable); diff --git a/frontend/src/components/ResizeTable/ResizableHeader.tsx b/frontend/src/components/ResizeTable/ResizableHeader.tsx new file mode 100644 index 0000000..8611a45 --- /dev/null +++ b/frontend/src/components/ResizeTable/ResizableHeader.tsx @@ -0,0 +1,44 @@ +import { SyntheticEvent, useMemo } from 'react'; +import { Resizable, ResizeCallbackData } from 'react-resizable'; + +import { enableUserSelectHack } from './config'; +import { SpanStyle } from './styles'; + +function ResizableHeader(props: ResizableHeaderProps): JSX.Element { + const { onResize, width, ...restProps } = props; + + const handle = useMemo( + () => ( + e.stopPropagation()} + /> + ), + [], + ); + + if (!width) { + // eslint-disable-next-line react/jsx-props-no-spreading + return
    + + + )} + + + + + + ); +} + +export default ExplorerControlPanel; diff --git a/frontend/src/container/ExplorerControlPanel/styles.ts b/frontend/src/container/ExplorerControlPanel/styles.ts new file mode 100644 index 0000000..0c9a799 --- /dev/null +++ b/frontend/src/container/ExplorerControlPanel/styles.ts @@ -0,0 +1,5 @@ +import styled from 'styled-components'; + +export const ContainerStyled = styled.div` + margin-bottom: 0.3rem; +`; diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptions.styles.scss b/frontend/src/container/ExplorerOptions/ExplorerOptions.styles.scss new file mode 100644 index 0000000..d76d18b --- /dev/null +++ b/frontend/src/container/ExplorerOptions/ExplorerOptions.styles.scss @@ -0,0 +1,321 @@ +.hide-update { + left: calc(50% - 41px) !important; +} +.explorer-update { + position: fixed; + bottom: 16px; + left: calc(50% - 225px); + display: flex; + align-items: center; + gap: 12px; + padding: 10px 12px; + border-radius: 50px; + border: 1px solid var(--bg-slate-400); + background: rgba(22, 24, 29, 0.6); + box-shadow: 4px 4px 16px 4px rgba(0, 0, 0, 0.25); + backdrop-filter: blur(20px); + + .action-icon { + display: flex; + justify-content: center; + align-items: center; + padding: 8px; + border-radius: 50px; + border: 1px solid var(--bg-slate-400); + background: var(--bg-slate-500); + cursor: pointer; + } + + .hidden { + display: none; + } + + .ant-divider { + margin: 0; + height: 28px; + border: 1px solid var(--bg-slate-400); + } +} + +.explorer-options { + display: flex; + gap: 16px; + padding: 10px 12px; + border-radius: 50px; + border: 1px solid var(--bg-slate-400); + background: rgba(22, 24, 29, 0.6); + box-shadow: 4px 4px 16px 4px rgba(0, 0, 0, 0.25); + backdrop-filter: blur(20px); + position: fixed; + bottom: 16px; + left: calc(50% + 240px); + transform: translate(calc(-50% - 120px), 0); + transition: left 0.2s linear; + + .ant-select-selector { + padding: 0 !important; + } + + hr { + border-color: #1d212d; + } + + .view-options, + .actions { + .hidden { + display: none; + } + + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + + button { + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + + border: 1px solid #1d2023; + color: #c0c1c3; + background-color: #161922; + + box-shadow: none !important; + + &.ant-btn-round { + padding-inline-start: 10px; + padding-inline-end: 8px; + font-weight: 500; + } + + &.ant-btn-round:disabled { + background-color: rgba(209, 209, 209, 0.074); + color: #5f5f5f; + } + } + + .ant-select-focused { + border-color: transparent !important; + + .ant-select-selector { + border-color: transparent !important; + box-shadow: none !important; + } + } + + .ant-select-selector { + border: transparent !important; + background-color: transparent !important; + + .ant-select-selection-placeholder { + margin-left: 12px; + } + } + } + .hidden { + display: none; + } +} + +.app-content { + &.collapsed { + .explorer-options { + left: calc(50% + 72px); + } + } +} + +.render-options { + display: flex; + align-items: center; + gap: 8px; + padding: 0 2px; + color: var(--bg-vanilla-400); + font-family: Inter; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 20px; /* 142.857% */ + letter-spacing: -0.07px; + + .dot { + margin-left: 6px; + min-height: 6px; + min-width: 6px; + border-radius: 50%; + backdrop-filter: blur(20px); + } +} + +.save-view-modal { + width: 384px !important; + .ant-modal-content { + padding: 0; + border-radius: 4px; + border: 1px solid var(--bg-slate-500); + background: var(--bg-ink-400); + box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2); + + .ant-modal-header { + padding: 16px; + background: var(--bg-ink-400); + border-bottom: 1px solid var(--bg-slate-500); + } + + .ant-modal-body { + padding: 12px 16px 0px 16px; + + .ant-typography { + color: var(--bg-vanilla-100); + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 20px; /* 142.857% */ + } + + .save-view-input { + margin-top: 8px; + display: flex; + gap: 8px; + } + + .ant-color-picker-trigger { + padding: 6px; + border-radius: 2px; + border: 1px solid var(--bg-slate-400); + background: var(--bg-ink-300); + width: 32px; + height: 32px; + + .ant-color-picker-color-block { + border-radius: 50px; + width: 16px; + height: 16px; + flex-shrink: 0; + + .ant-color-picker-color-block-inner { + display: flex; + justify-content: center; + align-items: center; + } + } + } + } + + .ant-modal-footer { + display: flex; + justify-content: flex-end; + padding: 16px 16px; + margin: 0; + + > button { + display: flex; + align-items: center; + border-radius: 2px; + background-color: var(--bg-robin-500) !important; + color: var(--bg-vanilla-100) !important; + font-family: Inter; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 24px; + } + } + } + .title { + color: var(--bg-vanilla-100); + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + } +} + +.lightMode { + .explorer-options { + border: 1px solid var(--bg-vanilla-300); + background: rgba(255, 255, 255, 0.8); + box-shadow: 4px 4px 16px 4px rgba(255, 255, 255, 0.55); + backdrop-filter: blur(20px); + + hr { + border-color: var(--bg-vanilla-300); + } + + .view-options, + .actions { + button { + border: 1px solid var(--bg-vanilla-300); + color: var(--bg-ink-200); + background-color: var(--bg-vanilla-300); + } + } + } + + .render-options { + color: var(--bg-ink-200); + } + + .explorer-update { + border: 1px solid var(--bg-vanilla-300); + background: transparent; + box-shadow: 4px 4px 16px 4px rgba(255, 255, 255, 0.55); + backdrop-filter: blur(20px); + + .action-icon { + border: 1px solid var(--bg-vanilla-300); + background: var(--bg-vanilla-300); + } + + .ant-divider { + border-color: var(--bg-vanilla-300); + } + } + + .ant-tooltip-arrow { + border-top-color: var(--bg-vanilla-300) !important; + } + + .ant-tooltip-inner { + background-color: var(--bg-vanilla-300); + color: var(--bg-ink-200); + } + + .save-view-modal { + .ant-modal-content { + background: var(--bg-vanilla-200); + border-color: var(--bg-vanilla-300); + + .ant-modal-header { + background: var(--bg-vanilla-200); + border-bottom: 1px solid var(--bg-vanilla-300); + } + + .ant-modal-body { + .ant-typography { + color: var(--bg-ink-200); + } + + .ant-color-picker-trigger { + border: 1px solid var(--bg-vanilla-300); + background: var(--bg-vanilla-100); + + .ant-color-picker-color-block { + .ant-color-picker-color-block-inner { + svg { + fill: var(--bg-ink-200); + } + } + } + } + } + } + + .title { + color: var(--bg-ink-200); + } + } +} diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx new file mode 100644 index 0000000..8322c69 --- /dev/null +++ b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx @@ -0,0 +1,434 @@ +import './ExplorerOptions.styles.scss'; + +import { Color } from '@signozhq/design-tokens'; +import { + Button, + ColorPicker, + Divider, + Input, + Modal, + RefSelectProps, + Select, + Tooltip, + Typography, +} from 'antd'; +import axios from 'axios'; +import cx from 'classnames'; +import { getViewDetailsUsingViewKey } from 'components/ExplorerCard/utils'; +import { SOMETHING_WENT_WRONG } from 'constants/api'; +import { QueryParams } from 'constants/query'; +import { PANEL_TYPES } from 'constants/queryBuilder'; +import ROUTES from 'constants/routes'; +import ExportPanelContainer from 'container/ExportPanel/ExportPanelContainer'; +import { useGetSearchQueryParam } from 'hooks/queryBuilder/useGetSearchQueryParam'; +import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; +import { useGetAllViews } from 'hooks/saveViews/useGetAllViews'; +import { useSaveView } from 'hooks/saveViews/useSaveView'; +import { useUpdateView } from 'hooks/saveViews/useUpdateView'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import useErrorNotification from 'hooks/useErrorNotification'; +import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange'; +import { useNotifications } from 'hooks/useNotifications'; +import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery'; +import { Check, ConciergeBell, Disc3, Plus, X, XCircle } from 'lucide-react'; +import { CSSProperties, useCallback, useMemo, useRef, useState } from 'react'; +import { useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import { AppState } from 'store/reducers'; +import { Dashboard } from 'types/api/dashboard/getAll'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; +import { DataSource } from 'types/common/queryBuilder'; +import AppReducer from 'types/reducer/app'; +import { USER_ROLES } from 'types/roles'; + +import { + DATASOURCE_VS_ROUTES, + generateRGBAFromHex, + getRandomColor, + saveNewViewHandler, +} from './utils'; + +const allowedRoles = [USER_ROLES.ADMIN, USER_ROLES.AUTHOR, USER_ROLES.EDITOR]; + +// eslint-disable-next-line sonarjs/cognitive-complexity +function ExplorerOptions({ + disabled, + isLoading, + onExport, + query, + sourcepage, +}: ExplorerOptionsProps): JSX.Element { + const [isExport, setIsExport] = useState(false); + const [isSaveModalOpen, setIsSaveModalOpen] = useState(false); + const [newViewName, setNewViewName] = useState(''); + const [color, setColor] = useState(Color.BG_SIENNA_500); + const { notifications } = useNotifications(); + const history = useHistory(); + const ref = useRef(null); + const isDarkMode = useIsDarkMode(); + + const onModalToggle = useCallback((value: boolean) => { + setIsExport(value); + }, []); + + const handleSaveViewModalToggle = (): void => { + setIsSaveModalOpen(!isSaveModalOpen); + }; + + const hideSaveViewModal = (): void => { + setIsSaveModalOpen(false); + }; + + const { role } = useSelector((state) => state.app); + + const onCreateAlertsHandler = useCallback(() => { + history.push( + `${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent( + JSON.stringify(query), + )}`, + ); + }, [history, query]); + + const onCancel = (value: boolean) => (): void => { + onModalToggle(value); + }; + + const onAddToDashboard = (): void => { + setIsExport(true); + }; + + const { + data: viewsData, + isLoading: viewsIsLoading, + error, + isRefetching, + refetch: refetchAllView, + } = useGetAllViews(sourcepage); + + const { + currentQuery, + panelType, + isStagedQueryUpdated, + redirectWithQueryBuilderData, + } = useQueryBuilder(); + + const compositeQuery = mapCompositeQueryFromQuery(currentQuery, panelType); + + const viewName = useGetSearchQueryParam(QueryParams.viewName) || ''; + const viewKey = useGetSearchQueryParam(QueryParams.viewKey) || ''; + + const extraData = viewsData?.data?.data?.find((view) => view.uuid === viewKey) + ?.extraData; + + const extraDataColor = extraData ? JSON.parse(extraData).color : ''; + const rgbaColor = generateRGBAFromHex( + extraDataColor || Color.BG_SIENNA_500, + 0.08, + ); + + const { + mutateAsync: updateViewAsync, + isLoading: isViewUpdating, + } = useUpdateView({ + compositeQuery, + viewKey, + extraData: extraData || JSON.stringify({ color: Color.BG_SIENNA_500 }), + sourcePage: sourcepage, + viewName, + }); + + const showErrorNotification = (err: Error): void => { + notifications.error({ + message: axios.isAxiosError(err) ? err.message : SOMETHING_WENT_WRONG, + }); + }; + + const onUpdateQueryHandler = (): void => { + const extraData = viewsData?.data?.data?.find((view) => view.uuid === viewKey) + ?.extraData; + updateViewAsync( + { + compositeQuery: mapCompositeQueryFromQuery(currentQuery, panelType), + viewKey, + extraData: extraData || JSON.stringify({ color: Color.BG_SIENNA_500 }), + sourcePage: sourcepage, + viewName, + }, + { + onSuccess: () => { + notifications.success({ + message: 'View Updated Successfully', + }); + refetchAllView(); + }, + onError: (err) => { + showErrorNotification(err); + }, + }, + ); + }; + + useErrorNotification(error); + + const { handleExplorerTabChange } = useHandleExplorerTabChange(); + + const onMenuItemSelectHandler = useCallback( + ({ key }: { key: string }): void => { + const currentViewDetails = getViewDetailsUsingViewKey( + key, + viewsData?.data?.data, + ); + if (!currentViewDetails) return; + const { + query, + name, + uuid, + panelType: currentPanelType, + } = currentViewDetails; + + handleExplorerTabChange(currentPanelType, { + query, + name, + uuid, + }); + }, + [viewsData, handleExplorerTabChange], + ); + + const handleSelect = ( + value: string, + option: { key: string; value: string }, + ): void => { + onMenuItemSelectHandler({ + key: option.key, + }); + if (ref.current) { + ref.current.blur(); + } + }; + + const handleClearSelect = (): void => { + history.replace(DATASOURCE_VS_ROUTES[sourcepage]); + }; + + const isQueryUpdated = isStagedQueryUpdated(viewsData?.data?.data, viewKey); + + const { + isLoading: isSaveViewLoading, + mutateAsync: saveViewAsync, + } = useSaveView({ + viewName: newViewName || '', + compositeQuery, + sourcePage: sourcepage, + extraData: JSON.stringify({ color }), + }); + + const onSaveHandler = (): void => { + saveNewViewHandler({ + compositeQuery, + handlePopOverClose: hideSaveViewModal, + extraData: JSON.stringify({ color }), + notifications, + panelType: panelType || PANEL_TYPES.LIST, + redirectWithQueryBuilderData, + refetchAllView, + saveViewAsync, + sourcePage: sourcepage, + viewName: newViewName, + setNewViewName, + }); + }; + + // TODO: Remove this and move this to scss file + const dropdownStyle: CSSProperties = useMemo( + () => ({ + borderRadius: '4px', + border: isDarkMode + ? `1px solid ${Color.BG_SLATE_400}` + : `1px solid ${Color.BG_VANILLA_300}`, + background: isDarkMode + ? 'linear-gradient(139deg, rgba(18, 19, 23, 0.80) 0%, rgba(18, 19, 23, 0.90) 98.68%)' + : 'linear-gradient(139deg, rgba(241, 241, 241, 0.8) 0%, rgba(241, 241, 241, 0.9) 98.68%)', + boxShadow: '4px 10px 16px 2px rgba(0, 0, 0, 0.20)', + backdropFilter: 'blur(20px)', + bottom: '74px', + width: '191px', + }), + [isDarkMode], + ); + + const isEditDeleteSupported = allowedRoles.includes(role as string); + + return ( + <> + {isQueryUpdated && ( +
    + +
    + )} +
    +
    + + showSearch + placeholder="Select a view" + loading={viewsIsLoading || isRefetching} + value={viewName || undefined} + onSelect={handleSelect} + style={{ + minWidth: 170, + }} + dropdownStyle={dropdownStyle} + className="views-dropdown" + allowClear={{ + clearIcon: , + }} + onClear={handleClearSelect} + ref={ref} + > + {viewsData?.data?.data?.map((view) => { + const extraData = + view.extraData !== '' ? JSON.parse(view.extraData) : ''; + let bgColor = getRandomColor(); + if (extraData !== '') { + bgColor = extraData.color; + } + return ( + +
    + {' '} + {view.name} +
    +
    + ); + })} + + + +
    + +
    + +
    + + + + + + + +
    +
    + + Save this view} + open={isSaveModalOpen} + closable + onCancel={hideSaveViewModal} + footer={[ + , + ]} + > + Label +
    + setColor(hex)} + /> + setNewViewName(e.target.value)} + /> +
    +
    + + + + + + ); +} + +export interface ExplorerOptionsProps { + isLoading?: boolean; + onExport: (dashboard: Dashboard | null) => void; + query: Query | null; + disabled: boolean; + sourcepage: DataSource; +} + +ExplorerOptions.defaultProps = { isLoading: false }; + +export default ExplorerOptions; diff --git a/frontend/src/container/ExplorerOptions/types.ts b/frontend/src/container/ExplorerOptions/types.ts new file mode 100644 index 0000000..398fe0d --- /dev/null +++ b/frontend/src/container/ExplorerOptions/types.ts @@ -0,0 +1,28 @@ +import { NotificationInstance } from 'antd/es/notification/interface'; +import { AxiosResponse } from 'axios'; +import { SaveViewWithNameProps } from 'components/ExplorerCard/types'; +import { PANEL_TYPES } from 'constants/queryBuilder'; +import { Dispatch, SetStateAction } from 'react'; +import { UseMutateAsyncFunction } from 'react-query'; +import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery'; +import { SaveViewPayloadProps, SaveViewProps } from 'types/api/saveViews/types'; +import { DataSource, QueryBuilderContextType } from 'types/common/queryBuilder'; + +export interface SaveNewViewHandlerProps { + viewName: string; + compositeQuery: ICompositeMetricQuery; + sourcePage: DataSource; + extraData: SaveViewProps['extraData']; + panelType: PANEL_TYPES | null; + notifications: NotificationInstance; + refetchAllView: SaveViewWithNameProps['refetchAllView']; + saveViewAsync: UseMutateAsyncFunction< + AxiosResponse, + Error, + SaveViewProps, + SaveViewPayloadProps + >; + handlePopOverClose: SaveViewWithNameProps['handlePopOverClose']; + redirectWithQueryBuilderData: QueryBuilderContextType['redirectWithQueryBuilderData']; + setNewViewName: Dispatch>; +} diff --git a/frontend/src/container/ExplorerOptions/utils.ts b/frontend/src/container/ExplorerOptions/utils.ts new file mode 100644 index 0000000..e3ac710 --- /dev/null +++ b/frontend/src/container/ExplorerOptions/utils.ts @@ -0,0 +1,69 @@ +import { Color } from '@signozhq/design-tokens'; +import { showErrorNotification } from 'components/ExplorerCard/utils'; +import { QueryParams } from 'constants/query'; +import ROUTES from 'constants/routes'; +import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi'; +import { DataSource } from 'types/common/queryBuilder'; + +import { SaveNewViewHandlerProps } from './types'; + +export const getRandomColor = (): Color => { + const colorKeys = Object.keys(Color) as (keyof typeof Color)[]; + const randomKey = colorKeys[Math.floor(Math.random() * colorKeys.length)]; + return Color[randomKey]; +}; + +export const DATASOURCE_VS_ROUTES: Record = { + [DataSource.METRICS]: '', + [DataSource.TRACES]: ROUTES.TRACES_EXPLORER, + [DataSource.LOGS]: ROUTES.LOGS_EXPLORER, +}; + +export const saveNewViewHandler = ({ + saveViewAsync, + refetchAllView, + notifications, + handlePopOverClose, + viewName, + compositeQuery, + sourcePage, + extraData, + redirectWithQueryBuilderData, + panelType, + setNewViewName, +}: SaveNewViewHandlerProps): void => { + saveViewAsync( + { + viewName, + compositeQuery, + sourcePage, + extraData, + }, + { + onSuccess: (data) => { + refetchAllView(); + redirectWithQueryBuilderData(mapQueryDataFromApi(compositeQuery), { + [QueryParams.panelTypes]: panelType, + [QueryParams.viewName]: viewName, + [QueryParams.viewKey]: data.data.data, + }); + notifications.success({ + message: 'View Saved Successfully', + }); + }, + onError: (err) => { + showErrorNotification(notifications, err); + }, + onSettled: () => { + handlePopOverClose(); + setNewViewName(''); + }, + }, + ); +}; + +export const generateRGBAFromHex = (hex: string, opacity: number): string => + `rgba(${parseInt(hex.slice(1, 3), 16)}, ${parseInt( + hex.slice(3, 5), + 16, + )}, ${parseInt(hex.slice(5, 7), 16)}, ${opacity})`; diff --git a/frontend/src/container/ExplorerOrderBy/index.tsx b/frontend/src/container/ExplorerOrderBy/index.tsx new file mode 100644 index 0000000..54747f5 --- /dev/null +++ b/frontend/src/container/ExplorerOrderBy/index.tsx @@ -0,0 +1,73 @@ +import { Select, Spin } from 'antd'; +import { OrderByFilterProps } from 'container/QueryBuilder/filters/OrderByFilter/OrderByFilter.interfaces'; +import { useOrderByFilter } from 'container/QueryBuilder/filters/OrderByFilter/useOrderByFilter'; +import { selectStyle } from 'container/QueryBuilder/filters/QueryBuilderSearch/config'; +import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys'; +import { memo, useMemo } from 'react'; +import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { StringOperators } from 'types/common/queryBuilder'; + +function ExplorerOrderBy({ query, onChange }: OrderByFilterProps): JSX.Element { + const { + debouncedSearchText, + selectedValue, + aggregationOptions, + generateOptions, + createOptions, + handleChange, + handleSearchKeys, + } = useOrderByFilter({ query, onChange }); + + const { data, isFetching } = useGetAggregateKeys( + { + aggregateAttribute: query.aggregateAttribute.key, + dataSource: query.dataSource, + aggregateOperator: query.aggregateOperator, + searchText: debouncedSearchText, + }, + { + keepPreviousData: true, + }, + ); + + const options = useMemo(() => { + const keysOptions = createOptions(data?.payload?.attributeKeys || []); + + const customOptions = createOptions([ + { key: 'timestamp', isColumn: true, type: '', dataType: DataTypes.EMPTY }, + ]); + + const baseOptions = [ + ...customOptions, + ...(query.aggregateOperator === StringOperators.NOOP + ? [] + : aggregationOptions), + ...keysOptions, + ]; + + return generateOptions(baseOptions); + }, [ + aggregationOptions, + createOptions, + data?.payload?.attributeKeys, + generateOptions, + query.aggregateOperator, + ]); + + return ( + + + + {/* +
    ; + } + + return ( + + {/* eslint-disable-next-line react/jsx-props-no-spreading */} + + + ); +} + +interface ResizableHeaderProps { + onResize: (e: SyntheticEvent, data: ResizeCallbackData) => void; + width: number; +} + +export default ResizableHeader; diff --git a/frontend/src/components/ResizeTable/ResizeTable.tsx b/frontend/src/components/ResizeTable/ResizeTable.tsx new file mode 100644 index 0000000..90cc588 --- /dev/null +++ b/frontend/src/components/ResizeTable/ResizeTable.tsx @@ -0,0 +1,89 @@ +/* eslint-disable react/jsx-props-no-spreading */ + +import { Table } from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import { dragColumnParams } from 'hooks/useDragColumns/configs'; +import { + SyntheticEvent, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; +import ReactDragListView from 'react-drag-listview'; +import { ResizeCallbackData } from 'react-resizable'; + +import ResizableHeader from './ResizableHeader'; +import { DragSpanStyle } from './styles'; +import { ResizeTableProps } from './types'; + +function ResizeTable({ + columns, + onDragColumn, + ...restProps +}: ResizeTableProps): JSX.Element { + const [columnsData, setColumns] = useState([]); + + const handleResize = useCallback( + (index: number) => ( + _e: SyntheticEvent, + { size }: ResizeCallbackData, + ): void => { + const newColumns = [...columnsData]; + newColumns[index] = { + ...newColumns[index], + width: size.width, + }; + setColumns(newColumns); + }, + [columnsData], + ); + + const mergedColumns = useMemo( + () => + columnsData.map((col, index) => ({ + ...col, + ...(onDragColumn && { + title: ( + + {col?.title?.toString() || ''} + + ), + }), + onHeaderCell: (column: ColumnsType[number]): unknown => ({ + width: column.width, + onResize: handleResize(index), + }), + })) as ColumnsType, + [columnsData, onDragColumn, handleResize], + ); + + const tableParams = useMemo( + () => ({ + ...restProps, + components: { header: { cell: ResizableHeader } }, + columns: mergedColumns, + }), + [mergedColumns, restProps], + ); + + useEffect(() => { + if (columns) { + setColumns(columns); + } + }, [columns]); + + return onDragColumn ? ( + + + + ) : ( +
    + ); +} + +ResizeTable.defaultProps = { + onDragColumn: undefined, +}; + +export default ResizeTable; diff --git a/frontend/src/components/ResizeTable/TableComponent/DateComponent.tsx b/frontend/src/components/ResizeTable/TableComponent/DateComponent.tsx new file mode 100644 index 0000000..87c5c57 --- /dev/null +++ b/frontend/src/components/ResizeTable/TableComponent/DateComponent.tsx @@ -0,0 +1,15 @@ +import { Typography } from 'antd'; + +import Time from './Time'; + +function DateComponent( + CreatedOrUpdateTime: string | number | Date, +): JSX.Element { + if (CreatedOrUpdateTime === null) { + return - ; + } + + return Creator + + + {APIKey?.createdByUser?.name?.substring(0, 1)} + + + {APIKey.createdByUser?.name} + +
    {APIKey.createdByUser?.email}
    + + + )} + + Created on + + {createdOn} + + + {updatedOn && ( + + Updated on + + {updatedOn} + + + )} + + + Expires on + + {expiresOn} + + + + ), + }, + ]; + + return ( +
    + + +
    +
    + + Last used + {formattedDateAndTime} +
    + + {!isExpired && expiresIn <= EXPIRATION_WITHIN_SEVEN_DAYS && ( +
    + Expires in {expiresIn} Days +
    + )} + + {isExpired && ( +
    + Expired +
    + )} +
    +
    + ); + }, + }, + ]; + + return ( +
    +
    +
    + Access Tokens + + Create and manage access tokens for the SigNoz API + +
    + +
    + } + value={searchValue} + onChange={handleSearch} + /> + + +
    + +
    + `${range[0]}-${range[1]} of ${total} tokens`, + }} + /> + + + {/* Delete Key Modal */} + Delete Token} + open={isDeleteModalOpen} + closable + afterClose={handleModalClose} + onCancel={hideDeleteViewModal} + destroyOnClose + footer={[ + , + , + ]} + > + + {t('delete_confirm_message', { + keyName: activeAPIKey?.name, + })} + + + + {/* Edit Key Modal */} + } + > + Cancel + , + , + ]} + > +
    + + + + + + + + +
    + Admin +
    +
    + +
    + Editor +
    +
    + +
    + Viewer +
    +
    +
    +
    +
    + +
    + + {/* Create New Key Modal */} + } + > + Copy token and close + , + ] + : [ + , + , + ] + } + > + {!showNewAPIKeyDetails && ( +
    + + + + + + + + +
    + Admin +
    +
    + +
    + Editor +
    +
    + +
    + Viewer +
    +
    +
    +
    +
    + + setSelectedKeys(e.target.value ? [e.target.value] : []) + + // Need to fix this logic, when the value in empty, it's setting undefined string as value + } + allowClear + defaultValue={getDefaultFilterValue( + filterKey, + getUpdatedServiceName, + getUpdatedExceptionType, + )} + onPressEnter={handleSearch(confirm, String(selectedKeys[0]), filterKey)} + /> + + + + ), + [getUpdatedExceptionType, getUpdatedServiceName, handleSearch], + ); + + const onExceptionTypeFilter: ColumnType['onFilter'] = useCallback( + (value: unknown, record: Exception): boolean => { + if (record.exceptionType && typeof value === 'string') { + return record.exceptionType.toLowerCase().includes(value.toLowerCase()); + } + return false; + }, + [], + ); + + const onApplicationTypeFilter = useCallback( + (value: unknown, record: Exception): boolean => { + if (record.serviceName && typeof value === 'string') { + return record.serviceName.toLowerCase().includes(value.toLowerCase()); + } + return false; + }, + [], + ); + + const getFilter = useCallback( + ( + onFilter: ColumnType['onFilter'], + placeholder: string, + filterKey: string, + ): ColumnType => ({ + onFilter, + filterIcon, + filterDropdown: ({ confirm, selectedKeys, setSelectedKeys }): JSX.Element => + filterDropdownWrapper({ + setSelectedKeys, + selectedKeys, + confirm, + placeholder, + filterKey, + }), + }), + [filterIcon, filterDropdownWrapper], + ); + + const columns: ColumnsType = [ + { + title: 'Exception Type', + width: 100, + dataIndex: 'exceptionType', + key: 'exceptionType', + ...getFilter(onExceptionTypeFilter, 'Search By Exception', 'exceptionType'), + render: (value, record): JSX.Element => ( + value}> + + {value} + + + ), + sorter: true, + defaultSortOrder: getDefaultOrder( + getUpdatedParams, + updatedOrder, + 'exceptionType', + ), + }, + { + title: 'Error Message', + dataIndex: 'exceptionMessage', + key: 'exceptionMessage', + width: 100, + render: (value): JSX.Element => ( + value}> + + {value} + + + ), + }, + { + title: 'Count', + width: 50, + dataIndex: 'exceptionCount', + key: 'exceptionCount', + sorter: true, + defaultSortOrder: getDefaultOrder( + getUpdatedParams, + updatedOrder, + 'exceptionCount', + ), + }, + { + title: 'Last Seen', + dataIndex: 'lastSeen', + width: 80, + key: 'lastSeen', + render: getDateValue, + sorter: true, + defaultSortOrder: getDefaultOrder( + getUpdatedParams, + updatedOrder, + 'lastSeen', + ), + }, + { + title: 'First Seen', + dataIndex: 'firstSeen', + width: 80, + key: 'firstSeen', + render: getDateValue, + sorter: true, + defaultSortOrder: getDefaultOrder( + getUpdatedParams, + updatedOrder, + 'firstSeen', + ), + }, + { + title: 'Application', + dataIndex: 'serviceName', + width: 100, + key: 'serviceName', + sorter: true, + defaultSortOrder: getDefaultOrder( + getUpdatedParams, + updatedOrder, + 'serviceName', + ), + ...getFilter( + onApplicationTypeFilter, + 'Search By Application', + 'serviceName', + ), + }, + ]; + + const onChangeHandler: TableProps['onChange'] = useCallback( + ( + paginations: TablePaginationConfig, + filters: Record, + sorter: SorterResult[] | SorterResult, + ) => { + if (!Array.isArray(sorter)) { + const { pageSize = 0, current = 0 } = paginations; + const { columnKey = '', order } = sorter; + const updatedOrder = order === 'ascend' ? 'ascending' : 'descending'; + const params = new URLSearchParams(window.location.search); + const { exceptionType, serviceName } = extractFilterValues(filters, { + serviceName: getFilterString(params.get(urlKey.serviceName)), + exceptionType: getFilterString(params.get(urlKey.exceptionType)), + }); + history.replace( + `${pathname}?${createQueryParams({ + order: updatedOrder, + offset: (current - 1) * pageSize, + orderParam: columnKey, + pageSize, + exceptionType, + serviceName, + })}`, + ); + } + }, + [pathname], + ); + + return ( + + ); +} + +export default AllErrors; diff --git a/frontend/src/container/AllError/types.ts b/frontend/src/container/AllError/types.ts new file mode 100644 index 0000000..81ce367 --- /dev/null +++ b/frontend/src/container/AllError/types.ts @@ -0,0 +1,9 @@ +import { FilterDropdownProps } from 'antd/es/table/interface'; + +export interface FilterDropdownExtendsProps { + placeholder: string; + filterKey: string; + confirm: FilterDropdownProps['confirm']; + setSelectedKeys: FilterDropdownProps['setSelectedKeys']; + selectedKeys: FilterDropdownProps['selectedKeys']; +} diff --git a/frontend/src/container/AllError/utils.test.ts b/frontend/src/container/AllError/utils.test.ts new file mode 100644 index 0000000..344d318 --- /dev/null +++ b/frontend/src/container/AllError/utils.test.ts @@ -0,0 +1,109 @@ +import { Order, OrderBy } from 'types/api/errors/getAll'; + +import { + getDefaultOrder, + getLimit, + getOffSet, + getOrder, + getOrderParams, + getUpdatePageSize, + isOrder, + isOrderParams, +} from './utils'; + +describe('Error utils', () => { + test('Valid OrderBy Params', () => { + expect(isOrderParams('serviceName')).toBe(true); + expect(isOrderParams('exceptionCount')).toBe(true); + expect(isOrderParams('lastSeen')).toBe(true); + expect(isOrderParams('firstSeen')).toBe(true); + expect(isOrderParams('exceptionType')).toBe(true); + }); + + test('Invalid OrderBy Params', () => { + expect(isOrderParams('invalid')).toBe(false); + expect(isOrderParams(null)).toBe(false); + expect(isOrderParams('')).toBe(false); + }); + + test('Valid Order', () => { + expect(isOrder('ascending')).toBe(true); + expect(isOrder('descending')).toBe(true); + }); + + test('Invalid Order', () => { + expect(isOrder('invalid')).toBe(false); + expect(isOrder(null)).toBe(false); + expect(isOrder('')).toBe(false); + }); + + test('Default Order', () => { + const OrderBy: OrderBy[] = [ + 'exceptionCount', + 'exceptionType', + 'firstSeen', + 'lastSeen', + 'serviceName', + ]; + + const order: Order[] = ['ascending', 'descending']; + + const ascOrd = order[0]; + const desOrd = order[1]; + + OrderBy.forEach((order) => { + expect(getDefaultOrder(order, ascOrd, order)).toBe('ascend'); + expect(getDefaultOrder(order, desOrd, order)).toBe('descend'); + }); + }); + + test('Limit', () => { + expect(getLimit(null)).toBe(10); + expect(getLimit('')).toBe(10); + expect(getLimit('0')).toBe(0); + expect(getLimit('1')).toBe(1); + expect(getLimit('10')).toBe(10); + expect(getLimit('11')).toBe(11); + expect(getLimit('100')).toBe(100); + expect(getLimit('101')).toBe(101); + }); + + test('Update Page Size', () => { + expect(getUpdatePageSize(null)).toBe(10); + expect(getUpdatePageSize('')).toBe(10); + expect(getUpdatePageSize('0')).toBe(0); + expect(getUpdatePageSize('1')).toBe(1); + expect(getUpdatePageSize('10')).toBe(10); + expect(getUpdatePageSize('11')).toBe(11); + expect(getUpdatePageSize('100')).toBe(100); + expect(getUpdatePageSize('101')).toBe(101); + }); + + test('Order Params', () => { + expect(getOrderParams(null)).toBe('serviceName'); + expect(getOrderParams('')).toBe('serviceName'); + expect(getOrderParams('serviceName')).toBe('serviceName'); + expect(getOrderParams('exceptionCount')).toBe('exceptionCount'); + expect(getOrderParams('lastSeen')).toBe('lastSeen'); + expect(getOrderParams('firstSeen')).toBe('firstSeen'); + expect(getOrderParams('exceptionType')).toBe('exceptionType'); + }); + + test('OffSet', () => { + expect(getOffSet(null)).toBe(0); + expect(getOffSet('')).toBe(0); + expect(getOffSet('0')).toBe(0); + expect(getOffSet('1')).toBe(1); + expect(getOffSet('10')).toBe(10); + expect(getOffSet('11')).toBe(11); + expect(getOffSet('100')).toBe(100); + expect(getOffSet('101')).toBe(101); + }); + + test('Order', () => { + expect(getOrder(null)).toBe('ascending'); + expect(getOrder('')).toBe('ascending'); + expect(getOrder('ascending')).toBe('ascending'); + expect(getOrder('descending')).toBe('descending'); + }); +}); diff --git a/frontend/src/container/AllError/utils.ts b/frontend/src/container/AllError/utils.ts new file mode 100644 index 0000000..c13cd8f --- /dev/null +++ b/frontend/src/container/AllError/utils.ts @@ -0,0 +1,184 @@ +import { FilterValue, SortOrder } from 'antd/lib/table/interface'; +import Timestamp from 'timestamp-nano'; +import { Order, OrderBy } from 'types/api/errors/getAll'; + +import { + DEFAULT_FILTER_VALUE, + EXCEPTION_TYPE_FILTER_NAME, + SERVICE_NAME_FILTER_NAME, +} from './constant'; + +export const isOrder = (order: string | null): order is Order => + !!(order === 'ascending' || order === 'descending'); + +export const urlKey = { + order: 'order', + offset: 'offset', + orderParam: 'orderParam', + pageSize: 'pageSize', + exceptionType: 'exceptionType', + serviceName: 'serviceName', +}; + +export const isOrderParams = (orderBy: string | null): orderBy is OrderBy => + !!( + orderBy === 'serviceName' || + orderBy === 'exceptionCount' || + orderBy === 'lastSeen' || + orderBy === 'firstSeen' || + orderBy === 'exceptionType' + ); + +export const getOrder = (order: string | null): Order => { + if (isOrder(order)) { + return order; + } + return 'ascending'; +}; + +export const getLimit = (limit: string | null): number => { + if (limit) { + return parseInt(limit, 10); + } + return 10; +}; + +export const getOffSet = (offset: string | null): number => { + if (offset && typeof offset === 'string') { + return parseInt(offset, 10); + } + return 0; +}; + +export const getOrderParams = (order: string | null): OrderBy => { + if (isOrderParams(order)) { + return order; + } + return 'serviceName'; +}; + +export const getDefaultOrder = ( + orderBy: OrderBy, + order: Order, + data: OrderBy, + // eslint-disable-next-line sonarjs/cognitive-complexity +): SortOrder | undefined => { + if (orderBy === 'exceptionType' && data === 'exceptionType') { + return order === 'ascending' ? 'ascend' : 'descend'; + } + if (orderBy === 'serviceName' && data === 'serviceName') { + return order === 'ascending' ? 'ascend' : 'descend'; + } + if (orderBy === 'exceptionCount' && data === 'exceptionCount') { + return order === 'ascending' ? 'ascend' : 'descend'; + } + if (orderBy === 'lastSeen' && data === 'lastSeen') { + return order === 'ascending' ? 'ascend' : 'descend'; + } + if (orderBy === 'firstSeen' && data === 'firstSeen') { + return order === 'ascending' ? 'ascend' : 'descend'; + } + return undefined; +}; + +export const getNanoSeconds = (date: string): string => + Math.floor(new Date(date).getTime() / 1e3).toString() + + String(Timestamp.fromString(date).getNano().toString()).padStart(9, '0'); + +export const getUpdatePageSize = (pageSize: string | null): number => { + if (pageSize) { + return parseInt(pageSize, 10); + } + return 10; +}; + +export const getFilterString = (filter: string | null): string => { + if (filter) { + return filter; + } + return ''; +}; + +export const getDefaultFilterValue = ( + filterKey: string | null, + serviceName: string, + exceptionType: string, +): string | undefined => { + let defaultValue: string | undefined; + switch (filterKey) { + case SERVICE_NAME_FILTER_NAME: + defaultValue = serviceName; + break; + case EXCEPTION_TYPE_FILTER_NAME: + defaultValue = exceptionType; + break; + default: + break; + } + return defaultValue; +}; + +export const getFilterValues = ( + serviceName: string, + exceptionType: string, + filterKey: string, + filterValue: string, +): { exceptionFilterValue: string; serviceFilterValue: string } => { + let serviceFilterValue = serviceName; + let exceptionFilterValue = exceptionType; + switch (filterKey) { + case EXCEPTION_TYPE_FILTER_NAME: + exceptionFilterValue = filterValue; + break; + case SERVICE_NAME_FILTER_NAME: + serviceFilterValue = filterValue; + break; + default: + break; + } + return { exceptionFilterValue, serviceFilterValue }; +}; + +type FilterValues = { exceptionType: string; serviceName: string }; + +const extractSingleFilterValue = ( + filterName: string, + filters: Filter, +): string => { + const filterValues = filters[filterName]; + + if ( + !filterValues || + !Array.isArray(filterValues) || + filterValues.length === 0 + ) { + return DEFAULT_FILTER_VALUE; + } + + return String(filterValues[0]); +}; + +type Filter = Record; + +export const extractFilterValues = ( + filters: Filter, + prefilledFilters: FilterValues, +): FilterValues => { + const filterValues: FilterValues = { + exceptionType: prefilledFilters.exceptionType, + serviceName: prefilledFilters.serviceName, + }; + if (filters[EXCEPTION_TYPE_FILTER_NAME]) { + filterValues.exceptionType = extractSingleFilterValue( + EXCEPTION_TYPE_FILTER_NAME, + filters, + ); + } + if (filters[SERVICE_NAME_FILTER_NAME]) { + filterValues.serviceName = extractSingleFilterValue( + SERVICE_NAME_FILTER_NAME, + filters, + ); + } + return filterValues; +}; diff --git a/frontend/src/container/AppLayout/AppLayout.styles.scss b/frontend/src/container/AppLayout/AppLayout.styles.scss new file mode 100644 index 0000000..21dc54f --- /dev/null +++ b/frontend/src/container/AppLayout/AppLayout.styles.scss @@ -0,0 +1,61 @@ +.app-layout { + position: relative; + height: 100%; + width: 100%; + + .app-content { + width: 100%; + overflow: auto; + + .content-container { + position: relative; + margin: 0 1rem; + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + } + } +} + +.isDarkMode { + .app-layout { + .app-content { + background: #0b0c0e; + } + } +} + +.isLightMode { + .app-layout { + .app-content { + background: #ffffff; + } + } +} + +.trial-expiry-banner { + padding: 8px; + background-color: #f25733; + color: white; + text-align: center; +} + +.upgrade-link { + padding: 0px; + padding-right: 4px; + display: inline !important; + color: white; + text-decoration: underline; + text-decoration-color: white; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + + &:hover { + color: white; + text-decoration: underline; + text-decoration-color: white; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } +} diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx new file mode 100644 index 0000000..5d18e7d --- /dev/null +++ b/frontend/src/container/AppLayout/index.tsx @@ -0,0 +1,346 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import './AppLayout.styles.scss'; + +import { Flex } from 'antd'; +import getLocalStorageKey from 'api/browser/localstorage/get'; +import getDynamicConfigs from 'api/dynamicConfigs/getDynamicConfigs'; +import getUserLatestVersion from 'api/user/getLatestVersion'; +import getUserVersion from 'api/user/getVersion'; +import cx from 'classnames'; +import { IS_SIDEBAR_COLLAPSED } from 'constants/app'; +import ROUTES from 'constants/routes'; +import SideNav from 'container/SideNav'; +import TopNav from 'container/TopNav'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import useLicense from 'hooks/useLicense'; +import { useNotifications } from 'hooks/useNotifications'; +import history from 'lib/history'; +import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; +import { + ReactNode, + useCallback, + useEffect, + useLayoutEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { ErrorBoundary } from 'react-error-boundary'; +import { Helmet } from 'react-helmet-async'; +import { useTranslation } from 'react-i18next'; +import { useQueries } from 'react-query'; +import { useDispatch, useSelector } from 'react-redux'; +import { useLocation } from 'react-router-dom'; +import { Dispatch } from 'redux'; +import { sideBarCollapse } from 'store/actions'; +import { AppState } from 'store/reducers'; +import AppActions from 'types/actions'; +import { + UPDATE_CONFIGS, + UPDATE_CURRENT_ERROR, + UPDATE_CURRENT_VERSION, + UPDATE_LATEST_VERSION, + UPDATE_LATEST_VERSION_ERROR, +} from 'types/actions/app'; +import AppReducer from 'types/reducer/app'; +import { getFormattedDate, getRemainingDays } from 'utils/timeUtils'; + +import { ChildrenContainer, Layout, LayoutContent } from './styles'; +import { getRouteKey } from './utils'; + +function AppLayout(props: AppLayoutProps): JSX.Element { + const { isLoggedIn, user, role } = useSelector( + (state) => state.app, + ); + + const [collapsed, setCollapsed] = useState( + getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true', + ); + + const isDarkMode = useIsDarkMode(); + + const { data: licenseData, isFetching } = useLicense(); + + const { pathname } = useLocation(); + const { t } = useTranslation(['titles']); + + const [ + getUserVersionResponse, + getUserLatestVersionResponse, + getDynamicConfigsResponse, + ] = useQueries([ + { + queryFn: getUserVersion, + queryKey: ['getUserVersion', user?.accessJwt], + enabled: isLoggedIn, + }, + { + queryFn: getUserLatestVersion, + queryKey: ['getUserLatestVersion', user?.accessJwt], + enabled: isLoggedIn, + }, + { + queryFn: getDynamicConfigs, + queryKey: ['getDynamicConfigs', user?.accessJwt], + }, + ]); + + useEffect(() => { + if (getUserLatestVersionResponse.status === 'idle' && isLoggedIn) { + getUserLatestVersionResponse.refetch(); + } + + if (getUserVersionResponse.status === 'idle' && isLoggedIn) { + getUserVersionResponse.refetch(); + } + if (getDynamicConfigsResponse.status === 'idle') { + getDynamicConfigsResponse.refetch(); + } + }, [ + getUserLatestVersionResponse, + getUserVersionResponse, + isLoggedIn, + getDynamicConfigsResponse, + ]); + + const { children } = props; + + const dispatch = useDispatch>(); + + const latestCurrentCounter = useRef(0); + const latestVersionCounter = useRef(0); + const latestConfigCounter = useRef(0); + + const { notifications } = useNotifications(); + + const onCollapse = useCallback(() => { + setCollapsed((collapsed) => !collapsed); + }, []); + + useLayoutEffect(() => { + dispatch(sideBarCollapse(collapsed)); + }, [collapsed, dispatch]); + + useEffect(() => { + if ( + getUserLatestVersionResponse.isFetched && + getUserLatestVersionResponse.isError && + latestCurrentCounter.current === 0 + ) { + latestCurrentCounter.current = 1; + + dispatch({ + type: UPDATE_LATEST_VERSION_ERROR, + payload: { + isError: true, + }, + }); + notifications.error({ + message: t('oops_something_went_wrong_version'), + }); + } + + if ( + getUserVersionResponse.isFetched && + getUserVersionResponse.isError && + latestVersionCounter.current === 0 + ) { + latestVersionCounter.current = 1; + + dispatch({ + type: UPDATE_CURRENT_ERROR, + payload: { + isError: true, + }, + }); + notifications.error({ + message: t('oops_something_went_wrong_version'), + }); + } + + if ( + getUserVersionResponse.isFetched && + getUserLatestVersionResponse.isSuccess && + getUserVersionResponse.data && + getUserVersionResponse.data.payload + ) { + dispatch({ + type: UPDATE_CURRENT_VERSION, + payload: { + currentVersion: getUserVersionResponse.data.payload.version, + ee: getUserVersionResponse.data.payload.ee, + setupCompleted: getUserVersionResponse.data.payload.setupCompleted, + }, + }); + } + + if ( + getUserLatestVersionResponse.isFetched && + getUserLatestVersionResponse.isSuccess && + getUserLatestVersionResponse.data && + getUserLatestVersionResponse.data.payload + ) { + dispatch({ + type: UPDATE_LATEST_VERSION, + payload: { + latestVersion: getUserLatestVersionResponse.data.payload.tag_name, + }, + }); + } + + if ( + getDynamicConfigsResponse.isFetched && + getDynamicConfigsResponse.isSuccess && + getDynamicConfigsResponse.data && + getDynamicConfigsResponse.data.payload && + latestConfigCounter.current === 0 + ) { + latestConfigCounter.current = 1; + + dispatch({ + type: UPDATE_CONFIGS, + payload: { + configs: getDynamicConfigsResponse.data.payload, + }, + }); + } + }, [ + dispatch, + isLoggedIn, + pathname, + t, + getUserLatestVersionResponse.isLoading, + getUserLatestVersionResponse.isError, + getUserLatestVersionResponse.data, + getUserVersionResponse.isLoading, + getUserVersionResponse.isError, + getUserVersionResponse.data, + getUserLatestVersionResponse.isFetched, + getUserVersionResponse.isFetched, + getUserLatestVersionResponse.isSuccess, + getDynamicConfigsResponse.data, + getDynamicConfigsResponse.isFetched, + getDynamicConfigsResponse.isSuccess, + notifications, + ]); + + const isToDisplayLayout = isLoggedIn; + + const routeKey = useMemo(() => getRouteKey(pathname), [pathname]); + const pageTitle = t(routeKey); + const renderFullScreen = + pathname === ROUTES.GET_STARTED || + pathname === ROUTES.WORKSPACE_LOCKED || + pathname === ROUTES.GET_STARTED_APPLICATION_MONITORING || + pathname === ROUTES.GET_STARTED_INFRASTRUCTURE_MONITORING || + pathname === ROUTES.GET_STARTED_LOGS_MANAGEMENT || + pathname === ROUTES.GET_STARTED_AWS_MONITORING; + + const [showTrialExpiryBanner, setShowTrialExpiryBanner] = useState(false); + + useEffect(() => { + if ( + !isFetching && + licenseData?.payload?.onTrial && + !licenseData?.payload?.trialConvertedToSubscription && + !licenseData?.payload?.workSpaceBlock && + getRemainingDays(licenseData?.payload.trialEnd) < 7 + ) { + setShowTrialExpiryBanner(true); + } + }, [licenseData, isFetching]); + + const handleUpgrade = (): void => { + if (role === 'ADMIN') { + history.push(ROUTES.BILLING); + } + }; + + const isLogsView = (): boolean => + routeKey === 'LOGS' || + routeKey === 'LOGS_EXPLORER' || + routeKey === 'LOGS_PIPELINES' || + routeKey === 'LOGS_SAVE_VIEWS'; + + const isTracesView = (): boolean => + routeKey === 'TRACES_EXPLORER' || routeKey === 'TRACES_SAVE_VIEWS'; + + useEffect(() => { + if (isDarkMode) { + document.body.classList.remove('lightMode'); + document.body.classList.add('darkMode'); + } else { + document.body.classList.add('lightMode'); + document.body.classList.remove('darkMode'); + } + }, [isDarkMode]); + + const isSideNavCollapsed = getLocalStorageKey(IS_SIDEBAR_COLLAPSED); + + return ( + + + {pageTitle} + + + {showTrialExpiryBanner && ( +
    + You are in free trial period. Your free trial will end on{' '} + + {getFormattedDate(licenseData?.payload?.trialEnd || Date.now())}. + + {role === 'ADMIN' ? ( + + {' '} + Please{' '} + + upgrade + + to continue using SigNoz features. + + ) : ( + 'Please contact your administrator for upgrading to a paid plan.' + )} +
    + )} + + + {isToDisplayLayout && !renderFullScreen && ( + + )} +
    + + + + {isToDisplayLayout && !renderFullScreen && } + {children} + + + +
    +
    +
    + ); +} + +interface AppLayoutProps { + children: ReactNode; +} + +export default AppLayout; diff --git a/frontend/src/container/AppLayout/styles.ts b/frontend/src/container/AppLayout/styles.ts new file mode 100644 index 0000000..c232305 --- /dev/null +++ b/frontend/src/container/AppLayout/styles.ts @@ -0,0 +1,24 @@ +import { Layout as LayoutComponent } from 'antd'; +import styled from 'styled-components'; + +export const Layout = styled(LayoutComponent)` + &&& { + display: flex; + position: relative; + min-height: calc(100vh - 8rem); + overflow: hidden; + height: 100%; + flex-direction: column !important; + } +`; + +export const LayoutContent = styled(LayoutComponent.Content)` + overflow-y: auto; + height: 100%; +`; + +export const ChildrenContainer = styled.div` + display: flex; + flex-direction: column; + height: 100%; +`; diff --git a/frontend/src/container/AppLayout/utils.ts b/frontend/src/container/AppLayout/utils.ts new file mode 100644 index 0000000..649dcc4 --- /dev/null +++ b/frontend/src/container/AppLayout/utils.ts @@ -0,0 +1,9 @@ +import ROUTES from 'constants/routes'; + +export function getRouteKey(pathname: string): string { + const [routeKey] = Object.entries(ROUTES).find( + ([, value]) => value === pathname, + ) || ['DEFAULT']; + + return routeKey; +} diff --git a/frontend/src/container/BillingContainer/BillingContainer.styles.scss b/frontend/src/container/BillingContainer/BillingContainer.styles.scss new file mode 100644 index 0000000..05a672b --- /dev/null +++ b/frontend/src/container/BillingContainer/BillingContainer.styles.scss @@ -0,0 +1,78 @@ +.billing-container { + padding-top: 36px; + width: 65%; + + .billing-summary { + margin: 24px 8px; + } + + .billing-details { + margin: 24px 0px; + + .ant-table-title { + color: var(--bg-vanilla-400); + background-color: rgb(27, 28, 32); + } + + .ant-table-cell { + background-color: var(--bg-ink-400); + border-color: var(--bg-slate-500); + } + + .ant-table-tbody { + td { + border-color: var(--bg-slate-500); + } + } + } + + .upgrade-plan-benefits { + margin: 0px 8px; + border: 1px solid #333; + border-radius: 5px; + padding: 0 48px; + .plan-benefits { + .plan-benefit { + display: flex; + align-items: center; + gap: 16px; + margin: 16px 0; + } + } + } + + .empty-graph-card { + .ant-card-body { + height: 40vh; + display: flex; + justify-content: center; + align-items: center; + } + } +} + +.ant-skeleton.ant-skeleton-element.ant-skeleton-active { + width: 100%; + min-width: 100%; +} + +.ant-skeleton.ant-skeleton-element .ant-skeleton-input { + min-width: 100% !important; +} + +.lightMode { + .billing-container { + .billing-details { + .ant-table-cell { + background: var(--bg-vanilla-100); + border-color: var(--bg-vanilla-200); + } + + .ant-table-tbody { + td { + border-color: var(--bg-vanilla-200); + } + } + } + } +} diff --git a/frontend/src/container/BillingContainer/BillingContainer.test.tsx b/frontend/src/container/BillingContainer/BillingContainer.test.tsx new file mode 100644 index 0000000..cd447e5 --- /dev/null +++ b/frontend/src/container/BillingContainer/BillingContainer.test.tsx @@ -0,0 +1,188 @@ +import { billingSuccessResponse } from 'mocks-server/__mockdata__/billing'; +import { + notOfTrailResponse, + trialConvertedToSubscriptionResponse, +} from 'mocks-server/__mockdata__/licenses'; +import { server } from 'mocks-server/server'; +import { rest } from 'msw'; +import { act, render, screen } from 'tests/test-utils'; +import { getFormattedDate } from 'utils/timeUtils'; + +import BillingContainer from './BillingContainer'; + +const lisenceUrl = 'http://localhost/api/v2/licenses'; + +jest.mock('uplot', () => { + const paths = { + spline: jest.fn(), + bars: jest.fn(), + }; + + const uplotMock = jest.fn(() => ({ + paths, + })); + + return { + paths, + default: uplotMock, + }; +}); + +window.ResizeObserver = + window.ResizeObserver || + jest.fn().mockImplementation(() => ({ + disconnect: jest.fn(), + observe: jest.fn(), + unobserve: jest.fn(), + })); + +describe('BillingContainer', () => { + test('Component should render', async () => { + act(() => { + render(); + }); + + const dataInjection = screen.getByRole('columnheader', { + name: /data ingested/i, + }); + expect(dataInjection).toBeInTheDocument(); + const pricePerUnit = screen.getByRole('columnheader', { + name: /price per unit/i, + }); + expect(pricePerUnit).toBeInTheDocument(); + const cost = screen.getByRole('columnheader', { + name: /cost \(billing period to date\)/i, + }); + expect(cost).toBeInTheDocument(); + + const manageBilling = screen.getByRole('button', { + name: /manage billing/i, + }); + expect(manageBilling).toBeInTheDocument(); + + const dollar = screen.getByText(/\$0/i); + expect(dollar).toBeInTheDocument(); + + const currentBill = screen.getByText('Billing'); + expect(currentBill).toBeInTheDocument(); + }); + + test('OnTrail', async () => { + act(() => { + render(); + }); + + const freeTrailText = await screen.findByText('Free Trial'); + expect(freeTrailText).toBeInTheDocument(); + + const currentBill = screen.getByText('Billing'); + expect(currentBill).toBeInTheDocument(); + + const dollar0 = await screen.findByText(/\$0/i); + expect(dollar0).toBeInTheDocument(); + const onTrail = await screen.findByText( + /You are in free trial period. Your free trial will end on 20 Oct 2023/i, + ); + expect(onTrail).toBeInTheDocument(); + + const numberOfDayRemaining = await screen.findByText( + /1 days remaining in your billing period./i, + ); + expect(numberOfDayRemaining).toBeInTheDocument(); + const upgradeButton = await screen.findAllByRole('button', { + name: /upgrade/i, + }); + expect(upgradeButton[1]).toBeInTheDocument(); + expect(upgradeButton.length).toBe(2); + const checkPaidPlan = await screen.findByText( + /Check out features in paid plans/i, + ); + expect(checkPaidPlan).toBeInTheDocument(); + + const link = screen.getByRole('link', { name: /here/i }); + expect(link).toBeInTheDocument(); + }); + + test('OnTrail but trialConvertedToSubscription', async () => { + server.use( + rest.get(lisenceUrl, (req, res, ctx) => + res(ctx.status(200), ctx.json(trialConvertedToSubscriptionResponse)), + ), + ); + + act(() => { + render(); + }); + + const currentBill = screen.getByText('Billing'); + expect(currentBill).toBeInTheDocument(); + + const dollar0 = await screen.findByText(/\$0/i); + expect(dollar0).toBeInTheDocument(); + + const onTrail = await screen.findByText( + /You are in free trial period. Your free trial will end on 20 Oct 2023/i, + ); + expect(onTrail).toBeInTheDocument(); + + const receivedCardDetails = await screen.findByText( + /We have received your card details, your billing will only start after the end of your free trial period./i, + ); + expect(receivedCardDetails).toBeInTheDocument(); + + const manageBillingButton = await screen.findByRole('button', { + name: /manage billing/i, + }); + expect(manageBillingButton).toBeInTheDocument(); + + const dayRemainingInBillingPeriod = await screen.findByText( + /1 days remaining in your billing period./i, + ); + expect(dayRemainingInBillingPeriod).toBeInTheDocument(); + }); + + test('Not on ontrail', async () => { + server.use( + rest.get(lisenceUrl, (req, res, ctx) => + res(ctx.status(200), ctx.json(notOfTrailResponse)), + ), + ); + const { findByText } = render(); + + const billingPeriodText = `Your current billing period is from ${getFormattedDate( + billingSuccessResponse.data.billingPeriodStart, + )} to ${getFormattedDate(billingSuccessResponse.data.billingPeriodEnd)}`; + + const billingPeriod = await findByText(billingPeriodText); + expect(billingPeriod).toBeInTheDocument(); + + const currentBill = screen.getByText('Billing'); + expect(currentBill).toBeInTheDocument(); + + const dollar0 = await screen.findByText(/\$1,278.3/i); + expect(dollar0).toBeInTheDocument(); + + const metricsRow = await screen.findByRole('row', { + name: /metrics 4012 Million 0.1 \$ 401.2/i, + }); + expect(metricsRow).toBeInTheDocument(); + + const logRow = await screen.findByRole('row', { + name: /Logs 497 GB 0.4 \$ 198.8/i, + }); + expect(logRow).toBeInTheDocument(); + }); + + test('Should render corrent day remaining in billing period', async () => { + server.use( + rest.get(lisenceUrl, (req, res, ctx) => + res(ctx.status(200), ctx.json(notOfTrailResponse)), + ), + ); + render(); + const dayRemainingInBillingPeriod = await screen.findByText( + /11 days remaining in your billing period./i, + ); + expect(dayRemainingInBillingPeriod).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/container/BillingContainer/BillingContainer.tsx b/frontend/src/container/BillingContainer/BillingContainer.tsx new file mode 100644 index 0000000..b31f9c4 --- /dev/null +++ b/frontend/src/container/BillingContainer/BillingContainer.tsx @@ -0,0 +1,475 @@ +/* eslint-disable @typescript-eslint/no-loop-func */ +import './BillingContainer.styles.scss'; + +import { CheckCircleOutlined } from '@ant-design/icons'; +import { Color } from '@signozhq/design-tokens'; +import { + Alert, + Button, + Card, + Col, + Flex, + Row, + Skeleton, + Table, + Tag, + Typography, +} from 'antd'; +import { ColumnsType } from 'antd/es/table'; +import updateCreditCardApi from 'api/billing/checkout'; +import getUsage from 'api/billing/getUsage'; +import manageCreditCardApi from 'api/billing/manage'; +import Spinner from 'components/Spinner'; +import { SOMETHING_WENT_WRONG } from 'constants/api'; +import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; +import useAnalytics from 'hooks/analytics/useAnalytics'; +import useAxiosError from 'hooks/useAxiosError'; +import useLicense from 'hooks/useLicense'; +import { useNotifications } from 'hooks/useNotifications'; +import { pick } from 'lodash-es'; +import { useCallback, useEffect, useState } from 'react'; +import { useMutation, useQuery } from 'react-query'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { CheckoutSuccessPayloadProps } from 'types/api/billing/checkout'; +import { License } from 'types/api/licenses/def'; +import AppReducer from 'types/reducer/app'; +import { isCloudUser } from 'utils/app'; +import { getFormattedDate, getRemainingDays } from 'utils/timeUtils'; + +import { BillingUsageGraph } from './BillingUsageGraph/BillingUsageGraph'; + +interface DataType { + key: string; + name: string; + unit: string; + dataIngested: string; + pricePerUnit: string; + cost: string; +} + +const renderSkeletonInput = (): JSX.Element => ( + +); + +const dummyData: DataType[] = [ + { + key: '1', + name: 'Logs', + unit: '', + dataIngested: '', + pricePerUnit: '', + cost: '', + }, + { + key: '2', + name: 'Traces', + unit: '', + dataIngested: '', + pricePerUnit: '', + cost: '', + }, + { + key: '3', + name: 'Metrics', + unit: '', + dataIngested: '', + pricePerUnit: '', + cost: '', + }, +]; + +const dummyColumns: ColumnsType = [ + { + title: '', + dataIndex: 'name', + key: 'name', + render: renderSkeletonInput, + }, + { + title: 'Unit', + dataIndex: 'unit', + key: 'unit', + render: renderSkeletonInput, + }, + { + title: 'Data Ingested', + dataIndex: 'dataIngested', + key: 'dataIngested', + render: renderSkeletonInput, + }, + { + title: 'Price per Unit', + dataIndex: 'pricePerUnit', + key: 'pricePerUnit', + render: renderSkeletonInput, + }, + { + title: 'Cost (Billing period to date)', + dataIndex: 'cost', + key: 'cost', + render: renderSkeletonInput, + }, +]; + +export default function BillingContainer(): JSX.Element { + const daysRemainingStr = 'days remaining in your billing period.'; + const [headerText, setHeaderText] = useState(''); + const [billAmount, setBillAmount] = useState(0); + const [activeLicense, setActiveLicense] = useState(null); + const [daysRemaining, setDaysRemaining] = useState(0); + const [isFreeTrial, setIsFreeTrial] = useState(false); + const [data, setData] = useState([]); + const [apiResponse, setApiResponse] = useState({}); + + const { trackEvent } = useAnalytics(); + + const { isFetching, data: licensesData, error: licenseError } = useLicense(); + + const { user, org } = useSelector((state) => state.app); + const { notifications } = useNotifications(); + + const handleError = useAxiosError(); + + const isCloudUserVal = isCloudUser(); + + const processUsageData = useCallback( + (data: any): void => { + const { + details: { breakdown = [], billTotal }, + billingPeriodStart, + billingPeriodEnd, + } = data?.payload || {}; + const formattedUsageData: any[] = []; + + if (breakdown && Array.isArray(breakdown)) { + for (let index = 0; index < breakdown.length; index += 1) { + const element = breakdown[index]; + + element?.tiers.forEach( + ( + tier: { quantity: number; unitPrice: number; tierCost: number }, + i: number, + ) => { + formattedUsageData.push({ + key: `${index}${i}`, + name: i === 0 ? element?.type : '', + dataIngested: `${tier.quantity} ${element?.unit}`, + pricePerUnit: tier.unitPrice, + cost: `$ ${tier.tierCost}`, + }); + }, + ); + } + } + + setData(formattedUsageData); + + if (!licensesData?.payload?.onTrial) { + const remainingDays = getRemainingDays(billingPeriodEnd) - 1; + + setHeaderText( + `Your current billing period is from ${getFormattedDate( + billingPeriodStart, + )} to ${getFormattedDate(billingPeriodEnd)}`, + ); + setDaysRemaining(remainingDays > 0 ? remainingDays : 0); + setBillAmount(billTotal); + } + + setApiResponse(data?.payload || {}); + }, + [licensesData?.payload?.onTrial], + ); + + const { isLoading, isFetching: isFetchingBillingData } = useQuery( + [REACT_QUERY_KEY.GET_BILLING_USAGE, user?.userId], + { + queryFn: () => getUsage(activeLicense?.key || ''), + onError: handleError, + enabled: activeLicense !== null, + onSuccess: processUsageData, + }, + ); + + useEffect(() => { + const activeValidLicense = + licensesData?.payload?.licenses?.find( + (license) => license.isCurrent === true, + ) || null; + + setActiveLicense(activeValidLicense); + + if (!isFetching && licensesData?.payload?.onTrial && !licenseError) { + const remainingDays = getRemainingDays(licensesData?.payload?.trialEnd); + + setIsFreeTrial(true); + setBillAmount(0); + setDaysRemaining(remainingDays > 0 ? remainingDays : 0); + setHeaderText( + `You are in free trial period. Your free trial will end on ${getFormattedDate( + licensesData?.payload?.trialEnd, + )}`, + ); + } + }, [isFetching, licensesData?.payload, licenseError]); + + const columns: ColumnsType = [ + { + title: '', + dataIndex: 'name', + key: 'name', + render: (text): JSX.Element =>
    {text}
    , + }, + { + title: 'Data Ingested', + dataIndex: 'dataIngested', + key: 'dataIngested', + }, + { + title: 'Price per Unit', + dataIndex: 'pricePerUnit', + key: 'pricePerUnit', + }, + { + title: 'Cost (Billing period to date)', + dataIndex: 'cost', + key: 'cost', + }, + ]; + + const renderTableSkeleton = (): JSX.Element => ( +
    ( + + )), + }} + /> + ); + + const handleBillingOnSuccess = ( + data: ErrorResponse | SuccessResponse, + ): void => { + if (data?.payload?.redirectURL) { + const newTab = document.createElement('a'); + newTab.href = data.payload.redirectURL; + newTab.target = '_blank'; + newTab.rel = 'noopener noreferrer'; + newTab.click(); + } + }; + + const handleBillingOnError = (): void => { + notifications.error({ + message: SOMETHING_WENT_WRONG, + }); + }; + + const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation( + updateCreditCardApi, + { + onSuccess: (data) => { + handleBillingOnSuccess(data); + }, + onError: handleBillingOnError, + }, + ); + + const { + mutate: manageCreditCard, + isLoading: isLoadingManageBilling, + } = useMutation(manageCreditCardApi, { + onSuccess: (data) => { + handleBillingOnSuccess(data); + }, + onError: handleBillingOnError, + }); + + const handleBilling = useCallback(async () => { + if (isFreeTrial && !licensesData?.payload?.trialConvertedToSubscription) { + trackEvent('Billing : Upgrade Plan', { + user: pick(user, ['email', 'userId', 'name']), + org, + }); + + updateCreditCard({ + licenseKey: activeLicense?.key || '', + successURL: window.location.href, + cancelURL: window.location.href, + }); + } else { + trackEvent('Billing : Manage Billing', { + user: pick(user, ['email', 'userId', 'name']), + org, + }); + + manageCreditCard({ + licenseKey: activeLicense?.key || '', + successURL: window.location.href, + cancelURL: window.location.href, + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + activeLicense?.key, + isFreeTrial, + licensesData?.payload?.trialConvertedToSubscription, + manageCreditCard, + updateCreditCard, + ]); + + const BillingUsageGraphCallback = useCallback( + () => + !isLoading && !isFetchingBillingData ? ( + + ) : ( + + + + ), + [apiResponse, billAmount, isLoading, isFetchingBillingData], + ); + + return ( +
    + + + Billing + + + Manage your billing information, invoices, and monitor costs. + + + + + + + + {isCloudUserVal ? 'Enterprise Cloud' : 'Enterprise'}{' '} + {isFreeTrial ? Free Trial : ''} + + {!isLoading && !isFetchingBillingData ? ( + + {daysRemaining} {daysRemainingStr} + + ) : null} + + + + + {licensesData?.payload?.onTrial && + licensesData?.payload?.trialConvertedToSubscription && ( + + We have received your card details, your billing will only start after + the end of your free trial period. + + )} + + {!isLoading && !isFetchingBillingData ? ( + + ) : ( + + )} + + + + +
    + {!isLoading && !isFetchingBillingData && ( +
    + )} + + {(isLoading || isFetchingBillingData) && renderTableSkeleton()} + + + {isFreeTrial && !licensesData?.payload?.trialConvertedToSubscription && ( +
    + +
    + + + Upgrade now to have uninterrupted access + + + + Your billing will start only after the trial period + + + + + Check out features in paid plans   + + here + + + + + + + + + + )} + + ); +} diff --git a/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.styles.scss b/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.styles.scss new file mode 100644 index 0000000..e5722d4 --- /dev/null +++ b/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.styles.scss @@ -0,0 +1,29 @@ +.billing-graph-card { + .ant-card-body { + height: 40vh; + .uplot-graph-container { + padding: 8px; + } + } + .total-spent { + font-family: 'SF Mono' monospace; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 24px; + } + + .total-spent-title { + font-size: 12px; + font-weight: 500; + line-height: 22px; + letter-spacing: 0.48px; + color: rgba(255, 255, 255, 0.5); + } +} + +.lightMode { + .total-spent-title { + color: var(--bg-ink-100); + } +} diff --git a/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx b/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx new file mode 100644 index 0000000..fa6ce81 --- /dev/null +++ b/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx @@ -0,0 +1,190 @@ +import './BillingUsageGraph.styles.scss'; +import '../../../lib/uPlotLib/uPlotLib.styles.scss'; + +import { Color } from '@signozhq/design-tokens'; +import { Card, Flex, Typography } from 'antd'; +import { getComponentForPanelType } from 'constants/panelTypes'; +import { PANEL_TYPES } from 'constants/queryBuilder'; +import { PropsTypePropsMap } from 'container/GridPanelSwitch/types'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { useResizeObserver } from 'hooks/useDimensions'; +import tooltipPlugin from 'lib/uPlotLib/plugins/tooltipPlugin'; +import getAxes from 'lib/uPlotLib/utils/getAxes'; +import getRenderer from 'lib/uPlotLib/utils/getRenderer'; +import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; +import { getXAxisScale } from 'lib/uPlotLib/utils/getXAxisScale'; +import { getYAxisScale } from 'lib/uPlotLib/utils/getYAxisScale'; +import { FC, useMemo, useRef } from 'react'; +import uPlot from 'uplot'; + +import { + convertDataToMetricRangePayload, + fillMissingValuesForQuantities, +} from './utils'; + +interface BillingUsageGraphProps { + data: any; + billAmount: number; +} +const paths = ( + u: any, + seriesIdx: number, + idx0: number, + idx1: number, + extendGap: boolean, + buildClip: boolean, +): uPlot.Series.PathBuilder => { + const s = u.series[seriesIdx]; + const style = s.drawStyle; + const interp = s.lineInterpolation; + + const renderer = getRenderer(style, interp); + + return renderer(u, seriesIdx, idx0, idx1, extendGap, buildClip); +}; + +export function BillingUsageGraph(props: BillingUsageGraphProps): JSX.Element { + const { data, billAmount } = props; + const graphCompatibleData = useMemo( + () => convertDataToMetricRangePayload(data), + [data], + ); + const chartData = getUPlotChartData(graphCompatibleData); + const graphRef = useRef(null); + const isDarkMode = useIsDarkMode(); + const containerDimensions = useResizeObserver(graphRef); + + const { billingPeriodStart: startTime, billingPeriodEnd: endTime } = data; + + const Component = getComponentForPanelType(PANEL_TYPES.BAR) as FC< + PropsTypePropsMap[PANEL_TYPES] + >; + + const getGraphSeries = (color: string, label: string): any => ({ + drawStyle: 'bars', + paths, + lineInterpolation: 'spline', + show: true, + label, + fill: color, + stroke: color, + width: 2, + spanGaps: true, + points: { + size: 5, + show: false, + stroke: color, + }, + }); + + const uPlotSeries: any = useMemo( + () => [ + { label: 'Timestamp', stroke: 'purple' }, + getGraphSeries( + '#7CEDBE', + graphCompatibleData.data.result[0]?.legend as string, + ), + getGraphSeries( + '#4E74F8', + graphCompatibleData.data.result[1]?.legend as string, + ), + getGraphSeries( + '#F24769', + graphCompatibleData.data.result[2]?.legend as string, + ), + ], + [graphCompatibleData.data.result], + ); + + const axesOptions = getAxes(isDarkMode, ''); + + const optionsForChart: uPlot.Options = useMemo( + () => ({ + id: 'billing-usage-breakdown', + series: uPlotSeries, + width: containerDimensions.width, + height: containerDimensions.height - 30, + axes: [ + { + ...axesOptions[0], + grid: { + ...axesOptions.grid, + show: false, + stroke: isDarkMode ? Color.BG_VANILLA_400 : Color.BG_INK_400, + }, + }, + { + ...axesOptions[1], + stroke: isDarkMode ? Color.BG_SLATE_200 : Color.BG_INK_400, + }, + ], + scales: { + x: { + ...getXAxisScale(startTime - 86400, endTime), // Minus 86400 from startTime to decrease a day to have a buffer start + }, + y: { + ...getYAxisScale({ + series: graphCompatibleData?.data.newResult.data.result, + yAxisUnit: '', + softMax: null, + softMin: null, + }), + }, + }, + legend: { + show: true, + live: false, + isolate: true, + }, + cursor: { + lock: false, + focus: { + prox: 1e6, + bias: 1, + }, + }, + focus: { + alpha: 0.3, + }, + padding: [32, 32, 16, 16], + plugins: [ + tooltipPlugin( + fillMissingValuesForQuantities(graphCompatibleData, chartData[0]), + '', + true, + ), + ], + }), + [ + axesOptions, + chartData, + containerDimensions.height, + containerDimensions.width, + endTime, + graphCompatibleData, + isDarkMode, + startTime, + uPlotSeries, + ], + ); + + const numberFormatter = new Intl.NumberFormat('en-US'); + + return ( + + + + + TOTAL SPENT + + + ${numberFormatter.format(billAmount)} + + + +
    + +
    +
    + ); +} diff --git a/frontend/src/container/BillingContainer/BillingUsageGraph/utils.ts b/frontend/src/container/BillingContainer/BillingUsageGraph/utils.ts new file mode 100644 index 0000000..d40c8a6 --- /dev/null +++ b/frontend/src/container/BillingContainer/BillingUsageGraph/utils.ts @@ -0,0 +1,87 @@ +import { isEmpty, isNull } from 'lodash-es'; +import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; + +export const convertDataToMetricRangePayload = ( + data: any, +): MetricRangePayloadProps => { + const emptyStateData = { + data: { + newResult: { data: { result: [], resultType: '' } }, + result: [], + resultType: '', + }, + }; + if (isEmpty(data)) { + return emptyStateData; + } + const { + details: { breakdown = [] }, + } = data || {}; + + if (isNull(breakdown) || breakdown.length === 0) { + return emptyStateData; + } + + const payload = breakdown.map((info: any) => { + const metric = info.type; + const sortedBreakdownData = (info?.dayWiseBreakdown?.breakdown || []).sort( + (a: any, b: any) => a.timestamp - b.timestamp, + ); + const values = (sortedBreakdownData || []).map((categoryInfo: any) => [ + categoryInfo.timestamp, + categoryInfo.total, + ]); + const queryName = info.type; + const legend = info.type; + const { unit } = info; + const quantity = sortedBreakdownData.map( + (categoryInfo: any) => categoryInfo.quantity, + ); + return { metric, values, queryName, legend, quantity, unit }; + }); + + const sortedData = payload.sort((a: any, b: any) => { + const sumA = a.values.reduce((acc: any, val: any) => acc + val[1], 0); + const avgA = a.values.length ? sumA / a.values.length : 0; + const sumB = b.values.reduce((acc: any, val: any) => acc + val[1], 0); + const avgB = b.values.length ? sumB / b.values.length : 0; + + return sumA === sumB ? avgB - avgA : sumB - sumA; + }); + + return { + data: { + newResult: { data: { result: sortedData, resultType: '' } }, + result: sortedData, + resultType: '', + }, + }; +}; + +export function fillMissingValuesForQuantities( + data: any, + timestampArray: number[], +): MetricRangePayloadProps { + const { result } = data.data; + + const transformedResultArr: any[] = []; + result.forEach((item: any) => { + const timestampToQuantityMap: { [timestamp: number]: number } = {}; + item.values.forEach((val: number[], index: number) => { + timestampToQuantityMap[val[0]] = item.quantity[index]; + }); + + const quantityArray = timestampArray.map( + (timestamp: number) => timestampToQuantityMap[timestamp] ?? null, + ); + transformedResultArr.push({ ...item, quantity: quantityArray }); + }); + + return { + data: { + newResult: { data: { result: transformedResultArr, resultType: '' } }, + result: transformedResultArr, + resultType: '', + }, + }; +} diff --git a/frontend/src/container/ConfigDropdown/Config/ErrorLink.tsx b/frontend/src/container/ConfigDropdown/Config/ErrorLink.tsx new file mode 100644 index 0000000..fa2f471 --- /dev/null +++ b/frontend/src/container/ConfigDropdown/Config/ErrorLink.tsx @@ -0,0 +1,33 @@ +import { PureComponent } from 'react'; + +interface State { + hasError: boolean; +} + +interface Props { + children: JSX.Element; +} + +class ErrorLink extends PureComponent { + constructor(props: Props) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(): State { + return { hasError: true }; + } + + render(): JSX.Element { + const { children } = this.props; + const { hasError } = this.state; + + if (hasError) { + return
    ; + } + + return children; + } +} + +export default ErrorLink; diff --git a/frontend/src/container/ConfigDropdown/Config/Link.tsx b/frontend/src/container/ConfigDropdown/Config/Link.tsx new file mode 100644 index 0000000..b31e374 --- /dev/null +++ b/frontend/src/container/ConfigDropdown/Config/Link.tsx @@ -0,0 +1,23 @@ +import { ReactNode } from 'react'; +import { Link } from 'react-router-dom'; + +function LinkContainer({ children, href }: LinkContainerProps): JSX.Element { + const isInternalLink = href.startsWith('/'); + + if (isInternalLink) { + return {children}; + } + + return ( + + {children} + + ); +} + +interface LinkContainerProps { + children: ReactNode; + href: string; +} + +export default LinkContainer; diff --git a/frontend/src/container/ConfigDropdown/Config/index.tsx b/frontend/src/container/ConfigDropdown/Config/index.tsx new file mode 100644 index 0000000..52879ce --- /dev/null +++ b/frontend/src/container/ConfigDropdown/Config/index.tsx @@ -0,0 +1,47 @@ +import { Menu, Space } from 'antd'; +import Spinner from 'components/Spinner'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { lazy, Suspense, useMemo } from 'react'; +import { ConfigProps } from 'types/api/dynamicConfigs/getDynamicConfigs'; + +import ErrorLink from './ErrorLink'; +import LinkContainer from './Link'; + +function HelpToolTip({ config }: HelpToolTipProps): JSX.Element { + const sortedConfig = useMemo( + () => config.components.sort((a, b) => a.position - b.position), + [config.components], + ); + + const isDarkMode = useIsDarkMode(); + + const items = sortedConfig.map((item) => { + const iconName = `${isDarkMode ? item.darkIcon : item.lightIcon}`; + const Component = lazy( + () => import(`@ant-design/icons/es/icons/${iconName}.js`), + ); + return { + key: item.text + item.href, + label: ( + + }> + + + + {item.text} + + + + + ), + }; + }); + + return ; +} + +interface HelpToolTipProps { + config: ConfigProps; +} + +export default HelpToolTip; diff --git a/frontend/src/container/ConfigDropdown/index.tsx b/frontend/src/container/ConfigDropdown/index.tsx new file mode 100644 index 0000000..519a05c --- /dev/null +++ b/frontend/src/container/ConfigDropdown/index.tsx @@ -0,0 +1,75 @@ +import { + CaretDownFilled, + CaretUpFilled, + QuestionCircleFilled, + QuestionCircleOutlined, +} from '@ant-design/icons'; +import { Space } from 'antd'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { useMemo, useState } from 'react'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { ConfigProps } from 'types/api/dynamicConfigs/getDynamicConfigs'; +import AppReducer from 'types/reducer/app'; + +import HelpToolTip from './Config'; +import { ConfigDropdown } from './styles'; + +function DynamicConfigDropdown({ + frontendId, +}: DynamicConfigDropdownProps): JSX.Element { + const { configs } = useSelector((state) => state.app); + const isDarkMode = useIsDarkMode(); + const [isHelpDropDownOpen, setIsHelpDropDownOpen] = useState(false); + + const config = useMemo( + () => + Object.values(configs).find( + (config) => config.frontendPositionId === frontendId, + ), + [frontendId, configs], + ); + + const onToggleHandler = (): void => { + setIsHelpDropDownOpen(!isHelpDropDownOpen); + }; + + const menu = useMemo( + () => ({ + items: [ + { + key: '1', + label: , + }, + ], + }), + [config], + ); + + if (!config) { + return
    ; + } + + const Icon = isDarkMode ? QuestionCircleOutlined : QuestionCircleFilled; + const DropDownIcon = isHelpDropDownOpen ? CaretUpFilled : CaretDownFilled; + + return ( + + + + + + + ); +} + +interface DynamicConfigDropdownProps { + frontendId: string; +} + +export default DynamicConfigDropdown; diff --git a/frontend/src/container/ConfigDropdown/styles.ts b/frontend/src/container/ConfigDropdown/styles.ts new file mode 100644 index 0000000..25144db --- /dev/null +++ b/frontend/src/container/ConfigDropdown/styles.ts @@ -0,0 +1,6 @@ +import { Dropdown } from 'antd'; +import styled from 'styled-components'; + +export const ConfigDropdown = styled(Dropdown)` + cursor: pointer; +`; diff --git a/frontend/src/container/Controls/config.ts b/frontend/src/container/Controls/config.ts new file mode 100644 index 0000000..51d8520 --- /dev/null +++ b/frontend/src/container/Controls/config.ts @@ -0,0 +1,9 @@ +import { CSSProperties } from 'react'; + +export const ITEMS_PER_PAGE_OPTIONS = [25, 50, 100, 200]; + +export const DEFAULT_PER_PAGE_VALUE = 100; + +export const defaultSelectStyle: CSSProperties = { + minWidth: '6rem', +}; diff --git a/frontend/src/container/Controls/index.tsx b/frontend/src/container/Controls/index.tsx new file mode 100644 index 0000000..c738939 --- /dev/null +++ b/frontend/src/container/Controls/index.tsx @@ -0,0 +1,91 @@ +import { LeftOutlined, RightOutlined } from '@ant-design/icons'; +import { Button, Select } from 'antd'; +import { DEFAULT_PER_PAGE_OPTIONS, Pagination } from 'hooks/queryPagination'; +import { memo, useMemo } from 'react'; +import { popupContainer } from 'utils/selectPopupContainer'; + +import { defaultSelectStyle } from './config'; +import { Container } from './styles'; + +function Controls({ + offset = 0, + perPageOptions = DEFAULT_PER_PAGE_OPTIONS, + isLoading, + totalCount, + countPerPage, + handleNavigatePrevious, + handleNavigateNext, + handleCountItemsPerPageChange, + isLogPanel = false, +}: ControlsProps): JSX.Element | null { + const isNextAndPreviousDisabled = useMemo( + () => isLoading || countPerPage < 0 || totalCount === 0, + [isLoading, countPerPage, totalCount], + ); + const isPreviousDisabled = useMemo( + () => (isLogPanel ? false : offset <= 0 || isNextAndPreviousDisabled), + [isLogPanel, isNextAndPreviousDisabled, offset], + ); + const isNextDisabled = useMemo( + () => + isLogPanel ? false : totalCount < countPerPage || isNextAndPreviousDisabled, + [countPerPage, isLogPanel, isNextAndPreviousDisabled, totalCount], + ); + + return ( + + + + + style={defaultSelectStyle} + loading={isLoading} + value={countPerPage} + onChange={handleCountItemsPerPageChange} + getPopupContainer={popupContainer} + > + {perPageOptions.map((count) => ( + {`${count} / page`} + ))} + + + ); +} + +Controls.defaultProps = { + offset: 0, + perPageOptions: DEFAULT_PER_PAGE_OPTIONS, + isLogPanel: false, +}; + +export interface ControlsProps { + offset?: Pagination['offset']; + perPageOptions?: number[]; + totalCount: number; + countPerPage: Pagination['limit']; + isLoading: boolean; + handleNavigatePrevious: () => void; + handleNavigateNext: () => void; + handleCountItemsPerPageChange: (value: Pagination['limit']) => void; + isLogPanel?: boolean; +} + +export default memo(Controls); diff --git a/frontend/src/container/Controls/styles.ts b/frontend/src/container/Controls/styles.ts new file mode 100644 index 0000000..0407b8f --- /dev/null +++ b/frontend/src/container/Controls/styles.ts @@ -0,0 +1,7 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; +`; diff --git a/frontend/src/container/CreateAlertChannels/config.ts b/frontend/src/container/CreateAlertChannels/config.ts new file mode 100644 index 0000000..3ee3882 --- /dev/null +++ b/frontend/src/container/CreateAlertChannels/config.ts @@ -0,0 +1,127 @@ +export interface Channel { + send_resolved?: boolean; + name: string; + filter?: Partial>; +} + +export interface SlackChannel extends Channel { + api_url?: string; + channel?: string; + title?: string; + text?: string; +} + +export interface WebhookChannel extends Channel { + api_url?: string; + // basic auth + username?: string; + password?: string; +} + +// PagerChannel configures alert manager to send +// events to pagerduty +export interface PagerChannel extends Channel { + // ref: https://prometheus.io/docs/alerting/latest/configuration/#pagerduty_config + routing_key?: string; + // displays source of the event in pager duty + client?: string; + client_url?: string; + // A description of the incident + description?: string; + // Severity of the incident + severity?: string; + // The part or component of the affected system that is broken + component?: string; + // A cluster or grouping of sources + group?: string; + // The class/type of the event. + class?: string; + + details?: string; + detailsArray?: Record; +} + +// OpsgenieChannel configures alert manager to send +// events to opsgenie +export interface OpsgenieChannel extends Channel { + // ref: https://prometheus.io/docs/alerting/latest/configuration/#opsgenie_config + api_key: string; + + message?: string; + + // A description of the incident + description?: string; + + // A backlink to the sender of the notification. + source?: string; + + // A set of arbitrary key/value pairs that provide further detail + // about the alert. + details?: string; + detailsArray?: Record; + + // Priority level of alert. Possible values are P1, P2, P3, P4, and P5. + priority?: string; +} + +export interface EmailChannel extends Channel { + // comma separated list of email addresses to send alerts to + to: string; + // HTML body of the email notification. + html: string; + // Further headers email header key/value pairs. + // [ headers: { : , ... } ] + headers: Record; +} + +export const ValidatePagerChannel = (p: PagerChannel): string => { + if (!p) { + return 'Received unexpected input for this channel, please contact your administrator '; + } + + if (!p.name || p.name === '') { + return 'Name is mandatory for creating a channel'; + } + + if (!p.routing_key || p.routing_key === '') { + return 'Routing Key is mandatory for creating pagerduty channel'; + } + + // validate details json + try { + JSON.parse(p.details || '{}'); + } catch (e) { + return 'failed to parse additional information, please enter a valid json'; + } + + return ''; +}; + +export enum ChannelType { + Slack = 'slack', + Email = 'email', + Webhook = 'webhook', + Pagerduty = 'pagerduty', + Opsgenie = 'opsgenie', + MsTeams = 'msteams', +} + +// LabelFilterStatement will be used for preparing filter conditions / matchers +export interface LabelFilterStatement { + // ref: https://prometheus.io/docs/alerting/latest/configuration/#matcher + + // label name + name: string; + + // comparators supported by promql are =, !=, =~, or !~. = + comparator: string; + + // filter value + value: string; +} + +export interface MsTeamsChannel extends Channel { + webhook_url?: string; + title?: string; + text?: string; +} diff --git a/frontend/src/container/CreateAlertChannels/defaults.ts b/frontend/src/container/CreateAlertChannels/defaults.ts new file mode 100644 index 0000000..f687164 --- /dev/null +++ b/frontend/src/container/CreateAlertChannels/defaults.ts @@ -0,0 +1,448 @@ +import { EmailChannel, OpsgenieChannel, PagerChannel } from './config'; + +export const PagerInitialConfig: Partial = { + description: `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} + {{- if gt (len .CommonLabels) (len .GroupLabels) -}} + {{" "}}( + {{- with .CommonLabels.Remove .GroupLabels.Names }} + {{- range $index, $label := .SortedPairs -}} + {{ if $index }}, {{ end }} + {{- $label.Name }}="{{ $label.Value -}}" + {{- end }} + {{- end -}} + ) + {{- end }}`, + severity: '{{ (index .Alerts 0).Labels.severity }}', + client: 'SigNoz Alert Manager', + client_url: 'https://enter-signoz-host-n-port-here/alerts', + details: JSON.stringify({ + firing: `{{ template "pagerduty.default.instances" .Alerts.Firing }}`, + resolved: `{{ template "pagerduty.default.instances" .Alerts.Resolved }}`, + num_firing: '{{ .Alerts.Firing | len }}', + num_resolved: '{{ .Alerts.Resolved | len }}', + }), +}; + +export const OpsgenieInitialConfig: Partial = { + message: '{{ .CommonLabels.alertname }}', + description: `{{ if gt (len .Alerts.Firing) 0 -}} + Alerts Firing: + {{ range .Alerts.Firing }} + - Message: {{ .Annotations.description }} + Labels: + {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} + {{ end }} Annotations: + {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} + {{ end }} Source: {{ .GeneratorURL }} + {{ end }} + {{- end }} + {{ if gt (len .Alerts.Resolved) 0 -}} + Alerts Resolved: + {{ range .Alerts.Resolved }} + - Message: {{ .Annotations.description }} + Labels: + {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} + {{ end }} Annotations: + {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} + {{ end }} Source: {{ .GeneratorURL }} + {{ end }} + {{- end }}`, + priority: + '{{ if eq (index .Alerts 0).Labels.severity "critical" }}P1{{ else if eq (index .Alerts 0).Labels.severity "warning" }}P2{{ else if eq (index .Alerts 0).Labels.severity "info" }}P3{{ else }}P4{{ end }}', +}; + +export const EmailInitialConfig: Partial = { + send_resolved: true, + html: ` + + + + + + {{ template "__subject" . }} + + + +
    + + + + + +
    +
    + + + {{ if gt (len .Alerts.Firing) 0 }} + + + + + +
    + {{ else }} + + {{ end }} + {{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for {{ range .GroupLabels.SortedPairs }} + {{ .Name }}={{ .Value }} + {{ end }} +
    + + {{ if gt (len .Alerts.Firing) 0 }} + + + + {{ end }} + {{ range .Alerts.Firing }} + + + + {{ end }} + {{ if gt (len .Alerts.Resolved) 0 }} + {{ if gt (len .Alerts.Firing) 0 }} + + + + {{ end }} + + + + {{ end }} + {{ range .Alerts.Resolved }} + + + + {{ end }} +
    + [{{ .Alerts.Firing | len }}] Firing +
    + Labels
    + {{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}
    {{ end }} + {{ if gt (len .Annotations) 0 }}Annotations
    {{ end }} + {{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}
    {{ end }} + Source
    +
    +
    +
    +
    +
    + [{{ .Alerts.Resolved | len }}] Resolved +
    + Labels
    + {{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}
    {{ end }} + {{ if gt (len .Annotations) 0 }}Annotations
    {{ end }} + {{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}
    {{ end }} + Source
    +
    +
    +
    +
    + + `, +}; diff --git a/frontend/src/container/CreateAlertChannels/index.tsx b/frontend/src/container/CreateAlertChannels/index.tsx new file mode 100644 index 0000000..51a0b62 --- /dev/null +++ b/frontend/src/container/CreateAlertChannels/index.tsx @@ -0,0 +1,529 @@ +import { Form } from 'antd'; +import createEmail from 'api/channels/createEmail'; +import createMsTeamsApi from 'api/channels/createMsTeams'; +import createOpsgenie from 'api/channels/createOpsgenie'; +import createPagerApi from 'api/channels/createPager'; +import createSlackApi from 'api/channels/createSlack'; +import createWebhookApi from 'api/channels/createWebhook'; +import testEmail from 'api/channels/testEmail'; +import testMsTeamsApi from 'api/channels/testMsTeams'; +import testOpsGenie from 'api/channels/testOpsgenie'; +import testPagerApi from 'api/channels/testPager'; +import testSlackApi from 'api/channels/testSlack'; +import testWebhookApi from 'api/channels/testWebhook'; +import ROUTES from 'constants/routes'; +import FormAlertChannels from 'container/FormAlertChannels'; +import { useNotifications } from 'hooks/useNotifications'; +import history from 'lib/history'; +import { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { + ChannelType, + EmailChannel, + MsTeamsChannel, + OpsgenieChannel, + PagerChannel, + SlackChannel, + ValidatePagerChannel, + WebhookChannel, +} from './config'; +import { + EmailInitialConfig, + OpsgenieInitialConfig, + PagerInitialConfig, +} from './defaults'; +import { isChannelType } from './utils'; + +function CreateAlertChannels({ + preType = ChannelType.Slack, +}: CreateAlertChannelsProps): JSX.Element { + // init namespace for translations + const { t } = useTranslation('channels'); + + const [formInstance] = Form.useForm(); + + const [selectedConfig, setSelectedConfig] = useState< + Partial< + SlackChannel & + WebhookChannel & + PagerChannel & + MsTeamsChannel & + OpsgenieChannel & + EmailChannel + > + >({ + text: `{{ range .Alerts -}} + *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} + + *Summary:* {{ .Annotations.summary }} + *Description:* {{ .Annotations.description }} + *RelatedLogs:* {{ .Annotations.related_logs }} + *RelatedTraces:* {{ .Annotations.related_traces }} + + *Details:* + {{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }} + {{ end }} + {{ end }}`, + title: `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} + {{- if gt (len .CommonLabels) (len .GroupLabels) -}} + {{" "}}( + {{- with .CommonLabels.Remove .GroupLabels.Names }} + {{- range $index, $label := .SortedPairs -}} + {{ if $index }}, {{ end }} + {{- $label.Name }}="{{ $label.Value -}}" + {{- end }} + {{- end -}} + ) + {{- end }}`, + }); + const [savingState, setSavingState] = useState(false); + const [testingState, setTestingState] = useState(false); + const { notifications } = useNotifications(); + + const [type, setType] = useState(preType); + const onTypeChangeHandler = useCallback( + (value: string) => { + const currentType = type; + setType(value as ChannelType); + + if (value === ChannelType.Pagerduty && currentType !== value) { + // reset config to pager defaults + setSelectedConfig({ + name: selectedConfig?.name, + send_resolved: selectedConfig.send_resolved, + ...PagerInitialConfig, + }); + } + + if (value === ChannelType.Opsgenie && currentType !== value) { + setSelectedConfig((selectedConfig) => ({ + ...selectedConfig, + ...OpsgenieInitialConfig, + })); + } + + // reset config to email defaults + if (value === ChannelType.Email && currentType !== value) { + setSelectedConfig((selectedConfig) => ({ + ...selectedConfig, + ...EmailInitialConfig, + })); + } + }, + [type, selectedConfig], + ); + + const prepareSlackRequest = useCallback( + () => ({ + api_url: selectedConfig?.api_url || '', + channel: selectedConfig?.channel || '', + name: selectedConfig?.name || '', + send_resolved: true, + text: selectedConfig?.text || '', + title: selectedConfig?.title || '', + }), + [selectedConfig], + ); + + const onSlackHandler = useCallback(async () => { + setSavingState(true); + + try { + const response = await createSlackApi(prepareSlackRequest()); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), + }); + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + } + } catch (error) { + notifications.error({ + message: 'Error', + description: t('channel_creation_failed'), + }); + } + setSavingState(false); + }, [prepareSlackRequest, t, notifications]); + + const prepareWebhookRequest = useCallback(() => { + // initial api request without auth params + let request: WebhookChannel = { + api_url: selectedConfig?.api_url || '', + name: selectedConfig?.name || '', + send_resolved: true, + }; + + if (selectedConfig?.username !== '' || selectedConfig?.password !== '') { + if (selectedConfig?.username !== '') { + // if username is not null then password must be passed + if (selectedConfig?.password !== '') { + request = { + ...request, + username: selectedConfig.username, + password: selectedConfig.password, + }; + } else { + notifications.error({ + message: 'Error', + description: t('username_no_password'), + }); + } + } else if (selectedConfig?.password !== '') { + // only password entered, set bearer token + request = { + ...request, + username: '', + password: selectedConfig.password, + }; + } + } + return request; + }, [notifications, t, selectedConfig]); + + const onWebhookHandler = useCallback(async () => { + setSavingState(true); + try { + const request = prepareWebhookRequest(); + const response = await createWebhookApi(request); + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), + }); + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + } + } catch (error) { + notifications.error({ + message: 'Error', + description: t('channel_creation_failed'), + }); + } + setSavingState(false); + }, [prepareWebhookRequest, t, notifications]); + + const preparePagerRequest = useCallback(() => { + const validationError = ValidatePagerChannel(selectedConfig as PagerChannel); + if (validationError !== '') { + notifications.error({ + message: 'Error', + description: validationError, + }); + return null; + } + + return { + name: selectedConfig?.name || '', + send_resolved: true, + routing_key: selectedConfig?.routing_key || '', + client: selectedConfig?.client || '', + client_url: selectedConfig?.client_url || '', + description: selectedConfig?.description || '', + severity: selectedConfig?.severity || '', + component: selectedConfig?.component || '', + group: selectedConfig?.group || '', + class: selectedConfig?.class || '', + details: selectedConfig.details || '', + detailsArray: JSON.parse(selectedConfig.details || '{}'), + }; + }, [selectedConfig, notifications]); + + const onPagerHandler = useCallback(async () => { + setSavingState(true); + const request = preparePagerRequest(); + + if (request) { + try { + const response = await createPagerApi(request); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), + }); + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + } + } catch (e) { + notifications.error({ + message: 'Error', + description: t('channel_creation_failed'), + }); + } + } + setSavingState(false); + }, [t, notifications, preparePagerRequest]); + + const prepareOpsgenieRequest = useCallback( + () => ({ + api_key: selectedConfig?.api_key || '', + name: selectedConfig?.name || '', + send_resolved: true, + description: selectedConfig?.description || '', + message: selectedConfig?.message || '', + priority: selectedConfig?.priority || '', + }), + [selectedConfig], + ); + + const onOpsgenieHandler = useCallback(async () => { + setSavingState(true); + + try { + const response = await createOpsgenie(prepareOpsgenieRequest()); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), + }); + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + } + } catch (error) { + notifications.error({ + message: 'Error', + description: t('channel_creation_failed'), + }); + } + setSavingState(false); + }, [prepareOpsgenieRequest, t, notifications]); + + const prepareEmailRequest = useCallback( + () => ({ + name: selectedConfig?.name || '', + send_resolved: true, + to: selectedConfig?.to || '', + html: selectedConfig?.html || '', + headers: selectedConfig?.headers || {}, + }), + [selectedConfig], + ); + + const onEmailHandler = useCallback(async () => { + setSavingState(true); + try { + const request = prepareEmailRequest(); + const response = await createEmail(request); + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), + }); + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + } + } catch (error) { + notifications.error({ + message: 'Error', + description: t('channel_creation_failed'), + }); + } + setSavingState(false); + }, [prepareEmailRequest, t, notifications]); + + const prepareMsTeamsRequest = useCallback( + () => ({ + webhook_url: selectedConfig?.webhook_url || '', + name: selectedConfig?.name || '', + send_resolved: true, + text: selectedConfig?.text || '', + title: selectedConfig?.title || '', + }), + [selectedConfig], + ); + + const onMsTeamsHandler = useCallback(async () => { + setSavingState(true); + + try { + const response = await createMsTeamsApi(prepareMsTeamsRequest()); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_creation_done'), + }); + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + } + } catch (error) { + notifications.error({ + message: 'Error', + description: t('channel_creation_failed'), + }); + } + setSavingState(false); + }, [prepareMsTeamsRequest, t, notifications]); + + const onSaveHandler = useCallback( + async (value: ChannelType) => { + const functionMapper = { + [ChannelType.Slack]: onSlackHandler, + [ChannelType.Webhook]: onWebhookHandler, + [ChannelType.Pagerduty]: onPagerHandler, + [ChannelType.Opsgenie]: onOpsgenieHandler, + [ChannelType.MsTeams]: onMsTeamsHandler, + [ChannelType.Email]: onEmailHandler, + }; + + if (isChannelType(value)) { + const functionToCall = functionMapper[value as keyof typeof functionMapper]; + + if (functionToCall) { + functionToCall(); + } else { + notifications.error({ + message: 'Error', + description: t('selected_channel_invalid'), + }); + } + } + }, + [ + onSlackHandler, + onWebhookHandler, + onPagerHandler, + onOpsgenieHandler, + onMsTeamsHandler, + onEmailHandler, + notifications, + t, + ], + ); + + const performChannelTest = useCallback( + async (channelType: ChannelType) => { + setTestingState(true); + try { + let request; + let response; + switch (channelType) { + case ChannelType.Webhook: + request = prepareWebhookRequest(); + response = await testWebhookApi(request); + break; + case ChannelType.Slack: + request = prepareSlackRequest(); + response = await testSlackApi(request); + break; + case ChannelType.Pagerduty: + request = preparePagerRequest(); + if (request) response = await testPagerApi(request); + break; + case ChannelType.MsTeams: + request = prepareMsTeamsRequest(); + response = await testMsTeamsApi(request); + break; + case ChannelType.Opsgenie: + request = prepareOpsgenieRequest(); + response = await testOpsGenie(request); + break; + case ChannelType.Email: + request = prepareEmailRequest(); + response = await testEmail(request); + break; + default: + notifications.error({ + message: 'Error', + description: t('test_unsupported'), + }); + setTestingState(false); + return; + } + + if (response && response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_test_done'), + }); + } else { + notifications.error({ + message: 'Error', + description: t('channel_test_failed'), + }); + } + } catch (error) { + notifications.error({ + message: 'Error', + description: t('channel_test_unexpected'), + }); + } + setTestingState(false); + }, + [ + prepareWebhookRequest, + t, + preparePagerRequest, + prepareOpsgenieRequest, + prepareSlackRequest, + prepareMsTeamsRequest, + prepareEmailRequest, + notifications, + ], + ); + + const onTestHandler = useCallback( + async (value: ChannelType) => { + performChannelTest(value); + }, + [performChannelTest], + ); + + return ( + + ); +} + +interface CreateAlertChannelsProps { + preType: ChannelType; +} + +export default CreateAlertChannels; diff --git a/frontend/src/container/CreateAlertChannels/utils.ts b/frontend/src/container/CreateAlertChannels/utils.ts new file mode 100644 index 0000000..ce7520a --- /dev/null +++ b/frontend/src/container/CreateAlertChannels/utils.ts @@ -0,0 +1,4 @@ +import { ChannelType } from './config'; + +export const isChannelType = (type: string): type is ChannelType => + Object.values(ChannelType).includes(type as ChannelType); diff --git a/frontend/src/container/CreateAlertRule/SelectAlertType/config.ts b/frontend/src/container/CreateAlertRule/SelectAlertType/config.ts new file mode 100644 index 0000000..c973684 --- /dev/null +++ b/frontend/src/container/CreateAlertRule/SelectAlertType/config.ts @@ -0,0 +1,27 @@ +import { TFunction } from 'i18next'; +import { AlertTypes } from 'types/api/alerts/alertTypes'; + +import { OptionType } from './types'; + +export const getOptionList = (t: TFunction): OptionType[] => [ + { + title: t('metric_based_alert'), + selection: AlertTypes.METRICS_BASED_ALERT, + description: t('metric_based_alert_desc'), + }, + { + title: t('log_based_alert'), + selection: AlertTypes.LOGS_BASED_ALERT, + description: t('log_based_alert_desc'), + }, + { + title: t('traces_based_alert'), + selection: AlertTypes.TRACES_BASED_ALERT, + description: t('traces_based_alert_desc'), + }, + { + title: t('exceptions_based_alert'), + selection: AlertTypes.EXCEPTIONS_BASED_ALERT, + description: t('exceptions_based_alert_desc'), + }, +]; diff --git a/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx b/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx new file mode 100644 index 0000000..60bf658 --- /dev/null +++ b/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx @@ -0,0 +1,53 @@ +import { Row, Typography } from 'antd'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { AlertTypes } from 'types/api/alerts/alertTypes'; + +import { getOptionList } from './config'; +import { AlertTypeCard, SelectTypeContainer } from './styles'; +import { OptionType } from './types'; + +function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element { + const { t } = useTranslation(['alerts']); + + const optionList = getOptionList(t); + + const renderOptions = useMemo( + () => ( + <> + {optionList.map((option: OptionType) => ( + { + onSelect(option.selection); + }} + > + {option.description} + + ))} + + ), + [onSelect, optionList], + ); + + return ( + + + {t('choose_alert_type')} + + {renderOptions} + + ); +} + +interface SelectAlertTypeProps { + onSelect: (typ: AlertTypes) => void; +} + +export default SelectAlertType; diff --git a/frontend/src/container/CreateAlertRule/SelectAlertType/styles.ts b/frontend/src/container/CreateAlertRule/SelectAlertType/styles.ts new file mode 100644 index 0000000..9c3323a --- /dev/null +++ b/frontend/src/container/CreateAlertRule/SelectAlertType/styles.ts @@ -0,0 +1,16 @@ +import { Card } from 'antd'; +import styled from 'styled-components'; + +export const SelectTypeContainer = styled.div` + &&& { + padding: 1rem; + } +`; + +export const AlertTypeCard = styled(Card)` + &&& { + margin: 5px; + width: 21rem; + cursor: pointer; + } +`; diff --git a/frontend/src/container/CreateAlertRule/SelectAlertType/types.ts b/frontend/src/container/CreateAlertRule/SelectAlertType/types.ts new file mode 100644 index 0000000..670f5a2 --- /dev/null +++ b/frontend/src/container/CreateAlertRule/SelectAlertType/types.ts @@ -0,0 +1,7 @@ +import { AlertTypes } from 'types/api/alerts/alertTypes'; + +export interface OptionType { + title: string; + selection: AlertTypes; + description: string; +} diff --git a/frontend/src/container/CreateAlertRule/config.ts b/frontend/src/container/CreateAlertRule/config.ts new file mode 100644 index 0000000..fe52bb1 --- /dev/null +++ b/frontend/src/container/CreateAlertRule/config.ts @@ -0,0 +1,8 @@ +import { AlertTypes } from 'types/api/alerts/alertTypes'; +import { DataSource } from 'types/common/queryBuilder'; + +export const ALERT_TYPE_VS_SOURCE_MAPPING = { + [DataSource.LOGS]: AlertTypes.LOGS_BASED_ALERT, + [DataSource.METRICS]: AlertTypes.METRICS_BASED_ALERT, + [DataSource.TRACES]: AlertTypes.TRACES_BASED_ALERT, +}; diff --git a/frontend/src/container/CreateAlertRule/defaults.ts b/frontend/src/container/CreateAlertRule/defaults.ts new file mode 100644 index 0000000..677f4ac --- /dev/null +++ b/frontend/src/container/CreateAlertRule/defaults.ts @@ -0,0 +1,152 @@ +import { ENTITY_VERSION_V4 } from 'constants/app'; +import { + initialQueryBuilderFormValuesMap, + initialQueryPromQLData, + PANEL_TYPES, +} from 'constants/queryBuilder'; +import { AlertTypes } from 'types/api/alerts/alertTypes'; +import { + AlertDef, + defaultCompareOp, + defaultEvalWindow, + defaultMatchType, +} from 'types/api/alerts/def'; +import { EQueryType } from 'types/common/dashboard'; + +const defaultAlertDescription = + 'This alert is fired when the defined metric (current value: {{$value}}) crosses the threshold ({{$threshold}})'; +const defaultAlertSummary = + 'The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}'; + +const defaultAnnotations = { + description: defaultAlertDescription, + summary: defaultAlertSummary, +}; + +export const alertDefaults: AlertDef = { + alertType: AlertTypes.METRICS_BASED_ALERT, + version: ENTITY_VERSION_V4, + condition: { + compositeQuery: { + builderQueries: { + A: initialQueryBuilderFormValuesMap.metrics, + }, + promQueries: { A: initialQueryPromQLData }, + chQueries: { + A: { + name: 'A', + query: ``, + legend: '', + disabled: false, + }, + }, + queryType: EQueryType.QUERY_BUILDER, + panelType: PANEL_TYPES.TIME_SERIES, + unit: undefined, + }, + op: defaultCompareOp, + matchType: defaultMatchType, + }, + labels: { + severity: 'warning', + }, + annotations: defaultAnnotations, + evalWindow: defaultEvalWindow, +}; + +export const logAlertDefaults: AlertDef = { + alertType: AlertTypes.LOGS_BASED_ALERT, + condition: { + compositeQuery: { + builderQueries: { + A: initialQueryBuilderFormValuesMap.logs, + }, + promQueries: { A: initialQueryPromQLData }, + chQueries: { + A: { + name: 'A', + query: `select \ntoStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 30 MINUTE) AS interval, \ntoFloat64(count()) as value \nFROM signoz_logs.distributed_logs \nWHERE timestamp BETWEEN {{.start_timestamp_nano}} AND {{.end_timestamp_nano}} \nGROUP BY interval;\n\n-- available variables:\n-- \t{{.start_timestamp_nano}}\n-- \t{{.end_timestamp_nano}}\n\n-- required columns (or alias):\n-- \tvalue\n-- \tinterval`, + legend: '', + disabled: false, + }, + }, + queryType: EQueryType.QUERY_BUILDER, + panelType: PANEL_TYPES.TIME_SERIES, + unit: undefined, + }, + op: defaultCompareOp, + matchType: '4', + }, + labels: { + severity: 'warning', + }, + annotations: defaultAnnotations, + evalWindow: defaultEvalWindow, +}; + +export const traceAlertDefaults: AlertDef = { + alertType: AlertTypes.TRACES_BASED_ALERT, + condition: { + compositeQuery: { + builderQueries: { + A: initialQueryBuilderFormValuesMap.traces, + }, + promQueries: { A: initialQueryPromQLData }, + chQueries: { + A: { + name: 'A', + query: `SELECT \n\ttoStartOfInterval(timestamp, INTERVAL 1 MINUTE) AS interval, \n\ttagMap['peer.service'] AS op_name, \n\ttoFloat64(avg(durationNano)) AS value \nFROM signoz_traces.distributed_signoz_index_v2 \nWHERE tagMap['peer.service']!='' \nAND timestamp BETWEEN {{.start_datetime}} AND {{.end_datetime}} \nGROUP BY (op_name, interval);\n\n-- available variables:\n-- \t{{.start_datetime}}\n-- \t{{.end_datetime}}\n\n-- required column alias:\n-- \tvalue\n-- \tinterval`, + legend: '', + disabled: false, + }, + }, + queryType: EQueryType.QUERY_BUILDER, + panelType: PANEL_TYPES.TIME_SERIES, + unit: undefined, + }, + op: defaultCompareOp, + matchType: '4', + }, + labels: { + severity: 'warning', + }, + annotations: defaultAnnotations, + evalWindow: defaultEvalWindow, +}; + +export const exceptionAlertDefaults: AlertDef = { + alertType: AlertTypes.EXCEPTIONS_BASED_ALERT, + condition: { + compositeQuery: { + builderQueries: { + A: initialQueryBuilderFormValuesMap.traces, + }, + promQueries: { A: initialQueryPromQLData }, + chQueries: { + A: { + name: 'A', + query: `SELECT \n\tcount() as value,\n\ttoStartOfInterval(timestamp, toIntervalMinute(1)) AS interval,\n\tserviceName\nFROM signoz_traces.distributed_signoz_error_index_v2\nWHERE exceptionType !='OSError'\nAND timestamp BETWEEN {{.start_datetime}} AND {{.end_datetime}}\nGROUP BY serviceName, interval;\n\n-- available variables:\n-- \t{{.start_datetime}}\n-- \t{{.end_datetime}}\n\n-- required column alias:\n-- \tvalue\n-- \tinterval`, + legend: '', + disabled: false, + }, + }, + queryType: EQueryType.QUERY_BUILDER, + panelType: PANEL_TYPES.TIME_SERIES, + unit: undefined, + }, + op: defaultCompareOp, + matchType: '4', + }, + labels: { + severity: 'warning', + }, + annotations: defaultAnnotations, + evalWindow: defaultEvalWindow, +}; + +export const ALERTS_VALUES_MAP: Record = { + [AlertTypes.METRICS_BASED_ALERT]: alertDefaults, + [AlertTypes.LOGS_BASED_ALERT]: logAlertDefaults, + [AlertTypes.TRACES_BASED_ALERT]: traceAlertDefaults, + [AlertTypes.EXCEPTIONS_BASED_ALERT]: exceptionAlertDefaults, +}; diff --git a/frontend/src/container/CreateAlertRule/index.tsx b/frontend/src/container/CreateAlertRule/index.tsx new file mode 100644 index 0000000..a592453 --- /dev/null +++ b/frontend/src/container/CreateAlertRule/index.tsx @@ -0,0 +1,85 @@ +import { Form, Row } from 'antd'; +import { ENTITY_VERSION_V4 } from 'constants/app'; +import FormAlertRules from 'container/FormAlertRules'; +import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam'; +import { useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { AlertTypes } from 'types/api/alerts/alertTypes'; +import { AlertDef } from 'types/api/alerts/def'; + +import { ALERT_TYPE_VS_SOURCE_MAPPING } from './config'; +import { + alertDefaults, + exceptionAlertDefaults, + logAlertDefaults, + traceAlertDefaults, +} from './defaults'; +import SelectAlertType from './SelectAlertType'; + +function CreateRules(): JSX.Element { + const [initValues, setInitValues] = useState(null); + const [alertType, setAlertType] = useState( + AlertTypes.METRICS_BASED_ALERT, + ); + + const location = useLocation(); + const queryParams = new URLSearchParams(location.search); + const version = queryParams.get('version'); + + const compositeQuery = useGetCompositeQueryParam(); + + const [formInstance] = Form.useForm(); + + const onSelectType = (typ: AlertTypes): void => { + setAlertType(typ); + switch (typ) { + case AlertTypes.LOGS_BASED_ALERT: + setInitValues(logAlertDefaults); + break; + case AlertTypes.TRACES_BASED_ALERT: + setInitValues(traceAlertDefaults); + break; + case AlertTypes.EXCEPTIONS_BASED_ALERT: + setInitValues(exceptionAlertDefaults); + break; + default: + setInitValues({ + ...alertDefaults, + version: version || ENTITY_VERSION_V4, + }); + } + }; + + useEffect(() => { + if (!compositeQuery) { + return; + } + const dataSource = compositeQuery?.builder?.queryData[0]?.dataSource; + + const alertType = ALERT_TYPE_VS_SOURCE_MAPPING[dataSource]; + + if (alertType) { + onSelectType(alertType); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [compositeQuery]); + + if (!initValues) { + return ( + + + + ); + } + + return ( + + ); +} + +export default CreateRules; diff --git a/frontend/src/container/Download/Download.styles.scss b/frontend/src/container/Download/Download.styles.scss new file mode 100644 index 0000000..9352530 --- /dev/null +++ b/frontend/src/container/Download/Download.styles.scss @@ -0,0 +1,4 @@ +.download-button { + display: flex; + align-items: center; +} \ No newline at end of file diff --git a/frontend/src/container/Download/Download.tsx b/frontend/src/container/Download/Download.tsx new file mode 100644 index 0000000..666b015 --- /dev/null +++ b/frontend/src/container/Download/Download.tsx @@ -0,0 +1,77 @@ +import './Download.styles.scss'; + +import { CloudDownloadOutlined } from '@ant-design/icons'; +import { Button, Dropdown, MenuProps } from 'antd'; +import { Excel } from 'antd-table-saveas-excel'; +import { unparse } from 'papaparse'; + +import { DownloadProps } from './Download.types'; + +function Download({ data, isLoading, fileName }: DownloadProps): JSX.Element { + const downloadExcelFile = (): void => { + const headers = Object.keys(Object.assign({}, ...data)).map((item) => { + const updatedTitle = item + .split('_') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + return { + title: updatedTitle, + dataIndex: item, + }; + }); + const excel = new Excel(); + excel + .addSheet(fileName) + .addColumns(headers) + .addDataSource(data, { + str2Percent: true, + }) + .saveAs(`${fileName}.xlsx`); + }; + + const downloadCsvFile = (): void => { + const csv = unparse(data); + const csvBlob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); + const csvUrl = URL.createObjectURL(csvBlob); + const downloadLink = document.createElement('a'); + downloadLink.href = csvUrl; + downloadLink.download = `${fileName}.csv`; + downloadLink.click(); + downloadLink.remove(); + }; + + const menu: MenuProps = { + items: [ + { + key: 'download-as-excel', + label: 'Excel', + onClick: downloadExcelFile, + }, + { + key: 'download-as-csv', + label: 'CSV', + onClick: downloadCsvFile, + }, + ], + }; + + return ( + + + + ); +} + +Download.defaultProps = { + isLoading: undefined, +}; + +export default Download; diff --git a/frontend/src/container/Download/Download.types.ts b/frontend/src/container/Download/Download.types.ts new file mode 100644 index 0000000..0757ed6 --- /dev/null +++ b/frontend/src/container/Download/Download.types.ts @@ -0,0 +1,10 @@ +export type DownloadOptions = { + isDownloadEnabled: boolean; + fileName: string; +}; + +export type DownloadProps = { + data: Record[]; + isLoading?: boolean; + fileName: string; +}; diff --git a/frontend/src/container/EditAlertChannels/index.tsx b/frontend/src/container/EditAlertChannels/index.tsx new file mode 100644 index 0000000..29d7816 --- /dev/null +++ b/frontend/src/container/EditAlertChannels/index.tsx @@ -0,0 +1,454 @@ +import { Form } from 'antd'; +import editEmail from 'api/channels/editEmail'; +import editMsTeamsApi from 'api/channels/editMsTeams'; +import editOpsgenie from 'api/channels/editOpsgenie'; +import editPagerApi from 'api/channels/editPager'; +import editSlackApi from 'api/channels/editSlack'; +import editWebhookApi from 'api/channels/editWebhook'; +import testEmail from 'api/channels/testEmail'; +import testMsTeamsApi from 'api/channels/testMsTeams'; +import testOpsgenie from 'api/channels/testOpsgenie'; +import testPagerApi from 'api/channels/testPager'; +import testSlackApi from 'api/channels/testSlack'; +import testWebhookApi from 'api/channels/testWebhook'; +import ROUTES from 'constants/routes'; +import { + ChannelType, + EmailChannel, + MsTeamsChannel, + OpsgenieChannel, + PagerChannel, + SlackChannel, + ValidatePagerChannel, + WebhookChannel, +} from 'container/CreateAlertChannels/config'; +import FormAlertChannels from 'container/FormAlertChannels'; +import { useNotifications } from 'hooks/useNotifications'; +import history from 'lib/history'; +import { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useParams } from 'react-router-dom'; + +function EditAlertChannels({ + initialValue, +}: EditAlertChannelsProps): JSX.Element { + // init namespace for translations + const { t } = useTranslation('channels'); + + const [formInstance] = Form.useForm(); + const [selectedConfig, setSelectedConfig] = useState< + Partial< + SlackChannel & + WebhookChannel & + PagerChannel & + MsTeamsChannel & + OpsgenieChannel & + EmailChannel + > + >({ + ...initialValue, + }); + const [savingState, setSavingState] = useState(false); + const [testingState, setTestingState] = useState(false); + const { notifications } = useNotifications(); + const { id } = useParams<{ id: string }>(); + + const [type, setType] = useState( + initialValue?.type ? (initialValue.type as ChannelType) : ChannelType.Slack, + ); + + const onTypeChangeHandler = useCallback((value: string) => { + setType(value as ChannelType); + }, []); + + useEffect(() => { + formInstance.setFieldsValue({ + ...initialValue, + }); + }, [formInstance, initialValue]); + + const prepareSlackRequest = useCallback( + () => ({ + api_url: selectedConfig?.api_url || '', + channel: selectedConfig?.channel || '', + name: selectedConfig?.name || '', + send_resolved: true, + text: selectedConfig?.text || '', + title: selectedConfig?.title || '', + id, + }), + [id, selectedConfig], + ); + + const onSlackEditHandler = useCallback(async () => { + setSavingState(true); + + if (selectedConfig?.api_url === '') { + notifications.error({ + message: 'Error', + description: t('webhook_url_required'), + }); + setSavingState(false); + return; + } + + const response = await editSlackApi(prepareSlackRequest()); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_edit_done'), + }); + + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_edit_failed'), + }); + } + setSavingState(false); + }, [prepareSlackRequest, t, notifications, selectedConfig]); + + const prepareWebhookRequest = useCallback(() => { + const { name, username, password } = selectedConfig; + return { + api_url: selectedConfig?.api_url || '', + name: name || '', + send_resolved: true, + username, + password, + id, + }; + }, [id, selectedConfig]); + + const onWebhookEditHandler = useCallback(async () => { + setSavingState(true); + const { username, password } = selectedConfig; + + const showError = (msg: string): void => { + notifications.error({ + message: 'Error', + description: msg, + }); + }; + + if (selectedConfig?.api_url === '') { + showError(t('webhook_url_required')); + setSavingState(false); + return; + } + + if (username && (!password || password === '')) { + showError(t('username_no_password')); + setSavingState(false); + return; + } + + const response = await editWebhookApi(prepareWebhookRequest()); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_edit_done'), + }); + + history.replace(ROUTES.ALL_CHANNELS); + } else { + showError(response.error || t('channel_edit_failed')); + } + setSavingState(false); + }, [prepareWebhookRequest, t, notifications, selectedConfig]); + + const prepareEmailRequest = useCallback( + () => ({ + name: selectedConfig?.name || '', + to: selectedConfig.to || '', + html: selectedConfig.html || '', + headers: selectedConfig.headers || {}, + id, + }), + [id, selectedConfig], + ); + + const onEmailEditHandler = useCallback(async () => { + setSavingState(true); + const request = prepareEmailRequest(); + const response = await editEmail(request); + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_edit_done'), + }); + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_edit_failed'), + }); + } + setSavingState(false); + }, [prepareEmailRequest, t, notifications]); + + const preparePagerRequest = useCallback( + () => ({ + name: selectedConfig.name || '', + routing_key: selectedConfig.routing_key, + client: selectedConfig.client, + client_url: selectedConfig.client_url, + description: selectedConfig.description, + severity: selectedConfig.severity, + component: selectedConfig.component, + class: selectedConfig.class, + group: selectedConfig.group, + details: selectedConfig.details, + detailsArray: JSON.parse(selectedConfig.details || '{}'), + id, + }), + [id, selectedConfig], + ); + + const onPagerEditHandler = useCallback(async () => { + setSavingState(true); + const validationError = ValidatePagerChannel(selectedConfig as PagerChannel); + + if (validationError !== '') { + notifications.error({ + message: 'Error', + description: validationError, + }); + setSavingState(false); + return; + } + const response = await editPagerApi(preparePagerRequest()); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_edit_done'), + }); + + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_edit_failed'), + }); + } + setSavingState(false); + }, [preparePagerRequest, notifications, selectedConfig, t]); + + const prepareOpsgenieRequest = useCallback( + () => ({ + name: selectedConfig.name || '', + api_key: selectedConfig.api_key || '', + message: selectedConfig.message || '', + description: selectedConfig.description || '', + priority: selectedConfig.priority || '', + id, + }), + [id, selectedConfig], + ); + + const onOpsgenieEditHandler = useCallback(async () => { + setSavingState(true); + + if (selectedConfig?.api_key === '') { + notifications.error({ + message: 'Error', + description: t('api_key_required'), + }); + setSavingState(false); + return; + } + + const response = await editOpsgenie(prepareOpsgenieRequest()); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_edit_done'), + }); + + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_edit_failed'), + }); + } + setSavingState(false); + }, [prepareOpsgenieRequest, t, notifications, selectedConfig]); + + const prepareMsTeamsRequest = useCallback( + () => ({ + webhook_url: selectedConfig?.webhook_url || '', + name: selectedConfig?.name || '', + send_resolved: true, + text: selectedConfig?.text || '', + title: selectedConfig?.title || '', + id, + }), + [id, selectedConfig], + ); + + const onMsTeamsEditHandler = useCallback(async () => { + setSavingState(true); + + if (selectedConfig?.webhook_url === '') { + notifications.error({ + message: 'Error', + description: t('webhook_url_required'), + }); + setSavingState(false); + return; + } + + const response = await editMsTeamsApi(prepareMsTeamsRequest()); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_edit_done'), + }); + + history.replace(ROUTES.ALL_CHANNELS); + } else { + notifications.error({ + message: 'Error', + description: response.error || t('channel_edit_failed'), + }); + } + setSavingState(false); + }, [prepareMsTeamsRequest, t, notifications, selectedConfig]); + + const onSaveHandler = useCallback( + (value: ChannelType) => { + if (value === ChannelType.Slack) { + onSlackEditHandler(); + } else if (value === ChannelType.Webhook) { + onWebhookEditHandler(); + } else if (value === ChannelType.Pagerduty) { + onPagerEditHandler(); + } else if (value === ChannelType.MsTeams) { + onMsTeamsEditHandler(); + } else if (value === ChannelType.Opsgenie) { + onOpsgenieEditHandler(); + } else if (value === ChannelType.Email) { + onEmailEditHandler(); + } + }, + [ + onSlackEditHandler, + onWebhookEditHandler, + onPagerEditHandler, + onMsTeamsEditHandler, + onOpsgenieEditHandler, + onEmailEditHandler, + ], + ); + + const performChannelTest = useCallback( + async (channelType: ChannelType) => { + setTestingState(true); + try { + let request; + let response; + switch (channelType) { + case ChannelType.Webhook: + request = prepareWebhookRequest(); + response = await testWebhookApi(request); + break; + case ChannelType.Slack: + request = prepareSlackRequest(); + response = await testSlackApi(request); + break; + case ChannelType.Pagerduty: + request = preparePagerRequest(); + if (request) response = await testPagerApi(request); + break; + case ChannelType.MsTeams: + request = prepareMsTeamsRequest(); + if (request) response = await testMsTeamsApi(request); + break; + case ChannelType.Opsgenie: + request = prepareOpsgenieRequest(); + if (request) response = await testOpsgenie(request); + break; + case ChannelType.Email: + request = prepareEmailRequest(); + if (request) response = await testEmail(request); + break; + default: + notifications.error({ + message: 'Error', + description: t('test_unsupported'), + }); + setTestingState(false); + return; + } + + if (response && response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: t('channel_test_done'), + }); + } else { + notifications.error({ + message: 'Error', + description: t('channel_test_failed'), + }); + } + } catch (error) { + notifications.error({ + message: 'Error', + description: t('channel_test_failed'), + }); + } + setTestingState(false); + }, + [ + t, + prepareWebhookRequest, + preparePagerRequest, + prepareSlackRequest, + prepareMsTeamsRequest, + prepareOpsgenieRequest, + prepareEmailRequest, + notifications, + ], + ); + + const onTestHandler = useCallback( + async (value: ChannelType) => { + performChannelTest(value); + }, + [performChannelTest], + ); + + return ( + + ); +} + +interface EditAlertChannelsProps { + initialValue: { + [x: string]: unknown; + }; +} + +export default EditAlertChannels; diff --git a/frontend/src/container/EditRules/index.tsx b/frontend/src/container/EditRules/index.tsx new file mode 100644 index 0000000..206c9d2 --- /dev/null +++ b/frontend/src/container/EditRules/index.tsx @@ -0,0 +1,30 @@ +import { Form } from 'antd'; +import FormAlertRules from 'container/FormAlertRules'; +import { AlertTypes } from 'types/api/alerts/alertTypes'; +import { AlertDef } from 'types/api/alerts/def'; + +function EditRules({ initialValue, ruleId }: EditRulesProps): JSX.Element { + const [formInstance] = Form.useForm(); + + return ( +
    + +
    + ); +} + +interface EditRulesProps { + initialValue: AlertDef; + ruleId: number; +} + +export default EditRules; diff --git a/frontend/src/container/EditRules/styles.ts b/frontend/src/container/EditRules/styles.ts new file mode 100644 index 0000000..0a201f8 --- /dev/null +++ b/frontend/src/container/EditRules/styles.ts @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +export const ButtonContainer = styled.div` + &&& { + display: flex; + justify-content: flex-end; + align-items: center; + + margin-top: 2rem; + } +`; diff --git a/frontend/src/container/EmptyLogsSearch/EmptyLogsSearch.styles.scss b/frontend/src/container/EmptyLogsSearch/EmptyLogsSearch.styles.scss new file mode 100644 index 0000000..c9e5459 --- /dev/null +++ b/frontend/src/container/EmptyLogsSearch/EmptyLogsSearch.styles.scss @@ -0,0 +1,30 @@ +.empty-logs-search-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 240px; + + .empty-logs-search-container-content { + display: flex; + flex-direction: column; + gap: 4px; + + color: var(--text-vanilla-400); + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 18px; /* 128.571% */ + letter-spacing: -0.07px; + + .empty-state-svg { + height: 50px; + width: 50px; + } + + .sub-text { + font-weight: 600; + } + } +} diff --git a/frontend/src/container/EmptyLogsSearch/EmptyLogsSearch.tsx b/frontend/src/container/EmptyLogsSearch/EmptyLogsSearch.tsx new file mode 100644 index 0000000..af3d768 --- /dev/null +++ b/frontend/src/container/EmptyLogsSearch/EmptyLogsSearch.tsx @@ -0,0 +1,21 @@ +import './EmptyLogsSearch.styles.scss'; + +import { Typography } from 'antd'; + +export default function EmptyLogsSearch(): JSX.Element { + return ( +
    +
    + thinking-emoji + + This query had no results. + Edit your query and try again! + +
    +
    + ); +} diff --git a/frontend/src/container/ErrorDetails/config.ts b/frontend/src/container/ErrorDetails/config.ts new file mode 100644 index 0000000..5ce48c4 --- /dev/null +++ b/frontend/src/container/ErrorDetails/config.ts @@ -0,0 +1,8 @@ +export const keyToExclude = [ + 'exceptionStacktrace', + 'exceptionType', + 'errorId', + 'timestamp', + 'exceptionMessage', + 'exceptionEscaped', +]; diff --git a/frontend/src/container/ErrorDetails/index.tsx b/frontend/src/container/ErrorDetails/index.tsx new file mode 100644 index 0000000..33a86f9 --- /dev/null +++ b/frontend/src/container/ErrorDetails/index.tsx @@ -0,0 +1,183 @@ +import './styles.scss'; + +import { Button, Divider, Space, Typography } from 'antd'; +import getNextPrevId from 'api/errors/getNextPrevId'; +import Editor from 'components/Editor'; +import { ResizeTable } from 'components/ResizeTable'; +import { getNanoSeconds } from 'container/AllError/utils'; +import dayjs from 'dayjs'; +import { useNotifications } from 'hooks/useNotifications'; +import createQueryParams from 'lib/createQueryParams'; +import history from 'lib/history'; +import { urlKey } from 'pages/ErrorDetails/utils'; +import { useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; +import { useLocation } from 'react-router-dom'; +import { PayloadProps as GetByErrorTypeAndServicePayload } from 'types/api/errors/getByErrorTypeAndService'; + +import { keyToExclude } from './config'; +import { DashedContainer, EditorContainer, EventContainer } from './styles'; + +function ErrorDetails(props: ErrorDetailsProps): JSX.Element { + const { idPayload } = props; + const { t } = useTranslation(['errorDetails', 'common']); + const { search, pathname } = useLocation(); + + const params = useMemo(() => new URLSearchParams(search), [search]); + + const errorId = params.get(urlKey.errorId); + const serviceName = params.get(urlKey.serviceName); + const errorType = params.get(urlKey.exceptionType); + const timestamp = params.get(urlKey.timestamp); + + const { data: nextPrevData, status: nextPrevStatus } = useQuery( + [ + idPayload.errorId, + idPayload.groupID, + idPayload.timestamp, + errorId, + serviceName, + errorType, + timestamp, + ], + { + queryFn: () => + getNextPrevId({ + errorID: errorId || idPayload.errorId, + groupID: idPayload.groupID, + timestamp: timestamp || getNanoSeconds(idPayload.timestamp), + }), + }, + ); + + const errorDetail = idPayload; + + const [stackTraceValue] = useState(errorDetail.exceptionStacktrace); + + const columns = useMemo( + () => [ + { + title: 'Key', + width: 100, + dataIndex: 'key', + key: 'key', + }, + { + title: 'Value', + dataIndex: 'value', + width: 100, + key: 'value', + }, + ], + [], + ); + + const { notifications } = useNotifications(); + + const onClickErrorIdHandler = async ( + id: string, + timestamp: string, + ): Promise => { + try { + if (id.length === 0) { + notifications.error({ + message: 'Error Id cannot be empty', + }); + return; + } + + const queryParams = { + groupId: idPayload.groupID, + timestamp: getNanoSeconds(timestamp), + errorId: id, + }; + + history.replace(`${pathname}?${createQueryParams(queryParams)}`); + } catch (error) { + notifications.error({ + message: t('something_went_wrong'), + }); + } + }; + + const timeStamp = dayjs(errorDetail.timestamp); + + const data: { key: string; value: string }[] = Object.keys(errorDetail) + .filter((e) => !keyToExclude.includes(e)) + .map((key) => ({ + key, + value: errorDetail[key as keyof GetByErrorTypeAndServicePayload], + })); + + const onClickTraceHandler = (): void => { + history.push(`/trace/${errorDetail.traceID}?spanId=${errorDetail.spanID}`); + }; + + return ( + <> + {errorDetail.exceptionType} + {errorDetail.exceptionMessage} + + + +
    + Event {errorDetail.errorId} + {timeStamp.format('MMM DD YYYY hh:mm:ss A')} +
    +
    + + + + +
    +
    + + + {t('see_trace_graph')} + + + + {t('stack_trace')} +
    + +
    + + + + + + + + ); +} + +interface ErrorDetailsProps { + idPayload: GetByErrorTypeAndServicePayload; +} + +export default ErrorDetails; diff --git a/frontend/src/container/ErrorDetails/styles.scss b/frontend/src/container/ErrorDetails/styles.scss new file mode 100644 index 0000000..31194b4 --- /dev/null +++ b/frontend/src/container/ErrorDetails/styles.scss @@ -0,0 +1,3 @@ +.error-container { + height: 50vh; +} diff --git a/frontend/src/container/ErrorDetails/styles.ts b/frontend/src/container/ErrorDetails/styles.ts new file mode 100644 index 0000000..d1cd032 --- /dev/null +++ b/frontend/src/container/ErrorDetails/styles.ts @@ -0,0 +1,28 @@ +import { grey } from '@ant-design/colors'; +import styled from 'styled-components'; + +export const DashedContainer = styled.div` + border: ${`1px dashed ${grey[0]}`}; + box-sizing: border-box; + border-radius: 0.25rem; + display: flex; + justify-content: space-between; + padding: 1rem; + margin-top: 1.875rem; + margin-bottom: 1.625rem; + align-items: center; +`; + +export const ButtonContainer = styled.div` + display: flex; + gap: 1rem; +`; + +export const EventContainer = styled.div` + display: flex; + justify-content: space-between; +`; + +export const EditorContainer = styled.div` + margin-top: 1.5rem; +`; diff --git a/frontend/src/container/ExplorerControlPanel/ExplorerControlPanel.interfaces.ts b/frontend/src/container/ExplorerControlPanel/ExplorerControlPanel.interfaces.ts new file mode 100644 index 0000000..b115897 --- /dev/null +++ b/frontend/src/container/ExplorerControlPanel/ExplorerControlPanel.interfaces.ts @@ -0,0 +1,8 @@ +import { OptionsMenuConfig } from 'container/OptionsMenu/types'; + +export type ExplorerControlPanelProps = { + selectedOptionFormat: string; + isShowPageSize: boolean; + isLoading: boolean; + optionsMenuConfig?: OptionsMenuConfig; +}; diff --git a/frontend/src/container/ExplorerControlPanel/index.tsx b/frontend/src/container/ExplorerControlPanel/index.tsx new file mode 100644 index 0000000..d0823ef --- /dev/null +++ b/frontend/src/container/ExplorerControlPanel/index.tsx @@ -0,0 +1,33 @@ +import { Col, Row } from 'antd'; +import OptionsMenu from 'container/OptionsMenu'; +import PageSizeSelect from 'container/PageSizeSelect'; + +import { ExplorerControlPanelProps } from './ExplorerControlPanel.interfaces'; +import { ContainerStyled } from './styles'; + +function ExplorerControlPanel({ + selectedOptionFormat, + isLoading, + isShowPageSize, + optionsMenuConfig, +}: ExplorerControlPanelProps): JSX.Element { + return ( + + + {optionsMenuConfig && ( +

    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 Qdd`az+n&6UPa5+3x*H@HPRFo~RDEoY6g&UD{ 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

-eR(w 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 z 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