mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
Make id required on IConstraint.
Enforce setting [constraintId] on incoming constraints
This commit is contained in:
parent
c394800a19
commit
71c9a53380
@ -9,7 +9,6 @@ import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsLis
|
||||
import { EditableConstraint } from 'component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/EditableConstraint';
|
||||
import { createEmptyConstraint } from '../../../../utils/createEmptyConstraint.ts';
|
||||
import { constraintId } from 'constants/constraintId.ts';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
export interface IEditableConstraintsListRef {
|
||||
addConstraint?: (contextName: string) => void;
|
||||
}
|
||||
@ -44,7 +43,6 @@ export const EditableConstraintsList = forwardRef<
|
||||
if (!constraints.every((constraint) => constraintId in constraint)) {
|
||||
setConstraints(
|
||||
constraints.map((constraint) => ({
|
||||
[constraintId]: uuidv4(),
|
||||
...constraint,
|
||||
})),
|
||||
);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { createEmptyConstraint } from 'utils/createEmptyConstraint';
|
||||
import { fromIConstraint, toIConstraint } from './editable-constraint-type.ts';
|
||||
import { constraintId } from 'constants/constraintId';
|
||||
import type { IConstraint } from 'interfaces/strategy.ts';
|
||||
|
||||
test('mapping to and from retains the constraint id', () => {
|
||||
const constraint = createEmptyConstraint('context');
|
||||
@ -11,18 +10,6 @@ test('mapping to and from retains the constraint id', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('mapping to an editable constraint adds a constraint id if there is none', () => {
|
||||
const constraint: IConstraint = createEmptyConstraint('context');
|
||||
delete constraint[constraintId];
|
||||
|
||||
const editableConstraint = fromIConstraint(constraint);
|
||||
|
||||
expect(editableConstraint[constraintId]).toBeDefined();
|
||||
|
||||
const iConstraint = toIConstraint(editableConstraint);
|
||||
expect(iConstraint[constraintId]).toEqual(editableConstraint[constraintId]);
|
||||
});
|
||||
|
||||
test('mapping from an empty constraint removes redundant value / values', () => {
|
||||
const constraint = { ...createEmptyConstraint('context'), value: '' };
|
||||
expect(constraint).toHaveProperty('value');
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { constraintId } from 'constants/constraintId';
|
||||
import {
|
||||
type DateOperator,
|
||||
isDateOperator,
|
||||
@ -11,7 +10,6 @@ import {
|
||||
isSemVerOperator,
|
||||
} from 'constants/operators';
|
||||
import type { IConstraint } from 'interfaces/strategy';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
type EditableConstraintBase = Omit<
|
||||
IConstraint,
|
||||
@ -74,14 +72,12 @@ export const fromIConstraint = (
|
||||
const { value, values, operator, ...rest } = constraint;
|
||||
if (isSingleValueOperator(operator)) {
|
||||
return {
|
||||
[constraintId]: uuidv4(),
|
||||
...rest,
|
||||
operator,
|
||||
value: value ?? '',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
[constraintId]: uuidv4(),
|
||||
...rest,
|
||||
operator,
|
||||
values: new Set(values),
|
||||
|
@ -13,6 +13,7 @@ import { vi } from 'vitest';
|
||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||
import type { ContextFieldSchema } from 'openapi';
|
||||
import { NUM_EQ } from '@server/util/constants';
|
||||
import { constraintId } from 'constants/constraintId.js';
|
||||
|
||||
const server = testServerSetup();
|
||||
|
||||
@ -25,6 +26,7 @@ test('calls onUpdate with new state', async () => {
|
||||
contextName: 'context-field',
|
||||
operator: NOT_IN,
|
||||
values: ['A', 'B'],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const onUpdate = vi.fn();
|
||||
@ -71,6 +73,7 @@ describe('validators', () => {
|
||||
contextName: 'context-field',
|
||||
operator: operator,
|
||||
value: '',
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
@ -94,6 +97,7 @@ describe('validators', () => {
|
||||
contextName: 'context-field',
|
||||
operator: operator,
|
||||
value: '',
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
@ -117,6 +121,7 @@ describe('validators', () => {
|
||||
contextName: 'context-field',
|
||||
operator: operator,
|
||||
value: '',
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
@ -140,6 +145,7 @@ describe('validators', () => {
|
||||
contextName: 'context-field',
|
||||
operator: operator,
|
||||
values: [],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
@ -162,6 +168,7 @@ describe('validators', () => {
|
||||
contextName: 'context-field',
|
||||
operator: operator,
|
||||
values: ['a', 'b'],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
@ -189,6 +196,7 @@ describe('legal values', () => {
|
||||
contextName: definition.name,
|
||||
operator: IN,
|
||||
values: [],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
@ -206,6 +214,7 @@ describe('legal values', () => {
|
||||
contextName: definition.name,
|
||||
operator: IN,
|
||||
values: [],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
@ -231,6 +240,7 @@ describe('legal values', () => {
|
||||
contextName: 'field-with-no-legal-values',
|
||||
operator: IN,
|
||||
values: [],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
@ -244,6 +254,7 @@ describe('legal values', () => {
|
||||
contextName: definition.name,
|
||||
operator: IN,
|
||||
values: ['A', 'B'],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
@ -260,6 +271,7 @@ describe('legal values', () => {
|
||||
contextName: definition.name,
|
||||
operator: NUM_EQ,
|
||||
values: [],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
|
@ -6,6 +6,7 @@ import { renderHook, act } from '@testing-library/react';
|
||||
import type { IConstraint } from 'interfaces/strategy';
|
||||
import { IN, STR_CONTAINS } from 'constants/operators';
|
||||
import type { Operator } from 'constants/operators';
|
||||
import { constraintId } from 'constants/constraintId.ts';
|
||||
|
||||
const createTestConstraint = (
|
||||
contextName: string,
|
||||
@ -15,6 +16,7 @@ const createTestConstraint = (
|
||||
contextName,
|
||||
operator,
|
||||
values,
|
||||
[constraintId]: 'constraint id',
|
||||
});
|
||||
|
||||
describe('areConstraintsEqual', () => {
|
||||
@ -24,6 +26,7 @@ describe('areConstraintsEqual', () => {
|
||||
operator: IN,
|
||||
values: ['user1', 'user2'],
|
||||
inverted: false,
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const constraint2: IConstraint = {
|
||||
@ -31,6 +34,7 @@ describe('areConstraintsEqual', () => {
|
||||
operator: IN,
|
||||
values: ['user1', 'user2'],
|
||||
inverted: false,
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
expect(areConstraintsEqual(constraint1, constraint2)).toBe(true);
|
||||
@ -41,12 +45,14 @@ describe('areConstraintsEqual', () => {
|
||||
contextName: 'userId',
|
||||
operator: IN,
|
||||
values: ['user1', 'user2', 'user3'],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const constraint2: IConstraint = {
|
||||
contextName: 'userId',
|
||||
operator: IN,
|
||||
values: ['user2', 'user3', 'user1'],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
expect(areConstraintsEqual(constraint1, constraint2)).toBe(true);
|
||||
@ -57,12 +63,14 @@ describe('areConstraintsEqual', () => {
|
||||
contextName: 'userId',
|
||||
operator: IN,
|
||||
values: ['user1', 'user2'],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const constraint2: IConstraint = {
|
||||
contextName: 'userId',
|
||||
operator: IN,
|
||||
values: ['user1', 'user3'],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
expect(areConstraintsEqual(constraint1, constraint2)).toBe(false);
|
||||
@ -73,12 +81,14 @@ describe('areConstraintsEqual', () => {
|
||||
contextName: 'userId',
|
||||
operator: IN,
|
||||
values: ['user1'],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
const constraint2: IConstraint = {
|
||||
contextName: 'userId',
|
||||
operator: STR_CONTAINS,
|
||||
values: ['user1'],
|
||||
[constraintId]: 'constraint id',
|
||||
};
|
||||
|
||||
expect(areConstraintsEqual(constraint1, constraint2)).toBe(false);
|
||||
|
@ -1,8 +1,6 @@
|
||||
import type { IConstraint } from 'interfaces/strategy';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSegmentValidation } from 'hooks/api/getters/useSegmentValidation/useSegmentValidation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { constraintId } from 'constants/constraintId';
|
||||
|
||||
export const useSegmentForm = (
|
||||
initialName = '',
|
||||
@ -14,7 +12,6 @@ export const useSegmentForm = (
|
||||
const [description, setDescription] = useState(initialDescription);
|
||||
const [project, setProject] = useState<string | undefined>(initialProject);
|
||||
const initialConstraintsWithId = initialConstraints.map((constraint) => ({
|
||||
[constraintId]: uuidv4(),
|
||||
...constraint,
|
||||
}));
|
||||
const [constraints, setConstraints] = useState<IConstraint[]>(
|
||||
|
@ -3,6 +3,7 @@ import type { IConstraint } from 'interfaces/strategy'; // Assuming you have you
|
||||
import type { FC } from 'react';
|
||||
import { useConstraintsValidation } from './useConstraintsValidation.ts';
|
||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||
import { constraintId } from 'constants/constraintId.ts';
|
||||
|
||||
const server = testServerSetup();
|
||||
|
||||
@ -26,12 +27,14 @@ test('should display Valid when constraints are valid', async () => {
|
||||
values: ['test'],
|
||||
operator: 'IN',
|
||||
contextName: 'irrelevant',
|
||||
[constraintId]: 'constraint id',
|
||||
},
|
||||
{
|
||||
value: 'test',
|
||||
values: ['test'],
|
||||
operator: 'IN',
|
||||
contextName: 'irrelevant',
|
||||
[constraintId]: 'constraint id 2',
|
||||
},
|
||||
];
|
||||
|
||||
@ -42,12 +45,19 @@ test('should display Valid when constraints are valid', async () => {
|
||||
|
||||
test('should display Invalid when constraints are invalid', async () => {
|
||||
const emptyValueAndValues: IConstraint[] = [
|
||||
{ value: '', values: [], operator: 'IN', contextName: 'irrelevant' },
|
||||
{
|
||||
value: '',
|
||||
values: [],
|
||||
operator: 'IN',
|
||||
contextName: 'irrelevant',
|
||||
[constraintId]: 'constraint id 3',
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
values: [],
|
||||
operator: 'IN',
|
||||
contextName: 'irrelevant',
|
||||
[constraintId]: 'constraint id 4',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -65,7 +65,7 @@ export interface IConstraint {
|
||||
caseInsensitive?: boolean;
|
||||
operator: Operator;
|
||||
contextName: string;
|
||||
[constraintId]?: string;
|
||||
[constraintId]: string;
|
||||
}
|
||||
|
||||
export interface IFeatureStrategySortOrder {
|
||||
|
@ -9,6 +9,7 @@ test('keys are ordered in the expected order', () => {
|
||||
operator: 'STR_CONTAINS',
|
||||
contextName: 'context',
|
||||
caseInsensitive: true,
|
||||
[constraintId]: 'constraint-id',
|
||||
};
|
||||
|
||||
const output = serializeConstraint(input);
|
||||
@ -30,6 +31,7 @@ test('only value OR values is present, not both', () => {
|
||||
operator: 'IN',
|
||||
contextName: 'context',
|
||||
caseInsensitive: true,
|
||||
[constraintId]: 'constraint-id',
|
||||
};
|
||||
|
||||
const noValue = serializeConstraint(input);
|
||||
|
@ -1,6 +1,9 @@
|
||||
import type { constraintId } from 'constants/constraintId';
|
||||
import { isSingleValueOperator } from 'constants/operators';
|
||||
import type { IConstraint } from 'interfaces/strategy';
|
||||
|
||||
type SerializedConstraint = Omit<IConstraint, typeof constraintId>;
|
||||
|
||||
export const serializeConstraint = ({
|
||||
value,
|
||||
values,
|
||||
@ -8,10 +11,10 @@ export const serializeConstraint = ({
|
||||
operator,
|
||||
contextName,
|
||||
caseInsensitive,
|
||||
}: IConstraint): IConstraint => {
|
||||
}: IConstraint): SerializedConstraint => {
|
||||
const makeConstraint = (
|
||||
valueProp: { value: string } | { values: string[] },
|
||||
): IConstraint => {
|
||||
): SerializedConstraint => {
|
||||
return {
|
||||
contextName,
|
||||
operator,
|
||||
|
Loading…
Reference in New Issue
Block a user