From d9ebc2b105d40bb2bb6e42e2378a8f223d0fa83c Mon Sep 17 00:00:00 2001 From: ikethecoder Date: Fri, 25 Oct 2024 16:36:25 -0700 Subject: [PATCH] feature mapping reporting --- src/services/keycloak/client-service.ts | 5 + src/services/keystone/gateway-service.ts | 67 +++++++++++- src/services/keystone/metrics.ts | 21 ++-- .../report/data/features/api_directory.ts | 10 ++ .../report/data/features/consumer_mgmt.ts | 14 +++ src/services/report/data/features/index.ts | 65 +++++++++++ .../report/data/features/production.ts | 23 ++++ .../report/data/features/protected.ts | 25 +++++ .../data/features/protected_externally.ts | 13 +++ .../report/data/features/shared_idp.ts | 10 ++ .../report/data/features/two_tiered_access.ts | 20 ++++ src/services/report/data/gateway-metrics.ts | 101 ++++++++++++++---- src/services/report/output/structure.ts | 41 ++++++- src/services/report/output/xls-generator.ts | 27 +++-- src/test/integrated/keycloak/client.ts | 86 +++++++++------ src/test/integrated/reports/gatewayMetrics.ts | 90 ++++++++++++++++ src/test/services/keystone/metrics.test.ts | 4 +- 17 files changed, 545 insertions(+), 77 deletions(-) create mode 100644 src/services/report/data/features/api_directory.ts create mode 100644 src/services/report/data/features/consumer_mgmt.ts create mode 100644 src/services/report/data/features/index.ts create mode 100644 src/services/report/data/features/production.ts create mode 100644 src/services/report/data/features/protected.ts create mode 100644 src/services/report/data/features/protected_externally.ts create mode 100644 src/services/report/data/features/shared_idp.ts create mode 100644 src/services/report/data/features/two_tiered_access.ts create mode 100644 src/test/integrated/reports/gatewayMetrics.ts diff --git a/src/services/keycloak/client-service.ts b/src/services/keycloak/client-service.ts index 353b44d0e..d1da437f3 100644 --- a/src/services/keycloak/client-service.ts +++ b/src/services/keycloak/client-service.ts @@ -9,6 +9,7 @@ import ClientScopeRepresentation from '@keycloak/keycloak-admin-client/lib/defs/ import CertificateRepresentation from '@keycloak/keycloak-admin-client/lib/defs/certificateRepresentation'; import RoleRepresentation from '@keycloak/keycloak-admin-client/lib/defs/roleRepresentation'; import ClientRepresentation from '@keycloak/keycloak-admin-client/lib/defs/clientRepresentation'; +import ProtocolMapperRepresentation from '@keycloak/keycloak-admin-client/lib/defs/protocolMapperRepresentation'; const logger = Logger('kc.client'); @@ -207,4 +208,8 @@ export class KeycloakClientService { roles ); } + + public async updateClient (id: string, mapperId: string, payload: ProtocolMapperRepresentation) { + await this.kcAdminClient.clients.updateProtocolMapper({id, mapperId}, payload) + } } diff --git a/src/services/keystone/gateway-service.ts b/src/services/keystone/gateway-service.ts index 1b0ebf528..e12d8ec28 100644 --- a/src/services/keystone/gateway-service.ts +++ b/src/services/keystone/gateway-service.ts @@ -122,12 +122,73 @@ export async function lookupServicesByNamespace( }); logger.debug('Query result %j', result); result.data.allGatewayServices.forEach((svc: GatewayService) => { - svc.plugins?.map((plugin) => (plugin.config = JSON.parse(plugin.config))); - svc.routes?.map((route) => + svc.plugins?.forEach( + (plugin) => (plugin.config = JSON.parse(plugin.config)) + ); + svc.routes?.forEach((route) => { route.plugins?.map( (plugin) => (plugin.config = JSON.parse(plugin.config)) - ) + ); + }); + }); + return result.data.allGatewayServices; +} + +export async function lookupServicesByNamespaceForReporting( + context: any, + ns: string +): Promise { + const result = await context.executeGraphQL({ + query: `query GetServicesForReporting($ns: String!) { + allGatewayServices(where: {namespace: $ns}) { + name + host + plugins { + name + config + } + routes { + name + methods + hosts + paths + plugins { + name + config + } + } + environment { + name + active + appId + flow + credentialIssuer { + inheritFrom { + name + } + } + product { + name + } + } + } + }`, + variables: { ns: ns }, + }); + logger.debug( + '[lookupServicesByNamespaceForReporting] Query result %j', + result + ); + result.data.allGatewayServices.forEach((svc: GatewayService) => { + svc.plugins?.forEach( + (plugin) => (plugin.config = JSON.parse(plugin.config)) ); + svc.routes?.forEach((route) => { + route.hosts = JSON.parse(route.hosts); + route.plugins?.map( + (plugin) => (plugin.config = JSON.parse(plugin.config)) + ); + }); }); return result.data.allGatewayServices; } diff --git a/src/services/keystone/metrics.ts b/src/services/keystone/metrics.ts index 15299cfcb..8b97b227a 100644 --- a/src/services/keystone/metrics.ts +++ b/src/services/keystone/metrics.ts @@ -7,6 +7,7 @@ import format from 'date-fns/format'; import times from 'lodash/times'; import sum from 'lodash/sum'; import formatISO from 'date-fns/formatISO'; +import { strictEqual } from 'assert'; interface DailyDatum { day: string; @@ -21,13 +22,13 @@ interface DailyDatum { const logger = Logger('keystone.metrics'); const getServiceMetricsQuery = gql` - query GetServiceMetrics($service: String!, $days: [String!]) { + query GetServiceMetrics($services: [String]!, $days: [String!]) { allMetrics( sortBy: day_ASC where: { query: "kong_http_requests_hourly_service" day_in: $days - service: { name_contains: $service } + service: { name_in: $services } } ) { query @@ -82,17 +83,23 @@ const getAllConsumerDailyMetricsQuery = gql` export async function getServiceMetrics( context: any, - service: string, + services: string[], days: string[] ): Promise { + strictEqual(services.length > 0, true); + const result = await context.executeGraphQL({ query: getServiceMetricsQuery, - variables: { service, days }, + variables: { services, days }, }); + + if (result.errors) { + logger.error('[getServiceMetrics] %j', result.errors); + } logger.debug( - '[getServiceMetrics] (%s) result row count %d', - service, - result.data.allMetrics.length + '[getServiceMetrics] (%j) result row count %d', + services, + result.data.allMetrics?.length ?? 0 ); return result.data.allMetrics; } diff --git a/src/services/report/data/features/api_directory.ts b/src/services/report/data/features/api_directory.ts new file mode 100644 index 000000000..e9bf4889a --- /dev/null +++ b/src/services/report/data/features/api_directory.ts @@ -0,0 +1,10 @@ +import { GatewayService } from '../../../../services/keystone/types'; +import { ReportOfNamespaces } from '../namespaces'; + +export function has_feature_api_directory( + ns: ReportOfNamespaces, + service: GatewayService, + routeName: string +): Boolean { + return Boolean(service?.environment?.active); +} diff --git a/src/services/report/data/features/consumer_mgmt.ts b/src/services/report/data/features/consumer_mgmt.ts new file mode 100644 index 000000000..141d1489a --- /dev/null +++ b/src/services/report/data/features/consumer_mgmt.ts @@ -0,0 +1,14 @@ +import { GatewayService } from '../../../keystone/types'; +import { ReportOfNamespaces } from '../namespaces'; + +export function has_feature_consumer_mgmt( + ns: ReportOfNamespaces, + service: GatewayService, + routeName: string +): Boolean { + return ( + ['client-credentials', 'kong-api-key-acl'].indexOf( + service.environment?.flow + ) >= 0 + ); +} diff --git a/src/services/report/data/features/index.ts b/src/services/report/data/features/index.ts new file mode 100644 index 000000000..7cbf40fde --- /dev/null +++ b/src/services/report/data/features/index.ts @@ -0,0 +1,65 @@ +import { + GatewayPlugin, + GatewayService, +} from '../../../../services/keystone/types'; +import { ReportOfNamespaces } from '../namespaces'; +import { has_feature_api_directory } from './api_directory'; +import { has_feature_consumer_mgmt } from './consumer_mgmt'; +import { is_production } from './production'; +import { has_feature_protected } from './protected'; +import { has_feature_protected_externally } from './protected_externally'; +import { has_feature_shared_idp } from './shared_idp'; +import { has_feature_two_tiered_access } from './two_tiered_access'; + +export const FeatureList: { [key: string]: Function } = { + api_directory: has_feature_api_directory, + shared_idp: has_feature_shared_idp, + consumer_mgmt: has_feature_consumer_mgmt, + protected: has_feature_protected, + protected_externally: has_feature_protected_externally, + two_tiered_access: has_feature_two_tiered_access, + production: is_production, +}; + +export function getFeatures( + ns: ReportOfNamespaces, + services: GatewayService[], + routeName: string +): string[] { + const service = findService(services, routeName); + const features: string[] = []; + Object.entries(FeatureList).forEach((func) => { + if (func[1](ns, service, routeName)) { + features.push(func[0]); + } + }); + return features; +} + +export function getPlugins( + ns: ReportOfNamespaces, + services: GatewayService[], + routeName: string +): string[] { + const plugins: string[] = []; + const service = findService(services, routeName); + + plugins.push.apply(plugins, getPluginNames(service.plugins)); + service.routes.forEach((route) => { + plugins.push.apply(plugins, getPluginNames(route.plugins)); + }); + return [...new Set(plugins)].sort(); +} + +function findService( + services: GatewayService[], + routeName: string +): GatewayService { + return services + .filter((s) => s.routes.filter((r) => r.name == routeName).length > 0) + .pop(); +} + +function getPluginNames(plugins: GatewayPlugin[]): string[] { + return plugins?.map((p) => p.name); +} diff --git a/src/services/report/data/features/production.ts b/src/services/report/data/features/production.ts new file mode 100644 index 000000000..ba82590e4 --- /dev/null +++ b/src/services/report/data/features/production.ts @@ -0,0 +1,23 @@ +import { GatewayService } from '../../../keystone/types'; +import { ReportOfNamespaces } from '../namespaces'; + +const re = /(dev.|test.|tst.|dlv.|delivery.|-dev|-test|-d.|-t.).*$/; + +export function is_production( + ns: ReportOfNamespaces, + service: GatewayService, + routeName: string +): Boolean { + return ( + service.routes.filter( + (r) => + r.name == routeName && + (r.hosts as any).filter((h: string) => checkNonProd(h) == false) + .length > 0 + ).length > 0 + ); +} + +function checkNonProd(host: string) { + return re.test(host); +} diff --git a/src/services/report/data/features/protected.ts b/src/services/report/data/features/protected.ts new file mode 100644 index 000000000..edf1e03b3 --- /dev/null +++ b/src/services/report/data/features/protected.ts @@ -0,0 +1,25 @@ +import { GatewayPlugin, GatewayService } from '../../../keystone/types'; +import { ReportOfNamespaces } from '../namespaces'; + +export function has_feature_protected( + ns: ReportOfNamespaces, + service: GatewayService, + routeName: string +): Boolean { + return ( + // check either a `jwt-keycloak`, `oidc` or `acl` + // plugins exists and is active + check(service.plugins) || + service.routes.filter((r) => r.name == routeName && check(r.plugins)) + .length > 0 + ); +} + +function check(plugins: GatewayPlugin[]): boolean { + return ( + plugins + // .filter((p: any) => p.enabled) + .filter((p: any) => ['jwt-keycloak', 'oidc', 'acl'].indexOf(p.name) >= 0) + .length > 0 + ); +} diff --git a/src/services/report/data/features/protected_externally.ts b/src/services/report/data/features/protected_externally.ts new file mode 100644 index 000000000..90ab29e44 --- /dev/null +++ b/src/services/report/data/features/protected_externally.ts @@ -0,0 +1,13 @@ +import { GatewayService } from '../../../keystone/types'; +import { ReportOfNamespaces } from '../namespaces'; + +// The reality is that this is not a real scenario +// by definition "protected externally" means there is no use +// of the gateway and therefore should not have Gateway Services +export function has_feature_protected_externally( + ns: ReportOfNamespaces, + service: GatewayService, + routeName: string +): Boolean { + return service.environment?.flow == 'protected-externally'; +} diff --git a/src/services/report/data/features/shared_idp.ts b/src/services/report/data/features/shared_idp.ts new file mode 100644 index 000000000..89316ea6e --- /dev/null +++ b/src/services/report/data/features/shared_idp.ts @@ -0,0 +1,10 @@ +import { GatewayService } from '../../../keystone/types'; +import { ReportOfNamespaces } from '../namespaces'; + +export function has_feature_shared_idp( + ns: ReportOfNamespaces, + service: GatewayService, + routeName: string +): Boolean { + return Boolean(service.environment?.credentialIssuer?.inheritFrom?.name); +} diff --git a/src/services/report/data/features/two_tiered_access.ts b/src/services/report/data/features/two_tiered_access.ts new file mode 100644 index 000000000..ad4b499ac --- /dev/null +++ b/src/services/report/data/features/two_tiered_access.ts @@ -0,0 +1,20 @@ +import { GatewayPlugin, GatewayService } from '../../../keystone/types'; +import { ReportOfNamespaces } from '../namespaces'; + +export function has_feature_two_tiered_access( + ns: ReportOfNamespaces, + service: GatewayService, + routeName: string +): Boolean { + return ( + // check either service or route plugin + // has the "config.anonymous" + check(service.plugins) || + service.routes.filter((r) => r.name == routeName && check(r.plugins)) + .length > 0 + ); +} + +function check(plugins: GatewayPlugin[]): boolean { + return plugins.filter((p) => (p.config as any).anonymous).length > 0; +} diff --git a/src/services/report/data/gateway-metrics.ts b/src/services/report/data/gateway-metrics.ts index 42128f0b1..b5de4965a 100644 --- a/src/services/report/data/gateway-metrics.ts +++ b/src/services/report/data/gateway-metrics.ts @@ -1,17 +1,24 @@ -import { lookupServicesByNamespace } from '../../keystone/gateway-service'; +import { lookupServicesByNamespaceForReporting } from '../../keystone/gateway-service'; import { Keystone } from '@keystonejs/keystone'; import { ReportOfNamespaces } from './namespaces'; import { getServiceMetrics, calculateStats } from '../../keystone'; import { dateRange } from '../../utils'; +import { getFeatures, getPlugins } from './features'; export interface ReportOfGatewayMetrics { namespace: string; - displayName?: string; + display_name?: string; + data_plane: string; + service_name: string; + request_uri_host: string; prod_name?: string; prod_env_name?: string; prod_env_app_id?: string; - service_name: string; day_30_total: number; + route_names: string; + plugins: string; + features: string; + upstream: string; } /* @@ -20,26 +27,78 @@ export async function getGatewayMetrics( ksCtx: Keystone, namespaces: ReportOfNamespaces[] ): Promise { + const days = dateRange(30); + const dataPromises = namespaces.map( async (ns): Promise => { - const services = await lookupServicesByNamespace(ksCtx, ns.name); + const services = await lookupServicesByNamespaceForReporting( + ksCtx, + ns.name + ); + + const metrics = + services.length == 0 + ? [] + : await getServiceMetrics( + ksCtx, + services.map((s) => s.name), + days + ); let data: ReportOfGatewayMetrics[] = []; const subPromises = services.map(async (svc) => { - const days = dateRange(30); - const metrics = await getServiceMetrics(ksCtx, svc.name, days); - - const { totalRequests } = calculateStats(metrics); - - data.push({ - namespace: ns.name, - displayName: ns.displayName, - service_name: svc.name, - prod_name: svc.environment?.product?.name, - prod_env_name: svc.environment?.name, - prod_env_app_id: svc.environment?.appId, - day_30_total: totalRequests, - }); + const { totalRequests } = metrics + ? calculateStats( + metrics.filter((metric) => metric.service?.name == svc.name) + ) + : { totalRequests: -1 }; + + const prelimRouteList: any[] = []; + for (const route of svc.routes) { + for (const host of route.hosts) { + prelimRouteList.push({ + route_name: route.name, + request_uri_host: host, + plugins: getPlugins(ns, services, route.name), + features: getFeatures(ns, services, route.name), + }); + } + } + + // we want to have a row for each request_uri_host + // and merge/dedup route_name, plugins and features + const requestUriHosts = [ + ...new Set(prelimRouteList.map((route) => route.request_uri_host)), + ]; + + for (const host of requestUriHosts) { + const prelimListForRoute = prelimRouteList.filter( + (route) => route.request_uri_host == host + ); + + data.push({ + namespace: ns.name, + display_name: ns.displayName, + data_plane: ns.permDataPlane ?? 'default', + service_name: svc.name, + request_uri_host: host, + upstream: svc.host, + prod_name: svc.environment?.product?.name, + prod_env_name: svc.environment?.name, + prod_env_app_id: svc.environment?.appId, + day_30_total: totalRequests, + route_names: prelimListForRoute + .map((route) => route.route_name) + .sort() + .join(', '), + plugins: mergeAndDedup( + prelimListForRoute.map((route) => route.plugins) + ).join(', '), + features: mergeAndDedup( + prelimListForRoute.map((route) => route.features) + ).join(', '), + }); + } }); await Promise.all(subPromises); @@ -50,3 +109,9 @@ export async function getGatewayMetrics( const reportOfReports = await Promise.all(dataPromises); return [].concat.apply([], reportOfReports); } + +function mergeAndDedup(items: string[][]): string[] { + const newList: string[] = []; + items.forEach((values) => newList.push.apply(newList, values)); + return [...new Set(newList)].sort(); +} diff --git a/src/services/report/output/structure.ts b/src/services/report/output/structure.ts index 6c0d77b22..959dd159a 100644 --- a/src/services/report/output/structure.ts +++ b/src/services/report/output/structure.ts @@ -38,15 +38,16 @@ export const reportStructure: any = { key: 'permDataPlane', width: 25, }, + { header: 'Decommissioned', key: 'decommissioned', width: 10 }, { header: 'Org', - key: 'org', - width: 40, + key: 'org.title', + width: 50, }, { header: 'Org Unit', - key: 'orgUnit', - width: 25, + key: 'orgUnit.title', + width: 50, }, ], }, @@ -90,9 +91,29 @@ export const reportStructure: any = { }, { header: 'Gateway Display Name', - key: 'displayName', + key: 'display_name', width: 26, }, + { + header: 'Data Plane', + key: 'data_plane', + width: 20, + }, + { + header: 'Route Host', + key: 'request_uri_host', + width: 50, + }, + { + header: 'Features', + key: 'features', + width: 32, + }, + { + header: 'Plugins', + key: 'plugins', + width: 32, + }, { header: 'Product', key: 'prod_name', @@ -108,6 +129,16 @@ export const reportStructure: any = { key: 'service_name', width: 40, }, + { + header: 'Service Upstream', + key: 'upstream', + width: 40, + }, + { + header: 'Route', + key: 'route_names', + width: 40, + }, { header: '30 Day Total', key: 'day_30_total', width: 20 }, ], }, diff --git a/src/services/report/output/xls-generator.ts b/src/services/report/output/xls-generator.ts index fc342565a..d1470e77a 100644 --- a/src/services/report/output/xls-generator.ts +++ b/src/services/report/output/xls-generator.ts @@ -10,25 +10,30 @@ function toText(field: any, value: any) { } } +function getValueByPath(raw: any, path: string) { + return path.split('.').reduce((acc, key) => acc && acc[key], raw); +} + export function generateExcelWorkbook(data: any) { const workbook = new ExcelJS.Workbook(); reportOrder.forEach((tab: string) => { const struct = reportStructure[tab]; - const sheet = workbook.addWorksheet(struct.label); - sheet.columns = struct.fields.map((field: any) => - Object.assign(field, { - style: { font: { bold: false, size: 12, name: 'Arial' } }, - }) - ); + if (tab in data) { + const sheet = workbook.addWorksheet(struct.label); - sheet.getRow(1).font = { bold: true, size: 12, name: 'Arial' }; + sheet.columns = struct.fields.map((field: any) => + Object.assign(field, { + style: { font: { bold: false, size: 12, name: 'Arial' } }, + }) + ); - if (tab in data) { - data[tab].forEach((raw: any) => { - const row = struct.fields.map((field: any) => - field.key in raw ? toText(field, raw[field.key]) : '' + sheet.getRow(1).font = { bold: true, size: 12, name: 'Arial' }; + + data[tab]?.forEach((raw: any) => { + const row = struct.fields.map( + (field: any) => toText(field, getValueByPath(raw, field.key)) ?? '' ); sheet.addRow(row); }); diff --git a/src/test/integrated/keycloak/client.ts b/src/test/integrated/keycloak/client.ts index 22852ed26..0b2e2435b 100644 --- a/src/test/integrated/keycloak/client.ts +++ b/src/test/integrated/keycloak/client.ts @@ -19,43 +19,67 @@ import { o } from '../util'; import { KeycloakClientService } from '../../../services/keycloak'; import { UMAResourceRegistrationService } from '../../../services/uma2'; +import ProtocolMapperRepresentation from '@keycloak/keycloak-admin-client/lib/defs/protocolMapperRepresentation'; (async () => { - if (false) { + if (true) { + // Cleanup of a Mapper for ClientId that has changed from "clientId" + // to "client_id" + const kc = new KeycloakClientService(process.env.ISSUER); await kc.login(process.env.CID, process.env.CSC); - await kc.regenerateSecret('6af832cb-6178-438f-a4fc-8c5e1d14d5d2'); - const res = await kc.list('42da4f15'); - o(res); + const cids: string[] = []; + // await kc.regenerateSecret('6af832cb-6178-438f-a4fc-8c5e1d14d5d2'); + for (const cid of cids) { + try { + const res = await kc.findByClientId(cid); + const mapper: ProtocolMapperRepresentation = res.protocolMappers + .filter((m) => m.name == 'Client ID') + .pop(); + if (mapper) { + console.log(`${cid} : ${mapper.config['claim.name']}`); + if (mapper.config['claim.name'] == 'clientId') { + console.log('Updating!'); + mapper.config['claim.name'] = 'client_id'; + await kc.updateClient(res.id, mapper.id, mapper); + console.log('Updated!'); + } + } else { + console.log(`${cid} : MISSING`); + } + } catch (e) { + console.log(`${cid} : SKIPPED`); + } + } } - const resourceRegistrationEndpoint = - process.env.ISSUER + '/authz/protection/resource_set'; - const accessToken = process.env.TOK; - const clientUuid = '250398da-a174-404d-ae18-6a6ebc9de06d'; - - const kcprotectApi = new UMAResourceRegistrationService( - resourceRegistrationEndpoint, - accessToken - ); - const resOwnerResourceIds = await kcprotectApi.listResources({ - owner: clientUuid, - type: 'namespace', - }); - - const namespaces = await kcprotectApi.listResourcesByIdList( - resOwnerResourceIds - ); - - const matched = namespaces - .filter((ns) => ns.name == 'testelson') - .map((ns) => ({ - id: ns.id, - name: ns.name, - scopes: ns.resource_scopes, - })); - - o(matched); + // const resourceRegistrationEndpoint = + // process.env.ISSUER + '/authz/protection/resource_set'; + // const accessToken = process.env.TOK; + // const clientUuid = '250398da-a174-404d-ae18-6a6ebc9de06d'; + + // const kcprotectApi = new UMAResourceRegistrationService( + // resourceRegistrationEndpoint, + // accessToken + // ); + // const resOwnerResourceIds = await kcprotectApi.listResources({ + // owner: clientUuid, + // type: 'namespace', + // }); + + // const namespaces = await kcprotectApi.listResourcesByIdList( + // resOwnerResourceIds + // ); + + // const matched = namespaces + // .filter((ns) => ns.name == 'testelson') + // .map((ns) => ({ + // id: ns.id, + // name: ns.name, + // scopes: ns.resource_scopes, + // })); + + // o(matched); })(); diff --git a/src/test/integrated/reports/gatewayMetrics.ts b/src/test/integrated/reports/gatewayMetrics.ts new file mode 100644 index 000000000..406051503 --- /dev/null +++ b/src/test/integrated/reports/gatewayMetrics.ts @@ -0,0 +1,90 @@ +/* +Wire up directly with Keycloak and use the Services +export TOK="" +To run: +npm run ts-build +npm run ts-watch +node dist/test/integrated/reports/gatewayMetrics.js +*/ +import { createWriteStream } from 'fs'; +import InitKeystone from '../keystonejs/init'; +import { o } from '../util'; +import { Logger } from '../../../logger'; +import { + getGatewayMetrics, + getNamespaceAccess, +} from '../../../services/report/data'; +import { + getGwaProductEnvironment, + injectResSvrAccessTokenToContext, +} from '../../../services/workflow'; +import { lookupProductEnvironmentServicesBySlug } from '../../../services/keystone'; +import { getNamespaces } from '../../../services/report/ops-metrics'; +import { generateExcelWorkbook } from '../../../services/report/output/xls-generator'; + +const logger = Logger('test.reports'); + +(async () => { + const keystone = await InitKeystone(); + + const ns = 'refactortime'; + const skipAccessControl = true; + + const identity = { + id: null, + name: 'Sample User', + username: 'sample_username', + namespace: ns, + roles: JSON.stringify(['access-manager']), + scopes: [], + //userId: '60c9124f3518951bb519084d', + userId: '60c9124f3518951bb519084d', // acope@idir + } as any; + + const ctx = keystone.createContext({ + skipAccessControl, + authentication: { item: identity }, + }); + ctx.req = { + headers: { + 'x-forwarded-access-token': process.env.TOK, + }, + }; + + ctx.req.user = { sub: '15a3cbbe-95b5-49f0-84ee-434a9b92d04a' }; + + // const envCtx = await getGwaProductEnvironment(ctx, true); + + //await injectResSvrAccessTokenToContext(envCtx); + + const nslist = await getNamespaces(ctx); + nslist.sort((a, b) => a.name.localeCompare(b.name)); + o(nslist); + + const gatewayMetrics = await getGatewayMetrics( + ctx, + nslist + // .filter((ns) => ['dss-loc-gold', 'dss-loc'].indexOf(ns.name) >= 0) + .map((ns) => ({ + resource_id: ns.id, + name: ns.name, + displayName: ns.displayName, + permDataPlane: ns.permDataPlane, + })) + ); + o(gatewayMetrics); + + const workbook = generateExcelWorkbook({ + namespaces: nslist, + gateway_metrics: gatewayMetrics, + }); + const buffer = await workbook.xlsx.writeBuffer(); + + var stream = createWriteStream('gatewaymetrics.xlsx'); + stream.once('open', function (fd) { + stream.write(buffer); + stream.end(); + }); + + await keystone.disconnect(); +})(); diff --git a/src/test/services/keystone/metrics.test.ts b/src/test/services/keystone/metrics.test.ts index 0348fe636..2d63a3f2f 100644 --- a/src/test/services/keystone/metrics.test.ts +++ b/src/test/services/keystone/metrics.test.ts @@ -15,9 +15,9 @@ describe('KeystoneJS', function () { '2021-10-18', '2021-10-17', ]; - const service = 'aps-authz'; + const services = ['aps-authz']; - const metrics = await getServiceMetrics(context, service, days); + const metrics = await getServiceMetrics(context, services, days); expect(metrics.length).toBe(5); const { totalRequests } = calculateStats(metrics);