From e9cb4f57520b1f0f117b74b4bd60103503e1a0bb Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Mon, 20 Jan 2025 14:07:57 +0100 Subject: [PATCH 1/2] fix(config-utils): enable minor config files proxy interception enables search index and services tiles config interception --- .../src/feo/check-outgoing-requests.ts | 13 +++++ packages/config-utils/src/feo/feo-types.ts | 24 ++++---- .../config-utils/src/feo/modify-response.ts | 56 +++++++++++++++++++ .../src/feo/service-tiles-interceptor.test.ts | 38 ++++++++----- .../src/feo/service-tiles-interceptor.ts | 12 ++-- packages/config-utils/src/proxy.ts | 42 ++++++++------ 6 files changed, 138 insertions(+), 47 deletions(-) create mode 100644 packages/config-utils/src/feo/modify-response.ts diff --git a/packages/config-utils/src/feo/check-outgoing-requests.ts b/packages/config-utils/src/feo/check-outgoing-requests.ts index dce10f54a..06f47b31f 100644 --- a/packages/config-utils/src/feo/check-outgoing-requests.ts +++ b/packages/config-utils/src/feo/check-outgoing-requests.ts @@ -1,3 +1,16 @@ export function matchNavigationRequest(url: string): boolean { return !!url.match(/\/api\/chrome-service\/v1\/static\/bundles-generated\.json/); } + +export function matchSearchIndexRequest(url: string): boolean { + return !!url.match(/\/api\/chrome-service\/v1\/static\/search-index-generated\.json/); +} + +export function matchServiceTilesRequest(url: string): boolean { + return !!url.match(/\/api\/chrome-service\/v1\/static\/service-tiles-generated\.json/); +} + +export function isInterceptAbleRequest(url: string): boolean { + const checks = [matchNavigationRequest, matchSearchIndexRequest, matchServiceTilesRequest]; + return checks.some((check) => check(url)); +} diff --git a/packages/config-utils/src/feo/feo-types.ts b/packages/config-utils/src/feo/feo-types.ts index c140ba0c8..9ecd541a5 100644 --- a/packages/config-utils/src/feo/feo-types.ts +++ b/packages/config-utils/src/feo/feo-types.ts @@ -107,16 +107,6 @@ export type ServiceTile = { frontendRef: string; }; -export type ServiceGroup = { - id: string; - tiles: ServiceTile[]; -}; - -export type ServiceCategory = { - id: string; - groups: ServiceGroup[]; -}; - export type ChromeWidgetEntry = { scope: string; module: string; @@ -141,3 +131,17 @@ export type CRDObject = { export type FrontendCRD = { objects: CRDObject[]; }; + +export type ServicesTilesGroupResponseEntry = { + id: string; + isGroup?: boolean; + title: string; + links: ServiceTile[]; +}; + +export type ServicesTilesResponseEntry = { + description: string; + icon: string; + id: string; + links: ServicesTilesGroupResponseEntry[]; +}; diff --git a/packages/config-utils/src/feo/modify-response.ts b/packages/config-utils/src/feo/modify-response.ts new file mode 100644 index 000000000..c24f34123 --- /dev/null +++ b/packages/config-utils/src/feo/modify-response.ts @@ -0,0 +1,56 @@ +import fecLogger, { LogType } from '../fec-logger'; +import { matchNavigationRequest, matchSearchIndexRequest, matchServiceTilesRequest } from './check-outgoing-requests'; +import { ChromeStaticSearchEntry, FrontendCRD, GeneratedBundles, ServicesTilesResponseEntry } from './feo-types'; +import navigationInterceptor from './navigation-interceptor'; +import searchInterceptor from './search-interceptor'; +import serviceTilesInterceptor from './service-tiles-interceptor'; + +function isGeneratedBundles( + body: GeneratedBundles | ChromeStaticSearchEntry[] | ServicesTilesResponseEntry[], + url: string +): body is GeneratedBundles { + return matchNavigationRequest(url); +} + +function isSearchIndex( + body: GeneratedBundles | ChromeStaticSearchEntry[] | ServicesTilesResponseEntry[], + url: string +): body is ChromeStaticSearchEntry[] { + return matchSearchIndexRequest(url); +} + +function isServiceTiles( + body: GeneratedBundles | ChromeStaticSearchEntry[] | ServicesTilesResponseEntry[], + url: string +): body is ServicesTilesResponseEntry[] { + return matchServiceTilesRequest(url); +} + +export function modifyRequest(body: string, url: string, frontendCrd: FrontendCRD): string { + // intentionally let the parse throw an error to parent to handle unfinished request chunks + const objectToModify = JSON.parse(body); + // return original if no match is found + let payload: string = body; + try { + if (isGeneratedBundles(objectToModify, url)) { + const resultBundles: GeneratedBundles = []; + objectToModify.forEach((bundle) => { + const navItems = navigationInterceptor(frontendCrd, bundle, bundle.id); + resultBundles.push({ ...bundle, navItems }); + }); + payload = JSON.stringify(resultBundles); + } else if (isSearchIndex(objectToModify, url)) { + const staticSearch = objectToModify as ChromeStaticSearchEntry[]; + const result = searchInterceptor(staticSearch, frontendCrd); + payload = JSON.stringify(result); + } else if (isServiceTiles(objectToModify, url)) { + const staticServicesTiles = objectToModify as ServicesTilesResponseEntry[]; + const result = serviceTilesInterceptor(staticServicesTiles, frontendCrd); + payload = JSON.stringify(result); + } + return payload; + } catch (error) { + fecLogger(LogType.error, `Error modifying proxy request via config interceptors: ${error}`); + return payload; + } +} diff --git a/packages/config-utils/src/feo/service-tiles-interceptor.test.ts b/packages/config-utils/src/feo/service-tiles-interceptor.test.ts index 95baf053a..3f381623e 100644 --- a/packages/config-utils/src/feo/service-tiles-interceptor.test.ts +++ b/packages/config-utils/src/feo/service-tiles-interceptor.test.ts @@ -1,4 +1,4 @@ -import { FrontendCRD, ServiceCategory } from './feo-types'; +import { FrontendCRD, ServicesTilesResponseEntry } from './feo-types'; import serviceTilesInterceptor from './service-tiles-interceptor'; describe('Service tiles interceptor', () => { @@ -38,13 +38,16 @@ describe('Service tiles interceptor', () => { }, ], }; - const remoteServiceTiles: ServiceCategory[] = [ + const remoteServiceTiles: ServicesTilesResponseEntry[] = [ { id: 'section-1', - groups: [ + description: 'section 1', + icon: 'icon', + links: [ { id: 'group-1', - tiles: [ + title: 'Group 1', + links: [ { section: 'section-1', group: 'group-1', @@ -63,10 +66,13 @@ describe('Service tiles interceptor', () => { }, { id: 'section-2', - groups: [ + description: 'section 2', + icon: 'icon', + links: [ { id: 'group-1', - tiles: [ + title: 'Group 1', + links: [ { section: 'section-2', group: 'group-1', @@ -78,14 +84,17 @@ describe('Service tiles interceptor', () => { ], }, ]; - const expectedServiceTiles: ServiceCategory[] = [ + const expectedServiceTiles: ServicesTilesResponseEntry[] = [ { id: 'section-1', - groups: [ + description: 'section 1', + icon: 'icon', + links: [ { + title: 'Group 1', id: 'group-1', - tiles: [ - remoteServiceTiles[0].groups[0].tiles[0], + links: [ + remoteServiceTiles[0].links[0].links[0], { section: 'section-1', group: 'group-1', @@ -104,11 +113,14 @@ describe('Service tiles interceptor', () => { }, { id: 'section-2', - groups: [ + description: 'section 2', + icon: 'icon', + links: [ { + title: 'Group 1', id: 'group-1', - tiles: [ - remoteServiceTiles[1].groups[0].tiles[0], + links: [ + remoteServiceTiles[1].links[0].links[0], { section: 'section-2', group: 'group-1', diff --git a/packages/config-utils/src/feo/service-tiles-interceptor.ts b/packages/config-utils/src/feo/service-tiles-interceptor.ts index c2793cee3..4b970e7e4 100644 --- a/packages/config-utils/src/feo/service-tiles-interceptor.ts +++ b/packages/config-utils/src/feo/service-tiles-interceptor.ts @@ -1,6 +1,6 @@ -import { FrontendCRD, ServiceCategory, ServiceTile } from './feo-types'; +import { FrontendCRD, ServiceTile, ServicesTilesResponseEntry } from './feo-types'; -function serviceTilesInterceptor(serviceCategories: ServiceCategory[], frontendCrd: FrontendCRD): ServiceCategory[] { +function serviceTilesInterceptor(serviceCategories: ServicesTilesResponseEntry[], frontendCrd: FrontendCRD): ServicesTilesResponseEntry[] { const frontendRef = frontendCrd.objects[0].metadata.name; let result = [...serviceCategories]; @@ -23,16 +23,16 @@ function serviceTilesInterceptor(serviceCategories: ServiceCategory[], frontendC }, {}) ?? {}; result = result.map((category) => { - const newGroups = category.groups.map((group) => { - const newTiles = group.tiles.filter((tile) => tile.frontendRef !== frontendRef); + const newGroups = category.links.map((group) => { + const newTiles = group.links.filter((tile) => tile.frontendRef !== frontendRef); return { ...group, - tiles: [...newTiles, ...(frontendCategories[category.id]?.[group.id] ?? [])], + links: [...newTiles, ...(frontendCategories[category.id]?.[group.id] ?? [])], }; }); return { ...category, - groups: newGroups, + links: newGroups, }; }); diff --git a/packages/config-utils/src/proxy.ts b/packages/config-utils/src/proxy.ts index 0bbe576e1..f058e8a45 100644 --- a/packages/config-utils/src/proxy.ts +++ b/packages/config-utils/src/proxy.ts @@ -8,11 +8,11 @@ import type { Configuration } from 'webpack-dev-server'; import { HttpsProxyAgent } from 'https-proxy-agent'; import chokidar from 'chokidar'; import cookieTransform from './cookieTransform'; -import { matchNavigationRequest } from './feo/check-outgoing-requests'; +import { isInterceptAbleRequest, matchNavigationRequest } from './feo/check-outgoing-requests'; import { hasFEOFeaturesEnabled, readFrontendCRD } from './feo/crd-check'; -import navigationInterceptor from './feo/navigation-interceptor'; -import { GeneratedBundles } from './feo/feo-types'; import fecLogger, { LogType } from './fec-logger'; +import { modifyRequest } from './feo/modify-response'; +import { FrontendCRD } from './feo/feo-types'; const defaultReposDir = path.join(__dirname, 'repos'); @@ -136,13 +136,22 @@ const proxy = ({ localApps = process.env.LOCAL_APPS, frontendCRDPath = path.resolve(process.cwd(), 'deploy/frontend.yaml'), }: ProxyOptions) => { - const frontendCrdRef = { current: readFrontendCRD(frontendCRDPath) }; - const FEOFeaturesEnabled = hasFEOFeaturesEnabled(frontendCrdRef.current); + const frontendCrdRef: { current?: FrontendCRD } = { current: undefined }; + let FEOFeaturesEnabled = false; + try { + frontendCrdRef.current = readFrontendCRD(frontendCRDPath); + FEOFeaturesEnabled = hasFEOFeaturesEnabled(frontendCrdRef.current); + } catch (e) { + fecLogger( + LogType.warn, + `FEO features are not enabled. Unable to find frontend CRD file at ${frontendCRDPath}. If you want FEO features for local development, make sure to have a "deploy/frontend.yaml" file in your project or specify its location via "frontendCRDPath" attribute.` + ); + } const proxy: ProxyConfigItem[] = []; const majorEnv = env.split('-')[0]; const defaultLocalAppHost = process.env.LOCAL_APP_HOST || majorEnv + '.foo.redhat.com'; - if (FEOFeaturesEnabled) { + if (FEOFeaturesEnabled && frontendCrdRef?.current) { fecLogger(LogType.info, 'Watching frontend CRC file for changes'); const watcher = chokidar.watch(frontendCRDPath).on('change', () => { fecLogger(LogType.info, 'Frontend CRD has changed, reloading the file'); @@ -226,7 +235,7 @@ const proxy = ({ changeOrigin: true, autoRewrite: true, onProxyReq: (proxyReq, req) => { - if (matchNavigationRequest(req.url)) { + if (isInterceptAbleRequest(req.url)) { // necessary to avoid gzip encoding and issues with parsing the json body proxyReq.setHeader('accept-encoding', 'gzip;q=0,deflate,sdch'); } @@ -235,7 +244,7 @@ const proxy = ({ // this should reading the aggregated bundles filed generated from chrome service // The functionality is disabled until the interceptor is ready // eslint-disable-next-line no-constant-condition - if (matchNavigationRequest(req.url)) { + if (isInterceptAbleRequest(req.url)) { // stub the original write function const _write = res.write; let body = ''; @@ -245,17 +254,14 @@ const proxy = ({ res.write = function () { try { - const objectToModify = JSON.parse(body) as GeneratedBundles; - const resultBundles: GeneratedBundles = []; - if (FEOFeaturesEnabled) { - // these will be filled in chrome service once migration is ready to start - objectToModify.forEach((bundle) => { - const navItems = navigationInterceptor(frontendCrdRef.current, bundle, bundle.id); - resultBundles.push({ ...bundle, navItems }); - }); + if (FEOFeaturesEnabled && frontendCrdRef.current) { + const payload = modifyRequest(body, req.url, frontendCrdRef.current); + // content length is necessary to update to prevent JSON parsing errors in browser + res.setHeader('content-length', payload.length); + _write.call(res, payload, 'utf8'); + } else { + _write.call(res, body, 'utf8'); } - const payload = JSON.stringify(resultBundles); - _write.call(res, payload, 'utf8'); return true; } catch { // wait for all the chunks to arrive From 48cce0e3c213159734572482920096ce5ea3f253 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Tue, 28 Jan 2025 12:30:56 +0100 Subject: [PATCH 2/2] chore: add missing version parent dependency --- packages/advisor-components/project.json | 3 ++- packages/chrome/project.json | 3 ++- packages/components/project.json | 3 ++- packages/config-utils/project.json | 5 +++-- packages/config/project.json | 3 ++- packages/eslint-config/project.json | 3 ++- packages/notifications/project.json | 3 ++- packages/remediations/project.json | 3 ++- packages/rule-components/project.json | 3 ++- packages/testing/project.json | 3 ++- packages/translations/project.json | 3 ++- packages/tsc-transform-imports/project.json | 3 ++- packages/types/project.json | 3 ++- packages/utils/project.json | 3 ++- 14 files changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/advisor-components/project.json b/packages/advisor-components/project.json index 74c4e1d32..dfeb8d0ef 100644 --- a/packages/advisor-components/project.json +++ b/packages/advisor-components/project.json @@ -84,7 +84,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/chrome/project.json b/packages/chrome/project.json index 68e221db6..715289d28 100644 --- a/packages/chrome/project.json +++ b/packages/chrome/project.json @@ -58,7 +58,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/components/project.json b/packages/components/project.json index a2b5bcaa7..412846338 100644 --- a/packages/components/project.json +++ b/packages/components/project.json @@ -83,7 +83,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/config-utils/project.json b/packages/config-utils/project.json index 65e5516e3..42c2b5333 100644 --- a/packages/config-utils/project.json +++ b/packages/config-utils/project.json @@ -33,7 +33,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", @@ -62,6 +63,6 @@ "options": { "jestConfig": "packages/config-utils/jest.config.ts" } - }, + } } } diff --git a/packages/config/project.json b/packages/config/project.json index 2e8f2bad0..594fe3720 100644 --- a/packages/config/project.json +++ b/packages/config/project.json @@ -41,7 +41,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/eslint-config/project.json b/packages/eslint-config/project.json index 9a3ca7745..7bbe8a0a9 100644 --- a/packages/eslint-config/project.json +++ b/packages/eslint-config/project.json @@ -28,7 +28,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/notifications/project.json b/packages/notifications/project.json index fd38ca317..35d6eb021 100644 --- a/packages/notifications/project.json +++ b/packages/notifications/project.json @@ -73,7 +73,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/remediations/project.json b/packages/remediations/project.json index 8895cdb64..8e1f717d4 100644 --- a/packages/remediations/project.json +++ b/packages/remediations/project.json @@ -74,7 +74,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/rule-components/project.json b/packages/rule-components/project.json index 5762b0b81..80a08a152 100644 --- a/packages/rule-components/project.json +++ b/packages/rule-components/project.json @@ -74,7 +74,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/testing/project.json b/packages/testing/project.json index 7483820b5..b687e122b 100644 --- a/packages/testing/project.json +++ b/packages/testing/project.json @@ -66,7 +66,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/translations/project.json b/packages/translations/project.json index e0b3a8842..ebc4f9ebd 100644 --- a/packages/translations/project.json +++ b/packages/translations/project.json @@ -85,7 +85,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/tsc-transform-imports/project.json b/packages/tsc-transform-imports/project.json index 17d95c1b1..009db45ff 100644 --- a/packages/tsc-transform-imports/project.json +++ b/packages/tsc-transform-imports/project.json @@ -33,7 +33,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/types/project.json b/packages/types/project.json index f1cc69b8f..886887614 100644 --- a/packages/types/project.json +++ b/packages/types/project.json @@ -27,7 +27,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github", diff --git a/packages/utils/project.json b/packages/utils/project.json index 325c100b1..f42be4d27 100644 --- a/packages/utils/project.json +++ b/packages/utils/project.json @@ -73,7 +73,8 @@ "push": true, "preset": "conventionalcommits", "commitMessageFormat": "chore: bump {projectName} to {version} [skip ci]" - } + }, + "dependsOn": ["^version"] }, "github": { "executor": "@jscutlery/semver:github",