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 @@ -26,6 +26,7 @@ export enum FLAG_SEARCH_KEY {
STATUS = 'status',
TAG = 'tag',
CONTEXT = 'context',
ID = 'id',
}

class IFeatureFlagSortParamsValidator {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum SEGMENT_SEARCH_KEY {
NAME = 'name',
TAG = 'tag',
CONTEXT = 'context',
ID = 'id',
}

class ISegmentSortParamsValidator {
Expand Down
40 changes: 25 additions & 15 deletions backend/packages/Upgrade/src/api/services/ExperimentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ import { CacheService } from './CacheService';
import { QueryService } from './QueryService';
import { ArchivedStats } from '../models/ArchivedStats';
import { ArchivedStatsRepository } from '../repositories/ArchivedStatsRepository';
import { validate } from 'class-validator';
import { isUUID, validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
import { StratificationFactorRepository } from '../repositories/StratificationFactorRepository';
import { ExperimentDetailsForCSVData } from '../repositories/AnalyticsRepository';
Expand Down Expand Up @@ -1797,33 +1797,43 @@ export class ExperimentService {
private paginatedSearchString(params: IExperimentSearchParams): string {
const type = params.key;
// escape % and ' characters
const serachString = params.string.replace(/%/g, '\\$&').replace(/'/g, "''");
const likeString = `ILIKE '%${serachString}%'`;
const searchString: string[] = [];
const searchString = params.string.replace(/%/g, '\\$&').replace(/'/g, "''");
if (type === EXPERIMENT_SEARCH_KEY.ID && !isUUID(searchString)) {
return '';
}

const likeString = `ILIKE '%${searchString}%'`;
const searchArray: string[] = [];
switch (type) {
case EXPERIMENT_SEARCH_KEY.NAME:
searchString.push(`${type} ${likeString}`);
searchArray.push(`${type} ${likeString}`);
break;
case EXPERIMENT_SEARCH_KEY.STATUS:
searchString.push(`state::TEXT ${likeString}`);
searchArray.push(`state::TEXT ${likeString}`);
break;
case EXPERIMENT_SEARCH_KEY.CONTEXT:
searchString.push(`ARRAY_TO_STRING(${type}, ',') ${likeString}`);
searchArray.push(`ARRAY_TO_STRING(${type}, ',') ${likeString}`);
break;
case EXPERIMENT_SEARCH_KEY.TAG:
searchString.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
searchArray.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
break;
case EXPERIMENT_SEARCH_KEY.ID:
searchArray.push(`experiment.id = '${searchString}'`);
break;
default:
searchString.push(`name ${likeString}`);
searchString.push(`state::TEXT ${likeString}`);
searchString.push(`ARRAY_TO_STRING(context, ',') ${likeString}`);
searchString.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
searchString.push(`partitions.site ${likeString}`);
searchString.push(`partitions.target ${likeString}`);
searchArray.push(`name ${likeString}`);
searchArray.push(`state::TEXT ${likeString}`);
searchArray.push(`ARRAY_TO_STRING(context, ',') ${likeString}`);
searchArray.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
searchArray.push(`partitions.site ${likeString}`);
searchArray.push(`partitions.target ${likeString}`);
if (isUUID(searchString)) {
searchArray.push(`experiment.id = '${searchString}'`);
}
break;
}

const searchStringConcatenated = `(${searchString.join(' OR ')})`;
const searchStringConcatenated = `(${searchArray.join(' OR ')})`;
return searchStringConcatenated;
}

Expand Down
35 changes: 22 additions & 13 deletions backend/packages/Upgrade/src/api/services/FeatureFlagService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import { ExperimentAssignmentService } from './ExperimentAssignmentService';
import { SegmentService } from './SegmentService';
import { ErrorWithType } from '../errors/ErrorWithType';
import { RequestedExperimentUser } from '../controllers/validators/ExperimentUserValidator';
import { validate } from 'class-validator';
import { isUUID, validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
import {
FeatureFlagImportDataValidation,
Expand Down Expand Up @@ -804,31 +804,40 @@ export class FeatureFlagService {
private paginatedSearchString(params: IFeatureFlagSearchParams): string {
const type = params.key;
// escape % and ' characters
const serachString = params.string.replace(/%/g, '\\$&').replace(/'/g, "''");
const likeString = `ILIKE '%${serachString}%'`;
const searchString: string[] = [];
const searchString = params.string.replace(/%/g, '\\$&').replace(/'/g, "''");
if (type === FLAG_SEARCH_KEY.ID && !isUUID(searchString)) {
return '';
}
const likeString = `ILIKE '%${searchString}%'`;
const searchArray: string[] = [];
switch (type) {
case FLAG_SEARCH_KEY.NAME:
searchString.push(`${type} ${likeString}`);
searchArray.push(`${type} ${likeString}`);
break;
case FLAG_SEARCH_KEY.STATUS:
searchString.push(`status::TEXT ${likeString}`);
searchArray.push(`status::TEXT ${likeString}`);
break;
case FLAG_SEARCH_KEY.CONTEXT:
searchString.push(`ARRAY_TO_STRING(${type}, ',') ${likeString}`);
searchArray.push(`ARRAY_TO_STRING(${type}, ',') ${likeString}`);
break;
case FLAG_SEARCH_KEY.TAG:
searchString.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
searchArray.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
break;
case FLAG_SEARCH_KEY.ID:
searchArray.push(`feature_flag.id = '${searchString}'`);
break;
default:
searchString.push(`name ${likeString}`);
searchString.push(`status::TEXT ${likeString}`);
searchString.push(`ARRAY_TO_STRING(context, ',') ${likeString}`);
searchString.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
searchArray.push(`name ${likeString}`);
searchArray.push(`status::TEXT ${likeString}`);
searchArray.push(`ARRAY_TO_STRING(context, ',') ${likeString}`);
searchArray.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
if (isUUID(searchString)) {
searchArray.push(`feature_flag.id = '${searchString}'`);
}
break;
}

const searchStringConcatenated = `(${searchString.join(' OR ')})`;
const searchStringConcatenated = `(${searchArray.join(' OR ')})`;
return searchStringConcatenated;
}

Expand Down
31 changes: 20 additions & 11 deletions backend/packages/Upgrade/src/api/services/SegmentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { FeatureFlagSegmentExclusionRepository } from '../repositories/FeatureFl
import { FeatureFlagSegmentInclusionRepository } from '../repositories/FeatureFlagSegmentInclusionRepository';
import { getSegmentData, getSegmentsData } from '../controllers/SegmentController';
import { CacheService } from './CacheService';
import { validate } from 'class-validator';
import { isUUID, validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
import path from 'path';
import { GroupEnrollmentRepository } from '../repositories/GroupEnrollmentRepository';
Expand Down Expand Up @@ -222,7 +222,7 @@ export class SegmentService {

if (searchParams) {
const whereClause = this.paginatedSearchString(searchParams);
paginatedParentSubQuery = paginatedParentSubQuery.andWhere(whereClause);
paginatedParentSubQuery = paginatedParentSubQuery.where(whereClause);
}
const countQuery = paginatedParentSubQuery.clone().andWhere('segment.type=:type', { type: SEGMENT_TYPE.PUBLIC });
paginatedParentSubQuery = paginatedParentSubQuery.andWhere('segment.type = :type').offset(skip).limit(take);
Expand All @@ -245,24 +245,33 @@ export class SegmentService {
private paginatedSearchString(params: ISegmentSearchParams): string {
const type = params.key;
// escape % and ' characters
const serachString = params.string.replace(/%/g, '\\$&').replace(/'/g, "''");
const likeString = `ILIKE '%${serachString}%'`;
const searchString: string[] = [];
const searchString = params.string.replace(/%/g, '\\$&').replace(/'/g, "''");
if (type === SEGMENT_SEARCH_KEY.ID && !isUUID(searchString)) {
return '';
}
const likeString = `ILIKE '%${searchString}%'`;
const searchArray: string[] = [];
switch (type) {
case SEGMENT_SEARCH_KEY.NAME || SEGMENT_SEARCH_KEY.CONTEXT:
searchString.push(`${type} ${likeString}`);
searchArray.push(`${type} ${likeString}`);
break;
case SEGMENT_SEARCH_KEY.TAG:
searchString.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
searchArray.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
break;
case SEGMENT_SEARCH_KEY.ID:
searchArray.push(`segment.id = '${searchString}'`);
break;
default:
searchString.push(`${SEGMENT_SEARCH_KEY.NAME} ${likeString}`);
searchString.push(`${SEGMENT_SEARCH_KEY.CONTEXT} ${likeString}`);
searchString.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
searchArray.push(`${SEGMENT_SEARCH_KEY.NAME} ${likeString}`);
searchArray.push(`${SEGMENT_SEARCH_KEY.CONTEXT} ${likeString}`);
searchArray.push(`ARRAY_TO_STRING(tags, ',') ${likeString}`);
if (isUUID(searchString)) {
searchArray.push(`segment.id = '${searchString}'`);
}
break;
}

const searchStringConcatenated = `(${searchString.join(' OR ')})`;
const searchStringConcatenated = `(${searchArray.join(' OR ')})`;
return searchStringConcatenated;
}

Expand Down
4 changes: 2 additions & 2 deletions backend/packages/Upgrade/src/api/services/UserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ export class UserService {
private paginatedSearchString(params: UserSearchParamsValidator): string {
const type = params.key;
// escape % and ' characters
const serachString = params.string.replace(/%/g, '\\$&').replace(/'/g, "''");
const likeString = `ILIKE '%${serachString}%'`;
const searchString = params.string.replace(/%/g, '\\$&').replace(/'/g, "''");
const likeString = `ILIKE '%${searchString}%'`;
const likes: string[] = [];
if (type === USER_SEARCH_SORT_KEY.ALL) {
likes.push(`users.email::TEXT ${likeString}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
[versionNumber]="flag.versionNumber"
[chipClass]="flag.status"
[showWarning]="shouldShowWarning$ | async"
[id]="flag.id"
></app-common-section-card-title-header>

<!-- header-right -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export class FeatureFlagRootSectionCardComponent {
featureFlagFilterOption = [
FLAG_SEARCH_KEY.ALL,
FLAG_SEARCH_KEY.NAME,
FLAG_SEARCH_KEY.ID,
FLAG_SEARCH_KEY.STATUS,
FLAG_SEARCH_KEY.CONTEXT,
FLAG_SEARCH_KEY.TAG,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class ExperimentListComponent implements OnInit, OnDestroy, AfterViewInit
experimentFilterOptions = [
EXPERIMENT_SEARCH_KEY.ALL,
EXPERIMENT_SEARCH_KEY.NAME,
EXPERIMENT_SEARCH_KEY.ID,
EXPERIMENT_SEARCH_KEY.STATUS,
EXPERIMENT_SEARCH_KEY.TAG,
EXPERIMENT_SEARCH_KEY.CONTEXT,
Expand Down Expand Up @@ -126,14 +127,17 @@ export class ExperimentListComponent implements OnInit, OnDestroy, AfterViewInit
data.state.toLocaleLowerCase().includes(filter) ||
!!data.tags.filter((tags) => tags.toLocaleLowerCase().includes(filter)).length ||
this.isPartitionFound(data, filter) ||
!!data.context.filter((context) => context.toLocaleLowerCase().includes(filter)).length
!!data.context.filter((context) => context.toLocaleLowerCase().includes(filter)).length ||
data.id.toLowerCase() === filter
);
case EXPERIMENT_SEARCH_KEY.NAME:
return data.name.toLocaleLowerCase().includes(filter);
case EXPERIMENT_SEARCH_KEY.TAG:
return !!data.tags.filter((tags) => tags.toLocaleLowerCase().includes(filter)).length;
case EXPERIMENT_SEARCH_KEY.CONTEXT:
return !!data.context.filter((context) => context.toLocaleLowerCase().includes(filter)).length;
case EXPERIMENT_SEARCH_KEY.ID:
return data.id.toLowerCase() === filter;
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
('home.view-experiment.updated-at.text' | translate) + (experiment.updatedAt | date : 'MMM d, y, h:mm a')
}}
</span>
<span class="ft-14-700">ID: {{ experiment.id }}</span>
</span>

<!-- <div class="action-buttons">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
[updatedAt]="segment.updatedAt"
[versionNumber]="segment.versionNumber"
[chipClass]="segment.status.toLowerCase()"
[id]="segment.id"
></app-common-section-card-title-header>
<!-- TODO: Update the SEGMENT_STATUS enum to lowercase and remove toLowerCase() once the legacy Segments code is removed -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export class SegmentRootSectionCardComponent {
segmentFilterOptions = [
SEGMENT_SEARCH_KEY.ALL,
SEGMENT_SEARCH_KEY.NAME,
SEGMENT_SEARCH_KEY.ID,
SEGMENT_SEARCH_KEY.CONTEXT,
SEGMENT_SEARCH_KEY.TAG,
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ <h5 class="title ft-18-700">
</span>
</ng-container>
</p>
<ng-container *ngIf="id">
<div class="subtitle ft-14-400">ID: {{ id }}</div>
</ng-container>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ export class CommonSectionCardTitleHeaderComponent {
@Input() versionNumber?: number;
@Input() chipClass?: STATUS_INDICATOR_CHIP_TYPE;
@Input() showWarning?: boolean;
@Input() id?: string;
}
3 changes: 3 additions & 0 deletions types/src/Experiment/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export enum EXPERIMENT_SEARCH_KEY {
STATUS = 'status',
TAG = 'tag',
CONTEXT = 'context',
ID = 'id',
}

export enum EXPERIMENT_SORT_KEY {
Expand Down Expand Up @@ -218,6 +219,7 @@ export enum SEGMENT_SEARCH_KEY {
TAG = 'tag',
STATUS = 'status',
CONTEXT = 'context',
ID = 'id',
}

export enum SEGMENT_SORT_KEY {
Expand All @@ -232,6 +234,7 @@ export enum FLAG_SEARCH_KEY {
STATUS = 'status',
TAG = 'tag',
CONTEXT = 'context',
ID = 'id',
}

export enum METRIC_SEARCH_KEY {
Expand Down