diff --git a/.github/workflows/pre-merge.yml b/.github/workflows/pre-merge.yml index 05804beac..416287fea 100644 --- a/.github/workflows/pre-merge.yml +++ b/.github/workflows/pre-merge.yml @@ -5,6 +5,7 @@ on: types: [opened, synchronize, reopened, ready_for_review] branches: - dev + - main push: branches: - dev @@ -63,7 +64,7 @@ jobs: env: PROD_CYPRESS_EMAIL: ${{ secrets.PROD_CYPRESS_EMAIL }} PROD_CYPRESS_PASSWORD: ${{ secrets.PROD_CYPRESS_PASSWORD }} - VITE_ENVIRONMENT: "production" + VITE_ENVIRONMENT: 'production' with: build: yarn build start: yarn dev --logLevel=warn @@ -162,25 +163,25 @@ jobs: needs: [run-tests, download_and_merge] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/download-artifact@v4 - with: - name: coverage_unit_report - - - uses: actions/download-artifact@v4 - with: - name: coverage_e2e_report - - - name: Extract E2E Coverage Report - run: | - unzip coverage.zip -d coverage_e2e - pwd - find ./ - - - name: SonarCloud Scan - uses: sonarsource/sonarcloud-github-action@v2.3.0 - env: - SONAR_TOKEN: ${{ secrets.SONAR_CLOUD_TOKEN }} + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/download-artifact@v4 + with: + name: coverage_unit_report + + - uses: actions/download-artifact@v4 + with: + name: coverage_e2e_report + + - name: Extract E2E Coverage Report + run: | + unzip coverage.zip -d coverage_e2e + pwd + find ./ + + - name: SonarCloud Scan + uses: sonarsource/sonarcloud-github-action@v2.3.0 + env: + SONAR_TOKEN: ${{ secrets.SONAR_CLOUD_TOKEN }} diff --git a/src/helpers/extract-api-error.js b/src/helpers/extract-api-error.js new file mode 100644 index 000000000..04553710e --- /dev/null +++ b/src/helpers/extract-api-error.js @@ -0,0 +1,34 @@ +/** + * Extracts the first error message from an API response. + * For generic errors, returns the detail message. + * For field validation errors, returns "field: error message". + * + * @param {Object} httpResponse - The HTTP response object. + * @param {Object} httpResponse.body - The response body. + * @returns {string} The formatted error message. + */ +export const extractApiError = (httpResponse) => { + const errorBody = httpResponse.body + + if (errorBody.detail) { + return errorBody.detail + } + + const findFirstError = (obj) => { + for (const [key, value] of Object.entries(obj)) { + if (Array.isArray(value)) { + return `${key}: ${value[0]}` + } + if (typeof value === 'object' && value !== null) { + const nestedError = findFirstError(value) + if (nestedError) { + const cleanFieldName = nestedError.replace(/^[^:]+\./, '') + return cleanFieldName + } + } + } + return null + } + + return findFirstError(errorBody) || 'Unknown error occurred' +} diff --git a/src/router/routes/domains-routes/index.js b/src/router/routes/domains-routes/index.js index df93394fd..685cc3d99 100644 --- a/src/router/routes/domains-routes/index.js +++ b/src/router/routes/domains-routes/index.js @@ -3,6 +3,7 @@ import * as DomainServices from '@/services/domains-services' import * as DomainServicesV4 from '@/services/domains-services/v4' import * as DigitalCertificatesServices from '@/services/digital-certificates-services' import * as EdgeApplicationServices from '@/services/edge-application-services' +import * as EdgeFirewallServicesV4 from '@/services/edge-firewall-services/v4' /** @type {import('vue-router').RouteRecordRaw} */ export const domainsRoutes = { @@ -36,6 +37,8 @@ export const domainsRoutes = { createDomainService: DomainServices.createDomainService, listDigitalCertificatesService: DigitalCertificatesServices.listDigitalCertificatesService, listEdgeApplicationsService: EdgeApplicationServices.listEdgeApplicationsService, + listEdgeFirewallService: EdgeFirewallServicesV4.listEdgeFirewallService, + loadEdgeFirewallService: EdgeFirewallServicesV4.loadEdgeFirewallService, clipboardWrite: Helpers.clipboardWrite }, meta: { @@ -60,6 +63,8 @@ export const domainsRoutes = { listDigitalCertificatesService: DigitalCertificatesServices.listDigitalCertificatesService, listEdgeApplicationsService: EdgeApplicationServices.listEdgeApplicationsService, loadDomainService: DomainServices.loadDomainService, + listEdgeFirewallService: EdgeFirewallServicesV4.listEdgeFirewallService, + loadEdgeFirewallService: EdgeFirewallServicesV4.loadEdgeFirewallService, updatedRedirect: 'list-domains', clipboardWrite: Helpers.clipboardWrite }, diff --git a/src/services/edge-firewall-services/v4/index.js b/src/services/edge-firewall-services/v4/index.js index cb42c72aa..0cafe796c 100644 --- a/src/services/edge-firewall-services/v4/index.js +++ b/src/services/edge-firewall-services/v4/index.js @@ -1,5 +1,6 @@ import { listEdgeFirewallService } from './list-edge-firewall-service' import { cloneEdgeFirewallService } from './clone-edge-firewall-service' +import { loadEdgeFirewallService } from './load-edge-firewall-service' /** * @typedef {Object} ExportedServicesType - The type of the exported services @@ -9,4 +10,4 @@ import { cloneEdgeFirewallService } from './clone-edge-firewall-service' /** * @type {ExportedServicesType} */ -export { listEdgeFirewallService, cloneEdgeFirewallService } +export { listEdgeFirewallService, cloneEdgeFirewallService, loadEdgeFirewallService } diff --git a/src/services/edge-firewall-services/v4/load-edge-firewall-service.js b/src/services/edge-firewall-services/v4/load-edge-firewall-service.js new file mode 100644 index 000000000..eba4efdbe --- /dev/null +++ b/src/services/edge-firewall-services/v4/load-edge-firewall-service.js @@ -0,0 +1,38 @@ +import { extractApiError } from '@/helpers/extract-api-error' +import { AxiosHttpClientAdapter, parseHttpResponse } from '@/services/axios/AxiosHttpClientAdapter' +import { makeEdgeFirewallBaseUrl } from './make-edge-firewall-base-url' + +export const loadEdgeFirewallService = async ({ id }) => { + let httpResponse = await AxiosHttpClientAdapter.request({ + url: `${makeEdgeFirewallBaseUrl()}/${id}`, + method: 'GET' + }) + + httpResponse = adapt(httpResponse) + + return parseHttpResponse(httpResponse) +} + +const adapt = ({ body, statusCode }) => { + if (statusCode !== 200) { + throw new Error(extractApiError({ body })).message + } + + const payload = body.data + const parsedBody = { + id: payload.id, + name: payload.name, + isActive: payload.active, + edgeFunctionsEnabled: payload.modules.edge_functions_enabled, + networkProtectionEnabled: payload.modules.network_protection_enabled, + wafEnabled: payload.modules.waf_enabled, + debugRules: payload.debug_rules, + domains: payload.domains || [], + ddosProtectionUnmetered: true + } + + return { + body: parsedBody, + statusCode + } +} diff --git a/src/templates/form-fields-inputs/fieldDropdownLazyLoader.vue b/src/templates/form-fields-inputs/fieldDropdownLazyLoader.vue new file mode 100644 index 000000000..16d7f2df6 --- /dev/null +++ b/src/templates/form-fields-inputs/fieldDropdownLazyLoader.vue @@ -0,0 +1,364 @@ + + + diff --git a/src/views/Domains/CreateView.vue b/src/views/Domains/CreateView.vue index 806321a10..e319fc7eb 100644 --- a/src/views/Domains/CreateView.vue +++ b/src/views/Domains/CreateView.vue @@ -17,12 +17,12 @@ @@ -50,7 +50,6 @@ import * as yup from 'yup' import { useToast } from 'primevue/usetoast' import { handleTrackerError } from '@/utils/errorHandlingTracker' - import { listEdgeFirewallService } from '@/services/edge-firewall-services' /**@type {import('@/plugins/analytics/AnalyticsTrackerAdapter').AnalyticsTrackerAdapter} */ const tracker = inject('tracker') @@ -83,6 +82,14 @@ updateDigitalCertificates: { type: Function, required: true + }, + loadEdgeFirewallService: { + type: Function, + required: true + }, + listEdgeFirewallService: { + type: Function, + required: true } }) @@ -109,7 +116,6 @@ const toast = useToast() const loadingEdgeApplications = ref(true) const domainName = ref() - const edgeFirewallsData = ref([]) const isLoadingEdgeFirewalls = ref(true) const requestEdgeApplications = async () => { @@ -155,29 +161,10 @@ domainName.value = domain.name } - const requestEdgeFirewalls = async () => { - isLoadingEdgeFirewalls.value = true - try { - edgeFirewallsData.value = await listEdgeFirewallService({}) - } catch (error) { - toastError(error) - } finally { - isLoadingEdgeFirewalls.value = false - } - } - - const handleEdgeFirewallCreated = async () => { - await requestEdgeFirewalls() - } - onMounted(async () => { try { scrollToTop() - await Promise.all([ - requestEdgeApplications(), - requestDigitalCertificates(), - requestEdgeFirewalls() - ]) + await Promise.all([requestEdgeApplications(), requestDigitalCertificates()]) } catch (error) { toastError(error) } finally { diff --git a/src/views/Domains/FormFields/FormFieldsCreateDomains.vue b/src/views/Domains/FormFields/FormFieldsCreateDomains.vue index 2a875cbe5..efdb98f6c 100644 --- a/src/views/Domains/FormFields/FormFieldsCreateDomains.vue +++ b/src/views/Domains/FormFields/FormFieldsCreateDomains.vue @@ -12,6 +12,7 @@ import FieldGroupRadio from '@/templates/form-fields-inputs/fieldGroupRadio' import DigitalCertificatesDrawer from '@/views/DigitalCertificates/Drawer' import FieldSwitchBlock from '@/templates/form-fields-inputs/fieldSwitchBlock' + import FieldDropdownLazyLoader from '@/templates/form-fields-inputs/fieldDropdownLazyLoader' import DrawerEdgeFirewall from '@/views/EdgeFirewall/Drawer' import { useField } from 'vee-validate' import { computed, ref, watch } from 'vue' @@ -25,10 +26,6 @@ type: Array, required: true }, - edgeFirewallsData: { - type: Array, - required: true - }, isLoadingEdgeFirewalls: { type: Boolean, required: true @@ -44,6 +41,14 @@ updateDigitalCertificates: { type: Function, required: true + }, + listEdgeFirewallService: { + type: Function, + required: true + }, + loadEdgeFirewallService: { + type: Function, + required: true } }) @@ -91,12 +96,6 @@ return props.edgeApplicationsData.map((edgeApp) => ({ name: edgeApp.name, value: edgeApp.id })) }) - const edgeFirewallOptions = computed(() => { - return props.edgeFirewallsData.map((edgeFirewall) => ({ - name: edgeFirewall.name, - value: edgeFirewall.id - })) - }) const edgeCertificatesOptions = computed(() => { const defaultCertificate = [ { name: 'Azion (SAN)', value: 0 }, @@ -247,17 +246,15 @@
- @@ -280,7 +277,7 @@ - +
{ - return props.edgeFirewallsData.map((edgeFirewall) => ({ - name: edgeFirewall.name, - value: edgeFirewall.id - })) - }) - const edgeCertificates = computed(() => { return props.digitalCertificates.filter((certificate) => certificate.type === EDGE_CERTIFICATE) }) @@ -328,17 +326,15 @@ ref="drawerEdgeFirewallRef" @onSuccess="handleEdgeFirewallCreated" /> - @@ -361,7 +357,7 @@ - +