mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-21 13:47:39 +02:00
feat: validate strategies
This commit is contained in:
parent
d28c046e51
commit
50408cd63b
@ -165,6 +165,14 @@ class FeatureToggleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async validateConstraints(constraints: IConstraint[]): Promise<void> {
|
||||||
|
const validations = constraints.map((constraint) => {
|
||||||
|
return this.validateConstraint(constraint);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(validations);
|
||||||
|
}
|
||||||
|
|
||||||
async validateConstraint(constraint: IConstraint): Promise<void> {
|
async validateConstraint(constraint: IConstraint): Promise<void> {
|
||||||
const { operator } = constraint;
|
const { operator } = constraint;
|
||||||
await constraintSchema.validateAsync(constraint);
|
await constraintSchema.validateAsync(constraint);
|
||||||
@ -272,6 +280,11 @@ class FeatureToggleService {
|
|||||||
): Promise<IStrategyConfig> {
|
): Promise<IStrategyConfig> {
|
||||||
const { featureName, projectId, environment } = context;
|
const { featureName, projectId, environment } = context;
|
||||||
await this.validateFeatureContext(context);
|
await this.validateFeatureContext(context);
|
||||||
|
|
||||||
|
if (strategyConfig.constraints.length > 0) {
|
||||||
|
await this.validateConstraints(strategyConfig.constraints);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newFeatureStrategy =
|
const newFeatureStrategy =
|
||||||
await this.featureStrategiesStore.createStrategyFeatureEnv({
|
await this.featureStrategiesStore.createStrategyFeatureEnv({
|
||||||
@ -334,6 +347,10 @@ class FeatureToggleService {
|
|||||||
updates,
|
updates,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (updates.constraints?.length > 0) {
|
||||||
|
await this.validateConstraints(updates.constraints);
|
||||||
|
}
|
||||||
|
|
||||||
// Store event!
|
// Store event!
|
||||||
const tags = await this.tagStore.getAllTagsForFeature(featureName);
|
const tags = await this.tagStore.getAllTagsForFeature(featureName);
|
||||||
const data = this.featureStrategyToPublic(strategy);
|
const data = this.featureStrategyToPublic(strategy);
|
||||||
|
@ -39,6 +39,19 @@ test('semver validation should fail partial semver', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('semver validation should fail with leading v', () => {
|
||||||
|
const leadingV = 'v1.2.0';
|
||||||
|
expect.assertions(1);
|
||||||
|
|
||||||
|
try {
|
||||||
|
validateSemver(leadingV);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toBe(
|
||||||
|
`the provided value is not a valid semver format. The value provided was: ${leadingV}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/* Legal values tests */
|
/* Legal values tests */
|
||||||
test('should fail validation if value does not exist in single legal value', () => {
|
test('should fail validation if value does not exist in single legal value', () => {
|
||||||
const legalValues = ['100', '200', '300'];
|
const legalValues = ['100', '200', '300'];
|
||||||
|
@ -16,9 +16,11 @@ export const validateString = async (value: unknown): Promise<void> => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const validateSemver = (value: unknown): void => {
|
export const validateSemver = (value: unknown): void => {
|
||||||
|
const cleanValue = semver.clean(value) === value;
|
||||||
|
|
||||||
const result = semver.valid(value);
|
const result = semver.valid(value);
|
||||||
|
|
||||||
if (result) return;
|
if (result && cleanValue) return;
|
||||||
throw new BadDataError(
|
throw new BadDataError(
|
||||||
`the provided value is not a valid semver format. The value provided was: ${value}`,
|
`the provided value is not a valid semver format. The value provided was: ${value}`,
|
||||||
);
|
);
|
||||||
|
@ -149,3 +149,42 @@ test('should ignore name in the body when updating feature toggle', async () =>
|
|||||||
expect(featureOne.description).toBe(`I'm changed`);
|
expect(featureOne.description).toBe(`I'm changed`);
|
||||||
expect(featureTwo.description).toBe('Second toggle');
|
expect(featureTwo.description).toBe('Second toggle');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Should validate co', async () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const projectId = 'default';
|
||||||
|
const username = 'co-strategy';
|
||||||
|
const featureName = 'co-validation';
|
||||||
|
const config: Omit<IStrategyConfig, 'id'> = {
|
||||||
|
name: 'default',
|
||||||
|
constraints: [
|
||||||
|
{
|
||||||
|
operator: 'INVALID',
|
||||||
|
// @ts-expect-error
|
||||||
|
values: NaN,
|
||||||
|
contextName: 'Hello',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
parameters: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
await service.createFeatureToggle(
|
||||||
|
projectId,
|
||||||
|
{
|
||||||
|
name: featureName,
|
||||||
|
},
|
||||||
|
'test',
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await service.createStrategy(
|
||||||
|
config,
|
||||||
|
{ projectId, featureName, environment: DEFAULT_ENV },
|
||||||
|
username,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.details[0].message).toBe(
|
||||||
|
'"operator" must be one of [NOT_IN, IN, STR_ENDS_WITH, STR_STARTS_WITH, STR_CONTAINS, NUM_EQ, NUM_GT, NUM_GTE, NUM_LT, NUM_LTE, DATE_AFTER, DATE_BEFORE, SEMVER_EQ, SEMVER_GT, SEMVER_LT]',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user