From 0ad018f617c1fbcdbda0685b09ed41751f6dcad7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:46:25 +0200 Subject: [PATCH 1/4] chore(deps): bump pbkdf2 from 3.1.2 to 3.1.3 in /website (#10200) --- website/yarn.lock | 85 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/website/yarn.lock b/website/yarn.lock index c573d153f1..c3949c5748 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -6176,7 +6176,7 @@ __metadata: languageName: node linkType: hard -"create-hash@npm:^1.1.0, create-hash@npm:^1.1.2, create-hash@npm:^1.2.0": +"create-hash@npm:^1.1.0, create-hash@npm:^1.2.0": version: 1.2.0 resolution: "create-hash@npm:1.2.0" dependencies: @@ -6189,7 +6189,19 @@ __metadata: languageName: node linkType: hard -"create-hmac@npm:^1.1.4, create-hmac@npm:^1.1.7": +"create-hash@npm:~1.1.3": + version: 1.1.3 + resolution: "create-hash@npm:1.1.3" + dependencies: + cipher-base: "npm:^1.0.1" + inherits: "npm:^2.0.1" + ripemd160: "npm:^2.0.0" + sha.js: "npm:^2.4.0" + checksum: 10c0/dbcf4a1b13c8dd5f2a69f5f30bd2701f919ed7d3fbf5aa530cf00b17a950c2b77f63bfe6a2981735a646ae2620d96c8f4584bf70aeeabf050a31de4e46219d08 + languageName: node + linkType: hard + +"create-hmac@npm:^1.1.7": version: 1.1.7 resolution: "create-hmac@npm:1.1.7" dependencies: @@ -8697,6 +8709,15 @@ __metadata: languageName: node linkType: hard +"hash-base@npm:^2.0.0": + version: 2.0.2 + resolution: "hash-base@npm:2.0.2" + dependencies: + inherits: "npm:^2.0.1" + checksum: 10c0/283f6060277b52e627a734c4d19d4315ba82326cab5a2f4f2f00b924d747dc7cc902a8cedb1904c7a3501075fcbb24c08de1152bae296698fdc5ad75b33986af + languageName: node + linkType: hard + "hash-base@npm:^3.0.0": version: 3.1.0 resolution: "hash-base@npm:3.1.0" @@ -9735,7 +9756,7 @@ __metadata: languageName: node linkType: hard -"is-typed-array@npm:^1.1.3": +"is-typed-array@npm:^1.1.14, is-typed-array@npm:^1.1.3": version: 1.1.15 resolution: "is-typed-array@npm:1.1.15" dependencies: @@ -9774,6 +9795,13 @@ __metadata: languageName: node linkType: hard +"isarray@npm:^2.0.5": + version: 2.0.5 + resolution: "isarray@npm:2.0.5" + checksum: 10c0/4199f14a7a13da2177c66c31080008b7124331956f47bca57dd0b6ea9f11687aa25e565a2c7a2b519bc86988d10398e3049a1f5df13c9f6b7664154690ae79fd + languageName: node + linkType: hard + "isarray@npm:~1.0.0": version: 1.0.0 resolution: "isarray@npm:1.0.0" @@ -12859,15 +12887,16 @@ __metadata: linkType: hard "pbkdf2@npm:^3.1.2": - version: 3.1.2 - resolution: "pbkdf2@npm:3.1.2" + version: 3.1.3 + resolution: "pbkdf2@npm:3.1.3" dependencies: - create-hash: "npm:^1.1.2" - create-hmac: "npm:^1.1.4" - ripemd160: "npm:^2.0.1" - safe-buffer: "npm:^5.0.1" - sha.js: "npm:^2.4.8" - checksum: 10c0/5a30374e87d33fa080a92734d778cf172542cc7e41b96198c4c88763997b62d7850de3fbda5c3111ddf79805ee7c1da7046881c90ac4920b5e324204518b05fd + create-hash: "npm:~1.1.3" + create-hmac: "npm:^1.1.7" + ripemd160: "npm:=2.0.1" + safe-buffer: "npm:^5.2.1" + sha.js: "npm:^2.4.11" + to-buffer: "npm:^1.2.0" + checksum: 10c0/12779463dfb847701f186e0b7e5fd538a1420409a485dcf5100689c2b3ec3cb113204e82a68668faf3b6dd76ec19260b865313c9d3a9c252807163bdc24652ae languageName: node linkType: hard @@ -14941,6 +14970,16 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard +"ripemd160@npm:=2.0.1": + version: 2.0.1 + resolution: "ripemd160@npm:2.0.1" + dependencies: + hash-base: "npm:^2.0.0" + inherits: "npm:^2.0.1" + checksum: 10c0/d4cbb4713c1268bb35e44815b12e3744a952a72b72e6a72110c8f3932227ddf68841110285fe2ed1c04805e2621d85f905deb5f55f9d91fa1bfc0f8081a244e6 + languageName: node + linkType: hard + "ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1": version: 2.0.2 resolution: "ripemd160@npm:2.0.2" @@ -15324,7 +15363,7 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard -"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8": +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8": version: 2.4.11 resolution: "sha.js@npm:2.4.11" dependencies: @@ -16122,6 +16161,17 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard +"to-buffer@npm:^1.2.0": + version: 1.2.1 + resolution: "to-buffer@npm:1.2.1" + dependencies: + isarray: "npm:^2.0.5" + safe-buffer: "npm:^5.2.1" + typed-array-buffer: "npm:^1.0.3" + checksum: 10c0/bbf07a2a7d6ff9e3ffe503c689176c7149cf3ec25887ce7c4aa5c4841a8845cc71121cd7b4a4769957f823b3f31dbf6b1be6e0a5955798ad864bf2245ee8b5e4 + languageName: node + linkType: hard + "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -16232,6 +16282,17 @@ plugin-image-zoom@flexanalytics/plugin-image-zoom: languageName: node linkType: hard +"typed-array-buffer@npm:^1.0.3": + version: 1.0.3 + resolution: "typed-array-buffer@npm:1.0.3" + dependencies: + call-bound: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + is-typed-array: "npm:^1.1.14" + checksum: 10c0/1105071756eb248774bc71646bfe45b682efcad93b55532c6ffa4518969fb6241354e4aa62af679ae83899ec296d69ef88f1f3763657cdb3a4d29321f7b83079 + languageName: node + linkType: hard + "typedarray-to-buffer@npm:^3.1.5": version: 3.1.5 resolution: "typedarray-to-buffer@npm:3.1.5" From e875604e1d9ac7639b909e42accf9137fb8c4258 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:53:47 +0200 Subject: [PATCH 2/4] chore(deps): update actions/checkout action to v4 (#10192) --- .github/workflows/hypermod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hypermod.yml b/.github/workflows/hypermod.yml index f91a0d82ef..88f52437f7 100644 --- a/.github/workflows/hypermod.yml +++ b/.github/workflows/hypermod.yml @@ -12,7 +12,7 @@ jobs: permissions: write-all runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Run Hypermod CLI uses: hypermod-io/action@v1 with: From 772fe23aa0aaef395a164d50c18c3d29ddb1fce8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 10:37:16 +0200 Subject: [PATCH 3/4] chore(deps): update dependency tsc-watch to v7 (#10197) --- package.json | 2 +- yarn.lock | 32 ++++++++++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 03e5672a81..a0626367ae 100644 --- a/package.json +++ b/package.json @@ -180,7 +180,7 @@ "superagent": "10.2.0", "supertest": "7.0.0", "ts-node": "10.9.2", - "tsc-watch": "6.2.1", + "tsc-watch": "7.1.1", "typescript": "5.8.3", "vite-node": "^3.1.3", "vitest": "^3.1.3", diff --git a/yarn.lock b/yarn.lock index 81f6b6cfc2..e96414cb2a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2727,6 +2727,17 @@ __metadata: languageName: node linkType: hard +"cross-spawn@npm:^7.0.6": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 + languageName: node + linkType: hard + "d@npm:1, d@npm:^1.0.1, d@npm:^1.0.2": version: 1.0.2 resolution: "d@npm:1.0.2" @@ -7125,13 +7136,6 @@ __metadata: languageName: node linkType: hard -"string-argv@npm:^0.3.1": - version: 0.3.1 - resolution: "string-argv@npm:0.3.1" - checksum: 10c0/f59582070f0a4a2d362d8331031f313771ad2b939b223b0593d7765de2689c975e0069186cef65977a29af9deec248c7e480ea4015d153ead754aea5e4bcfe7c - languageName: node - linkType: hard - "string-argv@npm:^0.3.2": version: 0.3.2 resolution: "string-argv@npm:0.3.2" @@ -7557,19 +7561,19 @@ __metadata: languageName: node linkType: hard -"tsc-watch@npm:6.2.1": - version: 6.2.1 - resolution: "tsc-watch@npm:6.2.1" +"tsc-watch@npm:7.1.1": + version: 7.1.1 + resolution: "tsc-watch@npm:7.1.1" dependencies: - cross-spawn: "npm:^7.0.3" + cross-spawn: "npm:^7.0.6" node-cleanup: "npm:^2.1.2" ps-tree: "npm:^1.2.0" - string-argv: "npm:^0.3.1" + string-argv: "npm:^0.3.2" peerDependencies: typescript: "*" bin: tsc-watch: dist/lib/tsc-watch.js - checksum: 10c0/f5fe19e5ac9f4c42a5600c20aee9ff49e282f11813aead65ed58fa11d98a20f5a82bf4f931897270f49f6475dd54e9aab9c46a07c3801b8d237dfbe77bcf1bfc + checksum: 10c0/e69b530c2664213574aa67bb47544cf8d4e55ea46cd2a8929f44d8b8c8a70c3574b9ebe7b1752348690b276ece895f8db86fcf84c1d88be9868eb0705cce2dad languageName: node linkType: hard @@ -7866,7 +7870,7 @@ __metadata: tldts: "npm:7.0.6" ts-node: "npm:10.9.2" ts-toolbelt: "npm:^9.6.0" - tsc-watch: "npm:6.2.1" + tsc-watch: "npm:7.1.1" type-is: "npm:^2.0.0" typescript: "npm:5.8.3" ulidx: "npm:^2.4.1" From 3f9db9195cbfae9e27ffe60fb250facb2dc6f4a4 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Tue, 24 Jun 2025 13:18:06 +0200 Subject: [PATCH 4/4] feat: count created feature links with impact metrics (#10201) --- .../feature-link-service.test.ts | 18 ++++++++++ .../feature-links/feature-link-service.ts | 11 +++++- .../feature-links/feature-link.e2e.test.ts | 12 +------ .../metrics/impact/define-impact-metrics.ts | 10 ++++++ src/lib/server-impl.ts | 2 ++ src/test/fixtures/fake-impact-metrics.ts | 34 +++++++++++++++++++ 6 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 src/lib/features/metrics/impact/define-impact-metrics.ts create mode 100644 src/test/fixtures/fake-impact-metrics.ts diff --git a/src/lib/features/feature-links/feature-link-service.test.ts b/src/lib/features/feature-links/feature-link-service.test.ts index c7d1728299..277cfe992d 100644 --- a/src/lib/features/feature-links/feature-link-service.test.ts +++ b/src/lib/features/feature-links/feature-link-service.test.ts @@ -6,13 +6,21 @@ import { NotFoundError, OperationDeniedError, } from '../../error/index.js'; +import { fakeImpactMetricsResolver } from '../../../test/fixtures/fake-impact-metrics.js'; test('create, update and delete feature link', async () => { + const flagResolver = { impactMetrics: fakeImpactMetricsResolver() }; const { featureLinkStore, featureLinkService } = createFakeFeatureLinkService({ getLogger, + flagResolver, } as unknown as IUnleashConfig); + flagResolver.impactMetrics.defineCounter( + 'feature_link_count', + 'Count of feature links', + ); + const link = await featureLinkService.createLink( 'default', { @@ -29,6 +37,10 @@ test('create, update and delete feature link', async () => { domain: 'example', }); + expect( + flagResolver.impactMetrics.counters.get('feature_link_count')!.value, + ).toBe(1); + const newLink = await featureLinkService.updateLink( { projectId: 'default', linkId: link.id }, { @@ -53,8 +65,10 @@ test('create, update and delete feature link', async () => { }); test('cannot delete/update non existent link', async () => { + const flagResolver = { impactMetrics: fakeImpactMetricsResolver() }; const { featureLinkService } = createFakeFeatureLinkService({ getLogger, + flagResolver, } as unknown as IUnleashConfig); await expect( @@ -77,8 +91,10 @@ test('cannot delete/update non existent link', async () => { }); test('cannot create/update invalid link', async () => { + const flagResolver = { impactMetrics: fakeImpactMetricsResolver() }; const { featureLinkService } = createFakeFeatureLinkService({ getLogger, + flagResolver, } as unknown as IUnleashConfig); await expect( @@ -107,8 +123,10 @@ test('cannot create/update invalid link', async () => { }); test('cannot exceed allowed link count', async () => { + const flagResolver = { impactMetrics: fakeImpactMetricsResolver() }; const { featureLinkService } = createFakeFeatureLinkService({ getLogger, + flagResolver, } as unknown as IUnleashConfig); for (let i = 0; i < 10; i++) { diff --git a/src/lib/features/feature-links/feature-link-service.ts b/src/lib/features/feature-links/feature-link-service.ts index ad9d1715b6..3be6043eb6 100644 --- a/src/lib/features/feature-links/feature-link-service.ts +++ b/src/lib/features/feature-links/feature-link-service.ts @@ -4,6 +4,7 @@ import { FeatureLinkRemovedEvent, FeatureLinkUpdatedEvent, type IAuditUser, + type IFlagResolver, type IUnleashConfig, } from '../../types/index.js'; import type { @@ -18,6 +19,7 @@ import { } from '../../error/index.js'; import normalizeUrl from 'normalize-url'; import { parse } from 'tldts'; +import { FEAUTRE_LINK_COUNT } from '../metrics/impact/define-impact-metrics.js'; interface IFeatureLinkStoreObj { featureLinkStore: IFeatureLinkStore; @@ -27,15 +29,20 @@ export default class FeatureLinkService { private logger: Logger; private featureLinkStore: IFeatureLinkStore; private eventService: EventService; + private flagResolver: IFlagResolver; constructor( stores: IFeatureLinkStoreObj, - { getLogger }: Pick, + { + getLogger, + flagResolver, + }: Pick, eventService: EventService, ) { this.logger = getLogger('feature-links/feature-link-service.ts'); this.featureLinkStore = stores.featureLinkStore; this.eventService = eventService; + this.flagResolver = flagResolver; } async getAll(): Promise { @@ -72,6 +79,8 @@ export default class FeatureLinkService { domain: domainWithoutSuffix, }); + this.flagResolver.impactMetrics?.incrementCounter(FEAUTRE_LINK_COUNT); + await this.eventService.storeEvent( new FeatureLinkAddedEvent({ featureName: newLink.featureName, diff --git a/src/lib/features/feature-links/feature-link.e2e.test.ts b/src/lib/features/feature-links/feature-link.e2e.test.ts index 83a1a3c277..2a11b58c75 100644 --- a/src/lib/features/feature-links/feature-link.e2e.test.ts +++ b/src/lib/features/feature-links/feature-link.e2e.test.ts @@ -19,17 +19,7 @@ let featureLinkReadModel: IFeatureLinksReadModel; beforeAll(async () => { db = await dbInit('feature_link', getLogger); - app = await setupAppWithAuth( - db.stores, - { - experimental: { - flags: { - featureLinks: true, - }, - }, - }, - db.rawDatabase, - ); + app = await setupAppWithAuth(db.stores, {}, db.rawDatabase); eventStore = db.stores.eventStore; featureLinkStore = db.stores.featureLinkStore; featureLinkReadModel = new FeatureLinksReadModel( diff --git a/src/lib/features/metrics/impact/define-impact-metrics.ts b/src/lib/features/metrics/impact/define-impact-metrics.ts new file mode 100644 index 0000000000..68824fbf09 --- /dev/null +++ b/src/lib/features/metrics/impact/define-impact-metrics.ts @@ -0,0 +1,10 @@ +import type { IFlagResolver } from '../../../types/index.js'; + +export const FEAUTRE_LINK_COUNT = 'feature_link_count'; + +export const defineImpactMetrics = (flagResolver: IFlagResolver) => { + flagResolver.impactMetrics?.defineCounter( + FEAUTRE_LINK_COUNT, + 'Count of feature links', + ); +}; diff --git a/src/lib/server-impl.ts b/src/lib/server-impl.ts index 21e2cef94a..48999593a8 100644 --- a/src/lib/server-impl.ts +++ b/src/lib/server-impl.ts @@ -183,6 +183,7 @@ import { testDbPrefix } from '../test/e2e/helpers/database-init.js'; import type { RequestHandler } from 'express'; import { UPDATE_REVISION } from './features/feature-toggle/configuration-revision-service.js'; import type { IFeatureUsageInfo } from './services/version-service.js'; +import { defineImpactMetrics } from './features/metrics/impact/define-impact-metrics.js'; export async function initialServiceSetup( { authentication }: Pick, @@ -232,6 +233,7 @@ export async function createApp( scheduleServices(services, config); } + defineImpactMetrics(config.flagResolver); const metricsMonitor = fm.createMetricsMonitor(); const unleashSession = fm.createSessionDb(config, db); diff --git a/src/test/fixtures/fake-impact-metrics.ts b/src/test/fixtures/fake-impact-metrics.ts new file mode 100644 index 0000000000..440e79554d --- /dev/null +++ b/src/test/fixtures/fake-impact-metrics.ts @@ -0,0 +1,34 @@ +export const fakeImpactMetricsResolver = () => ({ + counters: new Map(), + gauges: new Map(), + + defineCounter(name: string, help: string) { + this.counters.set(name, { value: 0, help }); + }, + + defineGauge(name: string, help: string) { + this.gauges.set(name, { value: 0, help }); + }, + + incrementCounter(name: string, value: number = 1) { + const counter = this.counters.get(name); + + if (!counter) { + return; + } + + counter.value += value; + this.counters.set(name, counter); + }, + + updateGauge(name: string, value: number) { + const gauge = this.gauges.get(name); + + if (!gauge) { + return; + } + + gauge.value = value; + this.gauges.set(name, gauge); + }, +});