mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fixed segments not being copied (#2105)
* fixed segments not being copied * fix fmt * bug fix * return segmentId[] when getting a feature strategy * do not return segments if they are not there * Update src/lib/services/feature-toggle-service.ts Co-authored-by: Fredrik Strand Oseberg <fredrik.no@gmail.com> * fix lint * fix: more explicit column sorting and bug fix * update snapshot * rollback * add segment ids to feature strategies * bug fix Co-authored-by: Fredrik Strand Oseberg <fredrik.no@gmail.com>
This commit is contained in:
		
							parent
							
								
									10eb500360
								
							
						
					
					
						commit
						64b8df7ee0
					
				@ -12,6 +12,8 @@ import { useFeatureImmutable } from 'hooks/api/getters/useFeature/useFeatureImmu
 | 
				
			|||||||
import { getFeatureStrategyIcon } from 'utils/strategyNames';
 | 
					import { getFeatureStrategyIcon } from 'utils/strategyNames';
 | 
				
			||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
import { CopyButton } from './CopyButton/CopyButton';
 | 
					import { CopyButton } from './CopyButton/CopyButton';
 | 
				
			||||||
 | 
					import { useSegments } from '../../../../hooks/api/getters/useSegments/useSegments';
 | 
				
			||||||
 | 
					import { IFeatureStrategyPayload } from '../../../../interfaces/strategy';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IFeatureStrategyEmptyProps {
 | 
					interface IFeatureStrategyEmptyProps {
 | 
				
			||||||
    projectId: string;
 | 
					    projectId: string;
 | 
				
			||||||
@ -65,6 +67,7 @@ export const FeatureStrategyEmpty = ({
 | 
				
			|||||||
                    const { id, ...strategyCopy } = {
 | 
					                    const { id, ...strategyCopy } = {
 | 
				
			||||||
                        ...strategy,
 | 
					                        ...strategy,
 | 
				
			||||||
                        environment: environmentId,
 | 
					                        environment: environmentId,
 | 
				
			||||||
 | 
					                        copyOf: strategy.id,
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return addStrategyToFeature(
 | 
					                    return addStrategyToFeature(
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ import {
 | 
				
			|||||||
    Tooltip,
 | 
					    Tooltip,
 | 
				
			||||||
} from '@mui/material';
 | 
					} from '@mui/material';
 | 
				
			||||||
import { AddToPhotos as CopyIcon, Lock } from '@mui/icons-material';
 | 
					import { AddToPhotos as CopyIcon, Lock } from '@mui/icons-material';
 | 
				
			||||||
import { IFeatureStrategy } from 'interfaces/strategy';
 | 
					import { IFeatureStrategy, IFeatureStrategyPayload } from 'interfaces/strategy';
 | 
				
			||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
 | 
					import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
 | 
				
			||||||
import { IFeatureEnvironment } from 'interfaces/featureToggle';
 | 
					import { IFeatureEnvironment } from 'interfaces/featureToggle';
 | 
				
			||||||
import AccessContext from 'contexts/AccessContext';
 | 
					import AccessContext from 'contexts/AccessContext';
 | 
				
			||||||
@ -19,6 +19,7 @@ import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFe
 | 
				
			|||||||
import useToast from 'hooks/useToast';
 | 
					import useToast from 'hooks/useToast';
 | 
				
			||||||
import { useFeatureImmutable } from 'hooks/api/getters/useFeature/useFeatureImmutable';
 | 
					import { useFeatureImmutable } from 'hooks/api/getters/useFeature/useFeatureImmutable';
 | 
				
			||||||
import { formatUnknownError } from 'utils/formatUnknownError';
 | 
					import { formatUnknownError } from 'utils/formatUnknownError';
 | 
				
			||||||
 | 
					import { useSegments } from '../../../../../../../../../../hooks/api/getters/useSegments/useSegments';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ICopyStrategyIconMenuProps {
 | 
					interface ICopyStrategyIconMenuProps {
 | 
				
			||||||
    environments: IFeatureEnvironment['name'][];
 | 
					    environments: IFeatureEnvironment['name'][];
 | 
				
			||||||
@ -31,6 +32,8 @@ export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({
 | 
				
			|||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
    const projectId = useRequiredPathParam('projectId');
 | 
					    const projectId = useRequiredPathParam('projectId');
 | 
				
			||||||
    const featureId = useRequiredPathParam('featureId');
 | 
					    const featureId = useRequiredPathParam('featureId');
 | 
				
			||||||
 | 
					    const { segments } = useSegments(strategy.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
 | 
					    const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
 | 
				
			||||||
    const open = Boolean(anchorEl);
 | 
					    const open = Boolean(anchorEl);
 | 
				
			||||||
    const { addStrategyToFeature } = useFeatureStrategyApi();
 | 
					    const { addStrategyToFeature } = useFeatureStrategyApi();
 | 
				
			||||||
@ -48,6 +51,7 @@ export const CopyStrategyIconMenu: VFC<ICopyStrategyIconMenuProps> = ({
 | 
				
			|||||||
        const { id, ...strategyCopy } = {
 | 
					        const { id, ...strategyCopy } = {
 | 
				
			||||||
            ...strategy,
 | 
					            ...strategy,
 | 
				
			||||||
            environment: environmentId,
 | 
					            environment: environmentId,
 | 
				
			||||||
 | 
					            copyOf: strategy.id,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,7 @@ export interface IFeatureStrategyPayload {
 | 
				
			|||||||
    name?: string;
 | 
					    name?: string;
 | 
				
			||||||
    constraints: IConstraint[];
 | 
					    constraints: IConstraint[];
 | 
				
			||||||
    parameters: IFeatureStrategyParameters;
 | 
					    parameters: IFeatureStrategyParameters;
 | 
				
			||||||
 | 
					    copyOf?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IStrategy {
 | 
					export interface IStrategy {
 | 
				
			||||||
 | 
				
			|||||||
@ -101,7 +101,10 @@ export default class EnvironmentStore implements IEnvironmentStore {
 | 
				
			|||||||
    async getAll(query?: Object): Promise<IEnvironment[]> {
 | 
					    async getAll(query?: Object): Promise<IEnvironment[]> {
 | 
				
			||||||
        let qB = this.db<IEnvironmentsTable>(TABLE)
 | 
					        let qB = this.db<IEnvironmentsTable>(TABLE)
 | 
				
			||||||
            .select('*')
 | 
					            .select('*')
 | 
				
			||||||
            .orderBy('sort_order', 'created_at');
 | 
					            .orderBy([
 | 
				
			||||||
 | 
					                { column: 'sort_order', order: 'asc' },
 | 
				
			||||||
 | 
					                { column: 'created_at', order: 'asc' },
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
        if (query) {
 | 
					        if (query) {
 | 
				
			||||||
            qB = qB.where(query);
 | 
					            qB = qB.where(query);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -193,7 +193,10 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
 | 
				
			|||||||
                feature_name: featureName,
 | 
					                feature_name: featureName,
 | 
				
			||||||
                environment,
 | 
					                environment,
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .orderBy('sort_order', 'created_at');
 | 
					            .orderBy([
 | 
				
			||||||
 | 
					                { column: 'sort_order', order: 'asc' },
 | 
				
			||||||
 | 
					                { column: 'created_at', order: 'asc' },
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
        stopTimer();
 | 
					        stopTimer();
 | 
				
			||||||
        return rows.map(mapRow);
 | 
					        return rows.map(mapRow);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,9 @@ export const createFeatureStrategySchema = {
 | 
				
			|||||||
                $ref: '#/components/schemas/constraintSchema',
 | 
					                $ref: '#/components/schemas/constraintSchema',
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        copyOf: {
 | 
				
			||||||
 | 
					            type: 'string',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        parameters: {
 | 
					        parameters: {
 | 
				
			||||||
            $ref: '#/components/schemas/parametersSchema',
 | 
					            $ref: '#/components/schemas/parametersSchema',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
				
			|||||||
@ -21,6 +21,8 @@ export const publicSignupTokenSchema = {
 | 
				
			|||||||
            type: 'string',
 | 
					            type: 'string',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        url: {
 | 
					        url: {
 | 
				
			||||||
 | 
					            description:
 | 
				
			||||||
 | 
					                'The public signup link for the token. Users who follow this link will be taken to a signup page where they can create an Unleash user.',
 | 
				
			||||||
            type: 'string',
 | 
					            type: 'string',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        name: {
 | 
					        name: {
 | 
				
			||||||
@ -43,12 +45,15 @@ export const publicSignupTokenSchema = {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        users: {
 | 
					        users: {
 | 
				
			||||||
            type: 'array',
 | 
					            type: 'array',
 | 
				
			||||||
 | 
					            description: 'Array of users that have signed up using the token',
 | 
				
			||||||
            items: {
 | 
					            items: {
 | 
				
			||||||
                $ref: '#/components/schemas/userSchema',
 | 
					                $ref: '#/components/schemas/userSchema',
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            nullable: true,
 | 
					            nullable: true,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        role: {
 | 
					        role: {
 | 
				
			||||||
 | 
					            description:
 | 
				
			||||||
 | 
					                'Users who sign up using this token will be given this role.',
 | 
				
			||||||
            $ref: '#/components/schemas/roleSchema',
 | 
					            $ref: '#/components/schemas/roleSchema',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
				
			|||||||
@ -39,6 +39,7 @@ import { FeatureEnvironmentSchema } from '../../../openapi/spec/feature-environm
 | 
				
			|||||||
import { SetStrategySortOrderSchema } from '../../../openapi/spec/set-strategy-sort-order-schema';
 | 
					import { SetStrategySortOrderSchema } from '../../../openapi/spec/set-strategy-sort-order-schema';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { emptyResponse } from '../../../openapi/util/standard-responses';
 | 
					import { emptyResponse } from '../../../openapi/util/standard-responses';
 | 
				
			||||||
 | 
					import { SegmentService } from '../../../services/segment-service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface FeatureStrategyParams {
 | 
					interface FeatureStrategyParams {
 | 
				
			||||||
    projectId: string;
 | 
					    projectId: string;
 | 
				
			||||||
@ -68,7 +69,10 @@ const PATH_STRATEGY = `${PATH_STRATEGIES}/:strategyId`;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type ProjectFeaturesServices = Pick<
 | 
					type ProjectFeaturesServices = Pick<
 | 
				
			||||||
    IUnleashServices,
 | 
					    IUnleashServices,
 | 
				
			||||||
    'featureToggleServiceV2' | 'projectHealthService' | 'openApiService'
 | 
					    | 'featureToggleServiceV2'
 | 
				
			||||||
 | 
					    | 'projectHealthService'
 | 
				
			||||||
 | 
					    | 'openApiService'
 | 
				
			||||||
 | 
					    | 'segmentService'
 | 
				
			||||||
>;
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class ProjectFeaturesController extends Controller {
 | 
					export default class ProjectFeaturesController extends Controller {
 | 
				
			||||||
@ -76,15 +80,22 @@ export default class ProjectFeaturesController extends Controller {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private openApiService: OpenApiService;
 | 
					    private openApiService: OpenApiService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private segmentService: SegmentService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private readonly logger: Logger;
 | 
					    private readonly logger: Logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
        config: IUnleashConfig,
 | 
					        config: IUnleashConfig,
 | 
				
			||||||
        { featureToggleServiceV2, openApiService }: ProjectFeaturesServices,
 | 
					        {
 | 
				
			||||||
 | 
					            featureToggleServiceV2,
 | 
				
			||||||
 | 
					            openApiService,
 | 
				
			||||||
 | 
					            segmentService,
 | 
				
			||||||
 | 
					        }: ProjectFeaturesServices,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        super(config);
 | 
					        super(config);
 | 
				
			||||||
        this.featureService = featureToggleServiceV2;
 | 
					        this.featureService = featureToggleServiceV2;
 | 
				
			||||||
        this.openApiService = openApiService;
 | 
					        this.openApiService = openApiService;
 | 
				
			||||||
 | 
					        this.segmentService = segmentService;
 | 
				
			||||||
        this.logger = config.getLogger('/admin-api/project/features.ts');
 | 
					        this.logger = config.getLogger('/admin-api/project/features.ts');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.route({
 | 
					        this.route({
 | 
				
			||||||
@ -557,13 +568,29 @@ export default class ProjectFeaturesController extends Controller {
 | 
				
			|||||||
        res: Response<FeatureStrategySchema>,
 | 
					        res: Response<FeatureStrategySchema>,
 | 
				
			||||||
    ): Promise<void> {
 | 
					    ): Promise<void> {
 | 
				
			||||||
        const { projectId, featureName, environment } = req.params;
 | 
					        const { projectId, featureName, environment } = req.params;
 | 
				
			||||||
 | 
					        const { copyOf, ...strategyConfig } = req.body;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const userName = extractUsername(req);
 | 
					        const userName = extractUsername(req);
 | 
				
			||||||
        const strategy = await this.featureService.createStrategy(
 | 
					        const strategy = await this.featureService.createStrategy(
 | 
				
			||||||
            req.body,
 | 
					            strategyConfig,
 | 
				
			||||||
            { environment, projectId, featureName },
 | 
					            { environment, projectId, featureName },
 | 
				
			||||||
            userName,
 | 
					            userName,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        res.status(200).json(strategy);
 | 
					
 | 
				
			||||||
 | 
					        if (copyOf) {
 | 
				
			||||||
 | 
					            this.logger.info(
 | 
				
			||||||
 | 
					                `Cloning segments from: strategyId=${copyOf} to: strategyId=${strategy.id} `,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            await this.segmentService.cloneStrategySegments(
 | 
				
			||||||
 | 
					                copyOf,
 | 
				
			||||||
 | 
					                strategy.id,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const updatedStrategy = await this.featureService.getStrategy(
 | 
				
			||||||
 | 
					            strategy.id,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        res.status(200).json(updatedStrategy);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async getFeatureStrategies(
 | 
					    async getFeatureStrategies(
 | 
				
			||||||
 | 
				
			|||||||
@ -53,6 +53,7 @@ export class PublicInviteController extends Controller {
 | 
				
			|||||||
                openApiService.validPath({
 | 
					                openApiService.validPath({
 | 
				
			||||||
                    tags: ['Public signup tokens'],
 | 
					                    tags: ['Public signup tokens'],
 | 
				
			||||||
                    operationId: 'validatePublicSignupToken',
 | 
					                    operationId: 'validatePublicSignupToken',
 | 
				
			||||||
 | 
					                    summary: `Validates a public signup token exists, has not expired and is enabled`,
 | 
				
			||||||
                    responses: {
 | 
					                    responses: {
 | 
				
			||||||
                        200: emptyResponse,
 | 
					                        200: emptyResponse,
 | 
				
			||||||
                        ...getStandardResponses(400),
 | 
					                        ...getStandardResponses(400),
 | 
				
			||||||
@ -70,6 +71,8 @@ export class PublicInviteController extends Controller {
 | 
				
			|||||||
                openApiService.validPath({
 | 
					                openApiService.validPath({
 | 
				
			||||||
                    tags: ['Public signup tokens'],
 | 
					                    tags: ['Public signup tokens'],
 | 
				
			||||||
                    operationId: 'addPublicSignupTokenUser',
 | 
					                    operationId: 'addPublicSignupTokenUser',
 | 
				
			||||||
 | 
					                    summary:
 | 
				
			||||||
 | 
					                        'Create a user with the "viewer" root role and link them to a signup token',
 | 
				
			||||||
                    requestBody: createRequestSchema('createInvitedUserSchema'),
 | 
					                    requestBody: createRequestSchema('createInvitedUserSchema'),
 | 
				
			||||||
                    responses: {
 | 
					                    responses: {
 | 
				
			||||||
                        200: createResponseSchema('userSchema'),
 | 
					                        200: createResponseSchema('userSchema'),
 | 
				
			||||||
 | 
				
			|||||||
@ -45,6 +45,7 @@ import {
 | 
				
			|||||||
    IFeatureOverview,
 | 
					    IFeatureOverview,
 | 
				
			||||||
    IFeatureStrategy,
 | 
					    IFeatureStrategy,
 | 
				
			||||||
    IFeatureToggleQuery,
 | 
					    IFeatureToggleQuery,
 | 
				
			||||||
 | 
					    ISegment,
 | 
				
			||||||
    IStrategyConfig,
 | 
					    IStrategyConfig,
 | 
				
			||||||
    IVariant,
 | 
					    IVariant,
 | 
				
			||||||
    WeightType,
 | 
					    WeightType,
 | 
				
			||||||
@ -283,12 +284,14 @@ class FeatureToggleService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    featureStrategyToPublic(
 | 
					    featureStrategyToPublic(
 | 
				
			||||||
        featureStrategy: IFeatureStrategy,
 | 
					        featureStrategy: IFeatureStrategy,
 | 
				
			||||||
 | 
					        segments: ISegment[] = [],
 | 
				
			||||||
    ): Saved<IStrategyConfig> {
 | 
					    ): Saved<IStrategyConfig> {
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            id: featureStrategy.id,
 | 
					            id: featureStrategy.id,
 | 
				
			||||||
            name: featureStrategy.strategyName,
 | 
					            name: featureStrategy.strategyName,
 | 
				
			||||||
            constraints: featureStrategy.constraints || [],
 | 
					            constraints: featureStrategy.constraints || [],
 | 
				
			||||||
            parameters: featureStrategy.parameters,
 | 
					            parameters: featureStrategy.parameters,
 | 
				
			||||||
 | 
					            segments: segments.map((segment) => segment.id) ?? [],
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -330,7 +333,13 @@ class FeatureToggleService {
 | 
				
			|||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const tags = await this.tagStore.getAllTagsForFeature(featureName);
 | 
					            const tags = await this.tagStore.getAllTagsForFeature(featureName);
 | 
				
			||||||
            const strategy = this.featureStrategyToPublic(newFeatureStrategy);
 | 
					            const segments = await this.segmentService.getByStrategy(
 | 
				
			||||||
 | 
					                newFeatureStrategy.id,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            const strategy = this.featureStrategyToPublic(
 | 
				
			||||||
 | 
					                newFeatureStrategy,
 | 
				
			||||||
 | 
					                segments,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            await this.eventStore.store(
 | 
					            await this.eventStore.store(
 | 
				
			||||||
                new FeatureStrategyAddEvent({
 | 
					                new FeatureStrategyAddEvent({
 | 
				
			||||||
                    project: projectId,
 | 
					                    project: projectId,
 | 
				
			||||||
@ -385,10 +394,17 @@ class FeatureToggleService {
 | 
				
			|||||||
                updates,
 | 
					                updates,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const segments = await this.segmentService.getByStrategy(
 | 
				
			||||||
 | 
					                strategy.id,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 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, segments);
 | 
				
			||||||
            const preData = this.featureStrategyToPublic(existingStrategy);
 | 
					            const preData = this.featureStrategyToPublic(
 | 
				
			||||||
 | 
					                existingStrategy,
 | 
				
			||||||
 | 
					                segments,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            await this.eventStore.store(
 | 
					            await this.eventStore.store(
 | 
				
			||||||
                new FeatureStrategyUpdateEvent({
 | 
					                new FeatureStrategyUpdateEvent({
 | 
				
			||||||
                    project: projectId,
 | 
					                    project: projectId,
 | 
				
			||||||
@ -424,8 +440,14 @@ class FeatureToggleService {
 | 
				
			|||||||
                existingStrategy,
 | 
					                existingStrategy,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            const tags = await this.tagStore.getAllTagsForFeature(featureName);
 | 
					            const tags = await this.tagStore.getAllTagsForFeature(featureName);
 | 
				
			||||||
            const data = this.featureStrategyToPublic(strategy);
 | 
					            const segments = await this.segmentService.getByStrategy(
 | 
				
			||||||
            const preData = this.featureStrategyToPublic(existingStrategy);
 | 
					                strategy.id,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            const data = this.featureStrategyToPublic(strategy, segments);
 | 
				
			||||||
 | 
					            const preData = this.featureStrategyToPublic(
 | 
				
			||||||
 | 
					                existingStrategy,
 | 
				
			||||||
 | 
					                segments,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            await this.eventStore.store(
 | 
					            await this.eventStore.store(
 | 
				
			||||||
                new FeatureStrategyUpdateEvent({
 | 
					                new FeatureStrategyUpdateEvent({
 | 
				
			||||||
                    featureName,
 | 
					                    featureName,
 | 
				
			||||||
@ -488,6 +510,7 @@ class FeatureToggleService {
 | 
				
			|||||||
        featureName: string,
 | 
					        featureName: string,
 | 
				
			||||||
        environment: string = DEFAULT_ENV,
 | 
					        environment: string = DEFAULT_ENV,
 | 
				
			||||||
    ): Promise<Saved<IStrategyConfig>[]> {
 | 
					    ): Promise<Saved<IStrategyConfig>[]> {
 | 
				
			||||||
 | 
					        this.logger.debug('getStrategiesForEnvironment');
 | 
				
			||||||
        const hasEnv = await this.featureEnvironmentStore.featureHasEnvironment(
 | 
					        const hasEnv = await this.featureEnvironmentStore.featureHasEnvironment(
 | 
				
			||||||
            environment,
 | 
					            environment,
 | 
				
			||||||
            featureName,
 | 
					            featureName,
 | 
				
			||||||
@ -499,13 +522,22 @@ class FeatureToggleService {
 | 
				
			|||||||
                    featureName,
 | 
					                    featureName,
 | 
				
			||||||
                    environment,
 | 
					                    environment,
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            return featureStrategies.map((strat) => ({
 | 
					            const result = [];
 | 
				
			||||||
                id: strat.id,
 | 
					            for (const strat of featureStrategies) {
 | 
				
			||||||
                name: strat.strategyName,
 | 
					                const segments =
 | 
				
			||||||
                constraints: strat.constraints,
 | 
					                    (await this.segmentService.getByStrategy(strat.id)).map(
 | 
				
			||||||
                parameters: strat.parameters,
 | 
					                        (segment) => segment.id,
 | 
				
			||||||
                sortOrder: strat.sortOrder,
 | 
					                    ) ?? [];
 | 
				
			||||||
            }));
 | 
					                result.push({
 | 
				
			||||||
 | 
					                    id: strat.id,
 | 
				
			||||||
 | 
					                    name: strat.strategyName,
 | 
				
			||||||
 | 
					                    constraints: strat.constraints,
 | 
				
			||||||
 | 
					                    parameters: strat.parameters,
 | 
				
			||||||
 | 
					                    sortOrder: strat.sortOrder,
 | 
				
			||||||
 | 
					                    segments,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return result;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        throw new NotFoundError(
 | 
					        throw new NotFoundError(
 | 
				
			||||||
            `Feature ${featureName} does not have environment ${environment}`,
 | 
					            `Feature ${featureName} does not have environment ${environment}`,
 | 
				
			||||||
@ -727,12 +759,23 @@ class FeatureToggleService {
 | 
				
			|||||||
        const strategy = await this.featureStrategiesStore.getStrategyById(
 | 
					        const strategy = await this.featureStrategiesStore.getStrategyById(
 | 
				
			||||||
            strategyId,
 | 
					            strategyId,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        return {
 | 
					
 | 
				
			||||||
 | 
					        const segments = await this.segmentService.getByStrategy(strategyId);
 | 
				
			||||||
 | 
					        let result: Saved<IStrategyConfig> = {
 | 
				
			||||||
            id: strategy.id,
 | 
					            id: strategy.id,
 | 
				
			||||||
            name: strategy.strategyName,
 | 
					            name: strategy.strategyName,
 | 
				
			||||||
            constraints: strategy.constraints || [],
 | 
					            constraints: strategy.constraints || [],
 | 
				
			||||||
            parameters: strategy.parameters,
 | 
					            parameters: strategy.parameters,
 | 
				
			||||||
 | 
					            segments: [],
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (segments && segments.length > 0) {
 | 
				
			||||||
 | 
					            result = {
 | 
				
			||||||
 | 
					                ...result,
 | 
				
			||||||
 | 
					                segments: segments.map((segment) => segment.id),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async getEnvironmentInfo(
 | 
					    async getEnvironmentInfo(
 | 
				
			||||||
 | 
				
			|||||||
@ -124,7 +124,6 @@ export class SegmentService {
 | 
				
			|||||||
        const sourceStrategySegments = await this.getByStrategy(
 | 
					        const sourceStrategySegments = await this.getByStrategy(
 | 
				
			||||||
            sourceStrategyId,
 | 
					            sourceStrategyId,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					 | 
				
			||||||
        await Promise.all(
 | 
					        await Promise.all(
 | 
				
			||||||
            sourceStrategySegments.map((sourceStrategySegment) => {
 | 
					            sourceStrategySegments.map((sourceStrategySegment) => {
 | 
				
			||||||
                return this.addToStrategy(
 | 
					                return this.addToStrategy(
 | 
				
			||||||
 | 
				
			|||||||
@ -709,6 +709,9 @@ exports[`should serve the OpenAPI spec 1`] = `
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
            "type": "array",
 | 
					            "type": "array",
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
 | 
					          "copyOf": {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
          "name": {
 | 
					          "name": {
 | 
				
			||||||
            "type": "string",
 | 
					            "type": "string",
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
@ -2483,14 +2486,17 @@ exports[`should serve the OpenAPI spec 1`] = `
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          "role": {
 | 
					          "role": {
 | 
				
			||||||
            "$ref": "#/components/schemas/roleSchema",
 | 
					            "$ref": "#/components/schemas/roleSchema",
 | 
				
			||||||
 | 
					            "description": "Users who sign up using this token will be given this role.",
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "secret": {
 | 
					          "secret": {
 | 
				
			||||||
            "type": "string",
 | 
					            "type": "string",
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "url": {
 | 
					          "url": {
 | 
				
			||||||
 | 
					            "description": "The public signup link for the token. Users who follow this link will be taken to a signup page where they can create an Unleash user.",
 | 
				
			||||||
            "type": "string",
 | 
					            "type": "string",
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "users": {
 | 
					          "users": {
 | 
				
			||||||
 | 
					            "description": "Array of users that have signed up using the token",
 | 
				
			||||||
            "items": {
 | 
					            "items": {
 | 
				
			||||||
              "$ref": "#/components/schemas/userSchema",
 | 
					              "$ref": "#/components/schemas/userSchema",
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -7472,6 +7478,7 @@ If the provided project does not exist, the list of events will be empty.",
 | 
				
			|||||||
            "description": "The provided resource can not be created or updated because it would conflict with the current state of the resource or with an already existing resource, respectively.",
 | 
					            "description": "The provided resource can not be created or updated because it would conflict with the current state of the resource or with an already existing resource, respectively.",
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "summary": "Create a user with the "viewer" root role and link them to a signup token",
 | 
				
			||||||
        "tags": [
 | 
					        "tags": [
 | 
				
			||||||
          "Public signup tokens",
 | 
					          "Public signup tokens",
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
@ -7498,6 +7505,7 @@ If the provided project does not exist, the list of events will be empty.",
 | 
				
			|||||||
            "description": "The request data does not match what we expect.",
 | 
					            "description": "The request data does not match what we expect.",
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "summary": "Validates a public signup token exists, has not expired and is enabled",
 | 
				
			||||||
        "tags": [
 | 
					        "tags": [
 | 
				
			||||||
          "Public signup tokens",
 | 
					          "Public signup tokens",
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user