diff --git a/package.json b/package.json index 2887f66b6d..b97eb654bc 100644 --- a/package.json +++ b/package.json @@ -137,6 +137,7 @@ "json2csv": "^5.0.7", "knex": "^2.4.2", "lodash.get": "^4.4.2", + "lodash.groupby": "^4.6.0", "log4js": "^6.0.0", "make-fetch-happen": "^11.0.0", "memoizee": "^0.4.15", @@ -175,6 +176,7 @@ "@types/hash-sum": "^1.0.0", "@types/jest": "29.5.2", "@types/js-yaml": "4.0.5", + "@types/lodash.groupby": "4.6.7", "@types/make-fetch-happen": "10.0.1", "@types/memoizee": "0.4.8", "@types/mime": "3.0.1", diff --git a/src/lib/features/playground/playground-service.ts b/src/lib/features/playground/playground-service.ts index 17762c44da..d1b6d83b4c 100644 --- a/src/lib/features/playground/playground-service.ts +++ b/src/lib/features/playground/playground-service.ts @@ -11,12 +11,20 @@ import { FeatureStrategiesEvaluationResult } from 'lib/features/playground/featu import { ISegmentService } from 'lib/segments/segment-service-interface'; import { FeatureConfigurationClient } from '../../types/stores/feature-strategies-store'; import { generateObjectCombinations } from './generateObjectCombinations'; +import groupBy from 'lodash.groupby'; +import { omitKeys } from '../../util'; type EvaluationInput = { features: FeatureConfigurationClient[]; segments: ISegment[]; featureProject: Record; context: SdkContextSchema; + environment: string; +}; + +type AdvancedPlaygroundFeatureSchema = PlaygroundFeatureSchema & { + context: SdkContextSchema; + environment: string; }; export class PlaygroundService { @@ -42,7 +50,7 @@ export class PlaygroundService { projects: typeof ALL | string[], environments: string[], context: SdkContextSchema, - ): Promise { + ): Promise { const segments = await this.segmentService.getActive(); const environmentFeatures = await Promise.all( environments.map((env) => this.resolveFeatures(projects, env)), @@ -50,18 +58,32 @@ export class PlaygroundService { const contexts = generateObjectCombinations(context); const results = await Promise.all( - environmentFeatures.flatMap(({ features, featureProject }) => - contexts.map((singleContext) => - this.evaluate({ - features, - featureProject, - context: singleContext, - segments, - }), - ), + environmentFeatures.flatMap( + ({ features, featureProject, environment }) => + contexts.map((singleContext) => + this.evaluate({ + features, + featureProject, + context: singleContext, + segments, + environment, + }), + ), ), ); - return results.flat(); + const items = results.flat(); + const itemsByName = groupBy(items, (item) => item.name); + return Object.entries(itemsByName).map(([name, entries]) => { + const groupedEnvironments = groupBy( + entries, + (entry) => entry.environment, + ); + return { + name, + projectId: entries[0]?.projectId, + environments: groupedEnvironments, + }; + }); } private async evaluate({ @@ -69,7 +91,8 @@ export class PlaygroundService { features, segments, context, - }: EvaluationInput): Promise { + environment, + }: EvaluationInput): Promise { const [head, ...rest] = features; if (!head) { return []; @@ -92,7 +115,7 @@ export class PlaygroundService { ? new Date(context.currentTime) : undefined, }; - const output: PlaygroundFeatureSchema[] = client + return client .getFeatureToggleDefinitions() .map((feature: FeatureInterface) => { const strategyEvaluationResult: FeatureStrategiesEvaluationResult = @@ -112,18 +135,22 @@ export class PlaygroundService { projectId: featureProject[feature.name], variant: client.getVariant(feature.name, clientContext), name: feature.name, + environment, + context, variants: variantsMap[feature.name] || [], }; }); - - return output; } } private async resolveFeatures( projects: typeof ALL | string[], environment: string, - ): Promise> { + ): Promise< + Pick & { + environment: string; + } + > { const features = await this.featureToggleService.getClientFeatures( { project: projects === ALL ? undefined : projects, @@ -139,7 +166,7 @@ export class PlaygroundService { }, {}, ); - return { features, featureProject }; + return { features, featureProject, environment }; } async evaluateQuery( @@ -152,6 +179,13 @@ export class PlaygroundService { this.segmentService.getActive(), ]); - return this.evaluate({ features, featureProject, segments, context }); + const result = await this.evaluate({ + features, + featureProject, + segments, + context, + environment, + }); + return result.map((item) => omitKeys(item, 'environment', 'context')); } } diff --git a/src/lib/features/playground/playground.ts b/src/lib/features/playground/playground.ts index f3b387b6f4..22f18292de 100644 --- a/src/lib/features/playground/playground.ts +++ b/src/lib/features/playground/playground.ts @@ -14,7 +14,6 @@ import { } from '../../openapi/spec/playground-response-schema'; import { PlaygroundRequestSchema } from '../../openapi/spec/playground-request-schema'; import { PlaygroundService } from './playground-service'; -import { fixedAdvancedPlaygroundResponse } from './hardcodedReponse'; import { IFlagResolver } from '../../types'; export default class PlaygroundController extends Controller { @@ -90,7 +89,14 @@ export default class PlaygroundController extends Controller { res: Response, ): Promise { if (this.flagResolver.isEnabled('advancedPlayground')) { - res.json(fixedAdvancedPlaygroundResponse); + res.json({ + input: req.body, + features: await this.playgroundService.evaluateAdvancedQuery( + req.body.projects || '*', + req.body.environments, + req.body.context, + ), + }); } else { res.status(409).end(); } diff --git a/yarn.lock b/yarn.lock index 5edfdc8990..47c20169d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1316,6 +1316,18 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/lodash.groupby@4.6.7": + version "4.6.7" + resolved "https://registry.yarnpkg.com/@types/lodash.groupby/-/lodash.groupby-4.6.7.tgz#35fdb9647f100450d1004f65f74cbd964cdb567a" + integrity sha512-dFUR1pqdMgjIBbgPJ/8axJX6M1C7zsL+HF4qdYMQeJ7XOp0Qbf37I3zh9gpXr/ks6tgEYPDRqyZRAnFYvewYHQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.195" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" + integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg== + "@types/make-fetch-happen@10.0.1": version "10.0.1" resolved "https://registry.yarnpkg.com/@types/make-fetch-happen/-/make-fetch-happen-10.0.1.tgz#9e718d8f5f6ed388e2020bb9b4fbd2dc23009b38" @@ -5040,6 +5052,11 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== +lodash.groupby@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1" + integrity sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw== + lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"