From 1970e23083b83dc05efe47ed2242dae9037814a0 Mon Sep 17 00:00:00 2001 From: RoryPTB <47696929+RoryPTB@users.noreply.github.com> Date: Fri, 26 Jul 2024 18:30:18 +0200 Subject: [PATCH] fix: resolve Super-linter errors throughout the codebase --- .github/linters/.stylelintrc.json | 4 + .github/workflows/release-app.yml | 6 + .github/workflows/test-code-quality.yml | 1 + README.md | 99 +++++++-------- src/frontend/components/CatalogueView.vue | 37 ++---- .../components/sub-components/SearchBar.vue | 26 ++++ src/frontend/styles/settings.scss | 10 -- src/frontend/utils/constants.js | 32 ++--- src/frontend/utils/prometheusTools.js | 117 +++++++++--------- src/frontend/utils/topicTools.js | 98 ++++++++------- 10 files changed, 219 insertions(+), 211 deletions(-) create mode 100644 .github/linters/.stylelintrc.json create mode 100644 src/frontend/components/sub-components/SearchBar.vue diff --git a/.github/linters/.stylelintrc.json b/.github/linters/.stylelintrc.json new file mode 100644 index 0000000..f95af52 --- /dev/null +++ b/.github/linters/.stylelintrc.json @@ -0,0 +1,4 @@ +{ + "extends": "stylelint-config-standard", + "customSyntax": "postcss-scss" + } \ No newline at end of file diff --git a/.github/workflows/release-app.yml b/.github/workflows/release-app.yml index b0bbd9b..9641b24 100644 --- a/.github/workflows/release-app.yml +++ b/.github/workflows/release-app.yml @@ -5,6 +5,12 @@ on: - main workflow_dispatch: +permissions: + contents: read + packages: write + issues: write + pull-requests: write + jobs: build: # Run this job on the following operating systems diff --git a/.github/workflows/test-code-quality.yml b/.github/workflows/test-code-quality.yml index c595b4f..ad4360c 100644 --- a/.github/workflows/test-code-quality.yml +++ b/.github/workflows/test-code-quality.yml @@ -28,5 +28,6 @@ jobs: - name: Super-linter uses: super-linter/super-linter@v6.7.0 # x-release-please-version env: + VALIDATE_SCSS: true # To report GitHub Actions status checks GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 8afbeb2..42dffd6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ +# The WIS2 Subscription Manager -![rounded-toolbar](https://github.com/user-attachments/assets/6b62a099-d346-48ed-921d-e1c1263950d1) -# The WIS2 Subscription Manager -### The desktop application for managing your WIS2 Downloader +## The desktop application for managing your WIS2 Downloader + +
+ + License Badge + [![Super-Linter](https://github.com/wmo-im/wis2-subscription-manager/actions/workflows/test-code-quality.yml/badge.svg)](https://github.com/marketplace/actions/super-linter) + ![Release-App](https://github.com/wmo-im/wis2-subscription-manager/actions/workflows/release-app.yml/badge.svg) - -[![Super-Linter](https://github.com/wmo-im/wis2-subscription-manager/actions/workflows/test-code-quality.yml/badge.svg)](https://github.com/marketplace/actions/super-linter) -![Release-App](https://github.com/wmo-im/wis2-subscription-manager/actions/workflows/release-app.yml/badge.svg) +
The WIS2 Subscription Manager is an Electron application that allows you to easily maintain your on-going subscriptions, as well as explore new topics of interest on a Global Discovery Catalogue. @@ -14,42 +17,39 @@ The WIS2 Subscription Manager is an Electron application that allows you to easi
- Table of Contents -
    -
  1. - Features - -
  2. -
  3. Demos - -
  4. -
  5. - Getting Started - -
  6. -
  7. - Development - -
  8. -
  9. Bugs and Issues
  10. -
  11. Contract
  12. -
+Table of Contents +
    +
  1. Features + +
  2. +
  3. Demos + +
  4. +
  5. Getting Started + +
  6. +
  7. Development + +
  8. +
  9. Bugs and Issues
  10. +
  11. Contract
  12. +
## Features - +![rounded-toolbar](https://github.com/user-attachments/assets/6b62a099-d346-48ed-921d-e1c1263950d1) - **Configure Your Subscriptions**: Easily view and configure topics through a user-friendly interface, built with Vuetify 3. - **Explore the Global Discovery Catalogue (GDC)**: Browse a GDC of your choice and seamlessly add new topics to your subscription. - **Visualize Download Metrics**: Just click on a subscribed topic to see the number of files/bytes downloaded, file types, and failed downloads. @@ -75,7 +75,7 @@ The WIS2 Subscription Manager is an Electron application that allows you to easi ## Getting Started ### 1. Download -In the releases section, expand the 'Assets' drop-down and download the zip file for your operating system. +In the releases section, expand the 'Assets' drop-down and download the ZIP file for your operating system. ### 2. Install Extract this folder and double click on the install file. This should open the application and you're ready to go. @@ -95,19 +95,19 @@ Extract this folder and double click on the install file. This should open the a Firstly, clone this repository to your local directory -``` +```bash git clone https://github.com/wmo-im/wis2-subscription-manager ``` In this directory, install the required libraries with `npm`: -``` +```bash npm install ``` Lastly, to start the application -``` +```bash npm run start ``` @@ -120,13 +120,13 @@ This is already done automatically by the GitHub Actions workflow when there is Firstly, the Vue 3 frontend should be built using -``` +```bash npm run build ``` Then, we can Electron Forge's `make` command to package the application which will use the built frontend -``` +```bash npm run make ``` @@ -138,16 +138,17 @@ To package this application for Linux on a Windows computer, you will need to us Inside this environment, you can run the same commands as you would when making the Windows application. -*(Note: Ensure you are working on a new git clone of this repository, as the Windows `node_modules` folder may conflict with the Linux version, and vice versa.)* +*(Note: Ensure you are working on a new Git clone of this repository, as the Windows `node_modules` folder may conflict with the Linux version, and vice versa.)* This will create the application installer (`.deb`) in the `out/make/deb/x64` folder in your project root. -#### MacOS +#### macOS -It is not recommend to use a virtual machine to package this application for MacOS, as there will likely be compatibility issues. We recommend to run the commands on a genuine Macbook. +It is not recommend to use a virtual machine to package this application for macOS, as there will likely be compatibility issues. We recommend to run the commands on a genuine MacBook. The only difference here is the application must be made for both the x64 and arm64 architectures, the latter of which is used by the Apple silicon chips (M1 and above). This is done by running: -``` + +```bash npm run make -- --arch=universal --platform=darwin ``` diff --git a/src/frontend/components/CatalogueView.vue b/src/frontend/components/CatalogueView.vue index 9cbd857..12e7713 100644 --- a/src/frontend/components/CatalogueView.vue +++ b/src/frontend/components/CatalogueView.vue @@ -28,34 +28,13 @@ - - - - - - Browse the Catalogue - - - - - + - - - - - - Browse the Catalogue - - - - - + @@ -238,6 +217,7 @@ import { VCard, VCardTitle, VCardText, VCardItem, VForm, VBtn, VListGroup, VSele import { useDisplay } from 'vuetify'; // Sub-components +import SearchBar from '@/components/sub-components/SearchBar.vue'; import BboxView from "@/components/sub-components/BboxView.vue"; // Utilities @@ -259,6 +239,7 @@ export default defineComponent({ VTable, VDatePicker, VDivider, + SearchBar, BboxView }, setup() { @@ -275,7 +256,7 @@ export default defineComponent({ const limitOptions = [10, 100, 500, 1000]; // Breakpoints - const { smAndUp, mdAndUp, lgAndUp } = useDisplay(); + const { mdAndUp } = useDisplay(); // Reactive variables @@ -696,9 +677,7 @@ export default defineComponent({ // Static variables catalogueList, limitOptions, - smAndUp, mdAndUp, - lgAndUp, // Reactive variables offsetTop, diff --git a/src/frontend/components/sub-components/SearchBar.vue b/src/frontend/components/sub-components/SearchBar.vue new file mode 100644 index 0000000..32b15b5 --- /dev/null +++ b/src/frontend/components/sub-components/SearchBar.vue @@ -0,0 +1,26 @@ + + + \ No newline at end of file diff --git a/src/frontend/styles/settings.scss b/src/frontend/styles/settings.scss index b8e8564..b8aec9e 100644 --- a/src/frontend/styles/settings.scss +++ b/src/frontend/styles/settings.scss @@ -1,13 +1,3 @@ -/** - * src/styles/settings.scss - * - * Configures SASS variables and Vuetify overwrites - */ - -// https://vuetifyjs.com/features/sass-variables/` -// @use 'vuetify/settings' with ( -// $color-pack: false -// ); .max-form-width { max-width: 1500px; } diff --git a/src/frontend/utils/constants.js b/src/frontend/utils/constants.js index 4598e19..6b1cbee 100644 --- a/src/frontend/utils/constants.js +++ b/src/frontend/utils/constants.js @@ -1,17 +1,17 @@ export const HTTP_CODES = { - 200: 'OK', - 201: 'Created', - 202: 'Accepted', - 204: 'No Content', - 400: 'Bad Request', - 401: 'Unauthorized', - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 409: 'Conflict', - 500: 'Internal Server Error', - 501: 'Not Implemented', - 503: 'Service Unavailable', - 504: 'Gateway Timeout' -}; \ No newline at end of file + 200: "OK", + 201: "Created", + 202: "Accepted", + 204: "No Content", + 400: "Bad Request", + 401: "Unauthorized", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 409: "Conflict", + 500: "Internal Server Error", + 501: "Not Implemented", + 503: "Service Unavailable", + 504: "Gateway Timeout", +}; diff --git a/src/frontend/utils/prometheusTools.js b/src/frontend/utils/prometheusTools.js index 77d2d5a..910d3c1 100644 --- a/src/frontend/utils/prometheusTools.js +++ b/src/frontend/utils/prometheusTools.js @@ -1,80 +1,77 @@ export function parsePrometheusText(data) { + // Initalise object + const result = {}; - // Initalise object - const result = {}; + // Split the data into lines + const lines = data.split("\n"); - // Split the data into lines - const lines = data.split('\n'); + lines.forEach((line) => { + // Skip comments and empty lines + if (line.startsWith("#") || line.trim() === "") { + return; + } - lines.forEach(line => { - // Skip comments and empty lines - if (line.startsWith('#') || line.trim() === '') { - return; - } + // Metric type + labels and value are separated by a space + const [metricNameWithLabels, valueString] = line.split(" "); + const value = parseFloat(valueString); - // Metric type + labels and value are separated by a space - const [metricNameWithLabels, valueString] = line.split(' '); - const value = parseFloat(valueString); + // Non-labelled metrics are assigned directly to result {metric: value} + // e.g. queue size + if (!metricNameWithLabels.includes("{")) { + result[metricNameWithLabels] = value; + return; + } - // Non-labelled metrics are assigned directly to result {metric: value} - // e.g. queue size - if (!metricNameWithLabels.includes('{')) { - result[metricNameWithLabels] = value; - return; - } + // Otherwise get the metric name and labels + const [metricName, labelPart] = metricNameWithLabels.split("{"); - // Otherwise get the metric name and labels - const [metricName, labelPart] = metricNameWithLabels.split('{'); + // Remove the closing curly brace + const labelContent = labelPart.slice(0, -1); - // Remove the closing curly brace - const labelContent = labelPart.slice(0, -1); + // Get associated labels + const labels = parseLabels(labelContent); - // Get associated labels - const labels = parseLabels(labelContent); - - const topic = labels.topic; - const fileType = labels['file_type']; + const topic = labels.topic; + const fileType = labels["file_type"]; - // Add the topic to the object if it doesn't exist - if (!result[topic]) { - result[topic] = {}; - } + // Add the topic to the object if it doesn't exist + if (!result[topic]) { + result[topic] = {}; + } + // Labelled metrics without file type are assigned to metric: + // {topic: {metric: value}} + if (!fileType) { + result[topic][metricName] = value; + return; + } - // Labelled metrics without file type are assigned to metric: - // {topic: {metric: value}} - if (!fileType) { - result[topic][metricName] = value; - return; - } + // Labelled metrics with file type are nested: + // {topic: {metric: {fileType: value}}} + if (!result[topic][metricName]) { + result[topic][metricName] = {}; + } - // Labelled metrics with file type are nested: - // {topic: {metric: {fileType: value}}} - if (!result[topic][metricName]) { - result[topic][metricName] = {}; - } + result[topic][metricName][fileType] = value; + }); - result[topic][metricName][fileType] = value; - }) - - return result; -}; + return result; +} function parseLabels(labelPart) { - const labels = {}; - - // Get each key value label pair - const labelPairs = labelPart.split(','); + const labels = {}; - // Split each pair into key and value - // e.g. centre_id="in-imd" -> [centre_id, in-imd] - labelPairs.forEach(labelPair => { - const [key, value] = labelPair.split('='); + // Get each key value label pair + const labelPairs = labelPart.split(","); - // Remove quotes from the value - labels[key] = value.replace(/"/g, ''); - }); + // Split each pair into key and value + // e.g. centre_id="in-imd" -> [centre_id, in-imd] + labelPairs.forEach((labelPair) => { + const [key, value] = labelPair.split("="); - return labels; + // Remove quotes from the value + labels[key] = value.replace(/"/g, ""); + }); -} \ No newline at end of file + return labels; +} diff --git a/src/frontend/utils/topicTools.js b/src/frontend/utils/topicTools.js index fd15dc2..db8582d 100644 --- a/src/frontend/utils/topicTools.js +++ b/src/frontend/utils/topicTools.js @@ -1,53 +1,57 @@ export function topicsIntersect(topic1, topic2) { + // Quick check for exact match + if (topic1 === topic2) { + return true; + } + + const topic1Levels = topic1.split("/"); + const topic2Levels = topic2.split("/"); + + const maxLength = Math.max(topic1Levels.length, topic2Levels.length); + + for (let i = 0; i < maxLength; i++) { + const topic1Level = topic1Levels[i]; + const topic2Level = topic2Levels[i]; - // Quick check for exact match - if (topic1 === topic2) { - return true; + // Six possible cases: + // 1. One of the topics has ended (undefined level), and the previous level is a # wildcard -> True + // 2. One of the levels is a # wildcard -> True + // 3. Both levels are identical -> Continue + // 4. Both levels have + or # wildcards -> Continue + // 5. One level is a + wildcard and the other isn't -> Continue + // 6. Neither level is a wildcard and they don't match -> False + + if (topic1Level === undefined || topic2Level === undefined) { + return topic1Levels[i - 1] === "#" || topic2Levels[i - 1] === "#"; } - const topic1Levels = topic1.split('/'); - const topic2Levels = topic2.split('/'); - - const maxLength = Math.max(topic1Levels.length, topic2Levels.length); - - for (let i = 0; i < maxLength; i++) { - const topic1Level = topic1Levels[i]; - const topic2Level = topic2Levels[i]; - - // Six possible cases: - // 1. One of the topics has ended (undefined level), and the previous level is a # wildcard -> True - // 2. One of the levels is a # wildcard -> True - // 3. Both levels are identical -> Continue - // 4. Both levels have + or # wildcards -> Continue - // 5. One level is a + wildcard and the other isn't -> Continue - // 6. Neither level is a wildcard and they don't match -> False - - if (topic1Level === undefined || topic2Level === undefined) { - return topic1Levels[i - 1] === '#' || topic2Levels[i - 1] === '#'; - } - - if (topic1Level === '#' || topic2Level === '#') { - return true; - } - - if (topic1Level === topic2Level) { - continue; - } - - if ((topic1Level === '+' || topic1Level === '#') && - (topic2Level === '+' || topic2Level === '#')) { - continue; - } - - if ((topic1Level === '+' || topic2Level === '+') && (topic1Level !== topic2Level)) { - continue; - } - - if (topic1Level !== topic2Level) { - return false; - } + if (topic1Level === "#" || topic2Level === "#") { + return true; } - // If all levels pass, the topics intersect - return true; -}; \ No newline at end of file + if (topic1Level === topic2Level) { + continue; + } + + if ( + (topic1Level === "+" || topic1Level === "#") && + (topic2Level === "+" || topic2Level === "#") + ) { + continue; + } + + if ( + (topic1Level === "+" || topic2Level === "+") && + topic1Level !== topic2Level + ) { + continue; + } + + if (topic1Level !== topic2Level) { + return false; + } + } + + // If all levels pass, the topics intersect + return true; +}