mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-31 00:16:47 +01:00
feat: draft branch playground evaluation (#3967)
This commit is contained in:
parent
7d69750f3b
commit
555f77463d
@ -137,6 +137,7 @@
|
|||||||
"json2csv": "^5.0.7",
|
"json2csv": "^5.0.7",
|
||||||
"knex": "^2.4.2",
|
"knex": "^2.4.2",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
|
"lodash.groupby": "^4.6.0",
|
||||||
"log4js": "^6.0.0",
|
"log4js": "^6.0.0",
|
||||||
"make-fetch-happen": "^11.0.0",
|
"make-fetch-happen": "^11.0.0",
|
||||||
"memoizee": "^0.4.15",
|
"memoizee": "^0.4.15",
|
||||||
@ -175,6 +176,7 @@
|
|||||||
"@types/hash-sum": "^1.0.0",
|
"@types/hash-sum": "^1.0.0",
|
||||||
"@types/jest": "29.5.2",
|
"@types/jest": "29.5.2",
|
||||||
"@types/js-yaml": "4.0.5",
|
"@types/js-yaml": "4.0.5",
|
||||||
|
"@types/lodash.groupby": "4.6.7",
|
||||||
"@types/make-fetch-happen": "10.0.1",
|
"@types/make-fetch-happen": "10.0.1",
|
||||||
"@types/memoizee": "0.4.8",
|
"@types/memoizee": "0.4.8",
|
||||||
"@types/mime": "3.0.1",
|
"@types/mime": "3.0.1",
|
||||||
|
@ -11,12 +11,20 @@ import { FeatureStrategiesEvaluationResult } from 'lib/features/playground/featu
|
|||||||
import { ISegmentService } from 'lib/segments/segment-service-interface';
|
import { ISegmentService } from 'lib/segments/segment-service-interface';
|
||||||
import { FeatureConfigurationClient } from '../../types/stores/feature-strategies-store';
|
import { FeatureConfigurationClient } from '../../types/stores/feature-strategies-store';
|
||||||
import { generateObjectCombinations } from './generateObjectCombinations';
|
import { generateObjectCombinations } from './generateObjectCombinations';
|
||||||
|
import groupBy from 'lodash.groupby';
|
||||||
|
import { omitKeys } from '../../util';
|
||||||
|
|
||||||
type EvaluationInput = {
|
type EvaluationInput = {
|
||||||
features: FeatureConfigurationClient[];
|
features: FeatureConfigurationClient[];
|
||||||
segments: ISegment[];
|
segments: ISegment[];
|
||||||
featureProject: Record<string, string>;
|
featureProject: Record<string, string>;
|
||||||
context: SdkContextSchema;
|
context: SdkContextSchema;
|
||||||
|
environment: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AdvancedPlaygroundFeatureSchema = PlaygroundFeatureSchema & {
|
||||||
|
context: SdkContextSchema;
|
||||||
|
environment: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class PlaygroundService {
|
export class PlaygroundService {
|
||||||
@ -42,7 +50,7 @@ export class PlaygroundService {
|
|||||||
projects: typeof ALL | string[],
|
projects: typeof ALL | string[],
|
||||||
environments: string[],
|
environments: string[],
|
||||||
context: SdkContextSchema,
|
context: SdkContextSchema,
|
||||||
): Promise<PlaygroundFeatureSchema[]> {
|
): Promise<any> {
|
||||||
const segments = await this.segmentService.getActive();
|
const segments = await this.segmentService.getActive();
|
||||||
const environmentFeatures = await Promise.all(
|
const environmentFeatures = await Promise.all(
|
||||||
environments.map((env) => this.resolveFeatures(projects, env)),
|
environments.map((env) => this.resolveFeatures(projects, env)),
|
||||||
@ -50,18 +58,32 @@ export class PlaygroundService {
|
|||||||
const contexts = generateObjectCombinations(context);
|
const contexts = generateObjectCombinations(context);
|
||||||
|
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
environmentFeatures.flatMap(({ features, featureProject }) =>
|
environmentFeatures.flatMap(
|
||||||
|
({ features, featureProject, environment }) =>
|
||||||
contexts.map((singleContext) =>
|
contexts.map((singleContext) =>
|
||||||
this.evaluate({
|
this.evaluate({
|
||||||
features,
|
features,
|
||||||
featureProject,
|
featureProject,
|
||||||
context: singleContext,
|
context: singleContext,
|
||||||
segments,
|
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({
|
private async evaluate({
|
||||||
@ -69,7 +91,8 @@ export class PlaygroundService {
|
|||||||
features,
|
features,
|
||||||
segments,
|
segments,
|
||||||
context,
|
context,
|
||||||
}: EvaluationInput): Promise<PlaygroundFeatureSchema[]> {
|
environment,
|
||||||
|
}: EvaluationInput): Promise<AdvancedPlaygroundFeatureSchema[]> {
|
||||||
const [head, ...rest] = features;
|
const [head, ...rest] = features;
|
||||||
if (!head) {
|
if (!head) {
|
||||||
return [];
|
return [];
|
||||||
@ -92,7 +115,7 @@ export class PlaygroundService {
|
|||||||
? new Date(context.currentTime)
|
? new Date(context.currentTime)
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
const output: PlaygroundFeatureSchema[] = client
|
return client
|
||||||
.getFeatureToggleDefinitions()
|
.getFeatureToggleDefinitions()
|
||||||
.map((feature: FeatureInterface) => {
|
.map((feature: FeatureInterface) => {
|
||||||
const strategyEvaluationResult: FeatureStrategiesEvaluationResult =
|
const strategyEvaluationResult: FeatureStrategiesEvaluationResult =
|
||||||
@ -112,18 +135,22 @@ export class PlaygroundService {
|
|||||||
projectId: featureProject[feature.name],
|
projectId: featureProject[feature.name],
|
||||||
variant: client.getVariant(feature.name, clientContext),
|
variant: client.getVariant(feature.name, clientContext),
|
||||||
name: feature.name,
|
name: feature.name,
|
||||||
|
environment,
|
||||||
|
context,
|
||||||
variants: variantsMap[feature.name] || [],
|
variants: variantsMap[feature.name] || [],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async resolveFeatures(
|
private async resolveFeatures(
|
||||||
projects: typeof ALL | string[],
|
projects: typeof ALL | string[],
|
||||||
environment: string,
|
environment: string,
|
||||||
): Promise<Pick<EvaluationInput, 'features' | 'featureProject'>> {
|
): Promise<
|
||||||
|
Pick<EvaluationInput, 'features' | 'featureProject'> & {
|
||||||
|
environment: string;
|
||||||
|
}
|
||||||
|
> {
|
||||||
const features = await this.featureToggleService.getClientFeatures(
|
const features = await this.featureToggleService.getClientFeatures(
|
||||||
{
|
{
|
||||||
project: projects === ALL ? undefined : projects,
|
project: projects === ALL ? undefined : projects,
|
||||||
@ -139,7 +166,7 @@ export class PlaygroundService {
|
|||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
return { features, featureProject };
|
return { features, featureProject, environment };
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluateQuery(
|
async evaluateQuery(
|
||||||
@ -152,6 +179,13 @@ export class PlaygroundService {
|
|||||||
this.segmentService.getActive(),
|
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'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import {
|
|||||||
} from '../../openapi/spec/playground-response-schema';
|
} from '../../openapi/spec/playground-response-schema';
|
||||||
import { PlaygroundRequestSchema } from '../../openapi/spec/playground-request-schema';
|
import { PlaygroundRequestSchema } from '../../openapi/spec/playground-request-schema';
|
||||||
import { PlaygroundService } from './playground-service';
|
import { PlaygroundService } from './playground-service';
|
||||||
import { fixedAdvancedPlaygroundResponse } from './hardcodedReponse';
|
|
||||||
import { IFlagResolver } from '../../types';
|
import { IFlagResolver } from '../../types';
|
||||||
|
|
||||||
export default class PlaygroundController extends Controller {
|
export default class PlaygroundController extends Controller {
|
||||||
@ -90,7 +89,14 @@ export default class PlaygroundController extends Controller {
|
|||||||
res: Response<any>,
|
res: Response<any>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (this.flagResolver.isEnabled('advancedPlayground')) {
|
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 {
|
} else {
|
||||||
res.status(409).end();
|
res.status(409).end();
|
||||||
}
|
}
|
||||||
|
17
yarn.lock
17
yarn.lock
@ -1316,6 +1316,18 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||||
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
|
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":
|
"@types/make-fetch-happen@10.0.1":
|
||||||
version "10.0.1"
|
version "10.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/make-fetch-happen/-/make-fetch-happen-10.0.1.tgz#9e718d8f5f6ed388e2020bb9b4fbd2dc23009b38"
|
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"
|
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||||
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
|
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:
|
lodash.isequal@^4.5.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||||
|
Loading…
Reference in New Issue
Block a user