diff --git a/azion.config.cjs b/azion.config.cjs index 3f50c05b1..1016a155d 100644 --- a/azion.config.cjs +++ b/azion.config.cjs @@ -243,19 +243,6 @@ const backRules = [ rewrite: '/billing/graphql' } }, - { - name: 'Route List Client Ids to Github', - description: 'this route will get the client ids of the accounts released to the console', - match: '^/api/allowed-accounts', - behavior: { - forwardCookies: true, - setOrigin: { - name: 'origin-github-allowed-accounts', - type: 'single_origin' - }, - rewrite: '/aziontech/console-client-list/main/clids.json' - } - }, { name: 'Route Send Feedback', description: 'this route will send user feedback to jira', @@ -310,12 +297,6 @@ const AzionConfig = { addresses: [`api.azion.com`] }, ]), - { - name: 'origin-github-allowed-accounts', - type: 'single_origin', - hostHeader: `raw.githubusercontent.com`, - addresses: [`raw.githubusercontent.com`] - }, { name: 'origin-console-feedback', type: 'single_origin', diff --git a/azion/production/azion.json b/azion/production/azion.json index b0451adcc..89af18419 100644 --- a/azion/production/azion.json +++ b/azion/production/azion.json @@ -64,11 +64,6 @@ "origin-key": "4c361d2f-dbb5-42c0-a6b6-28b0e6e50d91", "name": "console_kit_31-07-2024_single" }, - { - "origin-id": 153663, - "origin-key": "6ec17c87-d2fb-4c28-a3b7-588b1ae61bf2", - "name": "origin-github-allowed-accounts" - }, { "origin-id": 154233, "origin-key": "8c16e835-20af-41d5-a688-cac8e6aefa94", @@ -153,11 +148,6 @@ "name": "Secure Headers", "phase": "response" }, - { - "id": 296352, - "name": "Route List Client Ids to Github", - "phase": "request" - }, { "id": 297983, "name": "Route Send Feedback", diff --git a/azion/stage/azion.json b/azion/stage/azion.json index e448aacba..5534d96f0 100644 --- a/azion/stage/azion.json +++ b/azion/stage/azion.json @@ -64,11 +64,6 @@ "origin-key": "c92f57e1-79d6-4bcc-9493-2af4ad9f4dd5", "name": "origin-cities" }, - { - "origin-id": 153113, - "origin-key": "ab771dc5-8ee9-4f9f-bb5d-3fa6fe9483c5", - "name": "origin-github-allowed-accounts" - }, { "origin-id": 153761, "origin-key": "f1f976fb-0558-4592-a930-dcad70ab2e7c", @@ -153,11 +148,6 @@ "name": "Secure Headers", "phase": "response" }, - { - "id": 294965, - "name": "Route List Client Ids to Github", - "phase": "request" - }, { "id": 296641, "name": "Route Send Feedback", diff --git a/package.json b/package.json index d432cd291..6affa2eb4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion-console-kit", - "version": "1.22.2", + "version": "1.22.3", "private": false, "type": "module", "repository": { diff --git a/src/helpers/convert-gql.js b/src/helpers/convert-gql.js index cb6929205..9164baf3a 100644 --- a/src/helpers/convert-gql.js +++ b/src/helpers/convert-gql.js @@ -16,7 +16,7 @@ const getGraphQLType = (value) => { return 'String' } - if (!isNaN(Date.parse(value))) { + if (isValidDate(value)) { return 'DateTime' } @@ -24,6 +24,16 @@ const getGraphQLType = (value) => { } } +function isValidDate(dateString) { + const dateRegexes = /^\d{2,4}[-/]\d{2}[-/]\d{2,4}?.*$/ + const isValidFormat = dateRegexes.test(dateString) + if (!isValidFormat) { + return false + } + + return !isNaN(Date.parse(dateString)) +} + /** * Builds a GraphQL query based on the provided parameters. * @@ -49,6 +59,15 @@ function buildGraphQLQuery({ filterParameter, dataset, limit, orderBy, filterQue ].join('\n') } +const formatValueContainOperator = (variable) => { + for (const key in variable) { + if (variable[key] && key.includes('Like')) { + variable[key] = `%${variable[key]}%` + } + } + return variable +} + /** * Convert filter and table to gql body * @@ -59,11 +78,11 @@ function buildGraphQLQuery({ filterParameter, dataset, limit, orderBy, filterQue const convertGQL = (filter, table) => { if (!table) throw new Error('Table parameter is required') - const variables = {} + let variables = {} const filterQuery = buildFilterQuery(filter, variables) - const fieldsFormat = table.fields.map((field) => `\t\t${field}`).join('\n') const filterParameter = formatFilterParameter(variables) + variables = formatValueContainOperator(variables) const queryConfig = { filterParameter, @@ -73,6 +92,7 @@ const convertGQL = (filter, table) => { filterQuery, fields: fieldsFormat } + const query = buildGraphQLQuery(queryConfig) return { @@ -138,6 +158,7 @@ const separateFieldsByType = (fields) => { const mergeFieldsIntoFilter = (fields, filter) => { fields.forEach(({ operator, valueField, value }) => { const filterKey = operator === 'In' ? 'in' : 'and' + filter[filterKey] = { ...filter[filterKey], [`${valueField}${operator}`]: value diff --git a/src/modules/real-time-metrics/constants/help-center-urls.js b/src/modules/real-time-metrics/constants/help-center-urls.js index 0445d7537..1f39c65a9 100644 --- a/src/modules/real-time-metrics/constants/help-center-urls.js +++ b/src/modules/real-time-metrics/constants/help-center-urls.js @@ -76,7 +76,21 @@ const HELP_CENTER_URLS = { sqlInjectionThreats: '/real-time-metrics/waf/threats/sql-injection-threats', threatsVsRequests: '/real-time-metrics/waf/threats/threats-vs-requests' } - } + }, + botManager: { + botManagerSummary: { + badBotHits: '/real-time-metrics/bot-manager-advanced/overview/bad-bot-hits', + goodBotHits: '/real-time-metrics/bot-manager-advanced/overview/good-bot-hits', + botHits: '/real-time-metrics/bot-manager-advanced/overview/bot-hits', + transactions: '/real-time-metrics/bot-manager-advanced/overview/transactions', + botTraffic: '/real-time-metrics/bot-manager-advanced/overview/bot-traffic', + topBotAction: '/real-time-metrics/bot-manager-advanced/overview/top-bot-action', + botCaptchaLine: '/real-time-metrics/bot-manager-advanced/overview/bot-captcha-line-graph', + topBotCaptchaPie: '/real-time-metrics/bot-manager-advanced/overview/top-bot-captcha-pie-graph', + topBotClassification: '/real-time-metrics/bot-manager-advanced/overview/top-bot-classifications', + botActivityMap: '/real-time-metrics/bot-manager-advanced/overview/bot-activity-map' + } + }, } export default HELP_CENTER_URLS diff --git a/src/modules/real-time-metrics/constants/reports-texts.js b/src/modules/real-time-metrics/constants/reports-texts.js index b3376e89c..deebd1cbc 100644 --- a/src/modules/real-time-metrics/constants/reports-texts.js +++ b/src/modules/real-time-metrics/constants/reports-texts.js @@ -189,7 +189,8 @@ const REPORTS_TEXTS = { description: 'Number of requests evaluated by Azion Bot Manager.' }, botTraffic: { - description: 'Sum of requests grouped by classification.' + description: + 'Sum of requests grouped by identifying traffic as Legitimate, Bad Bot, Good Bot, and Under Evaluation.' }, botCaptcha: { description: 'Sum of CAPTCHA challenge results returned for requests classified as bots.' @@ -199,7 +200,8 @@ const REPORTS_TEXTS = { 'Actions taken by Azion Bot Manager for requests identified as bots, displayed in both absolute values and percentages.' }, botClassification: { - description: 'Number of requests by bot categories.' + description: + 'Sum of requests classified according to the tactics used and the purpose of the bots.' }, botActivityMap: { description: 'Sum of requests identified as bots, presented by the country of origin.' diff --git a/src/modules/real-time-metrics/constants/reports.js b/src/modules/real-time-metrics/constants/reports.js index 8a08edcf2..57b3167c6 100644 --- a/src/modules/real-time-metrics/constants/reports.js +++ b/src/modules/real-time-metrics/constants/reports.js @@ -862,7 +862,7 @@ const REPORTS = [ orderDirection: 'DESC', dashboardId: '371360344901061482', variationType: 'inverse', - helpCenterPath: '' + helpCenterPath: HELP_CENTER_URLS.botManager.botManagerSummary.badBotHits }, { id: '934654293238823255', @@ -892,7 +892,7 @@ const REPORTS = [ orderDirection: 'DESC', dashboardId: '371360344901061482', variationType: 'neutral', - helpCenterPath: '' + helpCenterPath: HELP_CENTER_URLS.botManager.botManagerSummary.goodBotHits }, { id: '259047966206560862', @@ -922,7 +922,7 @@ const REPORTS = [ orderDirection: 'DESC', dashboardId: '371360344901061482', variationType: 'inverse', - helpCenterPath: '' + helpCenterPath: HELP_CENTER_URLS.botManager.botManagerSummary.botHits }, { id: '541669034905662013', @@ -949,7 +949,7 @@ const REPORTS = [ orderDirection: 'DESC', dashboardId: '371360344901061482', variationType: 'neutral', - helpCenterPath: '' + helpCenterPath: HELP_CENTER_URLS.botManager.botManagerSummary.transactions }, { id: '329891149133127508', @@ -975,12 +975,12 @@ const REPORTS = [ orderDirection: 'ASC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: HELP_CENTER_URLS.botManager.botManagerSummary.botTraffic }, { id: '577704475532819772', chartOwner: 'azion', - label: 'Bot Action', + label: 'Top Bot Action', description: REPORTS_TEXTS.botManager.botManagerSummary.botAction.description, aggregationType: 'sum', columns: 6, @@ -1007,7 +1007,7 @@ const REPORTS = [ orderDirection: 'DESC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: HELP_CENTER_URLS.botManager.botManagerSummary.topBotAction }, { id: '071851224118431167', @@ -1036,12 +1036,12 @@ const REPORTS = [ orderDirection: 'ASC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: HELP_CENTER_URLS.botManager.botManagerSummary.botCaptchaLine }, { id: '455330743572401794', chartOwner: 'azion', - label: 'Bot CAPTCHA', + label: 'Top Bot CAPTCHA', description: REPORTS_TEXTS.botManager.botManagerSummary.botCaptcha.description, aggregationType: 'sum', columns: 6, @@ -1066,12 +1066,12 @@ const REPORTS = [ orderDirection: 'DESC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: HELP_CENTER_URLS.botManager.botManagerSummary.topBotCaptchaPie }, { id: '424388331488145485', chartOwner: 'azion', - label: 'Bot Classifications', + label: 'Top Bot Classifications', description: REPORTS_TEXTS.botManager.botManagerSummary.botClassification.description, aggregationType: 'sum', columns: 6, @@ -1098,7 +1098,7 @@ const REPORTS = [ orderDirection: 'DESC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: HELP_CENTER_URLS.botManager.botManagerSummary.topBotClassification }, { id: '190246009413028885', @@ -1128,7 +1128,7 @@ const REPORTS = [ orderDirection: 'ASC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: HELP_CENTER_URLS.botManager.botManagerSummary.botActivityMap }, /** * OBSERVE diff --git a/src/plugins/analytics/AnalyticsTrackerAdapter.js b/src/plugins/analytics/AnalyticsTrackerAdapter.js index b74292c96..68587e4c5 100644 --- a/src/plugins/analytics/AnalyticsTrackerAdapter.js +++ b/src/plugins/analytics/AnalyticsTrackerAdapter.js @@ -1,4 +1,10 @@ -import { SignUpTracker, SignInTracker, CreateTracker, ProductTracker } from './trackers' +import { + SignUpTracker, + SignInTracker, + CreateTracker, + ProductTracker, + WafRulesTracker +} from './trackers' /** * @typedef {Object} TrackerEvent @@ -27,6 +33,8 @@ export class AnalyticsTrackerAdapter { #createTracker = null /** @type {ProductTracker} */ #productTracker = null + /** @type {WafTracker} */ + #wafRulesTracker = null /** * Creates an instance of AnalyticsTrackerAdapter. @@ -40,6 +48,7 @@ export class AnalyticsTrackerAdapter { this.#signInTracker = new SignInTracker(this) this.#createTracker = new CreateTracker(this) this.#productTracker = new ProductTracker(this) + this.#wafRulesTracker = new WafRulesTracker(this) } /** @@ -119,4 +128,11 @@ export class AnalyticsTrackerAdapter { get product() { return this.#productTracker } + + /** + * @return {ProductTracker} + */ + get wafRules() { + return this.#wafRulesTracker + } } diff --git a/src/plugins/analytics/trackers/ProductTracker.js b/src/plugins/analytics/trackers/ProductTracker.js index e0557248b..df5940e70 100644 --- a/src/plugins/analytics/trackers/ProductTracker.js +++ b/src/plugins/analytics/trackers/ProductTracker.js @@ -139,4 +139,17 @@ export class ProductTracker { }) return this.#trackerAdapter } + + /** + * @param {Object} payload + * @param {String} payload.target + * @returns {AnalyticsTrackerAdapter} + */ + clickedOn(payload) { + this.#trackerAdapter.addEvent({ + eventName: `Clicked on ${payload.target}`, + props: {} + }) + return this.#trackerAdapter + } } diff --git a/src/plugins/analytics/trackers/WafRulesTracker.js b/src/plugins/analytics/trackers/WafRulesTracker.js new file mode 100644 index 000000000..cdf6ef6f8 --- /dev/null +++ b/src/plugins/analytics/trackers/WafRulesTracker.js @@ -0,0 +1,69 @@ +export class WafRulesTracker { + /** + * Interface for TrackerAdapter. + * @typedef {Object} trackerAdapter + * @property {function({eventName: string, props: Object}): void} addEvent - Method to add an event. + */ + #trackerAdapter + + /** + * @param {trackerAdapter} trackerAdapter + */ + constructor(adapter) { + this.#trackerAdapter = adapter + } + + /** + * @param {Object} payload + * @param {'drawer'|'page'} payload.origin + * + * @returns {AnalyticsTrackerAdapter} + */ + clickedToAllowRules(payload) { + this.#trackerAdapter.addEvent({ + eventName: 'Clicked to Allow Rules', + props: { + origin: payload.origin + } + }) + return this.#trackerAdapter + } + + /** + * @param {Object} payload + * @param {'drawer'|'page'} payload.origin + * + * @returns {AnalyticsTrackerAdapter} + */ + allowedRules(payload) { + this.#trackerAdapter.addEvent({ + eventName: 'Allowed Rules', + props: { + origin: payload.origin + } + }) + return this.#trackerAdapter + } + + /** + * @param {Object} payload + * @param {AzionProductsNames} payload.productName + * @param {String} payload.errorType + * @param {String} payload.fieldName + * @param {String} payload.errorMessage + * @param {'drawer'|'page'} payload.origin + * @returns {AnalyticsTrackerAdapter} + */ + failedToAllowRules(payload) { + this.#trackerAdapter.addEvent({ + eventName: 'Failed to Allow Rules', + props: { + errorType: payload.errorType, + fieldName: payload.fieldName, + errorMessage: payload.errorMessage, + origin: payload.origin + } + }) + return this.#trackerAdapter + } +} diff --git a/src/plugins/analytics/trackers/index.js b/src/plugins/analytics/trackers/index.js index 39fd5890e..326a99c82 100644 --- a/src/plugins/analytics/trackers/index.js +++ b/src/plugins/analytics/trackers/index.js @@ -2,5 +2,6 @@ import { SignUpTracker } from './SignUpTracker' import { SignInTracker } from './SignInTracker' import { CreateTracker } from './CreateTracker' import { ProductTracker } from './ProductTracker' +import { WafRulesTracker } from './WafRulesTracker' -export { SignUpTracker, SignInTracker, CreateTracker, ProductTracker } +export { SignUpTracker, SignInTracker, CreateTracker, ProductTracker, WafRulesTracker } diff --git a/src/router/hooks/redirectToManager.js b/src/router/hooks/redirectToManager.js index 88c996b7c..9ae386530 100644 --- a/src/router/hooks/redirectToManager.js +++ b/src/router/hooks/redirectToManager.js @@ -1,7 +1,6 @@ import { getEnvironment, getStaticUrlsByEnvironment } from '@/helpers' import { loadContractServicePlan } from '@/services/contract-services' import { useAccountStore } from '@/stores/account' -import { listClientIdsReleasedForConsoleService } from '@/services/account-services' /** @type {import('vue-router').NavigationGuardWithThis} */ export default async function redirectToManager(to, from, next) { @@ -9,15 +8,15 @@ export default async function redirectToManager(to, from, next) { const isPrivateRoute = !to.meta.isPublic const accountData = accountStore.accountData - if (accountStore.shouldAvoidCalculateServicePlan || accountStore.hasAccessConsole) { - return forceRedirectViewAccess(to, next, from, accountStore) - } - try { if (accountStore.hasActiveUserId && isPrivateRoute) { + if (accountStore.metricsOnlyAccessRestriction) { + return handleNavigationRestriction(to, from, next) + } + const isAzion = accountData.email.includes('@azion.com') // Azion internal access to console. - if (isAzion) { + if (isAzion || accountStore.hasAccessConsole) { return next() } const isNotClientKind = !accountData.client_id @@ -29,20 +28,15 @@ export default async function redirectToManager(to, from, next) { } // account that are kind client, can access with developer service plan - const [{ isDeveloperSupportPlan }, consoleReleasedClient] = await Promise.all([ - loadContractServicePlan({ clientId: accountData.client_id }), - listClientIdsReleasedForConsoleService(accountData.client_id) - ]) + const { isDeveloperSupportPlan } = await loadContractServicePlan({ + clientId: accountData.client_id + }) accountStore.setAccountData({ - isDeveloperSupportPlan: isDeveloperSupportPlan, - consoleReleasedClient + isDeveloperSupportPlan: isDeveloperSupportPlan }) if (!isDeveloperSupportPlan) { - if (accountStore.hasAccessConsole) { - return forceRedirectViewAccess(to, next, from, accountStore) - } permanentRedirectToManager() } } @@ -60,18 +54,16 @@ function permanentRedirectToManager() { } } -function forceRedirectViewAccess(to, next, from, accountStore) { - const viewsAccessRestriction = accountStore.viewsAccessRestriction - const isPrivateRoute = !to.meta.isPublic +function handleNavigationRestriction(to, from, next) { + const metricsRouteName = 'real-time-metrics' + const isNotNavigatingToMetrics = to.name !== metricsRouteName + const isNavigatingFromMetrics = from.name === metricsRouteName - if (isPrivateRoute && viewsAccessRestriction?.length) { - if (!viewsAccessRestriction.includes(to.name)) { - if (viewsAccessRestriction.includes(from.name)) { - return next(false) - } else { - return next({ name: viewsAccessRestriction[0] }) - } + if (isNotNavigatingToMetrics) { + if (isNavigatingFromMetrics) { + return next(false) } + return next({ name: metricsRouteName }) } return next() diff --git a/src/services/account-services/index.js b/src/services/account-services/index.js index 3cdcb6090..e62e74438 100644 --- a/src/services/account-services/index.js +++ b/src/services/account-services/index.js @@ -1,5 +1,4 @@ import { getUserInfoService } from './get-user-info-service' import { getAccountInfoService } from './get-account-info-service' -import { listClientIdsReleasedForConsoleService } from './list-client-ids-released-for-console-service' -export { getAccountInfoService, getUserInfoService, listClientIdsReleasedForConsoleService } +export { getAccountInfoService, getUserInfoService } diff --git a/src/services/account-services/list-client-ids-released-for-console-service.js b/src/services/account-services/list-client-ids-released-for-console-service.js deleted file mode 100644 index 9a52b1edf..000000000 --- a/src/services/account-services/list-client-ids-released-for-console-service.js +++ /dev/null @@ -1,23 +0,0 @@ -import { AxiosHttpClientAdapter } from '../axios/AxiosHttpClientAdapter' -import { getEnvironment } from '@/helpers/get-environment' - -export const listClientIdsReleasedForConsoleService = async (clientId) => { - const httpResponse = await AxiosHttpClientAdapter.request({ - url: `/allowed-accounts`, - method: 'GET' - }) - return adapt(httpResponse, clientId) -} - -const adapt = (httpResponse, clientId) => { - const environment = getEnvironment() - const env = environment === 'production' ? 'production' : 'stage' - if (!clientId) return {} - - const client = httpResponse?.body?.[env]?.access[clientId] - - return { - hasAccessConsole: ['global', 'restricted'].includes(client?.type), - views: client?.views || [] - } -} diff --git a/src/stores/account.js b/src/stores/account.js index ff107bccc..29b9a8af4 100644 --- a/src/stores/account.js +++ b/src/stores/account.js @@ -14,6 +14,10 @@ export const useAccountStore = defineStore({ TRIAL: 'TRIAL', ONLINE: 'ONLINE', REGULAR: 'REGULAR' + }, + flags: { + RESTRICT_ACCESS_TO_METRICS_ONLY: 'allow_only_metrics_on_console', + FULL_CONSOLE_ACCESS: 'allow_console' } }), getters: { @@ -23,14 +27,17 @@ export const useAccountStore = defineStore({ hasActiveUserId(state) { return !!state.account?.id }, - shouldAvoidCalculateServicePlan(state) { - return !!state.account?.isDeveloperSupportPlan - }, - viewsAccessRestriction(state) { - return state.account?.consoleReleasedClient?.views + metricsOnlyAccessRestriction(state) { + const flags = state.flags + const client_flags = state.account?.client_flags || [] + return ( + client_flags.includes(flags.RESTRICT_ACCESS_TO_METRICS_ONLY) && + !client_flags.includes(flags.FULL_CONSOLE_ACCESS) + ) }, hasAccessConsole(state) { - return state.account?.consoleReleasedClient?.hasAccessConsole + const { client_flags = [], isDeveloperSupportPlan } = state.account + return client_flags.includes(state.flags.FULL_CONSOLE_ACCESS) || !!isDeveloperSupportPlan }, currentTheme(state) { return state.account?.colorTheme diff --git a/src/stores/table-definitions.js b/src/stores/table-definitions.js new file mode 100644 index 000000000..e653b165d --- /dev/null +++ b/src/stores/table-definitions.js @@ -0,0 +1,19 @@ +import { defineStore } from 'pinia' + +export const useTableDefinitionsStore = defineStore({ + id: 'tableDefinitions', + state: () => ({ + numberOfLinesPerPage: 10 + }), + persist: { + paths: ['numberOfLinesPerPage'] + }, + getters: { + getNumberOfLinesPerPage: (state) => state.numberOfLinesPerPage + }, + actions: { + setNumberOfLinesPerPage(numberOfLinesPerPage) { + this.numberOfLinesPerPage = numberOfLinesPerPage + } + } +}) diff --git a/src/templates/edit-form-block/index.vue b/src/templates/edit-form-block/index.vue index a69aae7c4..0a512adf7 100644 --- a/src/templates/edit-form-block/index.vue +++ b/src/templates/edit-form-block/index.vue @@ -101,7 +101,7 @@ const initialValues = await props.loadService({ id }) resetForm({ values: initialValues }) } catch (error) { - emit('on-load-fail') + emit('on-load-fail', error) showToast('error', error) } } diff --git a/src/templates/list-table-block/index.vue b/src/templates/list-table-block/index.vue index f92de9595..772c605f4 100644 --- a/src/templates/list-table-block/index.vue +++ b/src/templates/list-table-block/index.vue @@ -17,9 +17,10 @@ @row-click="editItemSelected" rowHover v-model:filters="filters" - :paginator="showPagination" + :paginator="true" :rowsPerPageOptions="[10, 20, 50, 100]" - :rows="MINIMUM_OF_ITEMS_PER_PAGE" + :rows="minimumOfItemsPerPage" + @page="changeNumberOfLinesPerPage" :globalFilterFields="filterBy" v-model:selection="selectedItems" :exportFilename="exportFileName" @@ -304,6 +305,7 @@ import { useToast } from 'primevue/usetoast' import { getCsvCellContentFromRowData } from '@/helpers' import { getArrayChangedIndexes } from '@/helpers/get-array-changed-indexes' + import { useTableDefinitionsStore } from '@/stores/table-definitions' defineOptions({ name: 'list-table-block-new' }) @@ -392,7 +394,9 @@ } }) - const MINIMUM_OF_ITEMS_PER_PAGE = 10 + const tableDefinitions = useTableDefinitionsStore() + + const minimumOfItemsPerPage = ref(tableDefinitions.getNumberOfLinesPerPage) const isRenderActions = !!props.actions?.length const isRenderOneOption = props.actions?.length === 1 const selectedId = ref(null) @@ -586,6 +590,12 @@ } } + const changeNumberOfLinesPerPage = (event) => { + const numberOfLinesPerPage = event.rows + tableDefinitions.setNumberOfLinesPerPage(numberOfLinesPerPage) + minimumOfItemsPerPage.value = numberOfLinesPerPage + } + const filterBy = computed(() => { const filtersPath = props.columns.filter((el) => el.filterPath).map((el) => el.filterPath) const filters = props.columns.map((item) => item.field) @@ -593,10 +603,6 @@ return [...filters, ...filtersPath] }) - const showPagination = computed(() => { - return data.value.length > MINIMUM_OF_ITEMS_PER_PAGE - }) - watch(data, (currentState) => { const hasData = currentState?.length > 0 emit('on-load-data', !!hasData) diff --git a/src/tests/helpers/convert-gql.test.js b/src/tests/helpers/convert-gql.test.js index da2062b5a..2d40ef12d 100644 --- a/src/tests/helpers/convert-gql.test.js +++ b/src/tests/helpers/convert-gql.test.js @@ -36,11 +36,11 @@ describe('convertGQL', () => { expect(sut(filter, fixtures.table)).toEqual({ query: expect.any(String), variables: { - tsRange_begin: '2022-01-01', - tsRange_end: '2022-01-31', and_field1: 'value1', and_field2: 'value2', - in_field3: ['value3', 'value4'] + in_field3: ['value3', 'value4'], + tsRange_begin: '2022-01-01', + tsRange_end: '2022-01-31' } }) }) @@ -141,4 +141,54 @@ describe('convertGQL', () => { } }) }) + + it('should return the correct type of variable in the query parameter', () => { + const filter = { + fields: [{ valueField: 'field2', operator: 'Like', value: '/2019' }] + } + + const { sut } = makeSut() + + const query = `query ( + $and_field2Like: String! +) { + myDataset ( + limit: 10 + orderBy: [field1] + filter: { + field2Like: $and_field2Like + } + ) { + field1 + field2 + field3 + } +}` + + expect(sut(filter, fixtures.table)).toEqual({ + query, + variables: { + and_field2Like: '%/2019%' + } + }) + }) + + it('Should format the value when the operator is contains', () => { + const filter = { + fields: [ + { valueField: 'field2', operator: 'Like', value: '/2019' }, + { valueField: 'field3', operator: 'Like', value: 'test' } + ] + } + + const { sut } = makeSut() + + expect(sut(filter, fixtures.table)).toEqual({ + query: expect.any(String), + variables: { + and_field2Like: '%/2019%', + and_field3Like: '%test%' + } + }) + }) }) diff --git a/src/tests/modules/real-time-metrics/constants/reports.test.js b/src/tests/modules/real-time-metrics/constants/reports.test.js index 5ccdc8344..a876f3328 100644 --- a/src/tests/modules/real-time-metrics/constants/reports.test.js +++ b/src/tests/modules/real-time-metrics/constants/reports.test.js @@ -830,7 +830,7 @@ describe('RealTimeMetricsModule', () => { classifiedEq: 'bad bot' }, groupBy: [], - helpCenterPath: '', + helpCenterPath: '/real-time-metrics/bot-manager-advanced/overview/bad-bot-hits', id: '892249168369791027', isTopX: false, label: 'Bad Bot Hits', @@ -860,7 +860,7 @@ describe('RealTimeMetricsModule', () => { classifiedEq: 'good bot' }, groupBy: [], - helpCenterPath: '', + helpCenterPath: '/real-time-metrics/bot-manager-advanced/overview/good-bot-hits', id: '934654293238823255', isTopX: false, label: 'Good Bot Hits', @@ -890,7 +890,7 @@ describe('RealTimeMetricsModule', () => { classifiedIn: ['bad bot', 'good bot'] }, groupBy: [], - helpCenterPath: '', + helpCenterPath: '/real-time-metrics/bot-manager-advanced/overview/bot-hits', id: '259047966206560862', isTopX: false, label: 'Bot Hits', @@ -917,7 +917,7 @@ describe('RealTimeMetricsModule', () => { description: 'Number of requests evaluated by Azion Bot Manager.', fields: [], groupBy: [], - helpCenterPath: '', + helpCenterPath: '/real-time-metrics/bot-manager-advanced/overview/transactions', id: '541669034905662013', isTopX: false, label: 'Transactions', @@ -941,9 +941,10 @@ describe('RealTimeMetricsModule', () => { dashboardId: '371360344901061482', dataUnit: 'count', dataset: 'botManagerMetrics', - description: 'Sum of requests grouped by classification.', + description: + 'Sum of requests grouped by identifying traffic as Legitimate, Bad Bot, Good Bot, and Under Evaluation.', groupBy: ['classified'], - helpCenterPath: '', + helpCenterPath: '/real-time-metrics/bot-manager-advanced/overview/bot-traffic', id: '329891149133127508', isTopX: false, label: 'Bot Traffic', @@ -957,7 +958,7 @@ describe('RealTimeMetricsModule', () => { { id: '577704475532819772', chartOwner: 'azion', - label: 'Bot Action', + label: 'Top Bot Action', description: 'Actions taken by Azion Bot Manager for requests identified as bots, displayed in both absolute values and percentages.', aggregationType: 'sum', @@ -985,7 +986,7 @@ describe('RealTimeMetricsModule', () => { orderDirection: 'DESC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: '/real-time-metrics/bot-manager-advanced/overview/top-bot-action' }, { id: '071851224118431167', @@ -1014,12 +1015,12 @@ describe('RealTimeMetricsModule', () => { orderDirection: 'ASC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: '/real-time-metrics/bot-manager-advanced/overview/bot-captcha-line-graph' }, { id: '455330743572401794', chartOwner: 'azion', - label: 'Bot CAPTCHA', + label: 'Top Bot CAPTCHA', description: 'Sum of CAPTCHA challenge results returned for requests classified as bots.', aggregationType: 'sum', columns: 6, @@ -1044,13 +1045,14 @@ describe('RealTimeMetricsModule', () => { orderDirection: 'DESC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: '/real-time-metrics/bot-manager-advanced/overview/top-bot-captcha-pie-graph' }, { id: '424388331488145485', chartOwner: 'azion', - label: 'Bot Classifications', - description: 'Number of requests by bot categories.', + label: 'Top Bot Classifications', + description: + 'Sum of requests classified according to the tactics used and the purpose of the bots.', aggregationType: 'sum', columns: 6, type: 'ordered-bar', @@ -1076,7 +1078,7 @@ describe('RealTimeMetricsModule', () => { orderDirection: 'DESC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: '/real-time-metrics/bot-manager-advanced/overview/top-bot-classifications' }, { id: '190246009413028885', @@ -1106,7 +1108,7 @@ describe('RealTimeMetricsModule', () => { orderDirection: 'ASC', dashboardId: '371360344901061482', variationType: 'regular', - helpCenterPath: '' + helpCenterPath: '/real-time-metrics/bot-manager-advanced/overview/bot-activity-map' }, { aggregationType: 'sum', diff --git a/src/tests/plugins/analytics/AnalyticsTrackerAdapter.test.js b/src/tests/plugins/analytics/AnalyticsTrackerAdapter.test.js index bb08d98f0..fee360258 100644 --- a/src/tests/plugins/analytics/AnalyticsTrackerAdapter.test.js +++ b/src/tests/plugins/analytics/AnalyticsTrackerAdapter.test.js @@ -515,4 +515,79 @@ describe('AnalyticsTrackerAdapter', () => { fieldName: fieldName }) }) + + it('should be able to track clicked on with correct params', () => { + const { sut, analyticsClientSpy } = makeSut() + const targetName = 'Search' + + sut.product.clickedOn({ + target: targetName + }) + + sut.track() + + expect(analyticsClientSpy.track).toHaveBeenCalledWith('Clicked on Search', { + application: fixtures.application + }) + }) + + // Waf Rules Tracker - Scenarios + it('should be able to track click to allow rules with correct params', () => { + const { sut, analyticsClientSpy } = makeSut() + const originName = 'page' + + sut.wafRules.clickedToAllowRules({ + origin: originName + }) + + sut.track() + + expect(analyticsClientSpy.track).toHaveBeenCalledWith('Clicked to Allow Rules', { + application: fixtures.application, + origin: 'page' + }) + }) + + it('should be able to track allow rules with correct params', () => { + const { sut, analyticsClientSpy } = makeSut() + const originName = 'page' + + sut.wafRules.allowedRules({ + origin: originName + }) + + sut.track() + + expect(analyticsClientSpy.track).toHaveBeenCalledWith('Allowed Rules', { + application: fixtures.application, + origin: originName + }) + }) + + it('should be able to track failed edited event with correct params', () => { + const { sut, analyticsClientSpy } = makeSut() + const productNameMock = 'Origin' + const errorMessageMock = 'message' + const errorTypeMock = 'API' + const fieldName = 'detail' + const originName = 'page' + + sut.wafRules.failedToAllowRules({ + productName: productNameMock, + errorMessage: errorMessageMock, + errorType: errorTypeMock, + fieldName: fieldName, + origin: 'page' + }) + + sut.track() + + expect(analyticsClientSpy.track).toHaveBeenCalledWith('Failed to Allow Rules', { + application: fixtures.application, + errorMessage: errorMessageMock, + errorType: errorTypeMock, + fieldName: fieldName, + origin: originName + }) + }) }) diff --git a/src/tests/services/account-services/list-client-ids-released-for-console-service.test.js b/src/tests/services/account-services/list-client-ids-released-for-console-service.test.js deleted file mode 100644 index dbfcda4d7..000000000 --- a/src/tests/services/account-services/list-client-ids-released-for-console-service.test.js +++ /dev/null @@ -1,90 +0,0 @@ -import { listClientIdsReleasedForConsoleService } from '@/services/account-services' -import { AxiosHttpClientAdapter } from '@/services/axios/AxiosHttpClientAdapter' -import { getEnvironment } from '@/helpers/get-environment' -import { describe, expect, it, vi, beforeEach } from 'vitest' - -vi.mock('@/helpers/get-environment') - -const fixtures = { - production: { access: { id1: { type: 'global' }, id2: { type: 'global' } } }, - stage: { - access: { - id3: { type: 'global' }, - id4: { type: 'restricted', views: ['real-time-metrics'] } - } - } -} - -const makeSut = () => { - const sut = listClientIdsReleasedForConsoleService - return { sut } -} - -describe('AccountServices', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - it('should call the API service with the correct parameters', async () => { - const requestSpy = vi.spyOn(AxiosHttpClientAdapter, 'request').mockResolvedValueOnce({ - statusCode: 200, - body: fixtures - }) - - getEnvironment.mockReturnValue('production') - - const { sut } = makeSut() - await sut() - - expect(requestSpy).toHaveBeenCalledWith({ - method: 'GET', - url: '/allowed-accounts' - }) - }) - - it('should return an array of production client IDs when the environment is production', async () => { - vi.spyOn(AxiosHttpClientAdapter, 'request').mockResolvedValueOnce({ - statusCode: 200, - body: fixtures - }) - getEnvironment.mockReturnValue('production') - - const { sut } = makeSut() - const result = await sut('id1') - - expect(result).toEqual({ - hasAccessConsole: true, - views: [] - }) - }) - - it('should return an array of stage client IDs when the environment is not production', async () => { - vi.spyOn(AxiosHttpClientAdapter, 'request').mockResolvedValueOnce({ - statusCode: 200, - body: fixtures - }) - - getEnvironment.mockReturnValue('stage') - - const { sut } = makeSut() - const result = await sut('id1') - - expect(result).toEqual({ - hasAccessConsole: false, - views: [] - }) - }) - - it('should return an empty array when the response body does not contain client_ids', async () => { - vi.spyOn(AxiosHttpClientAdapter, 'request').mockResolvedValueOnce({ - statusCode: 200, - body: { production: {}, stage: {} } - }) - getEnvironment.mockReturnValue('production') - - const { sut } = makeSut() - const result = await sut() - - expect(result).toEqual({}) - }) -}) diff --git a/src/views/EdgeApplicationsRulesEngine/Drawer/index.vue b/src/views/EdgeApplicationsRulesEngine/Drawer/index.vue index ba0a605e1..fd8a255a9 100644 --- a/src/views/EdgeApplicationsRulesEngine/Drawer/index.vue +++ b/src/views/EdgeApplicationsRulesEngine/Drawer/index.vue @@ -211,14 +211,24 @@ } } + const handleTrackCreation = () => { + tracker.product + .productCreated({ + productName: 'Rule', + product: 'edgeApplication' + }) + .track() + } + const handleFailedToCreate = (error) => { const { fieldName, message } = handleTrackerError(error) tracker.product .failedToCreate({ - productName: 'Rules Engine', + productName: 'Rule', errorType: 'api', fieldName: fieldName.trim(), - errorMessage: message + errorMessage: message, + product: 'edgeApplication' }) .track() } @@ -227,10 +237,11 @@ const { fieldName, message } = handleTrackerError(error) tracker.product .failedToEdit({ - productName: 'Rules Engine', + productName: 'Rule', errorMessage: message, fieldName: fieldName, - errorType: 'api' + errorType: 'api', + product: 'edgeApplication' }) .track() @@ -246,11 +257,14 @@ const handleCreateRulesEngine = () => { emit('onSuccess') + handleTrackCreation() closeDrawerCreate() } + const handleTrackSuccessEdit = () => { tracker.product.productEdited({ - productName: 'Rules Engine' + productName: 'Rule', + product: 'edgeApplication' }) tracker.product .productEdited({ diff --git a/src/views/EdgeFirewallRulesEngine/Drawer/index.vue b/src/views/EdgeFirewallRulesEngine/Drawer/index.vue index be6844281..f96854a60 100644 --- a/src/views/EdgeFirewallRulesEngine/Drawer/index.vue +++ b/src/views/EdgeFirewallRulesEngine/Drawer/index.vue @@ -133,7 +133,7 @@ const handleCreateWithSuccess = () => { emit('onSuccess') - handleTrackSuccessEdit() + handleTrackCreation() closeCreateDrawer() } @@ -143,7 +143,36 @@ closeEditDrawer() } + const handleTrackCreation = () => { + tracker.product + .productCreated({ + productName: 'Rule', + product: 'edgeFirewall' + }) + .track() + } + + const handleFailedToCreate = (error) => { + const { fieldName, message } = handleTrackerError(error) + tracker.product + .failedToCreate({ + productName: 'Rule', + errorType: 'api', + fieldName: fieldName.trim(), + errorMessage: message, + product: 'edgeFirewall' + }) + .track() + } + const handleTrackSuccessEdit = () => { + tracker.product + .productEdited({ + productName: 'Rule', + product: 'edgeFirewall' + }) + .track() + tracker.product .productEdited({ productName: 'Edge Firewall', @@ -156,18 +185,15 @@ const { fieldName, message } = handleTrackerError(error) tracker.product .failedToEdit({ - productName: 'Edge Firewall', + productName: 'Rule', errorType: 'api', fieldName: fieldName.trim(), + product: 'edgeFirewall', errorMessage: message }) .track() } - const handleErrorToTrackerEvent = (error) => { - handleFailedEditEdgeFirewallRules(error) - } - const listFunctionsServiceWithDecorator = async () => { return await props.listFunctionsService({ edgeFirewallID: props.edgeFirewallId @@ -237,7 +263,7 @@ :schema="validationSchema" :initialValues="initialValues" @onSuccess="handleCreateWithSuccess" - @onError="handleErrorToTrackerEvent" + @onError="handleFailedToCreate" title="Create Rule" >