Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SegmentInputValidator } from './SegmentInputValidator';
export class FeatureFlagListValidator {
@IsNotEmpty()
@IsUUID()
public flagId: string;
public id: string;

@IsDefined()
@IsBoolean()
Expand Down
18 changes: 9 additions & 9 deletions backend/packages/Upgrade/src/api/services/FeatureFlagService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,14 +600,14 @@ export class FeatureFlagService {

const featureFlags = await manager
.getRepository(FeatureFlag)
.findByIds(listsInput.map((listInput) => listInput.flagId));
.findByIds(listsInput.map((listInput) => listInput.id));

const featureFlagSegmentInclusionOrExclusionArray = listsInput.map((listInput) => {
const featureFlagSegmentInclusionOrExclusion =
filterType === 'inclusion' ? new FeatureFlagSegmentInclusion() : new FeatureFlagSegmentExclusion();
featureFlagSegmentInclusionOrExclusion.enabled = listInput.enabled;
featureFlagSegmentInclusionOrExclusion.listType = listInput.listType;
featureFlagSegmentInclusionOrExclusion.featureFlag = featureFlags.find((flag) => flag.id === listInput.flagId);
featureFlagSegmentInclusionOrExclusion.featureFlag = featureFlags.find((flag) => flag.id === listInput.id);
featureFlagSegmentInclusionOrExclusion.segment = newSegments.find(
(segment) => segment.id === listInput.segment.id
);
Expand Down Expand Up @@ -679,23 +679,23 @@ export class FeatureFlagService {
return await this.dataSource.transaction(async (transactionalEntityManager) => {
// Find the existing record
let existingRecord: FeatureFlagSegmentInclusion | FeatureFlagSegmentExclusion;
const featureFlag = await this.findOne(listInput.flagId);
const featureFlag = await this.findOne(listInput.id);

if (filterType === FEATURE_FLAG_LIST_FILTER_MODE.INCLUSION) {
existingRecord = await this.featureFlagSegmentInclusionRepository.findOne({
where: { featureFlag: { id: listInput.flagId }, segment: { id: listInput.segment.id } },
where: { featureFlag: { id: listInput.id }, segment: { id: listInput.segment.id } },
relations: ['featureFlag', 'segment'],
});
} else {
existingRecord = await this.featureFlagSegmentExclusionRepository.findOne({
where: { featureFlag: { id: listInput.flagId }, segment: { id: listInput.segment.id } },
where: { featureFlag: { id: listInput.id }, segment: { id: listInput.segment.id } },
relations: ['featureFlag', 'segment'],
});
}

if (!existingRecord) {
throw new Error(
`No existing ${filterType} record found for feature flag ${listInput.flagId} and segment ${listInput.segment.id}`
`No existing ${filterType} record found for feature flag ${listInput.id} and segment ${listInput.segment.id}`
);
}

Expand Down Expand Up @@ -961,7 +961,7 @@ export class FeatureFlagService {
return {
...segmentInclusionList,
enabled: false,
flagId: newFlag.id,
id: newFlag.id,
segment: { ...segmentInclusionList.segment, userIds, subSegmentIds, groups },
};
});
Expand All @@ -981,7 +981,7 @@ export class FeatureFlagService {

return {
...segmentExclusionList,
flagId: newFlag.id,
id: newFlag.id,
segment: { ...segmentExclusionList.segment, userIds, subSegmentIds, groups },
};
});
Expand Down Expand Up @@ -1171,7 +1171,7 @@ export class FeatureFlagService {
const listDoc: FeatureFlagListValidator = {
...list,
enabled: false,
flagId: featureFlagId,
id: featureFlagId,
segment: { ...list.segment, id: uuid() },
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ describe('Segment Controller Testing', () => {
subSegmentIds: ['seg2'],
};

const listInputData = {
parentSegmentId: uuid(),
name: 'list1',
description: 'list description',
context: 'home',
type: 'private',
userIds: ['user1', 'user2'],
groups: [],
subSegmentIds: [],
listType: 'Individual',
};

test('Get request for /api/segments', () => {
return request(app)
.get('/api/segments')
Expand Down Expand Up @@ -111,4 +123,46 @@ describe('Segment Controller Testing', () => {
.expect('Content-Type', /json/)
.expect(200);
});

test('Post request for /api/segments/list (addSegmentList)', () => {
return request(app)
.post('/api/segments/list')
.send(listInputData)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200);
});

test('Post request for /api/segments (updateSegmentList)', () => {
const updateListData = {
id: uuid(),
name: 'updated list',
description: 'updated description',
context: 'home',
type: 'private',
userIds: ['user1', 'user2', 'user3'],
groups: [],
subSegmentIds: [],
listType: 'Individual',
};

return request(app)
.post('/api/segments')
.send(updateListData)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200);
});

test('Delete request for /api/segments/list/:segmentId (deleteSegmentList)', () => {
const segmentId = uuid();
const parentSegmentId = uuid();

return request(app)
.delete(`/api/segments/list/${segmentId}`)
.send({ parentSegmentId })
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200);
});
});
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Service } from 'typedi';

@Service()
export default class ExcludeServiceMock {
export default class SegmentServiceMock {
public getAllSegments(): Promise<[]> {
return Promise.resolve([]);
}

public getAllSegmentWithStatus(): Promise<[]> {
return Promise.resolve([]);
}

public getSegmentById(id: string): Promise<[]> {
return Promise.resolve([]);
}
Expand Down Expand Up @@ -53,4 +53,12 @@ export default class ExcludeServiceMock {
public exportSegmentCSV(id: string): Promise<[]> {
return Promise.resolve([]);
}

public addList(): Promise<[]> {
return Promise.resolve([]);
}

public deleteList(): Promise<[]> {
return Promise.resolve([]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe('Feature Flag Service Testing', () => {

const mockList = new FeatureFlagListValidator();
mockList.enabled = true;
mockList.flagId = mockFlag1.id;
mockList.id = mockFlag1.id;
mockList.listType = 'individual';
mockList.segment = mockSegment;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface FeatureFlagSegmentListDetails {
featureFlag: FeatureFlag;
enabled: boolean;
listType: MemberTypes | string;
parentSegmentId?: string;
}

export enum UPSERT_FEATURE_FLAG_ACTION {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ import { HttpClient, HttpParams } from '@angular/common/http';
import { of } from 'rxjs';
import { environment } from '../../../environments/environment';
import { SegmentsDataService } from './segments.data.service';
import { Segment, SegmentFile, SegmentInput } from './store/segments.model';
import {
AddPrivateSegmentListRequest,
EditPrivateSegmentListRequest,
Segment,
SegmentFile,
SegmentInput,
} from './store/segments.model';
import { SEGMENT_STATUS, SEGMENT_TYPE } from 'upgrade_types';
import { Environment } from '../../../environments/environment-types';
import { FeatureFlagSegmentListDetails } from '../feature-flags/store/feature-flags.model';

class MockHTTPClient {
get = jest.fn().mockReturnValue(of());
Expand All @@ -20,13 +27,17 @@ describe('SegmentDataService', () => {
let mockSegmentId: string;
let mockEnvironment: Environment;
let mockSegmentFile: SegmentFile;
let mockAddPrivateSegmentListRequest: AddPrivateSegmentListRequest;
let mockEditPrivateSegmentListRequest: EditPrivateSegmentListRequest;
let mockParentSegmentId: string;

beforeEach(() => {
mockHttpClient = new MockHTTPClient();
mockEnvironment = { ...environment };
service = new SegmentsDataService(mockHttpClient as HttpClient, mockEnvironment);

mockSegmentId = 'segmentId1';
mockParentSegmentId = 'parentSegmentId1';
mockSegmentInput = {
createdAt: 'time',
updatedAt: 'time',
Expand Down Expand Up @@ -56,6 +67,37 @@ describe('SegmentDataService', () => {
status: SEGMENT_STATUS.UNUSED,
};
mockSegmentFile = { fileName: 'test', fileContent: JSON.stringify(mockSegment) };

mockAddPrivateSegmentListRequest = {
id: mockParentSegmentId,
enabled: true,
listType: 'Individual',
segment: {
name: 'Test Segment List',
description: 'Test description',
context: 'test',
type: SEGMENT_TYPE.PRIVATE,
userIds: ['user1', 'user2'],
groups: [],
subSegmentIds: [],
},
};

mockEditPrivateSegmentListRequest = {
id: mockParentSegmentId,
enabled: true,
listType: 'Individual',
segment: {
id: mockSegmentId,
name: 'Updated Segment List',
description: 'Updated description',
context: 'test',
type: SEGMENT_TYPE.PRIVATE,
userIds: ['user1', 'user2', 'user3'],
groups: [],
subSegmentIds: [],
},
};
});

describe('#fetchSegmentsPaginated', () => {
Expand Down Expand Up @@ -125,4 +167,130 @@ describe('SegmentDataService', () => {
expect(mockHttpClient.post).toHaveBeenCalledWith(mockUrl, [mockSegmentFile]);
});
});

describe('#addSegmentList', () => {
it('should post the addSegmentList http observable with transformed request', () => {
const expectedUrl = mockEnvironment.api.addSegmentList;
const mockSegment: Segment = {
id: 'newSegmentId',
name: 'New Segment',
description: 'Test description',
context: 'test',
tags: [],
type: SEGMENT_TYPE.PRIVATE,
status: SEGMENT_STATUS.UNUSED,
createdAt: 'test',
updatedAt: 'test',
versionNumber: 0,
individualForSegment: [],
groupForSegment: [],
subSegments: [],
};

const expectedOutput: FeatureFlagSegmentListDetails = {
segment: mockSegment,
featureFlag: null,
enabled: mockAddPrivateSegmentListRequest.enabled,
listType: mockAddPrivateSegmentListRequest.listType,
parentSegmentId: mockAddPrivateSegmentListRequest.id,
};

// Setup mock response
mockHttpClient.post.mockReturnValue(of(mockSegment));

// Expected transformed request
const expectedTransformedRequest = {
parentSegmentId: mockAddPrivateSegmentListRequest.id,
name: mockAddPrivateSegmentListRequest.segment.name,
description: mockAddPrivateSegmentListRequest.segment.description,
context: mockAddPrivateSegmentListRequest.segment.context,
type: mockAddPrivateSegmentListRequest.segment.type,
userIds: mockAddPrivateSegmentListRequest.segment.userIds,
groups: mockAddPrivateSegmentListRequest.segment.groups,
subSegmentIds: mockAddPrivateSegmentListRequest.segment.subSegmentIds,
listType: mockAddPrivateSegmentListRequest.listType,
};

// Call the method and subscribe to result
let result: FeatureFlagSegmentListDetails;
service.addSegmentList(mockAddPrivateSegmentListRequest).subscribe((res) => {
result = res;
});

// Verify HTTP call was made correctly
expect(mockHttpClient.post).toHaveBeenCalledWith(expectedUrl, expectedTransformedRequest);

// Verify expected output transformation
expect(result).toEqual(expectedOutput);
});
});

describe('#updateSegmentList', () => {
it('should post the updateSegmentList http observable with transformed request', () => {
const expectedUrl = mockEnvironment.api.segments;
const mockSegment: Segment = {
id: mockSegmentId,
name: 'Updated Segment',
description: 'Updated description',
context: 'test',
tags: [],
type: SEGMENT_TYPE.PRIVATE,
status: SEGMENT_STATUS.UNUSED,
createdAt: 'test',
updatedAt: 'test',
versionNumber: 0,
individualForSegment: [],
groupForSegment: [],
subSegments: [],
};

const expectedOutput: FeatureFlagSegmentListDetails = {
segment: mockSegment,
featureFlag: null,
enabled: mockEditPrivateSegmentListRequest.enabled,
listType: mockEditPrivateSegmentListRequest.listType,
parentSegmentId: mockEditPrivateSegmentListRequest.id,
};

// Setup mock response
mockHttpClient.post.mockReturnValue(of(mockSegment));

// Expected transformed request
const expectedTransformedRequest = {
id: mockEditPrivateSegmentListRequest.segment.id,
name: mockEditPrivateSegmentListRequest.segment.name,
description: mockEditPrivateSegmentListRequest.segment.description,
context: mockEditPrivateSegmentListRequest.segment.context,
type: mockEditPrivateSegmentListRequest.segment.type,
userIds: mockEditPrivateSegmentListRequest.segment.userIds,
groups: mockEditPrivateSegmentListRequest.segment.groups,
subSegmentIds: mockEditPrivateSegmentListRequest.segment.subSegmentIds,
listType: mockEditPrivateSegmentListRequest.listType,
};

// Call the method and subscribe to result
let result: FeatureFlagSegmentListDetails;
service.updateSegmentList(mockEditPrivateSegmentListRequest).subscribe(res => {
result = res;
});

// Verify HTTP call was made correctly
expect(mockHttpClient.post).toHaveBeenCalledWith(expectedUrl, expectedTransformedRequest);

// Verify expected output transformation
expect(result).toEqual(expectedOutput);
});
});

describe('#deleteSegmentList', () => {
it('should delete the segment list with the correct parameters', () => {
const segmentId = mockSegmentId;
const parentSegmentId = mockParentSegmentId;
const expectedUrl = `${mockEnvironment.api.addSegmentList}/${segmentId}`;

service.deleteSegmentList(segmentId, parentSegmentId);

expect(mockHttpClient.delete).toHaveBeenCalledWith(expectedUrl, { body: { parentSegmentId } });
});
});
});
Loading
Loading