diff --git a/src/services/report/data/features/index.ts b/src/services/report/data/features/index.ts index 7cbf40fde..3e4218149 100644 --- a/src/services/report/data/features/index.ts +++ b/src/services/report/data/features/index.ts @@ -7,7 +7,6 @@ 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'; @@ -16,7 +15,6 @@ export const FeatureList: { [key: string]: Function } = { 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, }; diff --git a/src/services/report/data/features/protected_externally.ts b/src/services/report/data/features/protected_externally.ts deleted file mode 100644 index 90ab29e44..000000000 --- a/src/services/report/data/features/protected_externally.ts +++ /dev/null @@ -1,13 +0,0 @@ -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/protected_exterrnally.ts b/src/services/report/data/features/protected_exterrnally.ts new file mode 100644 index 000000000..25240a679 --- /dev/null +++ b/src/services/report/data/features/protected_exterrnally.ts @@ -0,0 +1,10 @@ +import { GatewayService } from '../../../keystone/types'; +import { ReportOfNamespaces } from '../namespaces'; +import { ReportOfProducts } from '../products'; + +export function has_feature_protected_externally( + ns: ReportOfNamespaces, + product: ReportOfProducts +): Boolean { + return 'protected-externally' === product.prod_env_flow; +} diff --git a/src/services/report/data/gateway-metrics.ts b/src/services/report/data/gateway-metrics.ts index b5de4965a..7dd4fa4a8 100644 --- a/src/services/report/data/gateway-metrics.ts +++ b/src/services/report/data/gateway-metrics.ts @@ -15,9 +15,9 @@ export interface ReportOfGatewayMetrics { prod_env_name?: string; prod_env_app_id?: string; day_30_total: number; - route_names: string; - plugins: string; - features: string; + route_names: string[]; + plugins: string[]; + features: string[]; upstream: string; } @@ -89,14 +89,13 @@ export async function getGatewayMetrics( day_30_total: totalRequests, route_names: prelimListForRoute .map((route) => route.route_name) - .sort() - .join(', '), + .sort(), plugins: mergeAndDedup( prelimListForRoute.map((route) => route.plugins) - ).join(', '), + ), features: mergeAndDedup( prelimListForRoute.map((route) => route.features) - ).join(', '), + ), }); } }); diff --git a/src/services/report/data/namespaces.ts b/src/services/report/data/namespaces.ts index c348d5c0a..cc49d2c5b 100644 --- a/src/services/report/data/namespaces.ts +++ b/src/services/report/data/namespaces.ts @@ -1,6 +1,7 @@ import { transformSingleValueAttributes, camelCaseAttributes, + dedup, } from '../../utils'; import { KeycloakGroupService } from '../../keycloak'; import { getMyNamespaces } from '../../workflow'; @@ -9,6 +10,8 @@ import { NamespaceSummary, } from '../../workflow/get-namespaces'; import { GWAService } from '../../gwaapi'; +import { ReportOfGatewayMetrics } from './gateway-metrics'; +import { ReportOfProducts } from './products'; export interface ReportOfNamespaces { resource_id: string; @@ -20,6 +23,7 @@ export interface ReportOfNamespaces { org?: string; orgUnit?: string; decommissioned?: string; + features?: string[]; } /* @@ -76,3 +80,25 @@ export async function getNamespaces( return Promise.all(dataPromises); } + +export function rollupFeatures( + namespaces: ReportOfNamespaces[], + gatewayMetrics: ReportOfGatewayMetrics[], + products: ReportOfProducts[] +) { + namespaces.forEach((ns) => { + ns.features = []; + gatewayMetrics + .filter((gw) => gw.namespace == ns.name) + .forEach((gw) => { + ns.features.push.apply(ns.features, gw.features); + }); + products + .filter((gw) => gw.namespace == ns.name) + .forEach((gw) => { + ns.features.push.apply(ns.features, gw.features); + }); + + ns.features = dedup(ns.features).sort(); + }); +} diff --git a/src/services/report/data/products.ts b/src/services/report/data/products.ts new file mode 100644 index 000000000..eb0269414 --- /dev/null +++ b/src/services/report/data/products.ts @@ -0,0 +1,55 @@ +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'; +import { lookupEnvironmentsByNS } from '../../../services/keystone/product-environment'; +import { Environment } from '../../../services/keystone/types'; +import { has_feature_protected_externally } from './features/protected_exterrnally'; + +export interface ReportOfProducts { + namespace: string; + display_name?: string; + prod_name?: string; + prod_env_active: string; + prod_env_name?: string; + prod_env_flow?: string; + prod_env_app_id?: string; + features: string[]; +} + +export async function getProducts( + ksCtx: Keystone, + namespaces: ReportOfNamespaces[] +): Promise { + const dataPromises = namespaces.map( + async (ns): Promise => { + const environments = await lookupEnvironmentsByNS(ksCtx, ns.name); + return environments.map((env) => { + const product = { + namespace: ns.name, + display_name: ns.displayName, + prod_name: env.product?.name, + prod_env_name: env.name, + prod_env_app_id: env.appId, + prod_env_flow: env.flow, + prod_env_active: env.active ? 'Y' : 'N', + features: [], + } as ReportOfProducts; + + const featureProtectedExternally = has_feature_protected_externally( + ns, + product + ); + if (featureProtectedExternally) { + product.features.push('protected_externally'); + } + return product; + }); + } + ); + + const reportOfReports = await Promise.all(dataPromises); + return [].concat.apply([], reportOfReports); +} diff --git a/src/services/report/output/structure.ts b/src/services/report/output/structure.ts index 959dd159a..9c0e13075 100644 --- a/src/services/report/output/structure.ts +++ b/src/services/report/output/structure.ts @@ -38,7 +38,12 @@ export const reportStructure: any = { key: 'permDataPlane', width: 25, }, - { header: 'Decommissioned', key: 'decommissioned', width: 10 }, + { + header: 'Features', + key: 'features', + width: 60, + }, + { header: 'Decommissioned', key: 'decommissioned', width: 20 }, { header: 'Org', key: 'org.title', diff --git a/src/services/report/workbook.service.ts b/src/services/report/workbook.service.ts index c187cf09a..ba5aec348 100644 --- a/src/services/report/workbook.service.ts +++ b/src/services/report/workbook.service.ts @@ -17,6 +17,8 @@ import { getGatewayMetrics, ReportOfGatewayMetrics, } from './data'; +import { rollupFeatures } from './data/namespaces'; +import { getProducts } from './data/products'; export class WorkbookService { keystone: Keystone; @@ -43,6 +45,10 @@ export class WorkbookService { gateway_metrics ); + const products = await getProducts(this.keystone, namespaces); + + rollupFeatures(namespaces, gateway_metrics, products); + const gateway_controls = await getGatewayControls( this.keystone, namespaces, diff --git a/src/services/utils.ts b/src/services/utils.ts index 7bcfb87d4..307f46ab8 100644 --- a/src/services/utils.ts +++ b/src/services/utils.ts @@ -79,3 +79,7 @@ export async function fetchWithTimeout(resource: string, options: any = {}) { export function alphanumericNoSpaces(str: string) { return str.replace(/[^A-Za-z0-9 :-]/gim, '').replace(/[ :]/gim, '-'); } + +export function dedup(ls: string[]) { + return [...new Set(ls)]; +} diff --git a/src/test/integrated/reports/gatewayMetrics.ts b/src/test/integrated/reports/gatewayMetrics.ts index 406051503..b5289770d 100644 --- a/src/test/integrated/reports/gatewayMetrics.ts +++ b/src/test/integrated/reports/gatewayMetrics.ts @@ -21,6 +21,8 @@ import { import { lookupProductEnvironmentServicesBySlug } from '../../../services/keystone'; import { getNamespaces } from '../../../services/report/ops-metrics'; import { generateExcelWorkbook } from '../../../services/report/output/xls-generator'; +import { rollupFeatures } from '../../../services/report/data/namespaces'; +import { getProducts } from '../../../services/report/data/products'; const logger = Logger('test.reports'); @@ -61,18 +63,25 @@ const logger = Logger('test.reports'); 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, - })) + const filteredNS = nslist + // .filter( + // (ns) => ['dss-loc-gold', 'dss-loc', 'social-gold'].indexOf(ns.name) >= 0 + // ) + .map((ns) => ({ + resource_id: ns.id, + name: ns.name, + displayName: ns.displayName, + permDataPlane: ns.permDataPlane, + })); + + const products = await getProducts(ctx, filteredNS); + + const gatewayMetrics = await getGatewayMetrics(ctx, filteredNS); + gatewayMetrics.sort((a, b) => + a.request_uri_host.localeCompare(b.request_uri_host) ); - o(gatewayMetrics); + + rollupFeatures(nslist as any, gatewayMetrics, products); const workbook = generateExcelWorkbook({ namespaces: nslist,