├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── feature-request.yml │ ├── feature.yml │ ├── question.yml │ └── task.yml ├── pull_request_template.md └── workflows │ ├── docker-image.yml │ └── github-repo-stats.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── ROADMAP.md ├── api ├── .coveragerc ├── .env ├── Dockerfile ├── README.md ├── config │ ├── __init__.py │ ├── mysql_config.py │ └── settings.py ├── main.py ├── requirements.txt ├── src │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── analyze_api.py │ │ ├── configurations_api.py │ │ ├── environment_api.py │ │ └── tests_api.py │ ├── enums │ │ ├── __init__.py │ │ ├── algorithms.py │ │ ├── environment.py │ │ ├── metric.py │ │ └── status.py │ ├── exceptions │ │ ├── __init__.py │ │ └── exceptions.py │ ├── models │ │ ├── __init__.py │ │ ├── base.py │ │ ├── env_info.py │ │ ├── test_run.py │ │ ├── test_run_metric.py │ │ └── test_suite.py │ ├── services │ │ ├── __init__.py │ │ ├── analyze_service.py │ │ ├── cadvisor_service.py │ │ ├── environment_service.py │ │ ├── k8s_service.py │ │ ├── metrics_service.py │ │ └── test_suites_service.py │ └── utils │ │ ├── __init__.py │ │ ├── database_manager.py │ │ ├── metrics_collection_manager.py │ │ ├── metrics_collector.py │ │ └── test_suite_serializer.py └── tests │ ├── __init__.py │ ├── test_analyze_api.py │ ├── test_configurations_api.py │ ├── test_environment_api.py │ ├── test_metrics_collection_manager.py │ └── test_tests_api.py ├── curl ├── .env ├── .gitignore ├── Dockerfile ├── README.md ├── nest-cli.json ├── package-lock.json ├── package.json ├── scripts │ └── run-curl-loop.sh ├── src │ ├── app.module.ts │ ├── config │ │ └── configuration.ts │ ├── curl │ │ ├── curl.controller.spec.ts │ │ ├── curl.controller.ts │ │ ├── curl.module.ts │ │ ├── curl.service.spec.ts │ │ └── curl.service.ts │ ├── dto │ │ ├── curl-request.dto.spec.ts │ │ ├── curl-request.dto.ts │ │ └── curl-response.dto.ts │ └── main.ts ├── tsconfig.build.json └── tsconfig.json ├── mysql ├── Dockerfile └── scripts │ └── init-db.sql ├── nginx ├── Dockerfile ├── app.js ├── nginx.conf └── start.sh ├── portal ├── .DS_Store ├── .env ├── .env.development ├── .env.gh-pages ├── .env.production ├── .eslintrc.json ├── .gitignore ├── Dockerfile ├── README.md ├── docker-entrypoint.sh ├── mock-server │ ├── package.json │ ├── src │ │ ├── algorithms.json │ │ ├── all-experiments.json │ │ ├── classic-test.json │ │ ├── hybrid-test.json │ │ ├── iterations.json │ │ ├── message_sizes.json │ │ ├── quantum-test.json │ │ ├── router.ts │ │ ├── server.ts │ │ └── test.json │ └── tsconfig.json ├── nginx.conf ├── package.json ├── public │ ├── favicon.ico │ ├── fonts.css │ ├── fonts │ │ ├── ATTAleckSans_W_Bd.eot │ │ ├── ATTAleckSans_W_Bd.svg │ │ ├── ATTAleckSans_W_Bd.woff │ │ ├── ATTAleckSans_W_Bd.woff2 │ │ ├── ATTAleckSans_W_Md.eot │ │ ├── ATTAleckSans_W_Md.svg │ │ ├── ATTAleckSans_W_Md.woff │ │ ├── ATTAleckSans_W_Md.woff2 │ │ ├── ATTAleckSans_W_Rg.eot │ │ ├── ATTAleckSans_W_Rg.svg │ │ ├── ATTAleckSans_W_Rg.woff │ │ └── ATTAleckSans_W_Rg.woff2 │ ├── index.html │ ├── inject-env-variables.js │ ├── logo192.png │ └── robots.txt ├── src │ ├── app │ │ ├── App.module.scss │ │ ├── App.test.tsx │ │ ├── App.tsx │ │ ├── apis.ts │ │ ├── components │ │ │ ├── all-experiments │ │ │ │ ├── Experiments.module.scss │ │ │ │ ├── Experiments.test.tsx │ │ │ │ ├── Experiments.tsx │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── Experiments.test.tsx.snap │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useExperimentsData.test.ts │ │ │ │ │ └── useExperimentsData.ts │ │ │ │ ├── models │ │ │ │ │ └── experiments.interface.ts │ │ │ │ ├── translate │ │ │ │ │ └── en.ts │ │ │ │ └── utils │ │ │ │ │ ├── parse-experiments-data.utils.test.ts │ │ │ │ │ └── parse-experiments-data.utils.ts │ │ │ ├── dashboard │ │ │ │ ├── Dashboard.module.scss │ │ │ │ ├── Dashboard.test.tsx │ │ │ │ ├── Dashboard.tsx │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── Dashboard.test.tsx.snap │ │ │ │ ├── components │ │ │ │ │ ├── charts │ │ │ │ │ │ ├── BarChart │ │ │ │ │ │ │ ├── BarChart.module.scss │ │ │ │ │ │ │ ├── BarChart.test.tsx │ │ │ │ │ │ │ ├── BarChart.tsx │ │ │ │ │ │ │ ├── barChart.const.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── models │ │ │ │ │ │ │ │ └── BarChart.model.ts │ │ │ │ │ │ ├── Charts.const.ts │ │ │ │ │ │ ├── Charts.module.scss │ │ │ │ │ │ ├── Charts.test.tsx │ │ │ │ │ │ ├── Charts.tsx │ │ │ │ │ │ ├── LineChart │ │ │ │ │ │ │ ├── LineChart.const.ts │ │ │ │ │ │ │ ├── LineChart.module.scss │ │ │ │ │ │ │ ├── LineChart.test.tsx │ │ │ │ │ │ │ ├── LineChart.tsx │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── models │ │ │ │ │ │ │ │ └── LineChart.model.ts │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ └── Charts.test.tsx.snap │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ ├── charts.utils.test.ts │ │ │ │ │ │ │ └── charts.utils.ts │ │ │ │ │ └── main-boxes │ │ │ │ │ │ ├── MainBoxes.module.scss │ │ │ │ │ │ ├── MainBoxes.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── translate │ │ │ │ │ └── en.ts │ │ │ │ └── utils │ │ │ │ │ ├── dashboard-data-report.test.ts │ │ │ │ │ └── dashboard-data-report.util.ts │ │ │ ├── home │ │ │ │ ├── Home.module.scss │ │ │ │ ├── Home.test.tsx │ │ │ │ ├── Home.tsx │ │ │ │ ├── components │ │ │ │ │ ├── experiment │ │ │ │ │ │ ├── Experiment.module.scss │ │ │ │ │ │ ├── Experiment.test.tsx │ │ │ │ │ │ ├── Experiment.tsx │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── __mocks__ │ │ │ │ │ │ │ │ └── mocks.ts │ │ │ │ │ │ │ ├── charts │ │ │ │ │ │ │ │ ├── Charts.module.scss │ │ │ │ │ │ │ │ ├── Charts.test.tsx │ │ │ │ │ │ │ │ ├── Charts.tsx │ │ │ │ │ │ │ │ ├── __mocks__ │ │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ │ └── mocks.ts │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ └── dynamic-chart │ │ │ │ │ │ │ │ │ │ ├── DynamicChart.module.scss │ │ │ │ │ │ │ │ │ │ ├── DynamicChart.test.tsx │ │ │ │ │ │ │ │ │ │ ├── DynamicChart.tsx │ │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ ├── custom-dropdown-indicator │ │ │ │ │ │ │ │ │ │ │ ├── CustomDropdownIndicator.test.tsx │ │ │ │ │ │ │ │ │ │ │ ├── CustomDropdownIndicator.tsx │ │ │ │ │ │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ │ └── CustomDropdownIndicator.test.tsx.snap │ │ │ │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ │ │ │ ├── custom-option │ │ │ │ │ │ │ │ │ │ │ ├── CustomOption.module.scss │ │ │ │ │ │ │ │ │ │ │ ├── CustomOption.test.tsx │ │ │ │ │ │ │ │ │ │ │ ├── CustomOption.tsx │ │ │ │ │ │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ │ └── CustomOption.test.tsx.snap │ │ │ │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ │ │ │ └── custom-value-container │ │ │ │ │ │ │ │ │ │ │ ├── CustomValueContainer.module.scss │ │ │ │ │ │ │ │ │ │ │ ├── CustomValueContainer.test.tsx │ │ │ │ │ │ │ │ │ │ │ ├── CustomValueContainer.tsx │ │ │ │ │ │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ └── CustomValueContainer.test.tsx.snap │ │ │ │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ │ │ ├── useDynamicChartData.test.ts │ │ │ │ │ │ │ │ │ │ └── useDynamicChartData.ts │ │ │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ │ │ │ └── dynamic-chart.interface.ts │ │ │ │ │ │ │ │ │ │ ├── translate │ │ │ │ │ │ │ │ │ │ └── en.ts │ │ │ │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ │ │ │ ├── dynamic-chart.utils.test.ts │ │ │ │ │ │ │ │ │ │ └── dynamic-chart.utils.ts │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ │ ├── useChartsData.test.ts │ │ │ │ │ │ │ │ │ └── useChartsData.ts │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ │ │ ├── bar-chart.const.ts │ │ │ │ │ │ │ │ │ └── line-chart-data.interface.ts │ │ │ │ │ │ │ │ ├── translate │ │ │ │ │ │ │ │ │ └── en.ts │ │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ │ │ ├── __mocks__ │ │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ │ └── mocks.ts │ │ │ │ │ │ │ │ │ ├── chart.utils.test.ts │ │ │ │ │ │ │ │ │ ├── chart.utils.ts │ │ │ │ │ │ │ │ │ ├── test-run.utils.test.ts │ │ │ │ │ │ │ │ │ └── test-run.utils.ts │ │ │ │ │ │ │ ├── delete-experiment-modal │ │ │ │ │ │ │ │ ├── DeleteExperimentModal.module.scss │ │ │ │ │ │ │ │ ├── DeleteExperimentModal.test.tsx │ │ │ │ │ │ │ │ ├── DeleteExperimentModal.tsx │ │ │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ │ │ └── DeleteExperimentModal.test.tsx.snap │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ └── translate │ │ │ │ │ │ │ │ │ └── en.ts │ │ │ │ │ │ │ ├── edit-experiment-modal │ │ │ │ │ │ │ │ ├── EditExperimentModal.module.scss │ │ │ │ │ │ │ │ ├── EditExperimentModal.test.tsx │ │ │ │ │ │ │ │ ├── EditExperimentModal.tsx │ │ │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ │ │ └── EditExperimentModal.test.tsx.snap │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ └── translate │ │ │ │ │ │ │ │ │ └── en.ts │ │ │ │ │ │ │ ├── experiment-table │ │ │ │ │ │ │ │ ├── ExperimentTable.module.scss │ │ │ │ │ │ │ │ ├── ExperimentTable.test.tsx │ │ │ │ │ │ │ │ ├── ExperimentTable.tsx │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ └── translate │ │ │ │ │ │ │ │ │ └── en.ts │ │ │ │ │ │ │ ├── experiment-tabs │ │ │ │ │ │ │ │ ├── ExperimentTabs.module.scss │ │ │ │ │ │ │ │ ├── ExperimentTabs.test.tsx │ │ │ │ │ │ │ │ ├── ExperimentTabs.tsx │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ └── experiment-tab-button │ │ │ │ │ │ │ │ │ │ ├── ExperimentTabButton.module.scss │ │ │ │ │ │ │ │ │ │ ├── ExperimentTabButton.test.tsx │ │ │ │ │ │ │ │ │ │ ├── ExperimentTabButton.tsx │ │ │ │ │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ │ │ │ └── ExperimentTabButton.test.tsx.snap │ │ │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ ├── useExperimentData.test.ts │ │ │ │ │ │ │ │ └── useExperimentData.ts │ │ │ │ │ │ │ ├── sub-header │ │ │ │ │ │ │ │ ├── SubHeader.module.scss │ │ │ │ │ │ │ │ ├── SubHeader.test.tsx │ │ │ │ │ │ │ │ ├── SubHeader.tsx │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ ├── translate │ │ │ │ │ │ │ │ │ └── en.ts │ │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ │ │ ├── data-to-csv.util.test.ts │ │ │ │ │ │ │ │ │ ├── data-to-csv.util.ts │ │ │ │ │ │ │ │ │ └── sub-header.utils.ts │ │ │ │ │ │ │ └── table-options │ │ │ │ │ │ │ │ ├── TableOptions.module.scss │ │ │ │ │ │ │ │ ├── TableOptions.test.tsx │ │ │ │ │ │ │ │ ├── TableOptions.tsx │ │ │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ │ └── TableOptions.test.tsx.snap │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ └── select-columns-popup │ │ │ │ │ │ │ │ │ ├── SelectColumnsPopup.module.scss │ │ │ │ │ │ │ │ │ ├── SelectColumnsPopup.test.tsx │ │ │ │ │ │ │ │ │ ├── SelectColumnsPopup.tsx │ │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ │ ├── translate │ │ │ │ │ │ │ │ │ └── en.ts │ │ │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ │ │ ├── convert-data-to-options.utils.test.ts │ │ │ │ │ │ │ │ │ └── convert-data-to-options.utils.ts │ │ │ │ │ │ │ │ ├── constants │ │ │ │ │ │ │ │ └── table-options.const.ts │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ └── translate │ │ │ │ │ │ │ │ └── en.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── translate │ │ │ │ │ │ │ └── en.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── sections-scrolling.utils.test.ts │ │ │ │ │ │ │ └── sections-scrolling.utils.ts │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── protocol-query │ │ │ │ ├── ProtocolQuery.const.ts │ │ │ │ ├── ProtocolQuery.model.ts │ │ │ │ ├── ProtocolQuery.module.scss │ │ │ │ ├── ProtocolQuery.test.tsx │ │ │ │ ├── ProtocolQuery.tsx │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── ProtocolQuery.test.tsx.snap │ │ │ │ ├── constants │ │ │ │ │ ├── algorithms-sections.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useDuplicateData.test.ts │ │ │ │ │ ├── useDuplicateData.ts │ │ │ │ │ ├── useGetAlgorithms.test.ts │ │ │ │ │ ├── useGetAlgorithms.ts │ │ │ │ │ ├── useGetIterations.test.ts │ │ │ │ │ ├── useGetIterations.ts │ │ │ │ │ ├── useMessageSizeData.test.ts │ │ │ │ │ └── useMessageSizeData.ts │ │ │ │ ├── index.ts │ │ │ │ ├── translate │ │ │ │ │ └── en.ts │ │ │ │ └── utils │ │ │ │ │ ├── convertBytesToHumanReadable.test.ts │ │ │ │ │ ├── convertBytesToHumanReadable.ts │ │ │ │ │ ├── handleAlgorithmsChange.test.ts │ │ │ │ │ ├── handleAlgorithmsChange.ts │ │ │ │ │ └── index.ts │ │ │ └── sub-header │ │ │ │ ├── SubHeader.module.scss │ │ │ │ ├── SubHeader.test.tsx │ │ │ │ ├── SubHeader.tsx │ │ │ │ ├── __snapshots__ │ │ │ │ └── SubHeader.test.tsx.snap │ │ │ │ ├── constants │ │ │ │ └── sub-header.const.ts │ │ │ │ ├── index.ts │ │ │ │ ├── models │ │ │ │ └── sub-header.interface.ts │ │ │ │ └── translate │ │ │ │ └── en.ts │ │ ├── hooks │ │ │ ├── useDashboardData.test.ts │ │ │ ├── useDashboardData.ts │ │ │ ├── useErrorMessage │ │ │ │ ├── index.ts │ │ │ │ ├── useErrorMessage.test.tsx │ │ │ │ └── useErrorMessage.tsx │ │ │ ├── useOutsideClick.test.tsx │ │ │ └── useOutsideClick.ts │ │ ├── models │ │ │ └── server-error.interface.ts │ │ ├── routes-navigation.const.ts │ │ ├── shared │ │ │ ├── components │ │ │ │ ├── att-button │ │ │ │ │ ├── Button.const.ts │ │ │ │ │ ├── Button.model.ts │ │ │ │ │ ├── Button.module.scss │ │ │ │ │ ├── Button.reference.scss │ │ │ │ │ ├── Button.test.tsx │ │ │ │ │ ├── Button.tsx │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── Button.test.tsx.snap │ │ │ │ │ └── index.ts │ │ │ │ ├── att-link │ │ │ │ │ ├── ExternalLink.test.tsx │ │ │ │ │ ├── ExternalLink.tsx │ │ │ │ │ ├── InternalLink.test.tsx │ │ │ │ │ ├── InternalLink.tsx │ │ │ │ │ ├── Link.const.ts │ │ │ │ │ ├── Link.model.ts │ │ │ │ │ ├── Link.module.scss │ │ │ │ │ ├── Link.util.ts │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ ├── ExternalLink.test.tsx.snap │ │ │ │ │ │ └── InternalLink.test.tsx.snap │ │ │ │ │ └── index.ts │ │ │ │ ├── att-select │ │ │ │ │ ├── AttSelect.const.ts │ │ │ │ │ ├── AttSelect.model.ts │ │ │ │ │ ├── AttSelect.module.scss │ │ │ │ │ ├── AttSelect.reference.scss │ │ │ │ │ ├── AttSelect.test.tsx │ │ │ │ │ ├── AttSelect.tsx │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── AttSelect.test.tsx.snap │ │ │ │ │ └── index.ts │ │ │ │ ├── att-spinner │ │ │ │ │ ├── Spinner.const.ts │ │ │ │ │ ├── Spinner.model.ts │ │ │ │ │ ├── Spinner.module.scss │ │ │ │ │ ├── Spinner.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── global-header │ │ │ │ │ ├── GlobalHeader.module.scss │ │ │ │ │ ├── GlobalHeader.test.tsx │ │ │ │ │ ├── GlobalHeader.tsx │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── GlobalHeader.test.tsx.snap │ │ │ │ │ └── index.ts │ │ │ │ ├── loading_bar │ │ │ │ │ ├── LoadingBar.module.scss │ │ │ │ │ ├── LoadingBar.test.tsx │ │ │ │ │ ├── LoadingBar.tsx │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── LoadingBar.test.tsx.snap │ │ │ │ │ └── index.ts │ │ │ │ ├── modal │ │ │ │ │ ├── BaseModal.module.scss │ │ │ │ │ ├── BaseModal.test.tsx │ │ │ │ │ ├── BaseModal.tsx │ │ │ │ │ ├── base-modal.const.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── multicolor-icon │ │ │ │ │ ├── MulticolorIcon.module.scss │ │ │ │ │ ├── MulticolorIcon.test.tsx │ │ │ │ │ ├── MulticolorIcon.tsx │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── MulticolorIcon.test.tsx.snap │ │ │ │ │ └── index.ts │ │ │ │ ├── navigation-tab │ │ │ │ │ ├── NavigationTab.module.scss │ │ │ │ │ ├── NavigationTab.test.tsx │ │ │ │ │ ├── NavigationTab.tsx │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── NavigationTab.test.tsx.snap │ │ │ │ │ └── index.ts │ │ │ │ ├── selector-custom-option │ │ │ │ │ ├── SelectorCustomOption.module.scss │ │ │ │ │ ├── SelectorCustomOption.test.tsx │ │ │ │ │ ├── SelectorCustomOption.tsx │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── SelectorCustomOption.test.tsx.snap │ │ │ │ │ ├── components │ │ │ │ │ │ ├── CustomInput.module.scss │ │ │ │ │ │ ├── CustomInput.test.tsx │ │ │ │ │ │ ├── CustomInput.tsx │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ └── CustomInput.test.tsx.snap │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── translate │ │ │ │ │ │ └── en.ts │ │ │ │ ├── table │ │ │ │ │ ├── Table.module.scss │ │ │ │ │ ├── Table.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── toast │ │ │ │ │ ├── ATTToastContent.module.scss │ │ │ │ │ ├── ATTToastContent.test.tsx │ │ │ │ │ ├── ATTToastContent.tsx │ │ │ │ │ ├── att-toast.service.test.tsx │ │ │ │ │ ├── att-toast.service.tsx │ │ │ │ │ └── toast-container-config.const.ts │ │ │ ├── constants │ │ │ │ ├── dashboard.ts │ │ │ │ ├── http.ts │ │ │ │ └── navigation-tabs.const.ts │ │ │ ├── context │ │ │ │ ├── RootContextState.tsx │ │ │ │ └── spinner │ │ │ │ │ ├── SpinnerProviderWrapper.test.tsx │ │ │ │ │ ├── SpinnerProviderWrapper.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── spinner.context.test.tsx │ │ │ │ │ ├── spinner.context.tsx │ │ │ │ │ └── spinner.interface.ts │ │ │ ├── hooks │ │ │ │ ├── useFetch │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useFetch.enum.ts │ │ │ │ │ ├── useFetch.interface.ts │ │ │ │ │ ├── useFetch.test.ts │ │ │ │ │ └── useFetch.ts │ │ │ │ └── useFetchSpinner │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useFetchSpinner.test.tsx │ │ │ │ │ └── useFetchSpinner.tsx │ │ │ ├── middlewares │ │ │ │ └── axiosMiddleware.ts │ │ │ ├── models │ │ │ │ ├── quantum.interface.ts │ │ │ │ ├── server-error.interface.ts │ │ │ │ ├── test-run-result.interface.ts │ │ │ │ └── url-params.interface.ts │ │ │ ├── translate │ │ │ │ └── en.ts │ │ │ └── utils │ │ │ │ ├── parseValue.test.ts │ │ │ │ ├── parseValue.ts │ │ │ │ ├── replaceParams │ │ │ │ ├── index.ts │ │ │ │ ├── replaceParams.test.ts │ │ │ │ └── replaceParams.ts │ │ │ │ └── translate-parser │ │ │ │ ├── index.ts │ │ │ │ ├── translate-parser.interface.ts │ │ │ │ ├── translate-parser.service.test.ts │ │ │ │ └── translate-parser.service.ts │ │ └── utils │ │ │ ├── download │ │ │ ├── downloadCsvFile.test.ts │ │ │ ├── downloadCsvFile.ts │ │ │ ├── downloadDataAsFile.test.ts │ │ │ ├── downloadDataAsFile.ts │ │ │ └── index.ts │ │ │ ├── parseValue.test.ts │ │ │ ├── parseValue.ts │ │ │ ├── snakeCaseToCamelCase.test.ts │ │ │ └── snakeCaseToCamelCase.ts │ ├── assets │ │ └── images │ │ │ ├── arrow-down-icon.svg │ │ │ ├── arrow-down-selector.svg │ │ │ ├── arrow-down.svg │ │ │ ├── arrow-left.svg │ │ │ ├── arrow-up.svg │ │ │ ├── bar.svg │ │ │ ├── check.svg │ │ │ ├── checked.svg │ │ │ ├── clean.svg │ │ │ ├── close.svg │ │ │ ├── define-params.svg │ │ │ ├── download.svg │ │ │ ├── duplicate.svg │ │ │ ├── eye-hover.svg │ │ │ ├── eye.svg │ │ │ ├── handshake.svg │ │ │ ├── keys.svg │ │ │ ├── line.svg │ │ │ ├── lock.svg │ │ │ ├── no-permissions.png │ │ │ ├── pencil.svg │ │ │ ├── qujata-logo.svg │ │ │ ├── select-columns-checked.svg │ │ │ ├── select-columns.svg │ │ │ ├── sort-ascending.svg │ │ │ ├── sort-descending.svg │ │ │ ├── trash-hover.svg │ │ │ ├── trash.svg │ │ │ ├── unchecked.svg │ │ │ └── user-avatar.svg │ ├── declaration │ │ └── window.d.ts │ ├── environments │ │ ├── environment.interface.ts │ │ ├── environment.test.ts │ │ └── environment.ts │ ├── gh-pages │ │ ├── index.tsx │ │ └── reports.tsx │ ├── index.tsx │ ├── logo.svg │ ├── react-app-env.d.ts │ ├── reportWebVitals.ts │ ├── routes │ │ ├── Root.jsx │ │ ├── Root.module.scss │ │ └── index.jsx │ ├── setupProxy.js │ ├── setupTests.ts │ └── styles │ │ ├── att-toast.scss │ │ ├── colors.scss │ │ ├── global-reference.scss │ │ ├── global.scss │ │ ├── index.scss │ │ ├── normalize.scss │ │ ├── react-modal.scss │ │ ├── scrollbar.scss │ │ ├── select.scss │ │ ├── variables-keys.scss │ │ ├── variables.scss │ │ └── z-index.scss ├── svg-icons │ ├── arrow-left.svg │ ├── arrow-right.svg │ └── user-avatar.svg ├── tests │ ├── cypress.config.ts │ ├── cypress │ │ ├── component │ │ │ └── ComponentName.cy.ts │ │ ├── e2e │ │ │ └── spec.cy.ts │ │ ├── fixtures │ │ │ └── example.json │ │ └── support │ │ │ ├── commands.ts │ │ │ ├── component-index.html │ │ │ ├── component.ts │ │ │ └── e2e.ts │ ├── package.json │ ├── reporters-config │ │ └── config.json │ └── yarn.lock ├── tsconfig.json └── yarn.lock ├── reports ├── 1707053064011.json └── 1707053090601.json └── run ├── docker ├── docker-compose.yml ├── grafana │ ├── dashboard.json │ ├── dashboard.yaml │ └── datasource.yaml └── prometheus │ └── prometheus.yml └── kubernetes ├── Chart.yaml └── charts ├── api ├── .helmignore ├── Chart.yaml ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── permissions.yaml │ ├── service.yaml │ └── serviceaccount.yaml └── values.yaml ├── cadvisor ├── Chart.yaml ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deamonset.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── service.yaml │ └── serviceaccount.yaml └── values.yaml ├── curl ├── .helmignore ├── Chart.yaml ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── service.yaml │ ├── serviceaccount.yaml │ └── tests │ │ └── test-connection.yaml └── values.yaml ├── grafana ├── Chart.yaml ├── files │ ├── dashboard.json │ ├── dashboard.yaml │ └── datasource.yaml ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── cm.yaml │ ├── deployment.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── service.yaml │ └── serviceaccount.yaml └── values.yaml ├── mysql ├── Chart.yaml ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── service.yaml │ └── serviceaccount.yaml └── values.yaml ├── nginx ├── Chart.yaml ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── service.yaml │ └── serviceaccount.yaml └── values.yaml ├── portal ├── Chart.yaml ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── service.yaml │ └── serviceaccount.yaml └── values.yaml ├── prometheus ├── Chart.yaml ├── prometheus.yml ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── cm.yaml │ ├── deployment.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── permissions.yaml │ ├── service.yaml │ └── serviceaccount.yaml └── values.yaml └── pushgateway ├── Chart.yaml ├── templates ├── NOTES.txt ├── _helpers.tpl ├── deployment.yaml ├── hpa.yaml ├── ingress.yaml ├── service.yaml └── serviceaccount.yaml └── values.yaml /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for this project 3 | labels: [feature-request] 4 | projects: "att/5" 5 | body: 6 | - type: textarea 7 | id: feature-summary 8 | attributes: 9 | label: Feature Summary 10 | description: Provide a brief summary of the feature. 11 | placeholder: Summarize your feature suggestion in a few sentences. 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: feature-purpose 16 | attributes: 17 | label: Purpose of the Feature 18 | description: Explain what the feature is intended to accomplish. 19 | placeholder: What will this feature achieve? 20 | validations: 21 | required: true 22 | - type: textarea 23 | id: additional-information 24 | attributes: 25 | label: Additional Information 26 | description: Provide any additional information or context. 27 | placeholder: Add any other context or screenshots about the feature request here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | name: Feature 2 | description: Define a new feature for this project 3 | labels: [feature] 4 | projects: "att/5" 5 | body: 6 | - type: textarea 7 | id: feature-description 8 | attributes: 9 | label: Description 10 | description: Describe the new feature in detail. 11 | placeholder: Please provide a detailed description of the feature. 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: acceptance-criteria 16 | attributes: 17 | label: Acceptance Criteria 18 | description: Define the criteria that must be met for the feature to be accepted. 19 | placeholder: Please provide the acceptance criteria for the feature. 20 | validations: 21 | required: true 22 | - type: checkboxes 23 | id: tasks 24 | attributes: 25 | label: Tasks 26 | description: A list of tasks for this feature. 27 | options: 28 | - label: Task 1 29 | - label: Task 2 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.yml: -------------------------------------------------------------------------------- 1 | name: Question 2 | description: Ask a question about this project 3 | labels: [question] 4 | projects: "att/5" 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: Provide a brief summary of your question. 11 | placeholder: Summarize your question in a few sentences. 12 | validations: 13 | required: true 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/task.yml: -------------------------------------------------------------------------------- 1 | name: Task 2 | description: Create a new task 3 | labels: ["task"] 4 | projects: "att/5" 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | validations: 11 | required: true 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Checklist: 2 | - [ ] I have included a meaningful summary of my PR's content 3 | - [ ] If applicable, I have included screenshots to help describe my change 4 | - [ ] I have linked my PR to the relevant issue 5 | - [ ] I have performed a self-review of my code 6 | - [ ] I have commented my code, particularly in hard-to-understand areas 7 | - [ ] My changes do not generate new compliance warnings / errors 8 | - [ ] I have tested that my fix or enhancement is effective or that my feature works 9 | - [ ] New and existing unit tests pass locally with my changes 10 | - [ ] I have made corresponding changes to the documentation 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/github-repo-stats.yml: -------------------------------------------------------------------------------- 1 | name: github-repo-stats 2 | 3 | on: 4 | schedule: 5 | # Run this once per day, towards the end of the day for keeping the most 6 | # recent data point most meaningful (hours are interpreted in UTC). 7 | - cron: "0 23 * * *" 8 | workflow_dispatch: # Allow for running this manually. 9 | 10 | jobs: 11 | j1: 12 | name: github-repo-stats 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: run-ghrs 16 | # Use latest release. 17 | uses: jgehrcke/github-repo-stats@RELEASE 18 | with: 19 | ghtoken: ${{ secrets.ghrs_github_api_token }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | run/kubernetes/charts/*-0.1.0.tgz 2 | run/kubernetes/Chart.lock 3 | # compiled output 4 | node_modules 5 | dist 6 | 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | pnpm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | lerna-debug.log* 15 | 16 | # OS 17 | .DS_Store 18 | 19 | # Tests 20 | /coverage 21 | /.nyc_output 22 | 23 | # IDEs and editors 24 | /.idea 25 | .project 26 | .classpath 27 | .c9/ 28 | *.launch 29 | .settings/ 30 | *.sublime-workspace 31 | 32 | # IDE - VSCode 33 | .vscode/* 34 | !.vscode/settings.json 35 | !.vscode/tasks.json 36 | !.vscode/launch.json 37 | !.vscode/extensions.json 38 | 39 | # Temporary code data file 40 | tempStatsData.txt 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 AT&T Open Source 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /api/.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = 3 | src 4 | 5 | omit = 6 | src/main.py 7 | src/utils/database_manager.py -------------------------------------------------------------------------------- /api/.env: -------------------------------------------------------------------------------- 1 | CODE_RELEASE=1.1.0 # value should be increased in every new release 2 | PROTOCOL=TLS 1.3 3 | DEFAULT_GROUPS=prime256v1:secp384r1:frodo640aes:frodo640shake:frodo976aes:frodo976shake:frodo1344aes:frodo1344shake:kyber512:p256_kyber512:kyber768:p384_kyber768:x25519_kyber768:kyber1024:bikel1:bikel3:bikel5:hqc128:hqc192:hqc256 4 | REQUEST_TIMEOUT=900 5 | ITERATIONS_OPTIONS=100:500:1000:2000:5000:10000:50000 6 | MESSAGE_SIZES_OPTIONS=0:1:2:100:1024:102400:204800:1048576:2097152:10485760 7 | ENVIRONMENT=docker # value must be docker or kubernetes 8 | LOG_LEVEL=INFO # value must be one of DEBUG, INFO, WARNING, ERROR, CRITICAL 9 | 10 | #APIs 11 | CURL_URL=http://localhost:3010 12 | CADVISOR_URL=http://localhost:8080 13 | ENVIRONMENT=docker #value should be docker of kubernetes 14 | -------------------------------------------------------------------------------- /api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9 2 | 3 | WORKDIR /api 4 | 5 | COPY src src 6 | COPY config config 7 | COPY main.py main.py 8 | COPY requirements.txt . 9 | COPY .env . 10 | 11 | RUN pip3 install -r requirements.txt 12 | 13 | CMD ["python3", "-m", "main"] 14 | 15 | EXPOSE 3020 -------------------------------------------------------------------------------- /api/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/api/config/__init__.py -------------------------------------------------------------------------------- /api/config/mysql_config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | class MySQLConfig: 4 | SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URI','mysql+pymysql://root:qujata@localhost:3306/qujata') 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False -------------------------------------------------------------------------------- /api/requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.28.2 2 | flask==3.0.0 3 | Flask-Cors==4.0.0 4 | DateTime==5.2 5 | python-dotenv==1.0.0 6 | coverage==7.3.2 7 | flask-sqlalchemy==3.1.1 8 | pymysql==1.1.0 9 | cryptography==41.0.7 10 | pandas==2.1.4 11 | kubernetes==28.1.0 12 | prettytable==3.9.0 -------------------------------------------------------------------------------- /api/src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/api/src/__init__.py -------------------------------------------------------------------------------- /api/src/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/api/src/api/__init__.py -------------------------------------------------------------------------------- /api/src/enums/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/api/src/enums/__init__.py -------------------------------------------------------------------------------- /api/src/enums/algorithms.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class QuantumSafeAlgorithms(Enum): 4 | BIKEL1 = 'bikel1' 5 | BIKEL3 = 'bikel3' 6 | KYBER512 = 'kyber512' 7 | KYBER768 = 'kyber768' 8 | KYBER1024 = 'kyber1024' 9 | KYBER90S512 = 'kyber90s512' 10 | KYBER90S768 = 'kyber90s768' 11 | KYBER90S1024 = 'kyber90s1024' 12 | FRODO640AES = 'frodo640aes' 13 | FRODO640SHAKE = 'frodo640shake' 14 | FRODO976AES = 'frodo976aes' 15 | FRODO976SHAKE = 'frodo976shake' 16 | FRODO1344AES = 'frodo1344aes' 17 | FRODO1344SHAKE = 'frodo1344shake' 18 | HQC128 = 'hqc128' 19 | HQC192 = 'hqc192' 20 | HQC256 = 'hqc256' 21 | 22 | class ClassicAlgorithms(Enum): 23 | PRIME256V1 = 'prime256v1' 24 | SECP384R1 = 'secp384r1' 25 | 26 | class HybridAlgorithms(Enum): 27 | P256_KYBER512 = 'p256_kyber512' 28 | P384_KYBER768 = 'p384_kyber768' 29 | X25519_KYBER768 ='x25519_kyber768' 30 | 31 | 32 | -------------------------------------------------------------------------------- /api/src/enums/environment.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class Environment(Enum): 4 | DOCKER = 'docker' 5 | KUBERNETES = 'kubernetes' -------------------------------------------------------------------------------- /api/src/enums/metric.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class Metric(Enum): 4 | SERVER_AVERAGE_CPU_CORES = 'server_avg_cpu_cores' 5 | SERVER_AVERAGE_MEMORY = 'server_avg_memory' 6 | SERVER_AVERAGE_CPU = 'server_avg_cpu' 7 | CLIENT_AVERAGE_CPU_CORES = 'client_avg_cpu_cores' 8 | CLIENT_AVERAGE_MEMORY = 'client_avg_memory' 9 | CLIENT_AVERAGE_CPU = 'client_avg_cpu' 10 | ERROR_RATE = 'error_rate' 11 | BYTES_THROUGHPUT_PER_SECOND = 'bytes_throughput_per_sec' 12 | MESSAGES_THROUGHPUT_PER_SECOND = 'msg_throughput_per_sec' 13 | AVERAGE_TLS_HANDSHAKE_TIME = 'avg_tls_handshake_time' 14 | -------------------------------------------------------------------------------- /api/src/enums/status.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class Status(Enum): 4 | FAILED = "FAILED" 5 | SUCCESS = "SUCCESS" -------------------------------------------------------------------------------- /api/src/exceptions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/api/src/exceptions/__init__.py -------------------------------------------------------------------------------- /api/src/exceptions/exceptions.py: -------------------------------------------------------------------------------- 1 | class ApiException(Exception): 2 | def __init__(self, message, error, status_code): 3 | super().__init__(message) 4 | self.message = message 5 | self.error = error 6 | self.status_code = status_code 7 | 8 | class NotFoundException(Exception): 9 | def __init__(self, message, error): 10 | super().__init__(message) 11 | self.message = message 12 | self.error = error 13 | self.status_code = 404 14 | 15 | 16 | -------------------------------------------------------------------------------- /api/src/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .env_info import EnvInfo 2 | from .test_suite import TestSuite 3 | from .test_run import TestRun 4 | -------------------------------------------------------------------------------- /api/src/models/base.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext.declarative import declarative_base 2 | 3 | Base = declarative_base() 4 | 5 | -------------------------------------------------------------------------------- /api/src/models/env_info.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String 2 | from sqlalchemy.orm import relationship 3 | from .base import Base 4 | 5 | class EnvInfo(Base): 6 | __tablename__ = 'env_info' 7 | id = Column(Integer, primary_key=True, autoincrement=True) 8 | resource_name = Column(String(255)) 9 | operating_system = Column(String(255)) 10 | cpu = Column(String(255)) 11 | cpu_architecture = Column(String(255)) 12 | cpu_cores = Column(Integer) 13 | clock_speed = Column(String(255)) 14 | node_size = Column(String(255)) 15 | test_suites = relationship('TestSuite', back_populates='env_info') 16 | 17 | def to_dict(self): 18 | return { 19 | 'id': self.id, 20 | 'resource_name': self.resource_name, 21 | 'operating_system': self.operating_system, 22 | 'cpu': self.cpu, 23 | 'cpu_architecture': self.cpu_architecture, 24 | 'cpu_cores': self.cpu_cores, 25 | 'clock_speed': self.clock_speed, 26 | 'node_size': self.node_size 27 | } -------------------------------------------------------------------------------- /api/src/models/test_run_metric.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, Enum, ForeignKey, Double 2 | from sqlalchemy.orm import relationship 3 | from src.enums.metric import Metric 4 | from .base import Base 5 | 6 | class TestRunMetric(Base): 7 | __tablename__ = 'test_run_metrics' 8 | metric_name = Column(Enum(Metric, values_callable=lambda x: [e.value for e in x]), primary_key=True) 9 | value = Column(Double) 10 | test_run_id = Column(Integer, ForeignKey('test_runs.id'), primary_key=True) 11 | test_run = relationship('TestRun', back_populates='test_run_metrics') 12 | -------------------------------------------------------------------------------- /api/src/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/api/src/services/__init__.py -------------------------------------------------------------------------------- /api/src/services/environment_service.py: -------------------------------------------------------------------------------- 1 | from flask import current_app 2 | from src.models.env_info import EnvInfo 3 | 4 | 5 | def create(data): 6 | new_env_info = EnvInfo( 7 | resource_name=data.get('resource_name'), 8 | operating_system=data.get('operating_system'), 9 | cpu=data.get('cpu'), 10 | cpu_architecture=data.get('cpu_architecture'), 11 | cpu_cores=data.get('cpu_cores'), 12 | clock_speed=data.get('clock_speed'), 13 | node_size=data.get('node_size') 14 | ) 15 | current_app.database_manager.create(new_env_info) 16 | -------------------------------------------------------------------------------- /api/src/services/k8s_service.py: -------------------------------------------------------------------------------- 1 | from kubernetes import client, config 2 | 3 | NAMESPACE = 'qujata' 4 | 5 | __kubernetes_client = None 6 | 7 | def init_cluster(): 8 | global __kubernetes_client 9 | config.load_incluster_config() 10 | __kubernetes_client = client.CoreV1Api() 11 | 12 | def get_pod_by_label(label_name, label_value): 13 | pods = __get_pods_by_label("=".join([label_name, label_value])) 14 | if not pods.items: 15 | raise RuntimeError(label_value + " pod not found") 16 | return pods.items[0] # for now, we handle only one running pod. in the future, we should handle multiple pods. 17 | 18 | 19 | def get_pod_by_label_and_host(label_name, label_value, host): 20 | pods = __get_pods_by_label("=".join([label_name, label_value])) 21 | for pod in pods.items: 22 | if pod.status.host_ip == host: 23 | return pod 24 | raise RuntimeError(label_value + " pod not found with host_ip: " + host) 25 | 26 | 27 | def __get_pods_by_label(label): 28 | return __kubernetes_client.list_namespaced_pod(NAMESPACE, label_selector=label) 29 | -------------------------------------------------------------------------------- /api/src/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/api/src/utils/__init__.py -------------------------------------------------------------------------------- /api/src/utils/metrics_collection_manager.py: -------------------------------------------------------------------------------- 1 | from src.utils.metrics_collector import MetricsCollector 2 | import logging 3 | 4 | client_collector = MetricsCollector("qujata-curl") 5 | server_collector = MetricsCollector("qujata-nginx") 6 | 7 | # TODO: add lock validation 8 | def start_collecting(): 9 | client_collector.start() 10 | server_collector.start() 11 | 12 | def stop_collecting(): 13 | client_collector.stop() 14 | server_collector.stop() 15 | # print collectors results 16 | logging.info(server_collector.to_pretty_table()) 17 | logging.info(client_collector.to_pretty_table()) 18 | 19 | def get_metrics(): 20 | client_data = client_collector.get_data() 21 | server_data = server_collector.get_data() 22 | return client_data, server_data 23 | -------------------------------------------------------------------------------- /api/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/api/tests/__init__.py -------------------------------------------------------------------------------- /curl/.env: -------------------------------------------------------------------------------- 1 | DEFAULT_GROUPS=prime256v1:secp384r1:frodo640aes:frodo640shake:frodo976aes:frodo976shake:frodo1344aes:frodo1344shake:kyber512:p256_kyber512:kyber768:p384_kyber768:x25519_kyber768:kyber1024:bikel1:bikel3:bikel5:hqc128:hqc192:hqc256 2 | -------------------------------------------------------------------------------- /curl/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | pnpm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # OS 15 | .DS_Store 16 | 17 | # Tests 18 | /coverage 19 | /.nyc_output 20 | 21 | # IDEs and editors 22 | /.idea 23 | .project 24 | .classpath 25 | .c9/ 26 | *.launch 27 | .settings/ 28 | *.sublime-workspace 29 | 30 | # IDE - VSCode 31 | .vscode/* 32 | !.vscode/settings.json 33 | !.vscode/tasks.json 34 | !.vscode/launch.json 35 | !.vscode/extensions.json 36 | 37 | # Temporary code data file 38 | tempStatsData.txt 39 | -------------------------------------------------------------------------------- /curl/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | This is a `Post Quantum Cryptography` tool, 5 | using [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 6 | 7 | ## Pre-requisite 8 | [Node.js](#https://nodejs.org/en/download) (includes npm) have to be installed on your system in order to run this project. 9 | 10 | ## Installation 11 | 1. To start, clone the qujata repository: 12 | ```bash 13 | git clone https://github.com/att/qujata.git 14 | cd qujata/curl 15 | ``` 16 | 2. Install dependencies: 17 | ```bash 18 | npm install 19 | ``` 20 | 3. Run the application: 21 | ```bash 22 | npm run start 23 | ``` 24 | 25 | 4. Application is available now in: `http://localhost:3010`, curl example: 26 | ```bash 27 | curl --location 'http://localhost:3010/curl' \ 28 | --header 'Content-Type: application/json' \ 29 | --data '{ 30 | "algorithm": "kyber512", 31 | "iterationsCount": 5 32 | }' 33 | ``` 34 | 35 | 5. Running unit test: 36 | ```bash 37 | npm run test -- --coverage 38 | ``` 39 | -------------------------------------------------------------------------------- /curl/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /curl/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { CurlModule } from './curl/curl.module'; 3 | import configuration from "./config/configuration"; 4 | import { ConfigModule } from '@nestjs/config'; 5 | 6 | @Module({ 7 | imports: [ 8 | CurlModule, 9 | ConfigModule.forRoot({ 10 | load: [configuration], 11 | }), 12 | ] 13 | }) 14 | export class AppModule {} 15 | -------------------------------------------------------------------------------- /curl/src/config/configuration.ts: -------------------------------------------------------------------------------- 1 | export default () => ({ 2 | nginx: { 3 | host: process.env.NGINX_HOST || "nginx", 4 | port: process.env.NGINX_PORT || 4433 5 | }, 6 | algorithms: (process.env.DEFAULT_GROUPS || "prime256v1:secp384r1:frodo640aes:frodo640shake:frodo976aes:frodo976shake:frodo1344aes:frodo1344shake:kyber512:p256_kyber512:kyber768:p384_kyber768:x25519_kyber768:kyber1024:bikel1:bikel3:bikel5:hqc128:hqc192:hqc256").split(':') 7 | }); -------------------------------------------------------------------------------- /curl/src/curl/curl.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common'; 2 | import { CurlService } from './curl.service'; 3 | import { CurlRequest } from '../dto/curl-request.dto'; 4 | 5 | @Controller('curl') 6 | export class CurlController { 7 | constructor(private readonly curlService: CurlService) {} 8 | 9 | @Post() 10 | @HttpCode(HttpStatus.CREATED) 11 | async create(@Body() curlRequest: CurlRequest) { 12 | console.log('[CurlController:create] In Post func. Body: ', curlRequest); 13 | const curlInfo = await this.curlService.run(curlRequest); 14 | console.log('[CurlController:create] In Post func. Response: ', curlInfo) 15 | return curlInfo; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /curl/src/curl/curl.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { CurlService } from './curl.service'; 3 | import { CurlController } from './curl.controller'; 4 | import { ConfigModule } from '@nestjs/config'; 5 | 6 | @Module({ 7 | controllers: [CurlController], 8 | providers: [CurlService], 9 | imports: [ConfigModule], 10 | }) 11 | export class CurlModule {} 12 | -------------------------------------------------------------------------------- /curl/src/dto/curl-request.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsNumber, Min } from 'class-validator'; 2 | 3 | export class CurlRequest { 4 | 5 | @IsNotEmpty() 6 | algorithm: string; 7 | 8 | @IsNotEmpty() 9 | @IsNumber() 10 | @Min(1) 11 | iterationsCount: number; 12 | 13 | @IsNotEmpty() 14 | @IsNumber() 15 | @Min(0) 16 | messageSize: number; 17 | } 18 | -------------------------------------------------------------------------------- /curl/src/dto/curl-response.dto.ts: -------------------------------------------------------------------------------- 1 | export class CurlResponseDto { 2 | totalRequestSize: number; 3 | } 4 | -------------------------------------------------------------------------------- /curl/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import { ValidationPipe } from '@nestjs/common'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.create(AppModule); 7 | 8 | app.useGlobalPipes( 9 | new ValidationPipe({ 10 | // implicitly transform query and path parameters based on the expected type. 11 | transform: true, 12 | // for more examples : https://docs.nestjs.com/techniques/validation 13 | transformOptions: { 14 | // If set to true class-transformer will attempt conversion based on TS reflected type 15 | enableImplicitConversion: true, 16 | }, 17 | }), 18 | ); 19 | app.enableCors({ 20 | origin: true, 21 | methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', 22 | credentials: true, 23 | }); 24 | 25 | await app.listen(3010); 26 | } 27 | bootstrap(); 28 | -------------------------------------------------------------------------------- /curl/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /curl/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": false, 16 | "noImplicitAny": false, 17 | "strictBindCallApply": false, 18 | "forceConsistentCasingInFileNames": false, 19 | "noFallthroughCasesInSwitch": false 20 | }, 21 | "include": ["src/**/*.ts"], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /mysql/Dockerfile: -------------------------------------------------------------------------------- 1 | # mysql/Dockerfile 2 | 3 | FROM mysql:latest 4 | 5 | # Copy the SQL script into the Docker image 6 | COPY scripts/init-db.sql /docker-entrypoint-initdb.d/ 7 | 8 | # Expose MySQL default port 9 | EXPOSE 3306 10 | -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openquantumsafe/nginx:latest 2 | 3 | ADD nginx.conf /opt/nginx/nginx-conf/nginx.conf 4 | ADD app.js app.js 5 | ADD start.sh start.sh 6 | 7 | USER root 8 | 9 | RUN apk add --update nodejs npm 10 | RUN npm install express 11 | RUN chmod +x start.sh 12 | 13 | USER oqs 14 | 15 | 16 | 17 | CMD ["./start.sh"] 18 | -------------------------------------------------------------------------------- /nginx/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | app.use(express.json()); 4 | app.use(express.urlencoded({ extended: true })); 5 | app.get('/', function (req, res) { 6 | res.send('Received GET request.\n'); 7 | }); 8 | app.post('/', function (req, res) { 9 | res.send('Received POST request.'); 10 | }); 11 | app.listen(3000, function () { 12 | console.log('app listening on port 3000'); 13 | }); -------------------------------------------------------------------------------- /nginx/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | node app.js & 3 | nginx -c /opt/nginx/nginx-conf/nginx.conf -g 'daemon off;' 4 | -------------------------------------------------------------------------------- /portal/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/.DS_Store -------------------------------------------------------------------------------- /portal/.env: -------------------------------------------------------------------------------- 1 | REACT_APP__ENVIRONMENT=local 2 | REACT_APP__BASE_API_URL='' 3 | REACT_APP__DASHBOARD_LINK_HOST=http://localhost:3000 4 | REACT_APP__PUBLIC_URL='qujata' 5 | PUBLIC_URL='qujata' -------------------------------------------------------------------------------- /portal/.env.development: -------------------------------------------------------------------------------- 1 | # Environment Configurations - https://create-react-app.dev/docs/adding-custom-environment-variables 2 | # Environment Configurations - https://www.opcito.com/blogs/managing-multiple-environment-configurations-in-react-app/ 3 | # Merged with .env file 4 | 5 | REACT_APP__BASE_API_URL='http://localhost:3020/qujata-api' -------------------------------------------------------------------------------- /portal/.env.gh-pages: -------------------------------------------------------------------------------- 1 | REACT_APP__ENVIRONMENT=gh-pages 2 | REACT_APP__BASE_API_URL='' 3 | REACT_APP__DASHBOARD_LINK_HOST=http://localhost:3000 4 | REACT_APP__PUBLIC_URL='qujata' 5 | PUBLIC_URL='qujata' -------------------------------------------------------------------------------- /portal/.env.production: -------------------------------------------------------------------------------- 1 | # Environment Configurations - https://create-react-app.dev/docs/adding-custom-environment-variables 2 | # Environment Configurations - https://www.opcito.com/blogs/managing-multiple-environment-configurations-in-react-app/ 3 | # Merged with .env file 4 | REACT_APP__BASE_API_URL='http://localhost:3020/qujata-api' 5 | REACT_APP__PUBLIC_URL='qujata' -------------------------------------------------------------------------------- /portal/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "react-app", 5 | "eslint-config-react-app", 6 | "plugin:react-hooks/recommended" 7 | ], 8 | "plugins": [ 9 | "react-hooks", 10 | "no-null" 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "ecmaVersion": 2020, 15 | "sourceType": "module", 16 | "ecmaFeatures": { 17 | "jsx": true 18 | } 19 | }, 20 | "env": { 21 | "browser": true, 22 | "es2020": true 23 | }, 24 | "rules": { 25 | "react/react-in-jsx-scope": "off", 26 | "react-hooks/exhaustive-deps": "error", // Checks effect dependencies, 27 | "react/prop-types": [ 28 | "off" 29 | ] 30 | } 31 | } -------------------------------------------------------------------------------- /portal/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.25-alpine3.18 2 | 3 | RUN apk --no-cache add --update nodejs npm 4 | ARG BUILD_ENV 5 | 6 | ## Replace the default nginx index page with our portal 7 | RUN rm -rf /usr/share/nginx/html/* 8 | RUN mkdir /usr/share/nginx/html/qujata 9 | # RUN npm install -g yarn 10 | COPY . . 11 | RUN npm install -g yarn 12 | RUN yarn install 13 | RUN yarn add env-cmd 14 | RUN if [ "$BUILD_ENV" = "gh-pages" ]; then \ 15 | yarn build:gh-pages; \ 16 | else \ 17 | yarn build; \ 18 | fi \ 19 | && cp -r build/* /usr/share/nginx/html/qujata && cp build/index.html /usr/share/nginx/html/qujata/index.html.tpl && chmod 755 -R /usr/share/nginx/html/qujata 20 | 21 | COPY nginx.conf /etc/nginx/nginx.conf 22 | 23 | COPY docker-entrypoint.sh docker-entrypoint.sh 24 | RUN ["chmod", "+x", "docker-entrypoint.sh"] 25 | ENTRYPOINT ["./docker-entrypoint.sh"] 26 | CMD ["app:start"] 27 | -------------------------------------------------------------------------------- /portal/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | WORK_DIR=/usr/share/nginx/html/qujata 4 | 5 | 6 | case ${1} in 7 | app:start) 8 | echo "----------------------------------------------------------------" 9 | echo "---------------${1} PQC_PORTAL Starting... -------------------" 10 | echo "----------------------------------------------------------------" 11 | node -v 12 | # Injecting Environment Variables 13 | node ${WORK_DIR}/inject-env-variables.js $WORK_DIR 14 | ;; 15 | *) 16 | exec "$@" 17 | ;; 18 | esac 19 | 20 | chmod -R 755 /usr/share/nginx/html/qujata 21 | nginx -g 'daemon off;' 22 | -------------------------------------------------------------------------------- /portal/mock-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock-server", 3 | "version": "1.0.0", 4 | "description": "This is a mock server app", 5 | "main": "index.js", 6 | "scripts": { 7 | "serve": "yarn remove-dist-folder && yarn ts-to-js && yarn run-server", 8 | "remove-dist-folder": "rimraf dist/", 9 | "ts-to-js": "tsc", 10 | "watch-ts-to-js": "tsc -w", 11 | "run-server": "nodemon dist/server.js" 12 | }, 13 | "dependencies": { 14 | "body-parser": "^1.20.2", 15 | "cookies": "^0.8.0", 16 | "cors": "^2.8.5", 17 | "express": "^4.18.2", 18 | "nodemon": "^3.0.1" 19 | }, 20 | "devDependencies": { 21 | "@types/cookies": "^0.7.7", 22 | "@types/express": "^4.17.17", 23 | "@types/node": "^20.4.8", 24 | "concurrently": "^8.2.0", 25 | "rimraf": "^5.0.1", 26 | "typescript": "~5.1.6" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /portal/mock-server/src/algorithms.json: -------------------------------------------------------------------------------- 1 | { 2 | "classic": ["prime256v1","secp384r1"], 3 | "hybrid": ["p256_kyber512","p384_kyber768","x25519_kyber768"], 4 | "quantumSafe": ["bikel1","bikel3","kyber512","kyber768","kyber1024","frodo640aes","frodo640shake","frodo976aes","frodo976shake","frodo1344aes","frodo1344shake","hqc128","hqc192","hqc256"] 5 | } 6 | -------------------------------------------------------------------------------- /portal/mock-server/src/iterations.json: -------------------------------------------------------------------------------- 1 | { 2 | "iterations": [100, 500, 1000, 2000, 5000, 10000, 50000] 3 | } -------------------------------------------------------------------------------- /portal/mock-server/src/message_sizes.json: -------------------------------------------------------------------------------- 1 | { 2 | "message_sizes": [0, 1, 2, 100, 1024, 102400, 204800, 1048576, 2097152, 10485760] 3 | } -------------------------------------------------------------------------------- /portal/mock-server/src/server.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import cors from 'cors'; 3 | import * as bodyParser from 'body-parser'; 4 | import { AddressInfo } from 'net'; 5 | import router from './router'; 6 | 7 | const PORT = 2011; 8 | 9 | const app = express(); 10 | app.use(bodyParser.json()); 11 | app.use(cors()); 12 | app.use('', router); 13 | 14 | const server = app.listen(PORT, () => { 15 | const address: AddressInfo = server.address(); 16 | console.log('Portal app listening at http://localhost:%s', address.port); 17 | }); 18 | -------------------------------------------------------------------------------- /portal/mock-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "exclude": [ 4 | "api", 5 | "node_modules", 6 | "**/*.spec.ts" 7 | ], 8 | "compilerOptions": { 9 | "jsx": "react", 10 | "baseUrl": "./", 11 | "outDir": "./dist", 12 | "sourceMap": false, 13 | "resolveJsonModule": true, 14 | "esModuleInterop": true, 15 | "declaration": false, 16 | "module": "commonjs", 17 | "moduleResolution": "node", 18 | "emitDecoratorMetadata": true, 19 | "experimentalDecorators": true, 20 | "target": "es2015", 21 | "typeRoots": [ 22 | "node_modules/@types" 23 | ], 24 | "lib": [ 25 | "es2017", 26 | "dom" 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /portal/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/favicon.ico -------------------------------------------------------------------------------- /portal/public/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'ATTAleckSans'; 3 | font-display: swap; 4 | src: url('fonts/ATTAleckSans_W_Rg.woff2') format('woff2'), 5 | url('fonts/ATTAleckSans_W_Rg.woff') format('woff'); /* Pretty Modern Browsers */ 6 | } 7 | @font-face { 8 | font-family: 'ATTAleckSansBold'; 9 | font-display: swap; 10 | src: url('fonts/ATTAleckSans_W_Bd.woff2') format('woff2'), 11 | url('fonts/ATTAleckSans_W_Bd.woff') format('woff'); /* Pretty Modern Browsers */ 12 | } 13 | @font-face { 14 | font-family: 'ATTAleckSansMed'; 15 | font-display: swap; 16 | src: url('fonts/ATTAleckSans_W_Md.woff2') format('woff2'), 17 | url('fonts/ATTAleckSans_W_Md.woff') format('woff'); /* Pretty Modern Browsers */ 18 | } -------------------------------------------------------------------------------- /portal/public/fonts/ATTAleckSans_W_Bd.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/fonts/ATTAleckSans_W_Bd.eot -------------------------------------------------------------------------------- /portal/public/fonts/ATTAleckSans_W_Bd.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/fonts/ATTAleckSans_W_Bd.woff -------------------------------------------------------------------------------- /portal/public/fonts/ATTAleckSans_W_Bd.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/fonts/ATTAleckSans_W_Bd.woff2 -------------------------------------------------------------------------------- /portal/public/fonts/ATTAleckSans_W_Md.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/fonts/ATTAleckSans_W_Md.eot -------------------------------------------------------------------------------- /portal/public/fonts/ATTAleckSans_W_Md.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/fonts/ATTAleckSans_W_Md.woff -------------------------------------------------------------------------------- /portal/public/fonts/ATTAleckSans_W_Md.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/fonts/ATTAleckSans_W_Md.woff2 -------------------------------------------------------------------------------- /portal/public/fonts/ATTAleckSans_W_Rg.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/fonts/ATTAleckSans_W_Rg.eot -------------------------------------------------------------------------------- /portal/public/fonts/ATTAleckSans_W_Rg.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/fonts/ATTAleckSans_W_Rg.woff -------------------------------------------------------------------------------- /portal/public/fonts/ATTAleckSans_W_Rg.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/fonts/ATTAleckSans_W_Rg.woff2 -------------------------------------------------------------------------------- /portal/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/public/logo192.png -------------------------------------------------------------------------------- /portal/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /portal/src/app/App.module.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/src/app/App.module.scss -------------------------------------------------------------------------------- /portal/src/app/App.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, waitFor } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | describe('App', () => { 5 | test('should render App', async () => { 6 | const { container } = render(); 7 | await waitFor(() => { 8 | expect(container).toBeTruthy(); 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /portal/src/app/App.tsx: -------------------------------------------------------------------------------- 1 | import { RouterProvider } from 'react-router-dom'; 2 | import { RootContextState } from './shared/context/RootContextState'; 3 | import { ToastContainer } from 'react-toastify'; 4 | import { ToastContainerConfig } from './shared/components/toast/toast-container-config.const'; 5 | import { router } from '../routes'; 6 | 7 | const App: React.FC = () => ( 8 | 9 | 10 | 11 | 12 | ); 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /portal/src/app/apis.ts: -------------------------------------------------------------------------------- 1 | const testSuites = 'test_suites'; 2 | 3 | export const APIS: { [key in keyof typeof API_URLS]: string } = { 4 | analyze: 'analyze', 5 | algorithms: 'algorithms', 6 | iterations: 'iterations', 7 | message_sizes: 'message_sizes', 8 | testRunResults: `${testSuites}/:testSuiteId`, 9 | editExperiment: `${testSuites}/:testSuiteId`, 10 | deleteExperiment: `${testSuites}/:testSuiteId`, 11 | allExperiments: `${testSuites}`, 12 | deleteExperiments: `${testSuites}/delete`, 13 | }; 14 | 15 | enum API_URLS { 16 | analyze, 17 | algorithms, 18 | iterations, 19 | message_sizes, 20 | testRunResults, 21 | editExperiment, 22 | deleteExperiment, 23 | allExperiments, 24 | deleteExperiments 25 | } 26 | -------------------------------------------------------------------------------- /portal/src/app/components/all-experiments/__snapshots__/Experiments.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Experiments renders correctly 1`] = ` 4 |
5 |
8 |
9 | `; 10 | -------------------------------------------------------------------------------- /portal/src/app/components/all-experiments/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useExperimentsData'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/all-experiments/models/experiments.interface.ts: -------------------------------------------------------------------------------- 1 | import { ITestRunResult, ITestRunResultData } from '../../../shared/models/test-run-result.interface'; 2 | 3 | export type TestRunSubset = Pick; 4 | export type Experiment = Pick & { test_runs: TestRunSubset[] }; 5 | 6 | export interface ExperimentData { 7 | id: number; 8 | name: string; 9 | algorithms: string[]; 10 | iterations: number[]; 11 | message_sizes: number[]; 12 | end_time: number; 13 | }; 14 | -------------------------------------------------------------------------------- /portal/src/app/components/all-experiments/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const ALL_EXPERIMENTS_TABLE_EN = { 2 | TITLE: 'All Experiments', 3 | TABLE_COLUMNS: { 4 | CHECKBOX: 'checkbox', 5 | EXPERIMENT_NAME: { 6 | NAME: 'Experiment Name', 7 | ID: 'experimentName' 8 | }, 9 | ALGORITHMS: { 10 | NAME: 'Algorithms', 11 | ID: 'algorithms' 12 | }, 13 | ITERATIONS: { 14 | NAME: 'Iterations', 15 | ID: 'iterations' 16 | }, 17 | MESSAGE_SIZES: { 18 | NAME: 'Message Size (KB)', 19 | ID: 'message_size' 20 | }, 21 | DATE: { 22 | NAME: 'Date', 23 | ID: 'date' 24 | }, 25 | LINKS: { 26 | DUPLICATE: 'duplicate', 27 | } 28 | }, 29 | BUTTONS: { 30 | DELETE: 'Delete', 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/Dashboard.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | .wrapper{ 4 | margin-block-start: 32px; 5 | margin-block-end: 50px; 6 | } 7 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/BarChart/BarChart.module.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/src/app/components/dashboard/components/charts/BarChart/BarChart.module.scss -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/BarChart/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BarChart'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/BarChart/models/BarChart.model.ts: -------------------------------------------------------------------------------- 1 | export interface IDatasets { 2 | label: string; 3 | data: number[]; 4 | backgroundColor: string; 5 | borderWidth: number; 6 | } 7 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/Charts.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | .wrapper{ 4 | margin-block-start: 32px; 5 | inline-size: 100%; 6 | display: flex; 7 | justify-content: space-evenly; 8 | flex-wrap: wrap; 9 | gap: 31px; 10 | } 11 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/LineChart/LineChart.const.ts: -------------------------------------------------------------------------------- 1 | import { ChartOptions } from 'chart.js'; 2 | import { CHARTS_EN } from '../../../../home/components/experiment/components/charts/translate/en'; 3 | 4 | export const colors: string[] = ['#05BBFF', '#086CE1', '#FF8500', '#36a2eb33']; 5 | 6 | export let defaultOptions: ChartOptions = { 7 | responsive: true, 8 | aspectRatio: 2, 9 | scales: { 10 | y: { 11 | title: { 12 | display: true, 13 | text: CHARTS_EN.Y_AXIS_TITLE, 14 | font: { 15 | size: 14, 16 | }, 17 | padding: { bottom: 10 }, 18 | }, 19 | beginAtZero: true, 20 | ticks: { 21 | stepSize: 2, 22 | font: { 23 | size: 14, 24 | }, 25 | }, 26 | }, 27 | }, 28 | }; 29 | 30 | export const TITLE_PREFIX = 'Server Memory (%) vs.'; 31 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/LineChart/LineChart.module.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/src/app/components/dashboard/components/charts/LineChart/LineChart.module.scss -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/LineChart/index.ts: -------------------------------------------------------------------------------- 1 | export * from './LineChart'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/LineChart/models/LineChart.model.ts: -------------------------------------------------------------------------------- 1 | export interface IData { 2 | label: string; 3 | data: number[]; 4 | fill: boolean; 5 | backgroundColor: string; 6 | borderColor: string; 7 | yAxisID: string; 8 | } 9 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/__snapshots__/Charts.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Charts should render Charts 1`] = ` 4 |
7 | `; 8 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Charts'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/utils/charts.utils.test.ts: -------------------------------------------------------------------------------- 1 | import { getColorByName } from './charts.utils'; 2 | 3 | describe('Charts Util Test', () => { 4 | test('should get color by name', () => { 5 | expect(getColorByName('bikel1')).toBe('#FF8500'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/charts/utils/charts.utils.ts: -------------------------------------------------------------------------------- 1 | import { colors } from "../BarChart/barChart.const"; 2 | 3 | export function getColorByName(name: string): string { 4 | const firstLetter: string = name.trim().substring(0, 1); 5 | if (isLetter(firstLetter)) { 6 | const numberFromStr: number = firstLetter.toLowerCase().charCodeAt(0) - 97; 7 | return colors[numberFromStr % colors.length]; 8 | } 9 | 10 | return colors[0]; 11 | } 12 | 13 | function isLetter(str: string) { 14 | return str.length === 1 && str.match(/[a-z]/i); 15 | } 16 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/components/main-boxes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './MainBoxes'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Dashboard'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const DASHBOARD_EN = { 2 | CSV_REPORT: { 3 | TABLE: { 4 | COLUMNS: { 5 | ALGORITHM: 'Algorithm', 6 | SESSION_TIME: 'Session Time - Avg', 7 | SESSION_HANDSHAKE_TIME: 'Session Handshake Time - Avg', 8 | DOWNLOAD_SPEED: 'Download Speed - Avg', 9 | }, 10 | }, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /portal/src/app/components/dashboard/utils/dashboard-data-report.test.ts: -------------------------------------------------------------------------------- 1 | import { ChartDataMap } from '../../../shared/models/quantum.interface'; 2 | import { mapDashboardDataToCsvDataType } from './dashboard-data-report.util'; 3 | 4 | const mockData: Map = new Map(); 5 | mockData.set('key', { label: 'prime256v1', value: 'prime256v1' }); 6 | mockData.set('value', { totalTime: [23, 12, 3], connectTime: [3, 4 , 22], downloadSpeed: [2, 9, 11]}); 7 | 8 | describe('Dashboard Data Report Util Test', () => { 9 | it('should correctly map an data to CsvDataType', () => { 10 | const expectedResult = [ 11 | ['Algorithm', 12 | 'Session Time - Avg', 13 | 'Session Handshake Time - Avg', 14 | 'Download Speed - Avg',], 15 | [undefined, 'NaN', 'NaN', 'NaN'], 16 | [undefined, '12666.67', '9666.67', '0.01'] 17 | ]; 18 | 19 | const result = mapDashboardDataToCsvDataType(mockData as unknown as ChartDataMap); 20 | expect(result).toEqual(expectedResult); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/Experiment.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | .experiment_wrapper { 4 | padding: 40px; 5 | } 6 | 7 | .tabs_and_options_wrapper { 8 | position: sticky; 9 | inset-block-start: 6%; 10 | display: flex; 11 | justify-content: space-between; 12 | align-items: flex-end; 13 | z-index: 1; 14 | background-color: rgba(var($applicationContentBackgroundColor), 1); 15 | padding-block-end: 1%; 16 | margin-block-end: -1.5%; 17 | } 18 | 19 | .table_options_wrapper { 20 | position: relative; 21 | } 22 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/Charts.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | .charts_content { 4 | margin-block-start: 20px; 5 | } 6 | 7 | .charts_wrapper { 8 | display: flex; 9 | flex-wrap: wrap; 10 | } 11 | 12 | .chart { 13 | flex: 0 0 50%; 14 | box-sizing: border-box; 15 | overflow: hidden; 16 | padding: 0 10px 10px 0; 17 | 18 | &:nth-child(2n) { 19 | padding-inline-end: 0; 20 | } 21 | } 22 | 23 | .title { 24 | font-family: var($fontMedium); 25 | font-size: 18px; 26 | margin-block-end: 20px; 27 | } 28 | 29 | .row { 30 | display: flex; 31 | flex-wrap: wrap; 32 | justify-content: space-between; 33 | margin-block-end: 30px; 34 | } 35 | 36 | .chart_item { 37 | flex: 0 0 calc(50% - 15px); 38 | box-sizing: border-box; 39 | max-inline-size: 830px; 40 | margin-block-end: 16px; 41 | } 42 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/__mocks__/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mocks'; -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/DynamicChart.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | .chart_wrapper { 4 | inline-size: 100%; 5 | min-block-size: 550px; 6 | background-color: var($primaryWhite); 7 | border: 1px solid #BDC2C7; 8 | padding: 36px; 9 | } 10 | 11 | .chart_filters { 12 | display: flex; 13 | justify-content: space-between; 14 | flex-wrap: wrap; 15 | } 16 | 17 | .select_item { 18 | inline-size: 260px; 19 | margin-inline-start: 16px; 20 | } 21 | 22 | .select_type_item { 23 | inline-size: 187px; 24 | } 25 | 26 | .select_item_wrapper { 27 | display: flex; 28 | align-items: center; 29 | } 30 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/components/custom-dropdown-indicator/CustomDropdownIndicator.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { DropdownIndicatorProps, components } from "react-select"; 3 | import { AttSelectOption } from "../../../../../../../../../../shared/components/att-select"; 4 | import { ReactComponent as ArrowDownSelectorSvg } from '../../../../../../../../../../../assets/images/arrow-down-selector.svg'; 5 | 6 | export const CustomDropdownIndicator: React.FC> = (props) => { 7 | const { selectProps } = props; 8 | 9 | const handleClick: () => void = useCallback((): void => { 10 | if (selectProps.menuIsOpen) { 11 | selectProps.onMenuClose(); 12 | } else { 13 | selectProps.onMenuOpen(); 14 | } 15 | }, [selectProps]); 16 | 17 | return ( 18 |
19 | 20 | 21 | 22 |
23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/components/custom-dropdown-indicator/__snapshots__/CustomDropdownIndicator.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CustomDropdownIndicator should render CustomDropdownIndicator 1`] = ` 4 |
5 |
8 | 9 | arrow-down-selector.svg 10 | 11 |
12 |
13 | `; 14 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/components/custom-dropdown-indicator/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CustomDropdownIndicator'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/components/custom-option/CustomOption.module.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | inline-size: 14px; 3 | margin-inline-end: 12px; 4 | } 5 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/components/custom-option/__snapshots__/CustomOption.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CustomOption should render CustomOption 1`] = ` 4 |
8 | option1 13 | 14 | Option1 15 | 16 |
17 | `; 18 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/components/custom-option/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CustomOption'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/components/custom-value-container/CustomValueContainer.module.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | inline-size: 14px; 3 | margin-inline-end: 12px; 4 | } 5 | 6 | .input_wrapper { 7 | display: flex; 8 | align-items: center; 9 | } 10 | 11 | .value { 12 | margin-block-start: 5px; 13 | } 14 | 15 | .placeholder { 16 | color: #878c94; 17 | } 18 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/components/custom-value-container/__snapshots__/CustomValueContainer.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CustomValueContainer should render CustomValueContainer 1`] = ` 4 |
5 |
8 |
11 | 14 |
15 |
16 |
17 | `; 18 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/components/custom-value-container/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CustomValueContainer'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/hooks/useDynamicChartData.test.ts: -------------------------------------------------------------------------------- 1 | import { act, renderHook } from "@testing-library/react"; 2 | import { useDynamicChartData } from "./useDynamicChartData"; 3 | import { MOCK_DATA_FOR_EXPERIMENT } from "../../../../__mocks__/mocks"; 4 | 5 | describe('useDynamicChartData', () => { 6 | test('should get data', async () => { 7 | 8 | const { result } = renderHook(() => useDynamicChartData(MOCK_DATA_FOR_EXPERIMENT)); 9 | act(() => { 10 | expect(result.current).toEqual( {yAxiosOptions: [{label: "Average Cpu", value: "average_cpu"}, {label: "Average Memory", value: "average_memory"}, {label: "Bytes Throughput", value: "bytes_throughput"}, {label: "Request Throughput", value: "request_throughput"}]}); 11 | }); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DynamicChart'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/models/dynamic-chart.interface.ts: -------------------------------------------------------------------------------- 1 | import { AttSelectOption } from "../../../../../../../../../shared/components/att-select"; 2 | 3 | export enum ChartType { 4 | LINE = 'line', 5 | BAR = 'bar', 6 | } 7 | 8 | export const chartTypeOptions: AttSelectOption[] = Object.keys(ChartType).map((key) => ({ 9 | value: ChartType[key as keyof typeof ChartType], 10 | label: key, 11 | })); 12 | 13 | export enum XAxisType { 14 | NUMBER_OF_ITERATIONS = 'Number of Iterations', 15 | } 16 | 17 | export const xAxisTypeOptions: AttSelectOption[] = Object.keys(XAxisType).map((key) => ({ 18 | value: key, 19 | label: XAxisType[key as keyof typeof XAxisType], 20 | })); 21 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const DYNAMIC_CHART_EN = { 2 | SELECTORS: { 3 | LABELS: { 4 | Y_AXIOS: 'Y:', 5 | X_AXIOS: 'X:', 6 | }, 7 | PLACEHOLDERS: { 8 | Y_AXIOS: 'Select Y axios', 9 | X_AXIOS: 'Select X axios', 10 | CHART_TYPE: 'Select chart type', 11 | }, 12 | }, 13 | X_VALUES_TITLE: { 14 | ITERATIONS: 'Iterations', 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/utils/dynamic-chart.utils.test.ts: -------------------------------------------------------------------------------- 1 | import { ChartType } from '../models/dynamic-chart.interface'; 2 | import { capitalizeFirstLetter, getIconByValue, getTitleByXAxiosValue } from './dynamic-chart.utils'; 3 | import LineSvg from '../../../../../../../../../../../src/assets/images/line.svg'; 4 | import BarSvg from '../../../../../../../../../../../src/assets/images/bar.svg'; 5 | 6 | describe('Dynamic chart util test', () => { 7 | test('should get icon by value', () => { 8 | expect(getIconByValue(ChartType.LINE)).toBe(LineSvg); 9 | expect(getIconByValue(ChartType.BAR)).toBe(BarSvg); 10 | }); 11 | 12 | test('should capitalize first letter', () => { 13 | expect(capitalizeFirstLetter('test')).toBe('Test'); 14 | }); 15 | 16 | test('should get title by XAxios value', () => { 17 | expect(getTitleByXAxiosValue('NUMBER_OF_ITERATIONS')).toBe('Iterations'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/components/dynamic-chart/utils/dynamic-chart.utils.ts: -------------------------------------------------------------------------------- 1 | import { ChartType } from "../models/dynamic-chart.interface"; 2 | import LineSvg from '../../../../../../../../../../../src/assets/images/line.svg'; 3 | import BarSvg from '../../../../../../../../../../../src/assets/images/bar.svg'; 4 | import { DYNAMIC_CHART_EN } from "../translate/en"; 5 | 6 | export function getIconByValue(value: ChartType): string { 7 | return value === ChartType.LINE ? LineSvg : BarSvg; 8 | } 9 | 10 | export function capitalizeFirstLetter(str: string): string { 11 | return str.charAt(0).toUpperCase() + str.slice(1); 12 | } 13 | 14 | export function getTitleByXAxiosValue(value: string): string { 15 | return value === 'NUMBER_OF_ITERATIONS' ? DYNAMIC_CHART_EN.X_VALUES_TITLE.ITERATIONS : ''; 16 | } 17 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Charts'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/models/bar-chart.const.ts: -------------------------------------------------------------------------------- 1 | import { ITestRunResultData } from '../../../../../../../shared/models/test-run-result.interface'; 2 | import { CHARTS_EN } from '../translate/en'; 3 | 4 | type TooltipKeys = keyof ITestRunResultData; 5 | export const tooltipKeys: TooltipKeys[] = ['iterations', 'message_size']; 6 | export const tooltipLabels = ['Iterations', 'Message Size']; 7 | 8 | export enum ChartKey { 9 | average_cpu = 'average_cpu', 10 | average_memory = 'average_memory', 11 | } 12 | 13 | export type ChartTitleDisplayMappingType = { [key in keyof typeof ChartKey]: string; } 14 | export const ChartTitleDisplayMapping: ChartTitleDisplayMappingType = { 15 | [ChartKey.average_cpu]: `${CHARTS_EN.CHART_TITLES.PREFIX} ${CHARTS_EN.CHART_TITLES.AVERAGE_CPU}`, 16 | [ChartKey.average_memory]: `${CHARTS_EN.CHART_TITLES.PREFIX} ${CHARTS_EN.CHART_TITLES.AVERAGE_MEMORY}`, 17 | } as unknown as ChartTitleDisplayMappingType; 18 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/models/line-chart-data.interface.ts: -------------------------------------------------------------------------------- 1 | interface IDatasets { 2 | label: string; 3 | data: any; 4 | fill: boolean; 5 | backgroundColor: string; 6 | borderColor: string; 7 | borderWidth: number; 8 | } 9 | 10 | export interface ILineChartData { 11 | labels: number[]; 12 | datasets: IDatasets[]; 13 | } 14 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const CHARTS_EN = { 2 | TITLE: 'Visualization', 3 | Y_AXIS_TITLE: 'Memory (%)', 4 | CHART_TITLES: { 5 | PREFIX: 'Server Memory (%) vs.', 6 | AVERAGE_CPU: 'Average CPU', 7 | AVERAGE_MEMORY: 'Average Memory', 8 | ERROR_RATE: 'Error Rate', 9 | BYTES_THROUGHPUT: 'Bytes Throughput', 10 | MESSAGES_THROUGHPUT: 'Messages Throughput', 11 | AVERAGE_TLS_HANDSHAKE_TIME: 'Average TLS Handshake Time', 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/utils/__mocks__/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mocks'; -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/utils/chart.utils.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react'; 2 | import { getChartTitleByType, getKeysOfData, getLabels } from './chart.utils'; 3 | import { MOCK_DATA_FOR_CHART_UTILS } from './__mocks__'; 4 | 5 | describe('Chart utils', () => { 6 | test('should get labels from data', () => { 7 | const { result } = renderHook(() => getLabels(MOCK_DATA_FOR_CHART_UTILS)); 8 | expect(result.current).toEqual(['Algorithm1 (iteration 1) ', 'Algorithm2 (iteration 2) ', 'Algorithm1 (iteration 3) ']); 9 | }); 10 | 11 | test('should get keys of data', () => { 12 | const { result } = renderHook(() => getKeysOfData(MOCK_DATA_FOR_CHART_UTILS[0].results)); 13 | expect(result.current).toEqual(['average_cpu', 'average_memory', 'bytes_throughput', 'request_throughput']); 14 | }); 15 | 16 | test('should chart title by type', () => { 17 | const { result } = renderHook(() => getChartTitleByType('average_cpu')); 18 | expect(result.current).toEqual("Server Memory (%) vs. Average CPU"); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/utils/chart.utils.ts: -------------------------------------------------------------------------------- 1 | import { IResult, ITestRunResultData } from '../../../../../../../shared/models/test-run-result.interface'; 2 | import { ChartKey, ChartTitleDisplayMapping } from '../models/bar-chart.const'; 3 | 4 | export function getLabels(data: ITestRunResultData[]): string[] { 5 | return data.map((obj: ITestRunResultData, index: number) => `${obj.algorithm} (iteration ${index + 1}) `); 6 | } 7 | 8 | export function getKeysOfData(results: IResult): string[] { 9 | return Object.keys(results); 10 | } 11 | 12 | export function getChartTitleByType(type: string): string | undefined { 13 | return ChartTitleDisplayMapping[type as unknown as ChartKey]; 14 | } 15 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/charts/utils/test-run.utils.ts: -------------------------------------------------------------------------------- 1 | import { ITestRunResultData } from "../../../../../../../shared/models/test-run-result.interface"; 2 | 3 | export function sortDataByAlgorithm(dataObject: ITestRunResultData[]) { 4 | return dataObject.sort((a, b) => { 5 | if (a.algorithm < b.algorithm) { 6 | return -1; 7 | } 8 | if (a.algorithm > b.algorithm) { 9 | return 1; 10 | } 11 | if (a.iterations < b.iterations) { 12 | return -1; 13 | } 14 | if (a.iterations > b.iterations) { 15 | return 1; 16 | } 17 | return 0; 18 | }); 19 | } -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/delete-experiment-modal/DeleteExperimentModal.module.scss: -------------------------------------------------------------------------------- 1 | .submit_button { 2 | inline-size: 150px; 3 | block-size: 50px; 4 | } 5 | 6 | .description { 7 | font-size: 18px; 8 | padding-inline-start: 8px; 9 | } 10 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/delete-experiment-modal/DeleteExperimentModal.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/typedef */ 2 | import { act, fireEvent, render, RenderResult } from '@testing-library/react'; 3 | import { DeleteExperimentModal, DeleteExperimentModalProps } from './DeleteExperimentModal'; 4 | 5 | describe('EditExperimentModal', () => { 6 | test('renders edit Experiment modal correctly', () => { 7 | const props: DeleteExperimentModalProps = { 8 | name: ['Test'], 9 | onClose: jest.fn(), 10 | }; 11 | const { baseElement }: RenderResult = render(TestMe); 12 | expect(baseElement.firstChild).toMatchSnapshot(); 13 | }); 14 | 15 | test('click submit button', () => { 16 | const handleClose = jest.fn(); 17 | const props: DeleteExperimentModalProps = { 18 | name: ['Test'], 19 | onClose: handleClose, 20 | }; 21 | const { getByRole }: RenderResult = render(TestMe); 22 | act(() => { 23 | fireEvent.submit(getByRole('button', { name: /Confirm/i })); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/delete-experiment-modal/__snapshots__/DeleteExperimentModal.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`EditExperimentModal renders edit Experiment modal correctly 1`] = ` 4 |
9 | `; 10 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/delete-experiment-modal/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DeleteExperimentModal'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/delete-experiment-modal/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const DELETE_EXPERIMENT_MODAL_EN = { 2 | SUBMIT_ACTION: 'Confirm', 3 | TITLE: 'Delete Experiment', 4 | DESCRIPTION: 'Are you sure you want to delete the following experiment(s)?' 5 | }; 6 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/edit-experiment-modal/EditExperimentModal.module.scss: -------------------------------------------------------------------------------- 1 | .form_wrapper { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | .required_input { 7 | &:after { 8 | content: "*"; 9 | color: red; 10 | } 11 | } 12 | 13 | .label { 14 | font-size: 16px; 15 | margin-block-end: 8px; 16 | } 17 | 18 | .name_input { 19 | margin-block-end: 20px; 20 | } 21 | 22 | .form_input { 23 | border-radius: 20px; 24 | border: 1px solid #DCDFE3; 25 | color: #1D2329; 26 | 27 | &:focus { 28 | outline: none; 29 | } 30 | } 31 | 32 | .form_input_name { 33 | padding: 1px 6px 1px 22px; 34 | block-size: 40px; 35 | inline-size: 240px; 36 | } 37 | 38 | .form_input_description { 39 | inline-size: 600px; 40 | resize: none; 41 | padding: 12px 6px 12px 22px; 42 | } 43 | 44 | .submit_button { 45 | inline-size: 150px; 46 | block-size: 50px; 47 | } 48 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/edit-experiment-modal/__snapshots__/EditExperimentModal.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`EditExperimentModal renders edit Experiment modal correctly 1`] = ` 4 |
9 | `; 10 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/edit-experiment-modal/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EditExperimentModal'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/edit-experiment-modal/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const EDIT_EXPERIMENT_MODAL_EN = { 2 | SUBMIT_ACTION: 'Save', 3 | TITLE: 'Edit Experiment Details', 4 | FORM: { 5 | LABELS: { 6 | NAME: 'Experiment name', 7 | DESCRIPTION: 'Description', 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | .experiment_table_wrapper { 4 | font-size: 14px; 5 | margin-block-start: 30px; 6 | margin-block-end: 50px; 7 | display: flex; 8 | flex-wrap: wrap; 9 | } 10 | 11 | .experiment_table { 12 | text-align: center; 13 | 14 | th:first-child, 15 | td:first-child { 16 | inline-size: 80px; 17 | } 18 | } -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.test.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | import { ExperimentTable } from './ExperimentTable'; 3 | import { MOCK_DATA_FOR_EXPERIMENT_TABLE, MOCK_DATA_FOR_EXPERIMENT_WITH_NO_TEST_RUNS } from '../__mocks__/mocks'; 4 | 5 | jest.mock('../hooks/useExperimentData'); 6 | 7 | describe('ExperimentTable', () => { 8 | test('should render ExperimentTable', async () => { 9 | const { container } = render(); 10 | 11 | expect(container).toBeTruthy(); 12 | }); 13 | 14 | test('should render ExperimentTable with no data', async () => { 15 | const { container } = render(); 16 | 17 | expect(container).toBeTruthy(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-table/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ExperimentTable'; -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-table/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const EXPERIMENT_TABLE_EN = { 2 | TABLE_TITLES: { 3 | HASHTAG: '#', 4 | ALGORITHM: 'Algorithm', 5 | ITERATIONS: 'Iterations', 6 | MESSAGE_SIZE: 'Message Size (KB)', 7 | AVERAGE_CPU: 'Average CPU', 8 | AVERAGE_MEMORY: 'Average Memory', 9 | THROUGHPUT_BYTES: 'Throughput (bytes/sec)', 10 | THROUGHPUT_REQUEST: 'Throughput (message/sec)', 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-tabs/ExperimentTabs.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | @import "src/styles/z-index"; 3 | 4 | .experiment_tabs_buttons_wrapper { 5 | display: flex; 6 | align-items: center; 7 | } 8 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-tabs/ExperimentTabs.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./ExperimentTabs.module.scss"; 2 | import { ExperimentTabButton } from "./components/experiment-tab-button"; 3 | import { EXPERIMENT_EN } from "../../translate/en"; 4 | 5 | export interface ExperimentTabsProps { 6 | currentSection: string; 7 | handleButtonClick: (section: string) => void; 8 | } 9 | 10 | export const ExperimentTabs: React.FC = (props: ExperimentTabsProps) => { 11 | const titles = [EXPERIMENT_EN.TABS.RESULTS_DATA, EXPERIMENT_EN.TABS.VISUALIZATION]; 12 | 13 | return ( 14 |
15 | {titles.map((title) => ( 16 | props.handleButtonClick(title)} 19 | isSelected={props.currentSection === title} 20 | > 21 | {title} 22 | 23 | ))} 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-tabs/components/experiment-tab-button/ExperimentTabButton.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | .experiment_tab_button_wrapper { 4 | display: contents; 5 | } 6 | 7 | .experiment_tab_button { 8 | color: var($attPurple); 9 | font-size: 16px; 10 | margin-block-start: 30px; 11 | margin-inline-end: 30px; 12 | background-color: transparent; 13 | border: none; 14 | outline: none; 15 | cursor: pointer; 16 | position: relative; 17 | 18 | &:after { 19 | content: ""; 20 | display: block; 21 | block-size: 3px; 22 | background-color: var($attPurple); 23 | transform: scaleX(0); 24 | transition: transform 0.3s ease-in-out; 25 | position: absolute; 26 | inset-inline-start: 0; 27 | inset-inline-end: 0; 28 | inset-block-end: -10px; 29 | } 30 | 31 | &.selected:after { 32 | transform: scaleX(1); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-tabs/components/experiment-tab-button/ExperimentTabButton.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, RenderResult } from '@testing-library/react'; 2 | import { ExperimentTabButton, ExperimentTabButtonProps } from './ExperimentTabButton'; 3 | 4 | describe('ExperimentTabButton', () => { 5 | test('renders ExperimentTabButton', () => { 6 | const experimentTabButtonProps: ExperimentTabButtonProps = { 7 | isSelected: true, 8 | onClick: jest.fn(), 9 | children: undefined, 10 | }; 11 | 12 | const { container }: RenderResult = render(); 13 | expect(container.firstChild).toMatchSnapshot(); 14 | }); 15 | }); -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-tabs/components/experiment-tab-button/ExperimentTabButton.tsx: -------------------------------------------------------------------------------- 1 | import styles from './ExperimentTabButton.module.scss'; 2 | import cn from 'classnames'; 3 | 4 | export type ExperimentTabButtonProps = { 5 | onClick: (e: React.MouseEvent) => void; 6 | isSelected: boolean; 7 | children: React.ReactNode; 8 | }; 9 | 10 | export const ExperimentTabButton: React.FC = (props: ExperimentTabButtonProps) => { 11 | return ( 12 |
13 | 19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-tabs/components/experiment-tab-button/__snapshots__/ExperimentTabButton.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ExperimentTabButton renders ExperimentTabButton 1`] = ` 4 |
7 |
11 | `; 12 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-tabs/components/experiment-tab-button/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ExperimentTabButton'; -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/experiment-tabs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ExperimentTabs'; -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/sub-header/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SubHeader'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/sub-header/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const SUB_HEADER_EN = { 2 | ALGORITHM: 'Algorithm(s)', 3 | ITERATIONS: 'Iterations', 4 | CSV_REPORT: { 5 | FILE_NAME: 'Experiment-Data-Report', 6 | TABLE: { 7 | COLUMNS: { 8 | ID: 'ID', 9 | ALGORITHM: 'Algorithm', 10 | ITERATIONS: 'Iterations', 11 | AVERAGE_CPU: 'Average CPU', 12 | AVERAGE_MEMORY: 'Average Memory', 13 | }, 14 | }, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/sub-header/utils/data-to-csv.util.test.ts: -------------------------------------------------------------------------------- 1 | import { CsvDataType } from '../../../../../../../utils/download'; 2 | import { CSV_MOCK } from '../../__mocks__/mocks'; 3 | import { mapExperimentDataToCsvDataType } from './data-to-csv.util'; 4 | 5 | describe('mapExperimentDataToCsvDataType', () => { 6 | it('should map the input list to CsvDataType correctly', () => { 7 | const mockList = CSV_MOCK; 8 | 9 | const expectedOutput: CsvDataType = [ 10 | [ 'ID', 'Algorithm', 'Iterations', 'Average CPU', 'Average Memory' ], 11 | [ 1, 'App1', 1000, 2000, 3000 ], 12 | [ 2, 'App2', 4000, 5000, 6000 ] 13 | ]; 14 | 15 | const result = mapExperimentDataToCsvDataType(mockList); 16 | expect(result).toEqual(expectedOutput); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/sub-header/utils/data-to-csv.util.ts: -------------------------------------------------------------------------------- 1 | import { ITestRunResultData } from '../../../../../../../shared/models/test-run-result.interface'; 2 | import { CsvDataType } from '../../../../../../../utils/download'; 3 | import { SUB_HEADER_EN } from '../translate/en'; 4 | 5 | const ExperimentDataReportCsvFileTitles: string[] = [ 6 | SUB_HEADER_EN.CSV_REPORT.TABLE.COLUMNS.ID, 7 | SUB_HEADER_EN.CSV_REPORT.TABLE.COLUMNS.ALGORITHM, 8 | SUB_HEADER_EN.CSV_REPORT.TABLE.COLUMNS.ITERATIONS, 9 | SUB_HEADER_EN.CSV_REPORT.TABLE.COLUMNS.AVERAGE_CPU, 10 | SUB_HEADER_EN.CSV_REPORT.TABLE.COLUMNS.AVERAGE_MEMORY, 11 | ]; 12 | 13 | export function mapExperimentDataToCsvDataType(list: ITestRunResultData[]): CsvDataType { 14 | return [ 15 | ExperimentDataReportCsvFileTitles, 16 | ...list.map((item: ITestRunResultData) => [ 17 | item.id, 18 | item.algorithm, 19 | item.iterations, 20 | item.results.average_cpu, 21 | item.results.average_memory, 22 | ])]; 23 | } 24 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/sub-header/utils/sub-header.utils.ts: -------------------------------------------------------------------------------- 1 | import { uniq } from "lodash"; 2 | import { ITestRunResultData } from "../../../../../../../shared/models/test-run-result.interface"; 3 | 4 | export function getAlgorithmsName(data: ITestRunResultData[]): string { 5 | const algorithms: string[] = data.map((item: ITestRunResultData) => item.algorithm); 6 | const uniqueAlgorithms: string[] = uniq(algorithms); 7 | 8 | return uniqueAlgorithms.join(', ');; 9 | } 10 | 11 | export function getIterations(data: ITestRunResultData[]): string { 12 | const iterations: number[] = data.map((item: ITestRunResultData) => item.iterations); 13 | const uniqueIterations: number[] = uniq(iterations); 14 | const orderIterations: number[] = uniqueIterations.sort((a: number, b: number) => a - b); 15 | 16 | return orderIterations.join(', ');; 17 | } 18 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/table-options/components/select-columns-popup/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SelectColumnsPopup'; -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/table-options/components/select-columns-popup/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const SELECT_COLUMNS_EN = { 2 | TITLE: 'Select columns to display', 3 | RESET_TO_DEFAULT: 'Reset to default', 4 | SAVE: 'Save', 5 | } 6 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/table-options/components/select-columns-popup/utils/convert-data-to-options.utils.ts: -------------------------------------------------------------------------------- 1 | import { TableOptionsData } from '../../../constants/table-options.const'; 2 | 3 | export function convertDataToOptions(data: typeof TableOptionsData) { 4 | return [ 5 | { 6 | label: 'iterations', 7 | value: data.options[0] 8 | }, 9 | { 10 | label: 'results.average_cpu', 11 | value: data.options[2] 12 | }, 13 | { 14 | label: 'results.average_memory', 15 | value: data.options[3] 16 | }, 17 | { 18 | label: 'results.bytes_throughput', 19 | value: data.options[4] 20 | }, 21 | { 22 | label: 'results.request_throughput', 23 | value: data.options[5] 24 | }, 25 | { 26 | label: 'message_size', 27 | value: data.options[1] 28 | }, 29 | ]; 30 | } -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/table-options/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TableOptions'; -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/components/table-options/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const TABLE_OPTIONS_EN = { 2 | VIEW_TO_GRAFANA: 'View in Grafana', 3 | SELECT_COLUMNS: 'Select columns', 4 | FILTERS: 'Filters', 5 | } 6 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Experiment'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const EXPERIMENT_EN = { 2 | TABS: { 3 | RESULTS_DATA: 'Results Data', 4 | VISUALIZATION: 'Visualization', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sections-scrolling.utils'; -------------------------------------------------------------------------------- /portal/src/app/components/home/components/experiment/utils/sections-scrolling.utils.ts: -------------------------------------------------------------------------------- 1 | import { Dispatch, RefObject, SetStateAction } from "react"; 2 | 3 | export function handleSectionScrolling( 4 | resultsDataRef: RefObject, 5 | visualizationRef: RefObject, 6 | setCurrentSection: Dispatch>): () => void 7 | { 8 | const resultsDataNode = resultsDataRef.current; 9 | const visualizationNode = visualizationRef.current; 10 | const observer = new IntersectionObserver((entries) => { 11 | entries.forEach(entry => { 12 | if (entry.isIntersecting) { 13 | setCurrentSection(entry.target.id); 14 | } 15 | }); 16 | }, { threshold: 0.5 }); 17 | 18 | if (resultsDataNode) { 19 | observer.observe(resultsDataNode); 20 | } 21 | 22 | if (visualizationNode) { 23 | observer.observe(visualizationNode); 24 | } 25 | 26 | return () => { 27 | if (resultsDataNode) { 28 | observer.unobserve(resultsDataNode); 29 | } 30 | 31 | if (visualizationNode) { 32 | observer.unobserve(visualizationNode); 33 | } 34 | }; 35 | }; -------------------------------------------------------------------------------- /portal/src/app/components/home/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './experiment'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Home'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/protocol-query/ProtocolQuery.model.ts: -------------------------------------------------------------------------------- 1 | import { AttSelectOption } from '../../shared/components/att-select'; 2 | 3 | export enum AlgorithmTypeEnum { 4 | BIKE = 'BIKE', 5 | CRYSTALS_Kyber = 'CRYSTALS-Kyber', 6 | FrodoKEM = 'FrodoKEM', 7 | HQC = 'HQC' 8 | } 9 | 10 | export type AlgorithmsPerTypeMapType = { [key in AlgorithmTypeEnum]: AttSelectOption[] }; 11 | -------------------------------------------------------------------------------- /portal/src/app/components/protocol-query/constants/algorithms-sections.ts: -------------------------------------------------------------------------------- 1 | export const algorithmSections = ['All', 'Classic', 'Hybrid', 'PQ']; -------------------------------------------------------------------------------- /portal/src/app/components/protocol-query/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './algorithms-sections'; -------------------------------------------------------------------------------- /portal/src/app/components/protocol-query/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useDuplicateData'; 2 | export * from './useGetAlgorithms'; 3 | export * from './useGetIterations'; 4 | export * from './useMessageSizeData'; 5 | -------------------------------------------------------------------------------- /portal/src/app/components/protocol-query/hooks/useGetIterations.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react'; 2 | import { useFetch } from '../../../shared/hooks/useFetch'; 3 | import { useGetIterations } from './useGetIterations'; 4 | 5 | jest.mock('../../../shared/hooks/useFetch', () => ({ 6 | useFetch: jest.fn(), 7 | })); 8 | 9 | describe('useGetIterations', () => { 10 | test('Should be in Success mode', () => { 11 | const mockData = { 12 | iterations: [1, 2, 3, 4, 5, 6, 7, 8, 9], 13 | }; 14 | 15 | (useFetch as jest.Mock).mockReturnValue({ 16 | get: jest.fn(), 17 | data: mockData, 18 | cancelRequest: jest.fn(), 19 | }); 20 | 21 | const { result } = renderHook(() => useGetIterations()); 22 | expect(result.current.iterationsOptions.length).toEqual(mockData.iterations.length + 2); 23 | }); 24 | }); -------------------------------------------------------------------------------- /portal/src/app/components/protocol-query/hooks/useMessageSizeData.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react'; 2 | import { useFetch } from '../../../shared/hooks/useFetch'; 3 | import { useMessageSizeData } from './useMessageSizeData'; 4 | 5 | jest.mock('../../../shared/hooks/useFetch', () => ({ 6 | useFetch: jest.fn(), 7 | })); 8 | 9 | describe('useMessageSizeData', () => { 10 | test('Should be in Success mode', () => { 11 | const mockData = { 12 | message_sizes: [1, 2, 3, 4, 5, 6, 7, 8, 9], 13 | }; 14 | 15 | (useFetch as jest.Mock).mockReturnValue({ 16 | get: jest.fn(), 17 | data: mockData, 18 | cancelRequest: jest.fn(), 19 | }); 20 | 21 | const { result } = renderHook(() => useMessageSizeData()); 22 | expect(result.current.messageSizeOptions.length).toEqual(mockData.message_sizes.length + 2); 23 | }); 24 | }); -------------------------------------------------------------------------------- /portal/src/app/components/protocol-query/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProtocolQuery'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/protocol-query/translate/en.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/typedef 2 | export const PROTOCOL_QUERY_EN = { 3 | TITLE: 'Run new experiment', 4 | NOTE: { 5 | TITLE: 'Note:', 6 | TEXT: 'For each experiment you can select or type in one or more algorithms and number of iterations', 7 | }, 8 | ACTION_BUTTONS: { 9 | RUN: 'Run', 10 | EXPORT: 'Export', 11 | }, 12 | FIELDS_LABEL: { 13 | REQUIRED: '*', 14 | EXPERIMENT_NAME: 'Experiment name', 15 | ITERATIONS_NUMBER: 'Number of iterations', 16 | ALGORITHM: 'Algorithm(s)', 17 | MESSAGE_SIZE: 'Message size', 18 | DESCRIPTION: 'Description', 19 | }, 20 | FETCH_DATA: 'Loading data...', 21 | }; 22 | -------------------------------------------------------------------------------- /portal/src/app/components/protocol-query/utils/convertBytesToHumanReadable.ts: -------------------------------------------------------------------------------- 1 | import { filesize } from 'filesize'; 2 | import { AttSelectOption } from '../../../shared/components/att-select'; 3 | 4 | export function convertBytesToHumanReadable(messageSizeOptions: AttSelectOption[]) { 5 | return messageSizeOptions.map((option: AttSelectOption) => { 6 | if (!option.value || isNaN(+option.value)) { 7 | return option; 8 | } 9 | const sizeOption = filesize(+option.value, {round: 3}); 10 | return { 11 | label: sizeOption, 12 | value: sizeOption, 13 | metadata: option.metadata 14 | } as AttSelectOption; 15 | }); 16 | } -------------------------------------------------------------------------------- /portal/src/app/components/protocol-query/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './handleAlgorithmsChange'; 2 | export * from './convertBytesToHumanReadable'; -------------------------------------------------------------------------------- /portal/src/app/components/sub-header/SubHeader.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, RenderResult } from '@testing-library/react'; 2 | import { MemoryRouter } from 'react-router-dom'; 3 | import { Button } from '../../shared/components/att-button'; 4 | import { SubHeader } from './SubHeader'; 5 | 6 | jest.mock('../../shared/components/att-button'); 7 | 8 | describe('SubHeader', () => { 9 | test('renders sub header ', () => { 10 | (Button as jest.Mock).mockImplementation(() =>
Button
); 11 | const { container, getByText }: RenderResult = render(, { wrapper: MemoryRouter }); 12 | expect(container.firstChild).toMatchSnapshot(); 13 | expect(getByText('Deriving symmetric keys, establishing a secure channel for communication.')).toBeTruthy(); 14 | }); 15 | }); -------------------------------------------------------------------------------- /portal/src/app/components/sub-header/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SubHeader'; 2 | -------------------------------------------------------------------------------- /portal/src/app/components/sub-header/models/sub-header.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ISubHeaderItem { 2 | description: string; 3 | icon: string; 4 | alt: string; 5 | className?: string; 6 | } 7 | -------------------------------------------------------------------------------- /portal/src/app/components/sub-header/translate/en.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/typedef 2 | export const SUB_HEADER_EN = { 3 | TITLE: 'That’s what we are doing on each iteration:', 4 | DESCRIPTIONS: { 5 | STEP_1: 'Initiating handshake\nusing the selected\ncryptographic algorithm.', 6 | STEP_2: 'Exchanging keys using\nthe selected algorithm to\ncreate a shared secret.', 7 | STEP_3: 'Deriving symmetric keys,\nestablishing a secure channel\nfor communication.', 8 | STEP_4: 'Downloading content\nover the secure channel.', 9 | STEP_5: 'Closing\nthe session', 10 | }, 11 | ALT_TITLES: { 12 | HANDSHAKE: 'Handshake', 13 | KEYS: 'Keys', 14 | LOCK: 'Lock', 15 | DOWNLOAD: 'Download', 16 | CHECK: 'Check', 17 | }, 18 | TOGGLE_BUTTON: 'Learn about the process of each iteration', 19 | }; 20 | -------------------------------------------------------------------------------- /portal/src/app/hooks/useErrorMessage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useErrorMessage'; 2 | -------------------------------------------------------------------------------- /portal/src/app/hooks/useErrorMessage/useErrorMessage.test.tsx: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react'; 2 | import { useErrorMessage } from './useErrorMessage'; 3 | import { AxiosError } from 'axios'; 4 | import { attToast } from '../../shared/components/toast/att-toast.service'; 5 | import { IServerError } from '../../shared/models/server-error.interface'; 6 | 7 | jest.mock('../../shared/components/toast/att-toast.service'); 8 | 9 | describe('useErrorMessage', () => { 10 | test('should show error message when we receive error from BE', () => { 11 | const error: AxiosError = {} as AxiosError; 12 | renderHook(() => useErrorMessage(error)); 13 | attToast.error(error.message, 'Error', undefined); 14 | }); 15 | 16 | test('should not show error message when no error provided', () => { 17 | renderHook(() => useErrorMessage()); 18 | expect(attToast.error).not.toHaveBeenCalled(); 19 | }); 20 | 21 | }); 22 | -------------------------------------------------------------------------------- /portal/src/app/hooks/useErrorMessage/useErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { AxiosError } from 'axios'; 3 | import { attToast } from '../../shared/components/toast/att-toast.service'; 4 | import { IServerError } from '../../shared/models/server-error.interface'; 5 | 6 | export function useErrorMessage(error?: AxiosError): void { 7 | useEffect(() => { 8 | if (error) { 9 | attToast.error(error?.response?.data?.message as string, error?.response?.data?.error as string, undefined); 10 | } 11 | }, [error]); 12 | } 13 | -------------------------------------------------------------------------------- /portal/src/app/hooks/useOutsideClick.ts: -------------------------------------------------------------------------------- 1 | import { RefObject, useEffect } from "react"; 2 | 3 | export const useOutsideClick = (ref: RefObject, callback: () => void) => { 4 | useEffect(() => { 5 | const handleClickOutside = (event: MouseEvent) => { 6 | if (ref.current && !ref.current.contains(event.target as Node)) { 7 | callback(); 8 | } 9 | }; 10 | 11 | document.addEventListener('mousedown', handleClickOutside); 12 | return () => { 13 | document.removeEventListener('mousedown', handleClickOutside); 14 | }; 15 | }, [ref, callback]); 16 | }; 17 | -------------------------------------------------------------------------------- /portal/src/app/models/server-error.interface.ts: -------------------------------------------------------------------------------- 1 | import { Id, ToastOptions } from 'react-toastify'; 2 | 3 | export enum SeverityType { 4 | ERROR = 'error', 5 | ISSUE = 'issue', 6 | INFO = 'info', 7 | WARNING = 'warning', 8 | } 9 | export interface IServerError { 10 | ticket: string; 11 | code: number; 12 | message: string; 13 | variables: string[]; 14 | severity?: SeverityType; 15 | description?: string; 16 | } 17 | 18 | export type TATTToast = (message: string, title?: string, options?: ToastOptions, children?: React.ReactNode) => Id; 19 | -------------------------------------------------------------------------------- /portal/src/app/routes-navigation.const.ts: -------------------------------------------------------------------------------- 1 | export const NAVIGATION_ROUTES: Record = { 2 | home: '/qujata', 3 | not_found: 'not-found', 4 | }; 5 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-button/Button.const.ts: -------------------------------------------------------------------------------- 1 | import styles from './Button.module.scss'; 2 | import { ButtonSize, ButtonStyleType } from './Button.model'; 3 | 4 | export const ButtonCssClassByStyleType: Map = new Map([ 5 | [ButtonStyleType.PRIMARY, styles.primary_button], 6 | [ButtonStyleType.SECONDARY, styles.secondary_button], 7 | [ButtonStyleType.TEXT, styles.txt_button], 8 | [ButtonStyleType.WRAPPER, styles.wrapper_button], 9 | ]); 10 | 11 | export const ButtonCssClassBySize: Map = new Map([ 12 | [ButtonSize.SMALL, styles.sm_button], 13 | [ButtonSize.MEDIUM, styles.md_button], 14 | [ButtonSize.LARGE, styles.lg_button], 15 | [ButtonSize.NONE, ''], 16 | ]); 17 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-button/Button.model.ts: -------------------------------------------------------------------------------- 1 | export enum ButtonActionType { 2 | BUTTON = 'button', 3 | SUBMIT = 'submit', 4 | RESET = 'reset', 5 | } 6 | 7 | export enum ButtonStyleType { 8 | PRIMARY, 9 | SECONDARY, 10 | TEXT, 11 | WRAPPER, 12 | } 13 | 14 | export enum ButtonSize { 15 | SMALL, 16 | MEDIUM, 17 | LARGE, 18 | NONE, 19 | } 20 | 21 | export interface IButton { 22 | size: ButtonSize; 23 | styleType: ButtonStyleType; 24 | actionType: ButtonActionType; 25 | className?: string; 26 | disabled?: boolean; 27 | text: string; 28 | ariaLabel?: string; 29 | form?: string; 30 | e2eId?: string; 31 | onClick: (e?: React.MouseEvent) => void; 32 | } 33 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-button/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Button'; 2 | export * from './Button.model'; 3 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-link/ExternalLink.tsx: -------------------------------------------------------------------------------- 1 | import { BaseLinkProps, LinkRel, LinkTarget } from './Link.model'; 2 | import { getLinkClassName } from './Link.util'; 3 | 4 | export interface ExternalLinkProps extends BaseLinkProps { 5 | target?: LinkTarget; 6 | rel?: LinkRel; 7 | } 8 | 9 | export const ExternalLink: React.FC = (props: ExternalLinkProps) => ( 10 | 19 | {props.children} 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-link/Link.const.ts: -------------------------------------------------------------------------------- 1 | import styles from './Link.module.scss'; 2 | import { LinkSize, LinkStyle } from './Link.model'; 3 | 4 | export const LinkCssClassByStyleType: Map = new Map([ 5 | [LinkStyle.PRIMARY, styles.primary_link], 6 | [LinkStyle.SECONDARY, styles.secondary_link], 7 | [LinkStyle.WRAPPER, styles.wrapper_link], 8 | [LinkStyle.TEXT, styles.text_link], 9 | [LinkStyle.NONE, ''], 10 | ]); 11 | 12 | export const LinkCssClassBySize: Map = new Map([ 13 | [LinkSize.SMALL, styles.sm_link], 14 | [LinkSize.MEDIUM, styles.md_link], 15 | [LinkSize.LARGE, styles.lg_link], 16 | [LinkSize.NONE, ''], 17 | ]); 18 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-link/Link.module.scss: -------------------------------------------------------------------------------- 1 | @import './../att-button/Button.reference'; 2 | 3 | %linkDefinitions { 4 | align-items: center; 5 | display: flex; 6 | justify-content: center; 7 | text-decoration: none; 8 | 9 | @extend %basicButtonStyle; 10 | } 11 | 12 | .sm_link { 13 | @extend %smallSize; 14 | } 15 | 16 | .md_link { 17 | @extend %mediumSize; 18 | } 19 | 20 | .lg_link { 21 | @extend %largeSize; 22 | } 23 | 24 | .primary_link { 25 | @extend %linkDefinitions; 26 | @extend %primaryStyle; 27 | } 28 | 29 | .secondary_link { 30 | @extend %linkDefinitions; 31 | @extend %secondaryStyle; 32 | } 33 | 34 | .wrapper_link { 35 | text-decoration: none; 36 | color: var($primaryWhite); 37 | } 38 | 39 | .text_link { 40 | &:not(:hover) { 41 | color: var($primaryWhite); 42 | text-decoration: none; 43 | } 44 | 45 | &:hover { 46 | color: var($primaryWhite); 47 | text-decoration: underline; 48 | } 49 | } -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-link/Link.util.ts: -------------------------------------------------------------------------------- 1 | import cn from 'classnames'; 2 | import { LinkCssClassBySize, LinkCssClassByStyleType } from './Link.const'; 3 | import { BaseLinkProps } from './Link.model'; 4 | 5 | export function getLinkClassName({ className, size, styleType }: BaseLinkProps) { 6 | return cn(className, LinkCssClassBySize.get(size), LinkCssClassByStyleType.get(styleType)); 7 | } 8 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-link/__snapshots__/ExternalLink.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ExternalLink renders external link 1`] = ` 4 | 10 | external link 11 | 12 | `; 13 | 14 | exports[`ExternalLink renders external link with default target and rel attributes 1`] = ` 15 | 21 | external link 22 | 23 | `; 24 | 25 | exports[`ExternalLink renders external wrapper link 1`] = ` 26 | 32 | 33 | external wrapper link 34 | 35 | 36 | `; 37 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-link/__snapshots__/InternalLink.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`InternalLink renders large primary link 1`] = ` 4 | 8 | large primary link 9 | 10 | `; 11 | 12 | exports[`InternalLink renders medium secondary link 1`] = ` 13 | 17 | medium secondary link 18 | 19 | `; 20 | 21 | exports[`InternalLink renders regular link 1`] = ` 22 | 26 | regular link 27 | 28 | `; 29 | 30 | exports[`InternalLink renders wrapper link 1`] = ` 31 | 35 | 36 | wrapper link 37 | 38 | 39 | `; 40 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-link/index.ts: -------------------------------------------------------------------------------- 1 | export * from './InternalLink'; 2 | export * from './ExternalLink'; 3 | export * from './Link.model'; 4 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-select/AttSelect.const.ts: -------------------------------------------------------------------------------- 1 | import styles from './AttSelect.module.scss'; 2 | import { AttSelectTheme } from './AttSelect.model'; 3 | 4 | export const AttSelectCssClassByTheme: Map = new Map([ 5 | [AttSelectTheme.PRIMARY, styles.att_select_primary_theme], 6 | [AttSelectTheme.WRAPPER, styles.att_select_wrapper_theme], 7 | [AttSelectTheme.REGULAR, ''], 8 | ]); 9 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-select/AttSelect.model.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 2 | export interface AttSelectOption { 3 | value: string; 4 | label: string; 5 | metadata?: T; 6 | isDisabled?: boolean, 7 | } 8 | 9 | export enum AttSelectTheme { 10 | REGULAR = 'regular', 11 | PRIMARY = 'primary', 12 | WRAPPER = 'wrapper', 13 | } 14 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-select/AttSelect.reference.scss: -------------------------------------------------------------------------------- 1 | $selectSpinnerSize: --att-select-spinner-size; 2 | $selectSpinnerRightMargin: --att-select-spinner-right-margin; 3 | $selectSpinnerLeftMargin: --att-select-spinner-left-margin; 4 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-select/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AttSelect'; 2 | export * from './AttSelect.model'; 3 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-spinner/Spinner.const.ts: -------------------------------------------------------------------------------- 1 | import styles from './Spinner.module.scss'; 2 | import { SpinnerSize } from './Spinner.model'; 3 | 4 | export const SpinnerCssClassBySize: Map = new Map([ 5 | [SpinnerSize.EXTRA_SMALL, styles.xs_spinner], 6 | [SpinnerSize.SMALL, styles.sm_spinner], 7 | [SpinnerSize.MEDIUM, styles.md_spinner], 8 | [SpinnerSize.LARGE, styles.lg_spinner], 9 | [SpinnerSize.NONE, ''], 10 | ]); 11 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-spinner/Spinner.model.ts: -------------------------------------------------------------------------------- 1 | export enum SpinnerSize { 2 | EXTRA_SMALL, 3 | SMALL, 4 | MEDIUM, 5 | LARGE, 6 | NONE, 7 | } 8 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-spinner/Spinner.tsx: -------------------------------------------------------------------------------- 1 | import cn from 'classnames'; 2 | import { SpinnerSize } from './Spinner.model'; 3 | import styles from './Spinner.module.scss'; 4 | import { SpinnerCssClassBySize } from './Spinner.const'; 5 | 6 | /** 7 | * https://digitaldesign.att.com/individual-bits-and-pieces/page-section-loader.html 8 | * * */ 9 | 10 | export interface SpinnerProps { 11 | className?: string; 12 | size: SpinnerSize; 13 | } 14 | 15 | export const Spinner: React.FC = (props: SpinnerProps) => ( 16 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/att-spinner/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Spinner'; 2 | export * from './Spinner.model'; 3 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/global-header/GlobalHeader.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | $headerFontSize: 16px; 4 | $headerBackgroundColor: #6D3FFC; 5 | $headerSuperReader: #e1baff; 6 | $headerSuperUser: #a4f1c1; 7 | 8 | .global_header { 9 | background-color: $headerBackgroundColor; 10 | block-size: var($headerSize); 11 | color: var($primaryWhite); 12 | display: flex; 13 | font-size: $headerFontSize; 14 | line-height: var($headerSize); 15 | padding-inline-end: var($layoutPadding); 16 | padding-inline-start: 32px; 17 | position: sticky; 18 | inset-block-start: 0; 19 | inline-size: 100%; 20 | z-index: 2; 21 | } 22 | 23 | .header_left_block, 24 | .header_right_block { 25 | align-items: center; 26 | display: flex; 27 | } 28 | 29 | .header_left_block { 30 | margin-inline-end: 124px; 31 | } 32 | 33 | .home_icon { 34 | margin-inline-end: 10px; 35 | block-size: 100%; 36 | inline-size: 100%; 37 | } 38 | 39 | .avatar_style { 40 | block-size: 24px; 41 | inline-size: 24px; 42 | } 43 | 44 | .name { 45 | margin-inline-end: 20px; 46 | } 47 | 48 | .logo { 49 | display: flex; 50 | align-items: center; 51 | block-size: 30px; 52 | } 53 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/global-header/GlobalHeader.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, RenderResult } from '@testing-library/react'; 2 | import { MemoryRouter } from 'react-router-dom'; 3 | import { InternalLink } from '../att-link'; 4 | import { GlobalHeader } from './GlobalHeader'; 5 | 6 | const tabs = [ 7 | { 8 | link: '/', 9 | title: 'Home', 10 | }, 11 | { 12 | link: '/all-experiments', 13 | title: 'All Experiments', 14 | } 15 | ]; 16 | jest.mock('../att-link'); 17 | describe('GlobalHeader', () => { 18 | test('renders global header ', () => { 19 | (InternalLink as jest.Mock).mockImplementation(() =>
InternalLink
); 20 | const { container }: RenderResult = render(, { wrapper: MemoryRouter }); 21 | expect(container.firstChild).toMatchSnapshot(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/global-header/__snapshots__/GlobalHeader.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`GlobalHeader renders global header 1`] = ` 4 | 36 | `; 37 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/global-header/index.ts: -------------------------------------------------------------------------------- 1 | export * from './GlobalHeader'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/loading_bar/LoadingBar.module.scss: -------------------------------------------------------------------------------- 1 | @import './../../../../styles/variables-keys'; 2 | 3 | $loadingBarBackgroundColor: #dcdfe3; 4 | $loadingBarColor: var($attCobalt); 5 | 6 | .loading_bar_container { 7 | background-color: $loadingBarBackgroundColor; 8 | block-size: 2px; 9 | border-radius: 2px; 10 | inline-size: 130px; 11 | overflow: hidden; 12 | position: relative; 13 | transition: transform .3s ease-in; 14 | } 15 | 16 | .loading_bar { 17 | animation: loading-bar-animation 1.5s ease infinite; 18 | background-color: $loadingBarColor; 19 | block-size: 100%; 20 | border-radius: 2px; 21 | inline-size: 52%; 22 | position: absolute; 23 | transform: translate(-26%); 24 | } 25 | 26 | @keyframes loading-bar-animation { 27 | 0% { 28 | transform: translate(-50%) 29 | } 30 | 31 | 50% { 32 | transform: translate(150%) 33 | } 34 | 35 | to { 36 | transform: translate(-50%) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/loading_bar/LoadingBar.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, RenderResult } from '@testing-library/react'; 2 | import { LoadingBar, LoadingBarProps } from './LoadingBar'; 3 | 4 | describe('LoadingBar', () => { 5 | test('renders LoadingBar', () => { 6 | const props: LoadingBarProps = { 7 | }; 8 | const { container }: RenderResult = render(); 9 | expect(container.firstChild).toMatchSnapshot(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/loading_bar/LoadingBar.tsx: -------------------------------------------------------------------------------- 1 | import cn from 'classnames'; 2 | import styles from './LoadingBar.module.scss'; 3 | 4 | export interface LoadingBarProps { 5 | className?: string; 6 | } 7 | 8 | export const LoadingBar: React.FC = (props: LoadingBarProps) => ( 9 |
10 |
11 |
12 | ); 13 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/loading_bar/__snapshots__/LoadingBar.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`LoadingBar renders LoadingBar 1`] = ` 4 |
7 |
10 |
11 | `; 12 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/loading_bar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './LoadingBar'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/modal/BaseModal.test.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/typedef */ 2 | import { render, RenderResult } from '@testing-library/react'; 3 | import { BaseModalSize } from './base-modal.const'; 4 | import { ButtonActionType, ButtonSize, ButtonStyleType } from '../att-button'; 5 | import { BaseModal, BaseModalProps } from './BaseModal'; 6 | 7 | jest.mock('../att-button', ); 8 | 9 | describe('BaseModal', () => { 10 | test('renders modal', async () => { 11 | const handleClose = jest.fn(); 12 | const props: BaseModalProps = { 13 | title: 'Test Title', 14 | actionButton: [{ 15 | styleType: ButtonStyleType.PRIMARY, 16 | text: 'Save', 17 | onClick: handleClose, 18 | size: ButtonSize.LARGE, 19 | actionType: ButtonActionType.BUTTON, 20 | }], 21 | onCloseClick: jest.fn(), 22 | size: BaseModalSize.MEDIUM, 23 | }; 24 | const { getByText }: RenderResult = render(TestMe); 25 | expect(getByText('Test Title')).toBeTruthy(); 26 | expect(getByText('TestMe')).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/modal/base-modal.const.ts: -------------------------------------------------------------------------------- 1 | export const ReactModalConfig: Record = { 2 | overlayClassName: 'react-modal-overlay', 3 | contentClassName: 'react-modal-content', 4 | }; 5 | 6 | export enum BaseModalSize { 7 | EXTRA_SMALL, 8 | SMALL, 9 | MEDIUM, 10 | LARGE, 11 | } 12 | 13 | export const ModalCssClassBySize: Map = new Map([ 14 | [BaseModalSize.EXTRA_SMALL, 'extra-small-modal'], 15 | [BaseModalSize.SMALL, 'small-modal'], 16 | [BaseModalSize.MEDIUM, 'medium-modal'], 17 | [BaseModalSize.LARGE, 'large-modal'], 18 | ]); 19 | 20 | export const CloseAriaLabel: string = 'Close'; 21 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/modal/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BaseModal'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/multicolor-icon/MulticolorIcon.module.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/src/app/shared/components/multicolor-icon/MulticolorIcon.module.scss -------------------------------------------------------------------------------- /portal/src/app/shared/components/multicolor-icon/MulticolorIcon.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, RenderResult } from '@testing-library/react'; 2 | import { MulticolorIconMemoized, MulticolorIconProps } from './MulticolorIcon'; 3 | 4 | describe('MulticolorIcon', () => { 5 | test('renders multicolor icon with 4 nodes', () => { 6 | const props: MulticolorIconProps = { 7 | baseClass: 'eda', 8 | nameClass: 'xaaf-icon', 9 | shapesAmount: 4, 10 | }; 11 | const { container }: RenderResult = render(); 12 | expect(container.firstChild).toMatchSnapshot(); 13 | }); 14 | 15 | test('should not render when receive wrong size', () => { 16 | const props: MulticolorIconProps = { 17 | baseClass: 'eda', 18 | nameClass: 'xaaf-icon', 19 | shapesAmount: 0, 20 | }; 21 | const { container }: RenderResult = render(); 22 | expect(container.firstChild).toMatchSnapshot(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/multicolor-icon/MulticolorIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { NamedExoticComponent, ReactNode } from 'react'; 2 | import cn from 'classnames'; 3 | 4 | export interface MulticolorIconProps { 5 | baseClass: string; 6 | nameClass: string; 7 | shapesAmount: number; 8 | className?: string; 9 | } 10 | 11 | export const MulticolorIcon: React.FC = (props: MulticolorIconProps) => { 12 | if (props.shapesAmount < 1) { 13 | // eslint-disable-next-line no-null/no-null 14 | return null; 15 | } 16 | const items: ReactNode[] = []; 17 | for (let i: number = 0; i < props.shapesAmount; i += 1) { 18 | const iconPathClassName: string = `${props.nameClass}-path${i + 1}`; 19 | items.push(); 20 | } 21 | return ( 22 | 23 | {items} 24 | 25 | ); 26 | }; 27 | 28 | export const MulticolorIconMemoized: NamedExoticComponent = React.memo(MulticolorIcon); 29 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/multicolor-icon/__snapshots__/MulticolorIcon.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`MulticolorIcon renders multicolor icon with 4 nodes 1`] = ` 4 | 7 | 10 | 13 | 16 | 19 | 20 | `; 21 | 22 | exports[`MulticolorIcon should not render when receive wrong size 1`] = `null`; 23 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/multicolor-icon/index.ts: -------------------------------------------------------------------------------- 1 | export * from './MulticolorIcon'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/navigation-tab/NavigationTab.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | .tabs { 4 | display: flex; 5 | align-items: center; 6 | } 7 | 8 | .tab { 9 | color: var($primaryWhite); 10 | margin-inline-end: 30px; 11 | text-decoration: none; 12 | } 13 | 14 | .activeTab { 15 | text-decoration: underline; 16 | text-underline-offset: 5px; 17 | text-decoration-thickness: 2px; 18 | } 19 | 20 | .disabledTab { 21 | cursor: not-allowed; 22 | opacity: 0.5; 23 | } 24 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/navigation-tab/NavigationTab.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, RenderResult } from '@testing-library/react'; 2 | import { MemoryRouter } from 'react-router-dom'; 3 | import { NavigationTab } from './NavigationTab'; 4 | 5 | const tabs = [ 6 | { 7 | link: '/', 8 | title: 'Home', 9 | }, 10 | { 11 | link: '/all-experiments', 12 | title: 'All Experiments', 13 | } 14 | ]; 15 | describe('NavigationTab', () => { 16 | test('renders Navigation Tab', () => { 17 | const { container }: RenderResult = render(, { wrapper: MemoryRouter }); 18 | expect(container.firstChild).toMatchSnapshot(); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/navigation-tab/NavigationTab.tsx: -------------------------------------------------------------------------------- 1 | import { NavLink, useLocation } from 'react-router-dom'; 2 | import styles from './NavigationTab.module.scss'; 3 | import cn from 'classnames'; 4 | 5 | export interface INavigationTab { 6 | title: string; 7 | link: string; 8 | disabled?: boolean; 9 | } 10 | export interface NavigationTabProps { 11 | tabs: INavigationTab[]; 12 | } 13 | 14 | export const NavigationTab: React.FC = (props: NavigationTabProps) => { 15 | const { tabs } = props; 16 | const location = useLocation(); 17 | 18 | return ( 19 |
20 | {tabs.map(((tab: INavigationTab, index: number) => ( 21 |
22 | 26 | {tab.title} 27 | 28 |
29 | )))} 30 |
31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/navigation-tab/__snapshots__/NavigationTab.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`NavigationTab renders Navigation Tab 1`] = ` 4 |
7 | 16 | 24 |
25 | `; 26 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/navigation-tab/index.ts: -------------------------------------------------------------------------------- 1 | export * from './NavigationTab'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/selector-custom-option/SelectorCustomOption.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | .algorithms_input_option { 4 | margin-inline-end: 10px; 5 | margin-inline-start: 25px; 6 | cursor: pointer; 7 | } 8 | 9 | .algorithms_input_option_title { 10 | margin-inline-end: 10px; 11 | cursor: pointer; 12 | } 13 | 14 | .option_wrapper { 15 | inline-size: 17px; 16 | block-size: 17px; 17 | margin-inline-end: 12px; 18 | } 19 | 20 | .custom_input_option { 21 | margin-inline-end: 10px; 22 | cursor: pointer; 23 | } 24 | 25 | .input_option { 26 | display: none; 27 | } -------------------------------------------------------------------------------- /portal/src/app/shared/components/selector-custom-option/components/__snapshots__/CustomInput.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CustomInput should render AlgorithmsSelectorCustomOption correctly 1`] = `null`; 4 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/selector-custom-option/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CustomInput'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/selector-custom-option/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SelectorCustomOption'; -------------------------------------------------------------------------------- /portal/src/app/shared/components/selector-custom-option/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const SELECTOR_CUSTOM_OPTION_EN = { 2 | ADD_BUTTON: 'Add', 3 | ADD_NEW: 'Add new', 4 | ADD_NEW_BUTTON: '+ Add new', 5 | } -------------------------------------------------------------------------------- /portal/src/app/shared/components/table/Table.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | table { 4 | margin-block-start: 10px; 5 | border-spacing: 0; 6 | border: 1px solid var($backgroundColorGray); 7 | inline-size: 100%; 8 | table-layout: fixed; 9 | } 10 | 11 | .table_titles { 12 | padding: 16px; 13 | background-color: var($attPurple); 14 | color: var($backgroundColorWhite); 15 | 16 | .sort_style { 17 | cursor: pointer; 18 | } 19 | } 20 | 21 | .table_content { 22 | background-color: var($backgroundColorWhite); 23 | padding: 16px; 24 | border-block-end: 1px solid var($backgroundColorGray); 25 | vertical-align: middle; 26 | } 27 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/table/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Table'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/toast/ATTToastContent.module.scss: -------------------------------------------------------------------------------- 1 | @import "./../../../../styles/variables-keys"; 2 | 3 | .toast_wrapper { 4 | align-items: flex-start; 5 | display: flex; 6 | } 7 | 8 | .toast_content { 9 | word-break: break-word; 10 | align-self: center; 11 | line-height: 22px; 12 | } 13 | 14 | .toast_title { 15 | font-family: var($fontBold); 16 | font-size: 17px; 17 | margin-block-end: 4px; 18 | } 19 | 20 | .toast_message { 21 | white-space: pre-line; 22 | } 23 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/toast/ATTToastContent.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import { ATTToastContent } from './ATTToastContent'; 3 | import { ToastContentProps } from 'react-toastify'; 4 | 5 | describe('ATTToastContent', () => { 6 | let props: ToastContentProps; 7 | it('renders title and message', () => { 8 | render(); 9 | 10 | expect(screen.getByText('Test Title')).toBeInTheDocument(); 11 | expect(screen.getByText('Test Message')).toBeInTheDocument(); 12 | }); 13 | 14 | it('renders children', () => { 15 | render( 16 | 17 |
Test Child
18 |
19 | ); 20 | 21 | expect(screen.getByText('Test Child')).toBeInTheDocument(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/toast/ATTToastContent.tsx: -------------------------------------------------------------------------------- 1 | import { PropsWithChildren } from 'react'; 2 | import { ToastContentProps } from 'react-toastify'; 3 | import styles from './ATTToastContent.module.scss'; 4 | 5 | export interface ATTToastBasicContentProps { 6 | title: string; 7 | message: string; 8 | } 9 | 10 | type ATTToastContentProps = ATTToastBasicContentProps & ToastContentProps; 11 | 12 | export const ATTToastContent: React.FC> = ({ message, title, children }: PropsWithChildren) => ( 13 |
14 |
15 | {title &&
{title}
} 16 |
{message}
17 | {children} 18 |
19 |
20 | ); 21 | -------------------------------------------------------------------------------- /portal/src/app/shared/components/toast/toast-container-config.const.ts: -------------------------------------------------------------------------------- 1 | import { Slide, ToastContainerProps } from 'react-toastify'; 2 | 3 | export const ToastContainerConfig: ToastContainerProps = { 4 | position: 'bottom-right', 5 | autoClose: 4000, 6 | hideProgressBar: true, 7 | newestOnTop: true, 8 | closeOnClick: false, 9 | rtl: false, 10 | pauseOnFocusLoss: true, 11 | draggable: true, 12 | pauseOnHover: true, 13 | transition: Slide, 14 | icon: false, 15 | theme: 'light', 16 | 17 | className: 'att-toasts-container', 18 | toastClassName: 'att-toast-container', 19 | bodyClassName: 'att-toast-content', 20 | }; 21 | 22 | export const ToastErrorDefaultConfig: ToastContainerProps = { 23 | autoClose: 15000, 24 | }; 25 | -------------------------------------------------------------------------------- /portal/src/app/shared/constants/dashboard.ts: -------------------------------------------------------------------------------- 1 | export const DashBoardPrefixLink: string = 'd/b3adde71-2231-4c2f-b216-3af0a16d8d9v/qujata-analysis?orgId=1'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/constants/http.ts: -------------------------------------------------------------------------------- 1 | export enum HttpMethod { 2 | GET = 'get', 3 | POST = 'post', 4 | PUT = 'put', 5 | PATCH = 'patch', 6 | DELETE = 'delete', 7 | OPTIONS = 'options', 8 | HEAD = 'head', 9 | } 10 | 11 | export enum HttpStatusCode { 12 | OK = 200, 13 | CREATED = 201, 14 | BAD_REQUEST = 400, 15 | UNAUTHORIZED = 401, 16 | FORBIDDEN = 403, 17 | NOT_FOUND = 404, 18 | CONFLICT = 409, 19 | INTERNAL_SERVER_ERROR = 500, 20 | BAD_GATEWAY = 502 21 | } 22 | 23 | export enum RequestContentType { 24 | text = 'text/plain', 25 | json = 'application/json', 26 | } 27 | -------------------------------------------------------------------------------- /portal/src/app/shared/constants/navigation-tabs.const.ts: -------------------------------------------------------------------------------- 1 | import { SHARED_EN } from '../../../../src/app/shared/translate/en'; 2 | 3 | export const tabs = [ 4 | { 5 | link: '/qujata', 6 | title: SHARED_EN.NAVIGATION_TABS.HOME, 7 | }, 8 | { 9 | link: '/qujata/test_suites', 10 | title: SHARED_EN.NAVIGATION_TABS.ALL_EXPERIMENTS, 11 | } 12 | ]; 13 | -------------------------------------------------------------------------------- /portal/src/app/shared/context/RootContextState.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import { SpinnerProviderWrapper } from './spinner'; 3 | 4 | export const RootContextState: React.FC<{children: ReactNode}> = (props: {children: ReactNode}) => ( 5 | 6 | {props.children} 7 | 8 | ); 9 | -------------------------------------------------------------------------------- /portal/src/app/shared/context/spinner/SpinnerProviderWrapper.test.tsx: -------------------------------------------------------------------------------- 1 | import { act, render, screen } from '@testing-library/react'; 2 | import { SpinnerProviderWrapper } from './SpinnerProviderWrapper'; 3 | import { SpinnerContext } from './spinner.context'; 4 | 5 | describe('SpinnerProviderWrapper', () => { 6 | it('renders children', () => { 7 | render( 8 | 9 |
Test Child
10 |
11 | ); 12 | 13 | expect(screen.getByText('Test Child')).toBeInTheDocument(); 14 | }); 15 | 16 | it('updates isSpinnerOn when setSpinner is called', () => { 17 | let contextValue: any; 18 | render( 19 | 20 | 21 | {value => { 22 | contextValue = value; 23 | return null; 24 | }} 25 | 26 | 27 | ); 28 | 29 | expect(contextValue.isSpinnerOn).toBe(false); 30 | 31 | act(() => { 32 | contextValue.setSpinner(true); 33 | }); 34 | 35 | expect(contextValue.isSpinnerOn).toBe(true); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /portal/src/app/shared/context/spinner/SpinnerProviderWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode, useCallback, useState } from 'react'; 2 | import { ISpinner } from './spinner.interface'; 3 | import { SpinnerProvider } from './spinner.context'; 4 | 5 | export const SpinnerProviderWrapper: React.FC<{ children: ReactNode }> = (props: { children: ReactNode }) => { 6 | const [isSpinnerOn, setIsSpinnerOn] = useState(false); 7 | 8 | const setSpinner: (isActive: boolean) => void = useCallback((isActive: boolean) => { 9 | setIsSpinnerOn(isActive); 10 | }, []); 11 | 12 | const data: ISpinner = { isSpinnerOn, setSpinner }; 13 | 14 | return ( 15 | 16 | {props.children} 17 | 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /portal/src/app/shared/context/spinner/index.ts: -------------------------------------------------------------------------------- 1 | export * from './spinner.context'; 2 | export * from './spinner.interface'; 3 | export * from './SpinnerProviderWrapper'; 4 | -------------------------------------------------------------------------------- /portal/src/app/shared/context/spinner/spinner.context.test.tsx: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react'; 2 | import { PropsWithChildren } from 'react'; 3 | import {SpinnerProvider, useSpinnerContext} from './spinner.context'; 4 | 5 | describe('Spinner Context', () => { 6 | test('should context have default values', () => { 7 | const { result } = renderHook(() => useSpinnerContext()); 8 | expect(result.current.isSpinnerOn).toBe(false); 9 | expect(result.current.setSpinner(false)).toBe(undefined); 10 | }); 11 | 12 | test('should context have init provider values', () => { 13 | const setSpinner = () => undefined; 14 | const value = { isSpinnerOn: true, setSpinner }; 15 | const Wrapper = (props: PropsWithChildren<{ }>) => ({props.children}); 16 | const { result } = renderHook(() => useSpinnerContext(), { wrapper: Wrapper }); 17 | expect(result.current.isSpinnerOn).toBe(true); 18 | expect(result.current.setSpinner).toBe(setSpinner); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /portal/src/app/shared/context/spinner/spinner.context.tsx: -------------------------------------------------------------------------------- 1 | import { Context, createContext, Provider, useContext } from 'react'; 2 | import { ISpinner } from './spinner.interface'; 3 | 4 | const defaultData: ISpinner = { isSpinnerOn: false, setSpinner: (): void => undefined }; 5 | 6 | export const SpinnerContext: Context = createContext(defaultData); 7 | export const SpinnerProvider: Provider = SpinnerContext.Provider; 8 | export const useSpinnerContext: () => ISpinner = () => useContext(SpinnerContext); 9 | -------------------------------------------------------------------------------- /portal/src/app/shared/context/spinner/spinner.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ISpinner { 2 | isSpinnerOn: boolean, 3 | setSpinner:(isSpinnerOn: boolean) => void 4 | } 5 | -------------------------------------------------------------------------------- /portal/src/app/shared/hooks/useFetch/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useFetch'; 2 | export * from './useFetch.enum'; 3 | export * from './useFetch.interface'; 4 | -------------------------------------------------------------------------------- /portal/src/app/shared/hooks/useFetch/useFetch.enum.ts: -------------------------------------------------------------------------------- 1 | // @TODO - rename to "RequestState" 2 | export enum FetchDataStatus { 3 | Init, 4 | Fetching, 5 | Success, 6 | Error, 7 | Canceled, 8 | } 9 | -------------------------------------------------------------------------------- /portal/src/app/shared/hooks/useFetchSpinner/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useFetchSpinner'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/hooks/useFetchSpinner/useFetchSpinner.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { ISpinner, useSpinnerContext } from '../../context/spinner'; 3 | import { FetchDataStatus } from '../useFetch'; 4 | 5 | export function useFetchSpinner(fetchStatuses: FetchDataStatus, isSilentRequest?: boolean): void { 6 | const { setSpinner }: ISpinner = useSpinnerContext(); 7 | 8 | useEffect(() => { 9 | setSpinner(fetchStatuses === FetchDataStatus.Fetching && !isSilentRequest); 10 | }, [setSpinner, fetchStatuses, isSilentRequest]); 11 | 12 | // 1) On component destroyed we will set false 13 | // 2) On uniqueKey changed we will set false for previousKey value 14 | useEffect(() => () => { 15 | setSpinner(false); 16 | }, [setSpinner]); 17 | } 18 | -------------------------------------------------------------------------------- /portal/src/app/shared/middlewares/axiosMiddleware.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Environment } from '../../../environments/environment'; 3 | 4 | function setAxiosDefaults() { 5 | axios.defaults.responseType = 'json'; 6 | axios.defaults.withCredentials = true; 7 | axios.defaults.baseURL = Environment.baseApiUrl; 8 | } 9 | 10 | setAxiosDefaults(); 11 | 12 | export default axios; -------------------------------------------------------------------------------- /portal/src/app/shared/models/server-error.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IServerError { 2 | code: number; 3 | message: string; 4 | variables: string[]; 5 | error?: string; 6 | } 7 | 8 | export interface IAuthError { 9 | url: string 10 | } 11 | -------------------------------------------------------------------------------- /portal/src/app/shared/models/test-run-result.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IResult { 2 | average_cpu: number; 3 | average_memory: number; 4 | bytes_throughput: number; 5 | request_throughput: number; 6 | } 7 | 8 | export interface ITestRunResultData { 9 | id: number; 10 | algorithm: string; 11 | iterations: number; 12 | message_size: number; 13 | results: IResult; 14 | } 15 | export interface IEnvironmentInfo { 16 | codeRelease: string; 17 | cpu: string; 18 | cpuArchitecture: string; 19 | cpuClockSpeed: string; 20 | cpuCores: number; 21 | nodeSize: string; 22 | operatingSystem: string; 23 | resourceName: string; 24 | } 25 | 26 | export interface ITestRunResult { 27 | id: number; 28 | name: string; 29 | description: string; 30 | start_time: number; 31 | end_time: number; 32 | environment_info: IEnvironmentInfo; 33 | test_runs: ITestRunResultData[]; 34 | } 35 | -------------------------------------------------------------------------------- /portal/src/app/shared/models/url-params.interface.ts: -------------------------------------------------------------------------------- 1 | // Single entity url param type definition 2 | export type TestRunUrlParams = 'testSuiteId'; 3 | -------------------------------------------------------------------------------- /portal/src/app/shared/translate/en.ts: -------------------------------------------------------------------------------- 1 | export const SHARED_EN = { 2 | TITLE: 'Define parameters to view the dashboard ', 3 | WEB_PORTAL_NAME: 'AT&T Quantum Protocols System', 4 | INIT_STATE_DESCRIPTION: 'Define parameters to view the dashboard', 5 | LINK_TEXT: 'See dashboards in "Grafana"', 6 | TOAST_DESCRIPTIONS: { 7 | SUCCESS: 'Success', 8 | }, 9 | NAVIGATION_TABS: { 10 | HOME: 'Home', 11 | ALL_EXPERIMENTS: 'All Experiments', 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /portal/src/app/shared/utils/parseValue.test.ts: -------------------------------------------------------------------------------- 1 | import { parseValue } from './parseValue'; 2 | 3 | describe('parseValue', () => { 4 | test('should parseValue string "true" to boolean type', () => { 5 | expect(parseValue('true')).toBe(true); 6 | }); 7 | 8 | test('should parseValue number "5" to number type', () => { 9 | expect(parseValue('5')).toBe(5); 10 | }); 11 | 12 | test('should parse string {"a": 5} to object', () => { 13 | expect(parseValue('{"a": 5}')).toEqual({ a: 5 }); 14 | }); 15 | 16 | test('should keep string as is', () => { 17 | expect(parseValue('some text')).toBe('some text'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /portal/src/app/shared/utils/parseValue.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @description 4 | * parseValue will receive a string value and try to convert it to his real type like: 5 | * 'true' will parse to true as boolean 6 | * '5' will parse to 5 as number 7 | * 'some text' will not parse and returned as 'some text' 8 | */ 9 | export function parseValue(valueToParse: string): T { 10 | let value: T; 11 | try { 12 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 13 | value = JSON.parse(valueToParse); 14 | } catch { 15 | value = valueToParse as unknown as T; 16 | } 17 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 18 | return value; 19 | } 20 | -------------------------------------------------------------------------------- /portal/src/app/shared/utils/replaceParams/index.ts: -------------------------------------------------------------------------------- 1 | export * from './replaceParams'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/utils/replaceParams/replaceParams.test.ts: -------------------------------------------------------------------------------- 1 | import {replaceParams} from './replaceParams'; 2 | 3 | describe('replaceParams', () => { 4 | test('should replace url path params', () => { 5 | expect(replaceParams('v1/applications/:appIdOrName', {appIdOrName: 1})).toBe('v1/applications/1'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /portal/src/app/shared/utils/replaceParams/replaceParams.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | export function replaceParams(url: string, pathParams: Record): string { 3 | Object.keys(pathParams).forEach((key: string): void => { 4 | url = url.replace(`:${key}`, pathParams[key] as string); 5 | }); 6 | return url; 7 | } 8 | -------------------------------------------------------------------------------- /portal/src/app/shared/utils/translate-parser/index.ts: -------------------------------------------------------------------------------- 1 | export * from './translate-parser.service'; 2 | -------------------------------------------------------------------------------- /portal/src/app/shared/utils/translate-parser/translate-parser.interface.ts: -------------------------------------------------------------------------------- 1 | export interface TranslateParserParams { 2 | [key: string]: string | number | boolean | TranslateParserParams; 3 | } 4 | -------------------------------------------------------------------------------- /portal/src/app/utils/download/downloadCsvFile.ts: -------------------------------------------------------------------------------- 1 | // Create a download anchor element to "point" to it when downloading data as file 2 | import { downloadDataAsFile } from './downloadDataAsFile'; 3 | 4 | export type CsvDataType = DataTypeRow[]; 5 | type DataTypeRow = DataTypeCell[]; 6 | type DataTypeCell = string | number; 7 | 8 | function arrayToCSV(data: CsvDataType): string { 9 | return data.map(mapRow).join('\n'); 10 | } 11 | 12 | function mapRow(row: DataTypeRow): string { 13 | return row.map((cell: DataTypeCell) => `"${cell}"`).join(','); 14 | } 15 | 16 | export function downloadCsvFile(data: CsvDataType, filename: string = 'data.csv'): void { 17 | // Create a URL for the blob 18 | const csvData: string = arrayToCSV(data); 19 | const blob: Blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' }); 20 | downloadDataAsFile(filename, blob); 21 | } 22 | -------------------------------------------------------------------------------- /portal/src/app/utils/download/downloadDataAsFile.ts: -------------------------------------------------------------------------------- 1 | // Create a download anchor element to "point" to it when downloading data as file 2 | export const downloadAnchor: HTMLAnchorElement = document.createElement('a'); 3 | downloadAnchor.style.visibility = 'hidden'; 4 | 5 | export function downloadDataAsFile(fileName: string, data: Blob|string): void { 6 | // Create a URL for the blob 7 | const url: string = URL.createObjectURL(data as unknown as Blob); 8 | // Create an anchor element to "point" to it 9 | downloadAnchor.href = url; 10 | // Set the suggested filename for the file 11 | downloadAnchor.download = fileName; 12 | // Simulate a click on our anchor element 13 | downloadAnchor.click(); 14 | // Discard the object data 15 | URL.revokeObjectURL(url); 16 | } 17 | -------------------------------------------------------------------------------- /portal/src/app/utils/download/index.ts: -------------------------------------------------------------------------------- 1 | export * from './downloadCsvFile'; 2 | export * from './downloadDataAsFile'; 3 | -------------------------------------------------------------------------------- /portal/src/app/utils/parseValue.test.ts: -------------------------------------------------------------------------------- 1 | import { parseValue } from './parseValue'; 2 | 3 | describe('parseValue', () => { 4 | test('should parseValue string "true" to boolean type', () => { 5 | expect(parseValue('true')).toBe(true); 6 | }); 7 | 8 | test('should parseValue number "5" to number type', () => { 9 | expect(parseValue('5')).toBe(5); 10 | }); 11 | 12 | test('should parse string {"a": 5} to object', () => { 13 | expect(parseValue('{"a": 5}')).toEqual({ a: 5 }); 14 | }); 15 | 16 | test('should keep string as is', () => { 17 | expect(parseValue('some text')).toBe('some text'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /portal/src/app/utils/parseValue.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @description 4 | * parseValue will receive a string value and try to convert it to his real type like: 5 | * 'true' will parse to true as boolean 6 | * '5' will parse to 5 as number 7 | * 'some text' will not parse and returned as 'some text' 8 | */ 9 | export function parseValue(valueToParse: string): T { 10 | let value: T; 11 | try { 12 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 13 | value = JSON.parse(valueToParse); 14 | } catch { 15 | value = valueToParse as unknown as T; 16 | } 17 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 18 | return value; 19 | } 20 | -------------------------------------------------------------------------------- /portal/src/app/utils/snakeCaseToCamelCase.test.ts: -------------------------------------------------------------------------------- 1 | import { snakeCaseToCamelCase } from './snakeCaseToCamelCase'; 2 | 3 | describe('snakeCaseToCamelCase', () => { 4 | test('should convert snake case to camel case', () => { 5 | expect(snakeCaseToCamelCase('to_camel')).toBe('toCamel'); 6 | expect(snakeCaseToCamelCase('to-camel')).toBe('toCamel'); 7 | }); 8 | 9 | test('should not convert snake case to camel case for upper cases', () => { 10 | expect(snakeCaseToCamelCase('TO-CAMEL')).toBe('TO-CAMEL'); 11 | expect(snakeCaseToCamelCase('TO_CAMEL')).toBe('TO_CAMEL'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /portal/src/app/utils/snakeCaseToCamelCase.ts: -------------------------------------------------------------------------------- 1 | function snakeToCamelGroupReplacer(group: string): string { 2 | return group.toUpperCase().replace('-', '').replace('_', ''); 3 | } 4 | 5 | export function snakeCaseToCamelCase(str: string): string { 6 | return str.replace(/([-_][a-z])/g, snakeToCamelGroupReplacer); 7 | } 8 | -------------------------------------------------------------------------------- /portal/src/assets/images/arrow-down-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /portal/src/assets/images/arrow-down-selector.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /portal/src/assets/images/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /portal/src/assets/images/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /portal/src/assets/images/arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /portal/src/assets/images/bar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /portal/src/assets/images/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /portal/src/assets/images/checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /portal/src/assets/images/clean.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /portal/src/assets/images/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /portal/src/assets/images/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /portal/src/assets/images/eye-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /portal/src/assets/images/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /portal/src/assets/images/line.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /portal/src/assets/images/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /portal/src/assets/images/no-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/qujata/e3ddbd1a19c111593ef747cd6c94e3054af996ee/portal/src/assets/images/no-permissions.png -------------------------------------------------------------------------------- /portal/src/assets/images/pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /portal/src/assets/images/sort-ascending.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /portal/src/assets/images/sort-descending.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /portal/src/assets/images/trash-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /portal/src/assets/images/trash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /portal/src/assets/images/unchecked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /portal/src/assets/images/user-avatar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /portal/src/declaration/window.d.ts: -------------------------------------------------------------------------------- 1 | export declare global { 2 | interface Window { 3 | PQC_PORTAL_ENV: Record | undefined, 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /portal/src/environments/environment.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IEnvironment { 2 | baseApiUrl: string; 3 | dashboardLinkHost: string; 4 | environment: string; 5 | } 6 | -------------------------------------------------------------------------------- /portal/src/environments/environment.test.ts: -------------------------------------------------------------------------------- 1 | jest.mock('./environment'); 2 | 3 | describe('environment', () => { 4 | beforeEach(() => { 5 | jest.resetModules(); 6 | }); 7 | test('should add dynamic variables to environment object', async () => { 8 | window.PQC_PORTAL_ENV = { REACT_APP__BASE_API_URL: 'my test' }; 9 | const { Environment } = await import('./environment'); 10 | expect(Environment.baseApiUrl).toBe('my test'); 11 | }); 12 | 13 | 14 | test('should contain only static variables', async () => { 15 | const staticVariables = { PUBLIC_URL: 'static', NODE_ENV: process.env.NODE_ENV}; 16 | process.env = staticVariables; 17 | const { Environment } = await import('./environment'); 18 | expect(Environment.baseApiUrl).toBe('my test'); 19 | expect(Object.keys(Environment).length).toBe(4); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /portal/src/gh-pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { Reports } from "./reports"; 2 | 3 | export const GHPages: React.FC = () => ( 4 | 5 | ); 6 | 7 | // export default GHPages; -------------------------------------------------------------------------------- /portal/src/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM, { Root } from 'react-dom/client'; 2 | import App from './app/App'; 3 | import reportWebVitals from './reportWebVitals'; 4 | import './styles/index.scss'; 5 | 6 | const root: Root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); 7 | root.render(); 8 | 9 | // If you want to start measuring performance in your app, pass a function 10 | // to log results (for example: reportWebVitals(console.log)) 11 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 12 | reportWebVitals(); 13 | -------------------------------------------------------------------------------- /portal/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /portal/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals: () => void = (onPerfEntry?: ReportHandler): void => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | // eslint-disable-next-line @typescript-eslint/typedef 6 | import('web-vitals').then(({ 7 | getCLS, getFID, getFCP, getLCP, getTTFB, 8 | }): void => { 9 | getCLS(onPerfEntry); 10 | getFID(onPerfEntry); 11 | getFCP(onPerfEntry); 12 | getLCP(onPerfEntry); 13 | getTTFB(onPerfEntry); 14 | }); 15 | } 16 | }; 17 | 18 | export default reportWebVitals; 19 | -------------------------------------------------------------------------------- /portal/src/routes/Root.module.scss: -------------------------------------------------------------------------------- 1 | @import "src/styles/variables-keys"; 2 | 3 | .spinner_wrapper { 4 | position: sticky; 5 | inset-block-start: 50%; 6 | inset-inline-start: 50%; 7 | text-align: center; 8 | } 9 | 10 | .spinner_overlay { 11 | inline-size: 100%; 12 | block-size: 100%; 13 | position: absolute; 14 | background-color: var($primaryWhite); 15 | opacity: 0.6; 16 | inset-block-start: 0; 17 | inset-inline-start: 0; 18 | z-index: 4; 19 | } 20 | -------------------------------------------------------------------------------- /portal/src/routes/index.jsx: -------------------------------------------------------------------------------- 1 | import { createBrowserRouter } from 'react-router-dom'; 2 | import Root from './Root'; 3 | import { Home } from '../app/components/home/Home'; 4 | import { Experiment } from '../app/components/home/components/experiment/Experiment'; 5 | import { Experiments } from '../app/components/all-experiments/Experiments'; 6 | 7 | export const router = createBrowserRouter([ 8 | { 9 | path: '/qujata', 10 | element: , 11 | children: [ 12 | { 13 | path: '', 14 | index: true, 15 | element: , 16 | }, 17 | { 18 | path: 'experiment/:testSuiteId', 19 | element: , 20 | }, 21 | { 22 | path: 'test_suites', 23 | element: , 24 | }, 25 | ], 26 | }, 27 | ]); 28 | -------------------------------------------------------------------------------- /portal/src/setupProxy.js: -------------------------------------------------------------------------------- 1 | const { createProxyMiddleware } = require('http-proxy-middleware'); 2 | 3 | module.exports = (app) => { 4 | const target = process.env.REACT_SERVER_TARGET; 5 | if (!target) { 6 | return; 7 | } 8 | app.use( 9 | ['/analyze', '/algorithms', '/iterations', '/message_sizes', '/qujata-api', '/test_suites/*', '/test_suites'], 10 | createProxyMiddleware({ 11 | target, 12 | changeOrigin: true, 13 | logLevel: 'debug', 14 | secure: true, 15 | }), 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /portal/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /portal/src/styles/colors.scss: -------------------------------------------------------------------------------- 1 | @import './variables-keys'; 2 | 3 | :root { 4 | #{$primaryWhite}: white; 5 | #{$textBlack}: #1D2329; 6 | #{$backgroundColorBlack}: black; 7 | #{$backgroundColorGray}: #F2F2F2; 8 | #{$backgroundColorWhite}: white; 9 | #{$errorTextColor}: #c70032; 10 | #{$actionTextHoverColor}: #02254c; 11 | #{$subTextColor}: #02254c; 12 | 13 | 14 | // AT&T Brand Colors 15 | #{$attCobalt}: #0057B8; 16 | #{$attBlue}: #009fdb; 17 | #{$attPurple}: #6D3FFC; 18 | #{$attLime}: #91DC00; 19 | #{$attBerry}: #FF585D; 20 | #{$attTangerine}: #FFB000; 21 | #{$attMint}: #49EEDC; 22 | } -------------------------------------------------------------------------------- /portal/src/styles/global-reference.scss: -------------------------------------------------------------------------------- 1 | @import 'src/styles/variables-keys'; 2 | @import "src/styles/z-index"; 3 | 4 | %ellipsis { 5 | white-space: nowrap; 6 | overflow: hidden; 7 | text-overflow: ellipsis; 8 | } -------------------------------------------------------------------------------- /portal/src/styles/global.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import "./variables-keys"; 3 | @import "./global-reference"; 4 | 5 | $highlightColor: #D0E8FA; 6 | 7 | html { 8 | background-color: rgba(var($applicationContentBackgroundColor), 1); 9 | block-size: 100%; 10 | box-sizing: border-box; 11 | overflow-x: hidden; 12 | 13 | *, *::before, *::after { 14 | box-sizing: inherit; 15 | } 16 | } 17 | 18 | body { 19 | block-size: 100%; 20 | color: var($textBlack); 21 | font-family: var($fontRegular); 22 | font-size: 15px; 23 | margin: 0; 24 | } 25 | -------------------------------------------------------------------------------- /portal/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './normalize'; 2 | @import './colors'; 3 | @import './variables'; 4 | @import './global'; 5 | @import './scrollbar'; 6 | @import './select'; 7 | @import './att-toast.scss'; 8 | @import './react-modal.scss'; 9 | -------------------------------------------------------------------------------- /portal/src/styles/react-modal.scss: -------------------------------------------------------------------------------- 1 | @import "./variables-keys"; 2 | @import "./z-index"; 3 | 4 | $extraSmallModalSize: 540px; 5 | $smallModalSize: 738px; 6 | $mediumModalSize: 1140px; 7 | $largeModalSize: 1376px; 8 | 9 | .react-modal-overlay { 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | position: fixed; 14 | inset-block-start: 0; 15 | inset-inline-start: 0; 16 | inset-inline-end: 0; 17 | inset-block-end: 0; 18 | background-color: rgba(0, 0, 0, 0.5); 19 | z-index: 10; 20 | 21 | &.no-bg { 22 | background-color: unset; 23 | } 24 | } 25 | 26 | .react-modal-content { 27 | padding: 0; 28 | border-radius: 2px; 29 | background-color: var($backgroundColorWhite); 30 | inline-size: 75%; 31 | 32 | &:focus { 33 | outline: none; 34 | } 35 | 36 | &.extra-small-modal { 37 | max-inline-size: $extraSmallModalSize 38 | } 39 | 40 | &.small-modal { 41 | max-inline-size: $smallModalSize; 42 | } 43 | 44 | &.medium-modal { 45 | max-inline-size: $mediumModalSize; 46 | } 47 | 48 | &.large-modal { 49 | max-inline-size: $largeModalSize; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /portal/src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | @import "./variables-keys"; 2 | 3 | :root { 4 | #{$headerSize}: 64px; 5 | #{$fontRegular}: ATTAleckSans; 6 | #{$fontMedium}: ATTAleckSansMed; 7 | #{$fontBold}: ATTAleckSansBold; 8 | #{$layoutPadding}: 24px; 9 | #{$layoutContentPaddingRightLeft}: 32px; 10 | #{$layoutContentPaddingBottom}: 32px; 11 | #{$layoutContentPaddingTop}: 24px; 12 | #{$modalActionBackgroundColor}: #f9f9f9; 13 | #{$modalPaddingInline}: 32px; 14 | #{$sidePanelWidth}: 410px; 15 | #{$applicationContentBackgroundColor}: 249, 249, 249; 16 | 17 | #{$subHeaderBackgroundColor}: #f9f9f9; 18 | #{$subheaderFontSize}: 26px; 19 | #{$subheaderLineHeight}: 32px; 20 | #{$subheaderMarginBottom}: 24px; 21 | #{$subheaderPaddingInline}: var($layoutContentPaddingRightLeft); 22 | #{$subheaderPaddingBlock}: var($layoutContentPaddingTop); 23 | #{$subheaderPaddingBlockStart}: var($subheaderPaddingBlock); 24 | #{$subheaderPaddingBlockEnd}: var($subheaderPaddingBlock); 25 | 26 | #{$navigationTabsSize}: 50px; 27 | } 28 | -------------------------------------------------------------------------------- /portal/src/styles/z-index.scss: -------------------------------------------------------------------------------- 1 | $baseZindex: 1; 2 | $appSpinnerZindex: 4; 3 | $globalHeaderZindex: 10; 4 | $stickyFooterActions: 3; 5 | $selectOpenOptionsModeZindex: $stickyFooterActions + 1; 6 | 7 | -------------------------------------------------------------------------------- /portal/svg-icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /portal/svg-icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /portal/svg-icons/user-avatar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /portal/tests/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | fixturesFolder: './cypress/fixtures', 5 | supportFolder:'./cypress/support', 6 | reporter: 'cypress-multi-reporters', 7 | screenshotsFolder: './reports/screenshots', 8 | video: true, 9 | videosFolder: './reports/videos', 10 | videoUploadOnPasses: false, 11 | viewportHeight: 660, 12 | viewportWidth: 1000, 13 | reporterOptions: { 14 | configFile: './reporters-config/config.json' 15 | }, 16 | chromeWebSecurity: false, 17 | defaultCommandTimeout: 4000, 18 | env: { 19 | }, 20 | 21 | e2e: { 22 | setupNodeEvents(on, config) { 23 | // implement node event listeners here 24 | }, 25 | baseUrl: 'https://google.com', 26 | 27 | }, 28 | 29 | component: { 30 | devServer: { 31 | framework: "create-react-app", 32 | bundler: "webpack", 33 | }, 34 | 35 | }, 36 | }); 37 | -------------------------------------------------------------------------------- /portal/tests/cypress/component/ComponentName.cy.ts: -------------------------------------------------------------------------------- 1 | describe('ComponentName.cy.ts', () => { 2 | it('playground', () => { 3 | // cy.mount() 4 | }) 5 | }) -------------------------------------------------------------------------------- /portal/tests/cypress/e2e/spec.cy.ts: -------------------------------------------------------------------------------- 1 | describe('empty spec', () => { 2 | it('passes', () => { 3 | cy.visit('https://example.cypress.io') 4 | }) 5 | }) -------------------------------------------------------------------------------- /portal/tests/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /portal/tests/cypress/support/component-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Components App 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /portal/tests/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /portal/tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e2e", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "adi bar", 6 | "scripts": { 7 | "cypress:run": "cypress run", 8 | "cypress:open": "cypress open", 9 | "cypress:merge:report": "npx mochawesome-merge ./reports/mochawesome/mochawesome*.json -o ./reports/mochawesome_summary.json", 10 | "cypress:generate:report": "marge ./reports/mochawesome_summary.json -o ./reports -f mochawesome_summary.html --inline", 11 | "precypress:run": "rimraf ./reports", 12 | "e2e:ci": "yarn cypress:run" 13 | }, 14 | "devDependencies": { 15 | "@types/node": "^18.6.3", 16 | "cypress": "^10.3.1", 17 | "cypress-multi-reporters": "^1.6.1", 18 | "mocha-junit-reporter": "^2.0.2", 19 | "mochawesome": "^7.1.3", 20 | "mochawesome-merge": "^4.2.1", 21 | "mochawesome-report-generator": "^6.2.0", 22 | "rimraf": "^3.0.2" 23 | }, 24 | "dependencies": { 25 | "typescript": "^4.7.4" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /portal/tests/reporters-config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "reporterEnabled": "mochawesome, mocha-junit-reporter", 3 | "mochaJunitReporterReporterOptions": { 4 | "mochaFile": "./reports/junit/results-[hash].xml", 5 | "attachments": true 6 | }, 7 | "mochawesomeReporterOptions": { 8 | "reportDir": "./reports/mochawesome", 9 | "overwrite": false, 10 | "html": false, 11 | "json": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /portal/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /run/docker/grafana/dashboard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | providers: 3 | - name: 'default' 4 | type: file 5 | disableDeletion: false 6 | updateIntervalSeconds: 60 7 | options: 8 | path: /var/lib/grafana/dashboards 9 | 10 | dashboards: 11 | - name: 'dashboard' 12 | file: 'dashboard.json' 13 | -------------------------------------------------------------------------------- /run/docker/grafana/datasource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: 'Prometheus' 5 | type: 'prometheus' 6 | url: 'http://prometheus:9090' 7 | uid: 'PB08949F94AB73929' 8 | # isDefault: true 9 | -------------------------------------------------------------------------------- /run/docker/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 5s 3 | 4 | scrape_configs: 5 | - job_name: 'cadvisor' 6 | static_configs: 7 | - targets: ['qujata-cadvisor:8080'] 8 | 9 | - job_name: 'pushgateway' 10 | honor_labels: true 11 | static_configs: 12 | - targets: ['qujata-pushgateway:9091'] 13 | -------------------------------------------------------------------------------- /run/kubernetes/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: qujata-chart 3 | description: A parent chart for deploying multiple sub-charts 4 | version: 0.1.0 5 | dependencies: 6 | - name: portal 7 | version: 0.1.0 8 | repository: "file://./charts/portal" 9 | - name: api 10 | version: 0.1.0 11 | repository: "file://./charts/api" 12 | - name: curl 13 | version: 0.1.0 14 | repository: "file://./charts/curl" 15 | - name: nginx 16 | version: 0.1.0 17 | repository: "file://./charts/nginx" 18 | - name: cadvisor 19 | version: 0.1.0 20 | repository: "file://./charts/cadvisor" 21 | - name: prometheus 22 | version: 0.1.0 23 | repository: "file://./charts/prometheus" 24 | - name: grafana 25 | version: 0.1.0 26 | repository: "file://./charts/grafana" 27 | - name: pushgateway 28 | version: 0.1.0 29 | repository: "file://./charts/pushgateway" 30 | - name: mysql 31 | version: 0.1.0 32 | repository: "file://./charts/mysql" 33 | 34 | -------------------------------------------------------------------------------- /run/kubernetes/charts/api/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /run/kubernetes/charts/api/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "api.fullname" . }} 6 | labels: 7 | {{- include "api.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "api.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /run/kubernetes/charts/api/templates/permissions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: {{.Chart.Name}} 5 | rules: 6 | - apiGroups: 7 | - "" 8 | resources: 9 | - nodes 10 | - nodes/proxy 11 | - services 12 | - endpoints 13 | - pods 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - extensions 20 | resources: 21 | - ingresses 22 | verbs: 23 | - get 24 | - list 25 | - watch 26 | - nonResourceURLs: 27 | - /metrics 28 | verbs: 29 | - get 30 | 31 | --- 32 | apiVersion: rbac.authorization.k8s.io/v1 33 | kind: ClusterRoleBinding 34 | metadata: 35 | name: {{.Chart.Name}} 36 | roleRef: 37 | apiGroup: rbac.authorization.k8s.io 38 | kind: ClusterRole 39 | name: {{.Chart.Name}} 40 | subjects: 41 | - kind: ServiceAccount 42 | name: {{include "api.serviceAccountName" .}} 43 | namespace: {{.Release.Namespace}} 44 | 45 | 46 | -------------------------------------------------------------------------------- /run/kubernetes/charts/api/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "api.fullname" . }} 5 | labels: 6 | {{- include "api.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: {{ .Values.service.targetPort }} 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "api.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /run/kubernetes/charts/api/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "api.serviceAccountName" . }} 6 | labels: 7 | {{- include "api.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /run/kubernetes/charts/cadvisor/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "cadvisor.fullname" . }} 6 | labels: 7 | {{- include "cadvisor.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | cadvisorVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "cadvisor.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /run/kubernetes/charts/cadvisor/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "cadvisor.fullname" . }} 5 | labels: 6 | {{- include "cadvisor.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "cadvisor.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /run/kubernetes/charts/cadvisor/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "cadvisor.serviceAccountName" . }} 6 | labels: 7 | {{- include "cadvisor.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /run/kubernetes/charts/curl/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /run/kubernetes/charts/curl/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "curl.fullname" . }} 6 | labels: 7 | {{- include "curl.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "curl.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /run/kubernetes/charts/curl/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "curl.fullname" . }} 5 | labels: 6 | {{- include "curl.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: {{ .Values.service.targetPort }} 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "curl.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /run/kubernetes/charts/curl/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "curl.serviceAccountName" . }} 6 | labels: 7 | {{- include "curl.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /run/kubernetes/charts/curl/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "curl.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "curl.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "curl.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /run/kubernetes/charts/grafana/files/dashboard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | providers: 3 | - name: 'default' 4 | type: file 5 | disableDeletion: false 6 | updateIntervalSeconds: 60 7 | options: 8 | path: /var/lib/grafana/dashboards 9 | 10 | dashboards: 11 | - name: 'dashboard' 12 | file: 'dashboard.json' 13 | -------------------------------------------------------------------------------- /run/kubernetes/charts/grafana/files/datasource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: 'Prometheus' 5 | type: 'prometheus' 6 | url: 'http://qujata-prometheus:9090' 7 | uid: 'PB08949F94AB73929' 8 | # isDefault: true 9 | -------------------------------------------------------------------------------- /run/kubernetes/charts/grafana/templates/cm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "grafana.fullname" . }} 5 | labels: 6 | {{- include "grafana.labels" . | nindent 4 }} 7 | data: 8 | dashboard.json: 9 | {{ .Files.Get "files/dashboard.json" | toJson | indent 4 }} 10 | dashboard.yaml: 11 | {{ .Files.Get "files/dashboard.yaml" | toYaml | indent 4 }} 12 | datasource.yaml: 13 | {{ .Files.Get "files/datasource.yaml" | toYaml | indent 4 }} -------------------------------------------------------------------------------- /run/kubernetes/charts/grafana/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "grafana.fullname" . }} 6 | labels: 7 | {{- include "grafana.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | grafanaVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "grafana.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /run/kubernetes/charts/grafana/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "grafana.fullname" . }} 5 | labels: 6 | {{- include "grafana.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "grafana.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /run/kubernetes/charts/grafana/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "grafana.serviceAccountName" . }} 6 | labels: 7 | {{- include "grafana.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /run/kubernetes/charts/mysql/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "mysql.fullname" . }} 6 | labels: 7 | {{- include "mysql.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "mysql.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /run/kubernetes/charts/mysql/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "mysql.fullname" . }} 5 | labels: 6 | {{- include "mysql.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: {{ .Values.service.targetPort }} 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "mysql.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /run/kubernetes/charts/mysql/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "mysql.serviceAccountName" . }} 6 | labels: 7 | {{- include "mysql.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /run/kubernetes/charts/nginx/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "nginx.fullname" . }} 6 | labels: 7 | {{- include "nginx.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | nginxVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "nginx.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /run/kubernetes/charts/nginx/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "nginx.fullname" . }} 5 | labels: 6 | {{- include "nginx.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "nginx.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /run/kubernetes/charts/nginx/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "nginx.serviceAccountName" . }} 6 | labels: 7 | {{- include "nginx.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /run/kubernetes/charts/portal/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "portal.fullname" . }} 6 | labels: 7 | {{- include "portal.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "portal.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /run/kubernetes/charts/portal/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "portal.fullname" . }} 5 | labels: 6 | {{- include "portal.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: {{ .Values.service.targetPort }} 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "portal.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /run/kubernetes/charts/portal/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "portal.serviceAccountName" . }} 6 | labels: 7 | {{- include "portal.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /run/kubernetes/charts/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 5s 3 | 4 | scrape_configs: 5 | - job_name: 'kubernetes-cadvisor' 6 | kubernetes_sd_configs: 7 | - role: pod 8 | relabel_configs: 9 | - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] 10 | action: keep 11 | regex: true 12 | - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] 13 | action: replace 14 | regex: ([^:]+)(?::\d+)?;(\d+) 15 | replacement: $1:$2 16 | target_label: __address__ 17 | - source_labels: [__meta_kubernetes_pod_name] 18 | action: replace 19 | target_label: kubernetes_pod_name 20 | 21 | - job_name: 'pushgateway' 22 | honor_labels: true 23 | static_configs: 24 | - targets: ['qujata-pushgateway:9091'] 25 | 26 | -------------------------------------------------------------------------------- /run/kubernetes/charts/prometheus/templates/cm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "prometheus.fullname" . }} 5 | labels: 6 | {{- include "prometheus.labels" . | nindent 4 }} 7 | data: 8 | prometheus.yml: 9 | {{ .Files.Get "prometheus.yml" | toYaml | indent 4 }} -------------------------------------------------------------------------------- /run/kubernetes/charts/prometheus/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "prometheus.fullname" . }} 6 | labels: 7 | {{- include "prometheus.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | prometheusVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "prometheus.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /run/kubernetes/charts/prometheus/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "prometheus.fullname" . }} 5 | labels: 6 | {{- include "prometheus.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "prometheus.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /run/kubernetes/charts/prometheus/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "prometheus.serviceAccountName" . }} 6 | labels: 7 | {{- include "prometheus.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /run/kubernetes/charts/pushgateway/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "pushgateway.fullname" . }} 6 | labels: 7 | {{- include "pushgateway.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | pushgatewayVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "pushgateway.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /run/kubernetes/charts/pushgateway/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "pushgateway.fullname" . }} 5 | labels: 6 | {{- include "pushgateway.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "pushgateway.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /run/kubernetes/charts/pushgateway/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "pushgateway.serviceAccountName" . }} 6 | labels: 7 | {{- include "pushgateway.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | --------------------------------------------------------------------------------