2021-07-07 10:46:50 +02:00
import EventEmitter from 'events' ;
2023-01-30 09:02:44 +01:00
import { Db } from './db' ;
2021-07-07 10:46:50 +02:00
import { Logger , LogProvider } from '../logger' ;
import metricsHelper from '../util/metrics-helper' ;
import { DB_TIME } from '../metric-events' ;
2022-11-11 11:24:56 +01:00
import {
IEnvironment ,
IEnvironmentCreate ,
IProjectEnvironment ,
} from '../types/model' ;
2021-07-07 10:46:50 +02:00
import NotFoundError from '../error/notfound-error' ;
2021-08-12 15:04:37 +02:00
import { IEnvironmentStore } from '../types/stores/environment-store' ;
2021-09-13 15:57:38 +02:00
import { snakeCaseKeys } from '../util/snakeCase' ;
2023-05-05 13:32:44 +02:00
import { CreateFeatureStrategySchema } from '../openapi' ;
2021-07-07 10:46:50 +02:00
interface IEnvironmentsTable {
name : string ;
created_at? : Date ;
2021-09-13 15:57:38 +02:00
type : string ;
sort_order : number ;
enabled : boolean ;
protected : boolean ;
2021-07-07 10:46:50 +02:00
}
2022-11-11 11:24:56 +01:00
interface IEnvironmentsWithCountsTable extends IEnvironmentsTable {
project_count? : string ;
api_token_count? : string ;
enabled_toggle_count? : string ;
}
interface IEnvironmentsWithProjectCountsTable extends IEnvironmentsTable {
project_api_token_count? : string ;
project_enabled_toggle_count? : string ;
2023-05-05 13:32:44 +02:00
project_default_strategy? : CreateFeatureStrategySchema ;
2022-11-11 11:24:56 +01:00
}
2021-09-13 15:57:38 +02:00
const COLUMNS = [
'type' ,
'name' ,
'created_at' ,
'sort_order' ,
'enabled' ,
'protected' ,
] ;
2021-07-07 10:46:50 +02:00
function mapRow ( row : IEnvironmentsTable ) : IEnvironment {
return {
name : row.name ,
2021-09-13 15:57:38 +02:00
type : row . type ,
sortOrder : row.sort_order ,
enabled : row.enabled ,
protected : row . protected ,
2021-07-07 10:46:50 +02:00
} ;
}
2022-11-11 11:24:56 +01:00
function mapRowWithCounts (
row : IEnvironmentsWithCountsTable ,
) : IProjectEnvironment {
return {
. . . mapRow ( row ) ,
projectCount : row.project_count ? parseInt ( row . project_count , 10 ) : 0 ,
apiTokenCount : row.api_token_count
? parseInt ( row . api_token_count , 10 )
: 0 ,
enabledToggleCount : row.enabled_toggle_count
? parseInt ( row . enabled_toggle_count , 10 )
: 0 ,
} ;
}
function mapRowWithProjectCounts (
row : IEnvironmentsWithProjectCountsTable ,
) : IProjectEnvironment {
return {
. . . mapRow ( row ) ,
projectApiTokenCount : row.project_api_token_count
? parseInt ( row . project_api_token_count , 10 )
: 0 ,
projectEnabledToggleCount : row.project_enabled_toggle_count
? parseInt ( row . project_enabled_toggle_count , 10 )
: 0 ,
2023-05-05 13:32:44 +02:00
defaultStrategy : row.project_default_strategy
? ( row . project_default_strategy as any )
: undefined ,
2022-11-11 11:24:56 +01:00
} ;
}
2021-09-24 13:55:00 +02:00
function fieldToRow ( env : IEnvironment ) : IEnvironmentsTable {
return {
name : env.name ,
type : env . type ,
sort_order : env.sortOrder ,
enabled : env.enabled ,
protected : env . protected ,
} ;
}
2021-07-07 10:46:50 +02:00
const TABLE = 'environments' ;
2021-08-12 15:04:37 +02:00
export default class EnvironmentStore implements IEnvironmentStore {
2021-07-07 10:46:50 +02:00
private logger : Logger ;
2023-01-30 09:02:44 +01:00
private db : Db ;
2021-07-07 10:46:50 +02:00
private timer : ( string ) = > any ;
2023-01-30 09:02:44 +01:00
constructor ( db : Db , eventBus : EventEmitter , getLogger : LogProvider ) {
2021-07-07 10:46:50 +02:00
this . db = db ;
this . logger = getLogger ( 'db/environment-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 : 'environment' ,
action ,
} ) ;
}
2021-09-24 13:55:00 +02:00
async importEnvironments (
environments : IEnvironment [ ] ,
) : Promise < IEnvironment [ ] > {
const rows = await this . db ( TABLE )
. insert ( environments . map ( fieldToRow ) )
. returning ( COLUMNS )
. onConflict ( 'name' )
. ignore ( ) ;
return rows . map ( mapRow ) ;
}
2021-08-12 15:04:37 +02:00
async deleteAll ( ) : Promise < void > {
await this . db ( TABLE ) . del ( ) ;
}
2022-09-06 13:24:13 +02:00
count ( ) : Promise < number > {
return this . db
. from ( TABLE )
. count ( '*' )
. then ( ( res ) = > Number ( res [ 0 ] . count ) ) ;
}
2023-06-15 11:21:35 +02:00
getMaxSortOrder ( ) : Promise < number > {
return this . db
. from ( TABLE )
. max ( 'sort_order' )
. then ( ( res ) = > Number ( res [ 0 ] . max ) ) ;
}
2021-08-12 15:04:37 +02:00
async get ( key : string ) : Promise < IEnvironment > {
const row = await this . db < IEnvironmentsTable > ( TABLE )
. where ( { name : key } )
. first ( ) ;
if ( row ) {
return mapRow ( row ) ;
}
throw new NotFoundError ( ` Could not find environment with name: ${ key } ` ) ;
}
2021-09-29 11:09:35 +02:00
async getAll ( query? : Object ) : Promise < IEnvironment [ ] > {
let qB = this . db < IEnvironmentsTable > ( TABLE )
2021-09-13 15:57:38 +02:00
. select ( '*' )
2022-10-10 14:32:34 +02:00
. orderBy ( [
{ column : 'sort_order' , order : 'asc' } ,
{ column : 'created_at' , order : 'asc' } ,
] ) ;
2021-09-29 11:09:35 +02:00
if ( query ) {
qB = qB . where ( query ) ;
}
const rows = await qB ;
2021-07-07 10:46:50 +02:00
return rows . map ( mapRow ) ;
}
2022-11-11 11:24:56 +01:00
async getAllWithCounts ( query? : Object ) : Promise < IEnvironment [ ] > {
let qB = this . db < IEnvironmentsWithCountsTable > ( TABLE )
. select (
'*' ,
this . db . raw (
'(SELECT COUNT(*) FROM project_environments WHERE project_environments.environment_name = environments.name) as project_count' ,
) ,
this . db . raw (
'(SELECT COUNT(*) FROM api_tokens WHERE api_tokens.environment = environments.name) as api_token_count' ,
) ,
this . db . raw (
'(SELECT COUNT(*) FROM feature_environments WHERE enabled=true AND feature_environments.environment = environments.name) as enabled_toggle_count' ,
) ,
)
. orderBy ( [
{ column : 'sort_order' , order : 'asc' } ,
{ column : 'created_at' , order : 'asc' } ,
] ) ;
if ( query ) {
qB = qB . where ( query ) ;
}
const rows = await qB ;
return rows . map ( mapRowWithCounts ) ;
}
async getProjectEnvironments (
projectId : string ,
2023-01-19 13:27:50 +01:00
query? : Object ,
2022-11-11 11:24:56 +01:00
) : Promise < IProjectEnvironment [ ] > {
let qB = this . db < IEnvironmentsWithProjectCountsTable > ( TABLE )
. select (
'*' ,
this . db . raw (
'(SELECT COUNT(*) FROM api_tokens LEFT JOIN api_token_project ON api_tokens.secret = api_token_project.secret WHERE api_tokens.environment = environments.name AND (project = :projectId OR project IS null)) as project_api_token_count' ,
{ projectId } ,
) ,
this . db . raw (
'(SELECT COUNT(*) FROM feature_environments INNER JOIN features on feature_environments.feature_name = features.name WHERE enabled=true AND feature_environments.environment = environments.name AND project = :projectId) as project_enabled_toggle_count' ,
{ projectId } ,
) ,
2023-05-05 13:32:44 +02:00
this . db . raw (
'(SELECT default_strategy FROM project_environments pe WHERE pe.environment_name = environments.name AND pe.project_id = :projectId) as project_default_strategy' ,
{ projectId } ,
) ,
2022-11-11 11:24:56 +01:00
)
. orderBy ( [
{ column : 'sort_order' , order : 'asc' } ,
{ column : 'created_at' , order : 'asc' } ,
] ) ;
2023-01-19 13:27:50 +01:00
if ( query ) {
qB = qB . where ( query ) ;
}
2022-11-11 11:24:56 +01:00
const rows = await qB ;
2023-01-19 13:27:50 +01:00
2022-11-11 11:24:56 +01:00
return rows . map ( mapRowWithProjectCounts ) ;
}
2021-08-12 15:04:37 +02:00
async exists ( name : string ) : Promise < boolean > {
2021-07-07 10:46:50 +02:00
const result = await this . db . raw (
` SELECT EXISTS (SELECT 1 FROM ${ TABLE } WHERE name = ?) AS present ` ,
[ name ] ,
) ;
const { present } = result . rows [ 0 ] ;
return present ;
}
async getByName ( name : string ) : Promise < IEnvironment > {
const row = await this . db < IEnvironmentsTable > ( TABLE )
. where ( { name } )
. first ( ) ;
if ( ! row ) {
throw new NotFoundError (
` Could not find environment with name ${ name } ` ,
) ;
}
return mapRow ( row ) ;
}
2021-09-13 15:57:38 +02:00
async updateProperty (
id : string ,
field : string ,
value : string | number ,
) : Promise < void > {
await this . db < IEnvironmentsTable > ( TABLE )
. update ( {
[ field ] : value ,
} )
. where ( { name : id , protected : false } ) ;
}
async updateSortOrder ( id : string , value : number ) : Promise < void > {
2021-07-07 10:46:50 +02:00
await this . db < IEnvironmentsTable > ( TABLE )
2021-09-13 15:57:38 +02:00
. update ( {
sort_order : value ,
} )
. where ( { name : id } ) ;
}
async update (
2021-09-29 10:23:43 +02:00
env : Pick < IEnvironment , ' type ' | ' protected ' > ,
2021-09-13 15:57:38 +02:00
name : string ,
) : Promise < IEnvironment > {
const updatedEnv = await this . db < IEnvironmentsTable > ( TABLE )
. update ( snakeCaseKeys ( env ) )
. where ( { name , protected : false } )
. returning < IEnvironmentsTable > ( COLUMNS ) ;
return mapRow ( updatedEnv [ 0 ] ) ;
}
async create ( env : IEnvironmentCreate ) : Promise < IEnvironment > {
const row = await this . db < IEnvironmentsTable > ( TABLE )
. insert ( snakeCaseKeys ( env ) )
. returning < IEnvironmentsTable > ( COLUMNS ) ;
return mapRow ( row [ 0 ] ) ;
2021-07-07 10:46:50 +02:00
}
2022-03-11 14:52:13 +01:00
async disable ( environments : IEnvironment [ ] ) : Promise < void > {
2022-03-11 10:16:58 +01:00
await this . db ( TABLE )
. update ( {
enabled : false ,
} )
2022-03-11 14:52:13 +01:00
. whereIn (
'name' ,
environments . map ( ( env ) = > env . name ) ,
) ;
2022-03-11 10:16:58 +01:00
}
2022-03-11 14:52:13 +01:00
async enable ( environments : IEnvironment [ ] ) : Promise < void > {
2022-03-11 10:16:58 +01:00
await this . db ( TABLE )
. update ( {
enabled : true ,
} )
2022-03-11 14:52:13 +01:00
. whereIn (
'name' ,
environments . map ( ( env ) = > env . name ) ,
) ;
2022-03-11 10:16:58 +01:00
}
2021-07-07 10:46:50 +02:00
async delete ( name : string ) : Promise < void > {
2021-09-13 15:57:38 +02:00
await this . db ( TABLE ) . where ( { name , protected : false } ) . del ( ) ;
2021-07-07 10:46:50 +02:00
}
2021-08-12 15:04:37 +02:00
destroy ( ) : void { }
2021-07-07 10:46:50 +02:00
}