2021-07-07 10:46:50 +02:00
import { Knex } from 'knex' ;
import EventEmitter from 'events' ;
2021-09-10 11:42:11 +02:00
import { v4 as uuidv4 } from 'uuid' ;
2023-10-11 09:38:57 +02:00
import metricsHelper from '../../util/metrics-helper' ;
import { DB_TIME } from '../../metric-events' ;
import { Logger , LogProvider } from '../../logger' ;
import NotFoundError from '../../error/notfound-error' ;
2021-07-07 10:46:50 +02:00
import {
2021-08-12 15:04:37 +02:00
FeatureToggleWithEnvironment ,
2021-07-07 10:46:50 +02:00
IConstraint ,
2021-09-13 10:23:57 +02:00
IEnvironmentOverview ,
IFeatureOverview ,
2023-04-18 08:59:02 +02:00
IFeatureStrategiesStore ,
2021-08-12 15:04:37 +02:00
IFeatureStrategy ,
2022-11-16 15:35:39 +01:00
IFeatureToggleClient ,
2023-04-18 08:59:02 +02:00
IFlagResolver ,
2021-07-07 10:46:50 +02:00
IStrategyConfig ,
2023-07-13 16:04:55 +02:00
IStrategyVariant ,
2022-11-15 11:24:36 +01:00
ITag ,
2023-04-18 08:59:02 +02:00
PartialDeep ,
PartialSome ,
2023-10-11 09:38:57 +02:00
} from '../../types' ;
2022-07-01 13:51:26 +02:00
import FeatureToggleStore from './feature-toggle-store' ;
2023-10-11 09:38:57 +02:00
import { ensureStringValue , mapValues } from '../../util' ;
import { IFeatureProjectUserParams } from './feature-toggle-controller' ;
import { Db } from '../../db/db' ;
2023-04-18 08:59:02 +02:00
import Raw = Knex . Raw ;
2021-07-07 10:46:50 +02:00
const COLUMNS = [
'id' ,
'feature_name' ,
'project_name' ,
'environment' ,
'strategy_name' ,
2023-04-18 08:59:02 +02:00
'title' ,
2021-07-07 10:46:50 +02:00
'parameters' ,
'constraints' ,
2023-07-13 16:04:55 +02:00
'variants' ,
2021-07-07 10:46:50 +02:00
'created_at' ,
2023-04-21 11:09:07 +02:00
'disabled' ,
2021-07-07 10:46:50 +02:00
] ;
const T = {
features : 'features' ,
featureStrategies : 'feature_strategies' ,
2022-03-29 14:59:14 +02:00
featureStrategySegment : 'feature_strategy_segment' ,
2021-07-07 10:46:50 +02:00
featureEnvs : 'feature_environments' ,
2023-06-13 15:54:20 +02:00
strategies : 'strategies' ,
2021-07-07 10:46:50 +02:00
} ;
interface IFeatureStrategiesTable {
id : string ;
feature_name : string ;
project_name : string ;
environment : string ;
2023-04-18 08:59:02 +02:00
title? : string | null ;
2021-07-07 10:46:50 +02:00
strategy_name : string ;
parameters : object ;
constraints : string ;
2023-07-13 16:04:55 +02:00
variants : string ;
2021-09-17 15:11:17 +02:00
sort_order : number ;
2021-07-07 10:46:50 +02:00
created_at? : Date ;
2023-04-21 11:09:07 +02:00
disabled? : boolean | null ;
2021-07-07 10:46:50 +02:00
}
2022-11-29 16:06:08 +01:00
export interface ILoadFeatureToggleWithEnvsParams {
featureName : string ;
archived : boolean ;
withEnvironmentVariants : boolean ;
userId? : number ;
}
2021-07-07 10:46:50 +02:00
function mapRow ( row : IFeatureStrategiesTable ) : IFeatureStrategy {
return {
id : row.id ,
featureName : row.feature_name ,
2021-09-13 10:23:57 +02:00
projectId : row.project_name ,
2021-07-07 10:46:50 +02:00
environment : row.environment ,
strategyName : row.strategy_name ,
2023-04-18 08:59:02 +02:00
title : row.title ,
2022-06-29 10:11:34 +02:00
parameters : mapValues ( row . parameters || { } , ensureStringValue ) ,
2021-08-12 15:04:37 +02:00
constraints : ( row . constraints as unknown as IConstraint [ ] ) || [ ] ,
2023-07-13 16:04:55 +02:00
variants : ( row . variants as unknown as IStrategyVariant [ ] ) || [ ] ,
2021-07-07 10:46:50 +02:00
createdAt : row.created_at ,
2021-09-17 15:11:17 +02:00
sortOrder : row.sort_order ,
2023-04-21 11:09:07 +02:00
disabled : row.disabled ,
2021-07-07 10:46:50 +02:00
} ;
}
function mapInput ( input : IFeatureStrategy ) : IFeatureStrategiesTable {
return {
id : input.id ,
feature_name : input.featureName ,
2021-09-13 10:23:57 +02:00
project_name : input.projectId ,
2021-07-07 10:46:50 +02:00
environment : input.environment ,
strategy_name : input.strategyName ,
2023-04-18 08:59:02 +02:00
title : input.title ,
2021-07-07 10:46:50 +02:00
parameters : input.parameters ,
constraints : JSON.stringify ( input . constraints || [ ] ) ,
2023-07-13 16:04:55 +02:00
variants : JSON.stringify ( input . variants || [ ] ) ,
2021-07-07 10:46:50 +02:00
created_at : input.createdAt ,
2023-07-26 11:31:26 +02:00
sort_order : input.sortOrder ? ? 9999 ,
2023-04-21 11:09:07 +02:00
disabled : input.disabled ,
2021-07-07 10:46:50 +02:00
} ;
}
2023-10-10 07:34:21 +02:00
const getUniqueRows = ( rows : any [ ] ) = > {
const seen = { } ;
return rows . filter ( ( row ) = > {
const key = ` ${ row . environment } - ${ row . feature_name } ` ;
if ( seen [ key ] ) {
return false ;
}
seen [ key ] = true ;
return true ;
} ) ;
} ;
const sortEnvironments = ( overview : IFeatureOverview ) = > {
return Object . values ( overview ) . map ( ( data : IFeatureOverview ) = > ( {
. . . data ,
environments : data.environments
. filter ( ( f ) = > f . name )
. sort ( ( a , b ) = > {
if ( a . sortOrder === b . sortOrder ) {
return a . name . localeCompare ( b . name ) ;
}
return a . sortOrder - b . sortOrder ;
} ) ,
} ) ) ;
} ;
2021-07-07 10:46:50 +02:00
interface StrategyUpdate {
strategy_name : string ;
parameters : object ;
constraints : string ;
2023-07-13 16:04:55 +02:00
variants : string ;
2023-04-18 08:59:02 +02:00
title? : string ;
2023-04-21 11:09:07 +02:00
disabled? : boolean ;
2021-07-07 10:46:50 +02:00
}
function mapStrategyUpdate (
input : Partial < IStrategyConfig > ,
) : Partial < StrategyUpdate > {
const update : Partial < StrategyUpdate > = { } ;
if ( input . name !== null ) {
update . strategy_name = input . name ;
}
if ( input . parameters !== null ) {
update . parameters = input . parameters ;
}
2023-04-18 08:59:02 +02:00
if ( input . title !== null ) {
update . title = input . title ;
}
2023-04-21 11:09:07 +02:00
if ( input . disabled !== null ) {
update . disabled = input . disabled ;
}
2021-07-07 10:46:50 +02:00
update . constraints = JSON . stringify ( input . constraints || [ ] ) ;
2023-07-13 16:04:55 +02:00
update . variants = JSON . stringify ( input . variants || [ ] ) ;
2021-07-07 10:46:50 +02:00
return update ;
}
2021-08-12 15:04:37 +02:00
class FeatureStrategiesStore implements IFeatureStrategiesStore {
2023-01-30 09:02:44 +01:00
private db : Db ;
2021-07-07 10:46:50 +02:00
private logger : Logger ;
private readonly timer : Function ;
2022-11-15 11:24:36 +01:00
private flagResolver : IFlagResolver ;
constructor (
2023-01-30 09:02:44 +01:00
db : Db ,
2022-11-15 11:24:36 +01:00
eventBus : EventEmitter ,
getLogger : LogProvider ,
flagResolver : IFlagResolver ,
) {
2021-07-07 10:46:50 +02:00
this . db = db ;
this . logger = getLogger ( 'feature-toggle-store.ts' ) ;
2021-08-12 15:04:37 +02:00
this . timer = ( action ) = >
2021-07-07 10:46:50 +02:00
metricsHelper . wrapTimer ( eventBus , DB_TIME , {
store : 'feature-toggle-strategies' ,
action ,
} ) ;
2022-11-15 11:24:36 +01:00
this . flagResolver = flagResolver ;
2021-07-07 10:46:50 +02:00
}
2021-08-12 15:04:37 +02:00
async delete ( key : string ) : Promise < void > {
await this . db ( T . featureStrategies ) . where ( { id : key } ) . del ( ) ;
}
async deleteAll ( ) : Promise < void > {
await this . db ( T . featureStrategies ) . delete ( ) ;
}
destroy ( ) : void { }
async exists ( key : string ) : Promise < boolean > {
const result = await this . db . raw (
2022-03-29 14:59:14 +02:00
` SELECT EXISTS(SELECT 1 FROM ${ T . featureStrategies } WHERE id = ?) AS present ` ,
2021-08-12 15:04:37 +02:00
[ key ] ,
) ;
const { present } = result . rows [ 0 ] ;
return present ;
}
async get ( key : string ) : Promise < IFeatureStrategy > {
const row = await this . db ( T . featureStrategies )
. where ( { id : key } )
. first ( ) ;
2021-09-13 10:23:57 +02:00
if ( ! row ) {
throw new NotFoundError ( ` Could not find strategy with id= ${ key } ` ) ;
}
2021-08-12 15:04:37 +02:00
return mapRow ( row ) ;
}
2023-07-26 11:31:26 +02:00
private async nextSortOrder ( featureName : string , environment : string ) {
const [ { max } ] = await this . db ( T . featureStrategies )
. max ( 'sort_order as max' )
. where ( {
feature_name : featureName ,
environment ,
} ) ;
return Number . isInteger ( max ) ? max + 1 : 0 ;
}
2021-09-13 10:23:57 +02:00
async createStrategyFeatureEnv (
2022-03-29 14:59:14 +02:00
strategyConfig : PartialSome < IFeatureStrategy , ' id ' | ' createdAt ' > ,
2021-07-07 10:46:50 +02:00
) : Promise < IFeatureStrategy > {
2023-07-26 11:31:26 +02:00
const sortOrder =
strategyConfig . sortOrder ? ?
( await this . nextSortOrder (
strategyConfig . featureName ,
strategyConfig . environment ,
) ) ;
const strategyRow = mapInput ( {
id : uuidv4 ( ) ,
. . . strategyConfig ,
sortOrder ,
} ) ;
2021-07-07 10:46:50 +02:00
const rows = await this . db < IFeatureStrategiesTable > ( T . featureStrategies )
. insert ( strategyRow )
. returning ( '*' ) ;
return mapRow ( rows [ 0 ] ) ;
}
2021-09-13 10:23:57 +02:00
async removeAllStrategiesForFeatureEnv (
2021-07-07 10:46:50 +02:00
featureName : string ,
environment : string ,
) : Promise < void > {
await this . db ( 'feature_strategies' )
2021-09-13 10:23:57 +02:00
. where ( { feature_name : featureName , environment } )
2021-07-07 10:46:50 +02:00
. del ( ) ;
}
async getAll ( ) : Promise < IFeatureStrategy [ ] > {
const stopTimer = this . timer ( 'getAll' ) ;
const rows = await this . db
. select ( COLUMNS )
. from < IFeatureStrategiesTable > ( T . featureStrategies ) ;
stopTimer ( ) ;
return rows . map ( mapRow ) ;
}
2023-01-11 16:00:20 +01:00
async getAllByFeatures (
features : string [ ] ,
environment? : string ,
) : Promise < IFeatureStrategy [ ] > {
const query = this . db
. select ( COLUMNS )
. from < IFeatureStrategiesTable > ( T . featureStrategies )
2023-02-06 15:46:25 +01:00
. whereIn ( 'feature_name' , features )
. orderBy ( 'feature_name' , 'asc' ) ;
2023-01-27 11:34:20 +01:00
if ( environment ) {
query . where ( 'environment' , environment ) ;
2023-01-11 16:00:20 +01:00
}
const rows = await query ;
return rows . map ( mapRow ) ;
}
2021-09-13 10:23:57 +02:00
async getStrategiesForFeatureEnv (
projectId : string ,
featureName : string ,
2021-07-07 10:46:50 +02:00
environment : string ,
) : Promise < IFeatureStrategy [ ] > {
const stopTimer = this . timer ( 'getForFeature' ) ;
2021-09-17 15:11:17 +02:00
const rows = await this . db < IFeatureStrategiesTable > ( T . featureStrategies )
. where ( {
project_name : projectId ,
feature_name : featureName ,
environment ,
} )
2022-10-10 14:32:34 +02:00
. orderBy ( [
{ column : 'sort_order' , order : 'asc' } ,
{ column : 'created_at' , order : 'asc' } ,
] ) ;
2021-07-07 10:46:50 +02:00
stopTimer ( ) ;
return rows . map ( mapRow ) ;
}
2021-09-13 10:23:57 +02:00
async getFeatureToggleWithEnvs (
2021-07-07 10:46:50 +02:00
featureName : string ,
2022-11-29 16:06:08 +01:00
userId? : number ,
2021-07-07 10:46:50 +02:00
archived : boolean = false ,
2022-11-21 10:37:16 +01:00
) : Promise < FeatureToggleWithEnvironment > {
2022-11-29 16:06:08 +01:00
return this . loadFeatureToggleWithEnvs ( {
featureName ,
archived ,
withEnvironmentVariants : false ,
userId ,
} ) ;
2022-11-21 10:37:16 +01:00
}
async getFeatureToggleWithVariantEnvs (
featureName : string ,
2022-11-29 16:06:08 +01:00
userId? : number ,
2022-11-21 10:37:16 +01:00
archived : boolean = false ,
) : Promise < FeatureToggleWithEnvironment > {
2022-11-29 16:06:08 +01:00
return this . loadFeatureToggleWithEnvs ( {
featureName ,
archived ,
withEnvironmentVariants : true ,
userId ,
} ) ;
2022-11-21 10:37:16 +01:00
}
2022-11-29 16:06:08 +01:00
async loadFeatureToggleWithEnvs ( {
featureName ,
archived ,
withEnvironmentVariants ,
userId ,
} : ILoadFeatureToggleWithEnvsParams ) : Promise < FeatureToggleWithEnvironment > {
2021-07-07 10:46:50 +02:00
const stopTimer = this . timer ( 'getFeatureAdmin' ) ;
2022-11-29 16:06:08 +01:00
let query = this . db ( 'features_view' )
2022-11-21 10:37:16 +01:00
. where ( 'name' , featureName )
2022-07-01 13:51:26 +02:00
. modify ( FeatureToggleStore . filterByArchived , archived ) ;
2022-11-29 16:06:08 +01:00
2022-11-30 12:41:53 +01:00
let selectColumns = [ 'features_view.*' ] as ( string | Raw < any > ) [ ] ;
2023-10-10 14:40:36 +02:00
if ( this . flagResolver . isEnabled ( 'useLastSeenRefactor' ) ) {
query . leftJoin (
'last_seen_at_metrics' ,
'last_seen_at_metrics.environment' ,
'features_view.environment_name' ,
) ;
// Override feature view for now
selectColumns . push (
'last_seen_at_metrics.last_seen_at as env_last_seen_at' ,
) ;
}
2022-12-21 13:03:06 +01:00
if ( userId ) {
2022-11-30 08:47:57 +01:00
query = query . leftJoin ( ` favorite_features ` , function ( ) {
this . on (
'favorite_features.feature' ,
'features_view.name' ,
) . andOnVal ( 'favorite_features.user_id' , '=' , userId ) ;
2022-11-29 16:06:08 +01:00
} ) ;
2022-11-30 08:47:57 +01:00
selectColumns = [
. . . selectColumns ,
2022-11-30 12:41:53 +01:00
this . db . raw (
'favorite_features.feature is not null as favorite' ,
) ,
2022-11-30 08:47:57 +01:00
] ;
2022-11-29 16:06:08 +01:00
}
2023-10-10 14:40:36 +02:00
2022-11-29 16:06:08 +01:00
const rows = await query . select ( selectColumns ) ;
2021-07-07 10:46:50 +02:00
stopTimer ( ) ;
2023-10-10 14:40:36 +02:00
2021-07-07 10:46:50 +02:00
if ( rows . length > 0 ) {
const featureToggle = rows . reduce ( ( acc , r ) = > {
if ( acc . environments === undefined ) {
acc . environments = { } ;
}
2022-11-16 15:35:39 +01:00
2021-07-07 10:46:50 +02:00
acc . name = r . name ;
2022-11-30 12:41:53 +01:00
acc . favorite = r . favorite ;
2022-02-03 11:06:51 +01:00
acc . impressionData = r . impression_data ;
2021-07-07 10:46:50 +02:00
acc . description = r . description ;
2021-08-25 15:14:07 +02:00
acc . project = r . project ;
2021-07-07 10:46:50 +02:00
acc . stale = r . stale ;
2023-08-04 08:59:54 +02:00
acc . lastSeenAt = r . last_seen_at ;
2022-11-21 10:37:16 +01:00
2021-08-19 13:34:24 +02:00
acc . createdAt = r . created_at ;
2021-07-07 10:46:50 +02:00
acc . type = r . type ;
if ( ! acc . environments [ r . environment ] ) {
acc . environments [ r . environment ] = {
name : r.environment ,
2023-08-04 08:59:54 +02:00
lastSeenAt : r.env_last_seen_at ,
2021-07-07 10:46:50 +02:00
} ;
}
const env = acc . environments [ r . environment ] ;
2022-11-21 10:37:16 +01:00
const variants = r . variants || [ ] ;
variants . sort ( ( a , b ) = > a . name . localeCompare ( b . name ) ) ;
if ( withEnvironmentVariants ) {
env . variants = variants ;
}
2022-12-06 10:47:54 +01:00
// this code sets variants at the feature level (should be deprecated with variants per environment)
const currentVariants = new Map (
acc . variants ? . map ( ( v ) = > [ v . name , v ] ) ,
) ;
variants . forEach ( ( variant ) = > {
currentVariants . set ( variant . name , variant ) ;
} ) ;
acc . variants = Array . from ( currentVariants . values ( ) ) ;
2022-11-21 10:37:16 +01:00
2021-07-07 10:46:50 +02:00
env . enabled = r . enabled ;
2021-09-23 16:16:31 +02:00
env . type = r . environment_type ;
env . sortOrder = r . environment_sort_order ;
2021-07-07 10:46:50 +02:00
if ( ! env . strategies ) {
env . strategies = [ ] ;
}
2021-09-13 10:23:57 +02:00
if ( r . strategy_id ) {
2022-11-16 15:35:39 +01:00
const found = env . strategies . find (
( strategy ) = > strategy . id === r . strategy_id ,
Complete open api schemas for project features controller (#1563)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* revert
* revert
* revert
* revert
* revert
* mapper
* revert
* revert
* revert
* remove serialize-dates.ts
* remove serialize-dates.ts
* remove serialize-dates.ts
* remove serialize-dates.ts
* remove serialize-dates.ts
* revert
* revert
* add mappers
* add mappers
* fix pr comments
* ignore report.json
* ignore report.json
* Route permission required
Co-authored-by: olav <mail@olav.io>
2022-05-18 15:17:09 +02:00
) ;
2022-11-16 15:35:39 +01:00
if ( ! found ) {
env . strategies . push (
FeatureStrategiesStore . getAdminStrategy ( r ) ,
) ;
}
}
if ( r . segments ) {
this . addSegmentIdsToStrategy ( env , r ) ;
2021-09-13 10:23:57 +02:00
}
2021-07-07 10:46:50 +02:00
acc . environments [ r . environment ] = env ;
return acc ;
} , { } ) ;
featureToggle . environments = Object . values (
featureToggle . environments ,
2021-09-23 16:16:31 +02:00
) . sort ( ( a , b ) = > {
2022-06-07 11:49:17 +02:00
// @ts-expect-error
2021-09-23 16:16:31 +02:00
return a . sortOrder - b . sortOrder ;
} ) ;
2021-09-17 15:11:17 +02:00
featureToggle . environments = featureToggle . environments . map ( ( e ) = > {
e . strategies = e . strategies . sort (
( a , b ) = > a . sortOrder - b . sortOrder ,
) ;
2023-05-02 20:33:14 +02:00
if ( e . strategies && e . strategies . length === 0 ) {
e . enabled = false ;
}
2021-09-17 15:11:17 +02:00
return e ;
} ) ;
2023-05-02 20:33:14 +02:00
2021-08-26 13:59:11 +02:00
featureToggle . archived = archived ;
2021-07-07 10:46:50 +02:00
return featureToggle ;
}
throw new NotFoundError (
` Could not find feature toggle with name ${ featureName } ` ,
) ;
}
2022-11-16 15:35:39 +01:00
private addSegmentIdsToStrategy (
2023-05-02 20:33:14 +02:00
featureToggle : PartialDeep < IFeatureToggleClient > ,
2022-11-16 15:35:39 +01:00
row : Record < string , any > ,
) {
2023-05-02 20:33:14 +02:00
const strategy = featureToggle . strategies ? . find (
2023-04-18 08:59:02 +02:00
( s ) = > s ? . id === row . strategy_id ,
2022-11-16 15:35:39 +01:00
) ;
if ( ! strategy ) {
return ;
}
if ( ! strategy . segments ) {
strategy . segments = [ ] ;
}
strategy . segments . push ( row . segments ) ;
}
Complete open api schemas for project features controller (#1563)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* revert
* revert
* revert
* revert
* revert
* mapper
* revert
* revert
* revert
* remove serialize-dates.ts
* remove serialize-dates.ts
* remove serialize-dates.ts
* remove serialize-dates.ts
* remove serialize-dates.ts
* revert
* revert
* add mappers
* add mappers
* fix pr comments
* ignore report.json
* ignore report.json
* Route permission required
Co-authored-by: olav <mail@olav.io>
2022-05-18 15:17:09 +02:00
private static getEnvironment ( r : any ) : IEnvironmentOverview {
2021-09-13 10:23:57 +02:00
return {
name : r.environment ,
enabled : r.enabled ,
2021-09-23 16:16:31 +02:00
type : r . environment_type ,
sortOrder : r.environment_sort_order ,
2023-01-20 18:09:01 +01:00
variantCount : r.variants?.length || 0 ,
2023-08-04 08:59:54 +02:00
lastSeenAt : r.env_last_seen_at ,
2023-10-20 10:50:57 +02:00
hasStrategies : r.has_strategies ,
hasEnabledStrategies : r.has_enabled_strategies ,
2021-09-13 10:23:57 +02:00
} ;
}
2022-11-15 11:24:36 +01:00
private addTag (
2023-05-02 20:33:14 +02:00
featureToggle : Record < string , any > ,
2022-11-15 11:24:36 +01:00
row : Record < string , any > ,
) : void {
2023-05-02 20:33:14 +02:00
const tags = featureToggle . tags || [ ] ;
2022-11-15 11:24:36 +01:00
const newTag = FeatureStrategiesStore . rowToTag ( row ) ;
2023-05-02 20:33:14 +02:00
featureToggle . tags = [ . . . tags , newTag ] ;
2022-11-15 11:24:36 +01:00
}
private isNewTag (
2023-05-02 20:33:14 +02:00
featureToggle : Record < string , any > ,
2022-11-15 11:24:36 +01:00
row : Record < string , any > ,
) : boolean {
return (
row . tag_type &&
row . tag_value &&
2023-05-02 20:33:14 +02:00
! featureToggle . tags ? . some (
2022-11-15 11:24:36 +01:00
( tag ) = >
tag . type === row . tag_type && tag . value === row . tag_value ,
)
) ;
}
private static rowToTag ( r : any ) : ITag {
return {
value : r.tag_value ,
type : r . tag_type ,
} ;
}
2023-10-26 12:50:02 +02:00
// WIP copy of getFeatureOverview to get the search PoC working
async searchFeatures ( {
projectId ,
userId ,
queryString ,
} : { projectId : string ; userId? : number ; queryString : string } ) : Promise <
IFeatureOverview [ ]
> {
let query = this . db ( 'features' ) ;
if ( projectId ) {
query = query . where ( { project : projectId } ) ;
}
if ( queryString ? . trim ( ) ) {
// todo: we can run cheaper query when no colon is detected
const tagQuery = this . db
. from ( 'feature_tag' )
. select ( 'feature_name' )
. whereRaw ( "(?? || ':' || ??) LIKE ?" , [
'tag_type' ,
'tag_value' ,
` % ${ queryString } % ` ,
] ) ;
query = query
. whereILike ( 'features.name' , ` % ${ queryString } % ` )
. orWhereIn ( 'features.name' , tagQuery ) ;
}
query = query
. modify ( FeatureToggleStore . filterByArchived , false )
. leftJoin (
'feature_environments' ,
'feature_environments.feature_name' ,
'features.name' ,
)
. leftJoin (
'environments' ,
'feature_environments.environment' ,
'environments.name' ,
)
. leftJoin ( 'feature_tag as ft' , 'ft.feature_name' , 'features.name' ) ;
if ( this . flagResolver . isEnabled ( 'useLastSeenRefactor' ) ) {
query . leftJoin ( 'last_seen_at_metrics' , function ( ) {
this . on (
'last_seen_at_metrics.environment' ,
'=' ,
'environments.name' ,
) . andOn (
'last_seen_at_metrics.feature_name' ,
'=' ,
'features.name' ,
) ;
} ) ;
}
let selectColumns = [
'features.name as feature_name' ,
'features.description as description' ,
'features.type as type' ,
'features.created_at as created_at' ,
'features.last_seen_at as last_seen_at' ,
'features.stale as stale' ,
'features.impression_data as impression_data' ,
'feature_environments.enabled as enabled' ,
'feature_environments.environment as environment' ,
'feature_environments.variants as variants' ,
'environments.type as environment_type' ,
'environments.sort_order as environment_sort_order' ,
'ft.tag_value as tag_value' ,
'ft.tag_type as tag_type' ,
] as ( string | Raw < any > | Knex . QueryBuilder ) [ ] ;
if ( this . flagResolver . isEnabled ( 'useLastSeenRefactor' ) ) {
selectColumns . push (
'last_seen_at_metrics.last_seen_at as env_last_seen_at' ,
) ;
} else {
selectColumns . push (
'feature_environments.last_seen_at as env_last_seen_at' ,
) ;
}
if ( userId ) {
query = query . leftJoin ( ` favorite_features ` , function ( ) {
this . on ( 'favorite_features.feature' , 'features.name' ) . andOnVal (
'favorite_features.user_id' ,
'=' ,
userId ,
) ;
} ) ;
selectColumns = [
. . . selectColumns ,
this . db . raw (
'favorite_features.feature is not null as favorite' ,
) ,
] ;
}
if ( this . flagResolver . isEnabled ( 'featureSwitchRefactor' ) ) {
selectColumns = [
. . . selectColumns ,
this . db . raw (
'EXISTS (SELECT 1 FROM feature_strategies WHERE feature_strategies.feature_name = features.name AND feature_strategies.environment = feature_environments.environment) as has_strategies' ,
) ,
this . db . raw (
'EXISTS (SELECT 1 FROM feature_strategies WHERE feature_strategies.feature_name = features.name AND feature_strategies.environment = feature_environments.environment AND (feature_strategies.disabled IS NULL OR feature_strategies.disabled = false)) as has_enabled_strategies' ,
) ,
] ;
}
query = query . select ( selectColumns ) ;
const rows = await query ;
if ( rows . length > 0 ) {
const overview = this . getFeatureOverviewData ( getUniqueRows ( rows ) ) ;
return sortEnvironments ( overview ) ;
}
return [ ] ;
}
2022-11-29 16:06:08 +01:00
async getFeatureOverview ( {
projectId ,
archived ,
userId ,
2022-12-16 12:05:18 +01:00
tag ,
namePrefix ,
2022-11-29 16:06:08 +01:00
} : IFeatureProjectUserParams ) : Promise < IFeatureOverview [ ] > {
2022-12-16 12:05:18 +01:00
let query = this . db ( 'features' ) . where ( { project : projectId } ) ;
2023-10-26 12:50:02 +02:00
2022-12-16 12:05:18 +01:00
if ( tag ) {
const tagQuery = this . db
. from ( 'feature_tag' )
. select ( 'feature_name' )
. whereIn ( [ 'tag_type' , 'tag_value' ] , tag ) ;
query = query . whereIn ( 'features.name' , tagQuery ) ;
}
2023-09-29 14:18:21 +02:00
if ( namePrefix ? . trim ( ) ) {
2022-12-16 12:05:18 +01:00
let namePrefixQuery = namePrefix ;
if ( ! namePrefix . endsWith ( '%' ) ) {
2023-09-29 14:18:21 +02:00
namePrefixQuery = ` ${ namePrefixQuery } % ` ;
2022-12-16 12:05:18 +01:00
}
query = query . whereILike ( 'features.name' , namePrefixQuery ) ;
}
query = query
2022-07-01 13:51:26 +02:00
. modify ( FeatureToggleStore . filterByArchived , archived )
2022-07-26 10:02:28 +02:00
. leftJoin (
2021-07-07 10:46:50 +02:00
'feature_environments' ,
'feature_environments.feature_name' ,
'features.name' ,
)
2022-07-26 10:02:28 +02:00
. leftJoin (
2021-09-13 10:23:57 +02:00
'environments' ,
'feature_environments.environment' ,
'environments.name' ,
2022-12-12 14:21:12 +01:00
)
. leftJoin ( 'feature_tag as ft' , 'ft.feature_name' , 'features.name' ) ;
2022-11-15 11:24:36 +01:00
2023-10-10 07:34:21 +02:00
if ( this . flagResolver . isEnabled ( 'useLastSeenRefactor' ) ) {
2023-10-24 17:26:18 +02:00
query . leftJoin ( 'last_seen_at_metrics' , function ( ) {
this . on (
'last_seen_at_metrics.environment' ,
'=' ,
'environments.name' ,
) . andOn (
'last_seen_at_metrics.feature_name' ,
'=' ,
'features.name' ,
) ;
} ) ;
2023-10-10 07:34:21 +02:00
}
2022-11-29 16:06:08 +01:00
let selectColumns = [
'features.name as feature_name' ,
2023-04-07 11:16:00 +02:00
'features.description as description' ,
2022-11-29 16:06:08 +01:00
'features.type as type' ,
'features.created_at as created_at' ,
'features.last_seen_at as last_seen_at' ,
'features.stale as stale' ,
2023-04-07 11:16:00 +02:00
'features.impression_data as impression_data' ,
2022-11-29 16:06:08 +01:00
'feature_environments.enabled as enabled' ,
'feature_environments.environment as environment' ,
2023-01-20 18:09:01 +01:00
'feature_environments.variants as variants' ,
2022-11-29 16:06:08 +01:00
'environments.type as environment_type' ,
'environments.sort_order as environment_sort_order' ,
2022-12-12 14:21:12 +01:00
'ft.tag_value as tag_value' ,
'ft.tag_type as tag_type' ,
2023-10-20 10:50:57 +02:00
] as ( string | Raw < any > | Knex . QueryBuilder ) [ ] ;
2022-11-29 16:06:08 +01:00
2023-10-10 07:34:21 +02:00
if ( this . flagResolver . isEnabled ( 'useLastSeenRefactor' ) ) {
selectColumns . push (
'last_seen_at_metrics.last_seen_at as env_last_seen_at' ,
) ;
} else {
selectColumns . push (
'feature_environments.last_seen_at as env_last_seen_at' ,
) ;
}
2022-12-21 13:03:06 +01:00
if ( userId ) {
2022-11-30 12:41:53 +01:00
query = query . leftJoin ( ` favorite_features ` , function ( ) {
this . on ( 'favorite_features.feature' , 'features.name' ) . andOnVal (
'favorite_features.user_id' ,
2022-11-29 16:06:08 +01:00
'=' ,
userId ,
) ;
} ) ;
2022-11-30 12:41:53 +01:00
selectColumns = [
. . . selectColumns ,
this . db . raw (
'favorite_features.feature is not null as favorite' ,
) ,
] ;
2022-11-15 11:24:36 +01:00
}
2023-10-20 10:50:57 +02:00
if ( this . flagResolver . isEnabled ( 'featureSwitchRefactor' ) ) {
selectColumns = [
. . . selectColumns ,
this . db . raw (
'EXISTS (SELECT 1 FROM feature_strategies WHERE feature_strategies.feature_name = features.name AND feature_strategies.environment = feature_environments.environment) as has_strategies' ,
) ,
this . db . raw (
'EXISTS (SELECT 1 FROM feature_strategies WHERE feature_strategies.feature_name = features.name AND feature_strategies.environment = feature_environments.environment AND (feature_strategies.disabled IS NULL OR feature_strategies.disabled = false)) as has_enabled_strategies' ,
) ,
] ;
}
2022-11-29 16:06:08 +01:00
query = query . select ( selectColumns ) ;
2022-11-15 11:24:36 +01:00
const rows = await query ;
2021-09-13 10:23:57 +02:00
if ( rows . length > 0 ) {
2023-10-10 07:34:21 +02:00
const overview = this . getFeatureOverviewData ( getUniqueRows ( rows ) ) ;
return sortEnvironments ( overview ) ;
2021-07-07 10:46:50 +02:00
}
2021-09-13 10:23:57 +02:00
return [ ] ;
2021-07-07 10:46:50 +02:00
}
2023-10-10 07:34:21 +02:00
getFeatureOverviewData ( rows ) : IFeatureOverview {
return rows . reduce ( ( acc , row ) = > {
if ( acc [ row . feature_name ] !== undefined ) {
acc [ row . feature_name ] . environments . push (
FeatureStrategiesStore . getEnvironment ( row ) ,
) ;
if ( this . isNewTag ( acc [ row . feature_name ] , row ) ) {
this . addTag ( acc [ row . feature_name ] , row ) ;
}
} else {
acc [ row . feature_name ] = {
type : row . type ,
description : row.description ,
favorite : row.favorite ,
name : row.feature_name ,
createdAt : row.created_at ,
lastSeenAt : row.last_seen_at ,
stale : row.stale ,
impressionData : row.impression_data ,
environments : [ FeatureStrategiesStore . getEnvironment ( row ) ] ,
} ;
if ( this . isNewTag ( acc [ row . feature_name ] , row ) ) {
this . addTag ( acc [ row . feature_name ] , row ) ;
}
}
return acc ;
} , { } ) ;
}
2021-07-07 10:46:50 +02:00
async getStrategyById ( id : string ) : Promise < IFeatureStrategy > {
2021-08-12 15:04:37 +02:00
const strat = await this . db ( T . featureStrategies ) . where ( { id } ) . first ( ) ;
2021-07-07 10:46:50 +02:00
if ( strat ) {
return mapRow ( strat ) ;
}
throw new NotFoundError ( ` Could not find strategy with id: ${ id } ` ) ;
}
2022-07-26 14:16:30 +02:00
async updateSortOrder ( id : string , sortOrder : number ) : Promise < void > {
await this . db < IFeatureStrategiesTable > ( T . featureStrategies )
. where ( { id } )
. update ( { sort_order : sortOrder } ) ;
}
2021-07-07 10:46:50 +02:00
async updateStrategy (
id : string ,
updates : Partial < IFeatureStrategy > ,
) : Promise < IFeatureStrategy > {
const update = mapStrategyUpdate ( updates ) ;
const row = await this . db < IFeatureStrategiesTable > ( T . featureStrategies )
. where ( { id } )
. update ( update )
. returning ( '*' ) ;
return mapRow ( row [ 0 ] ) ;
}
Complete open api schemas for project features controller (#1563)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* Completed OpenAPI Schemas for ProjectFeatures Controller
Completed OpenAPI Schemas for Feature Controller (tags)
* bug fix
* bug fix
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* fix merge conflicts, some refactoring
* added emptyResponse, patch feature operation schemas and request
* added emptyResponse, patch feature operation schemas and request
* patch strategy
* patch strategy
* update strategy
* update strategy
* fix pr comment
* fix pr comments
* improvements
* added operationId to schema for better generation
* fix pr comment
* fix pr comment
* fix pr comment
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* improvements to generated and dynamic types
* Update response types to use inferred types
* Update addTag response status to 201
* refactor: move schema ref destructuring into createSchemaObject
* made serialize date handle deep objects
* made serialize date handle deep objects
* add `name` to IFeatureStrategy nad fix tests
* fix pr comments
* fix pr comments
* Add types to IAuthRequest
* Sync StrategySchema for FE and BE - into the rabbit hole
* Sync model with OAS spec
* revert
* revert
* revert
* revert
* revert
* mapper
* revert
* revert
* revert
* remove serialize-dates.ts
* remove serialize-dates.ts
* remove serialize-dates.ts
* remove serialize-dates.ts
* remove serialize-dates.ts
* revert
* revert
* add mappers
* add mappers
* fix pr comments
* ignore report.json
* ignore report.json
* Route permission required
Co-authored-by: olav <mail@olav.io>
2022-05-18 15:17:09 +02:00
private static getAdminStrategy (
2021-07-07 10:46:50 +02:00
r : any ,
includeId : boolean = true ,
) : IStrategyConfig {
const strategy = {
name : r.strategy_name ,
constraints : r.constraints || [ ] ,
2023-07-13 16:04:55 +02:00
variants : r.strategy_variants || [ ] ,
2021-07-07 10:46:50 +02:00
parameters : r.parameters ,
2021-09-17 15:11:17 +02:00
sortOrder : r.sort_order ,
2021-07-07 10:46:50 +02:00
id : r.strategy_id ,
2023-04-18 08:59:02 +02:00
title : r.strategy_title || '' ,
2023-04-21 11:09:07 +02:00
disabled : r.strategy_disabled || false ,
2021-07-07 10:46:50 +02:00
} ;
if ( ! includeId ) {
delete strategy . id ;
}
return strategy ;
}
async deleteConfigurationsForProjectAndEnvironment (
projectId : String ,
environment : String ,
) : Promise < void > {
await this . db ( T . featureStrategies )
. where ( { project_name : projectId , environment } )
. del ( ) ;
}
2021-10-19 09:49:43 +02:00
async setProjectForStrategiesBelongingToFeature (
featureName : string ,
newProjectId : string ,
) : Promise < void > {
await this . db ( T . featureStrategies )
. where ( { feature_name : featureName } )
. update ( { project_name : newProjectId } ) ;
}
2022-03-29 14:59:14 +02:00
async getStrategiesBySegment (
segmentId : number ,
) : Promise < IFeatureStrategy [ ] > {
const stopTimer = this . timer ( 'getStrategiesBySegment' ) ;
const rows = await this . db
. select ( this . prefixColumns ( ) )
. from < IFeatureStrategiesTable > ( T . featureStrategies )
. join (
T . featureStrategySegment ,
` ${ T . featureStrategySegment } .feature_strategy_id ` ,
` ${ T . featureStrategies } .id ` ,
)
. where ( ` ${ T . featureStrategySegment } .segment_id ` , '=' , segmentId ) ;
stopTimer ( ) ;
return rows . map ( mapRow ) ;
}
2023-06-09 11:00:17 +02:00
async getStrategiesByContextField (
contextFieldName : string ,
) : Promise < IFeatureStrategy [ ] > {
const stopTimer = this . timer ( 'getStrategiesByContextField' ) ;
const rows = await this . db
. select ( this . prefixColumns ( ) )
. from < IFeatureStrategiesTable > ( T . featureStrategies )
. where (
this . db . raw (
"EXISTS (SELECT 1 FROM jsonb_array_elements(constraints) AS elem WHERE elem ->> 'contextName' = ?)" ,
contextFieldName ,
) ,
) ;
stopTimer ( ) ;
return rows . map ( mapRow ) ;
}
2022-03-29 14:59:14 +02:00
prefixColumns ( ) : string [ ] {
return COLUMNS . map ( ( c ) = > ` ${ T . featureStrategies } . ${ c } ` ) ;
}
2023-06-13 15:54:20 +02:00
async getCustomStrategiesInUseCount ( ) : Promise < number > {
const stopTimer = this . timer ( 'getCustomStrategiesInUseCount' ) ;
const notBuiltIn = '0' ;
const columns = [
this . db . raw ( 'count(fes.strategy_name) as times_used' ) ,
'fes.strategy_name' ,
] ;
const rows = await this . db ( ` ${ T . strategies } as str ` )
. select ( columns )
. join (
` ${ T . featureStrategies } as fes ` ,
'fes.strategy_name' ,
'str.name' ,
)
. where ( ` str.built_in ` , '=' , notBuiltIn )
. groupBy ( 'strategy_name' ) ;
stopTimer ( ) ;
return rows . length ;
}
2021-07-07 10:46:50 +02:00
}
module .exports = FeatureStrategiesStore ;
export default FeatureStrategiesStore ;