mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: project applications e2e PoC (#6189)
1. Adding store layer 2. Updating schemas 3. Refactoring project files that I touched into feature oriented architecture Next steps E2E tests.
This commit is contained in:
		
							parent
							
								
									b48d25a226
								
							
						
					
					
						commit
						5a75093cbc
					
				| @ -9,7 +9,7 @@ import ClientApplicationsStore from './client-applications-store'; | |||||||
| import ContextFieldStore from './context-field-store'; | import ContextFieldStore from './context-field-store'; | ||||||
| import SettingStore from './setting-store'; | import SettingStore from './setting-store'; | ||||||
| import UserStore from './user-store'; | import UserStore from './user-store'; | ||||||
| import ProjectStore from './project-store'; | import ProjectStore from '../features/project/project-store'; | ||||||
| import TagStore from './tag-store'; | import TagStore from './tag-store'; | ||||||
| import TagTypeStore from '../features/tag-type/tag-type-store'; | import TagTypeStore from '../features/tag-type/tag-type-store'; | ||||||
| import AddonStore from './addon-store'; | import AddonStore from './addon-store'; | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ import { Logger, LogProvider } from '../logger'; | |||||||
| import metricsHelper from '../util/metrics-helper'; | import metricsHelper from '../util/metrics-helper'; | ||||||
| import { DB_TIME } from '../metric-events'; | import { DB_TIME } from '../metric-events'; | ||||||
| import EventEmitter from 'events'; | import EventEmitter from 'events'; | ||||||
| import { IProjectStats } from '../services/project-service'; | import { IProjectStats } from '../features/project/project-service'; | ||||||
| import { | import { | ||||||
|     ICreateEnabledDates, |     ICreateEnabledDates, | ||||||
|     IProjectStatsStore, |     IProjectStatsStore, | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ import { ImportTogglesStore } from './import-toggles-store'; | |||||||
| import FeatureToggleStore from '../feature-toggle/feature-toggle-store'; | import FeatureToggleStore from '../feature-toggle/feature-toggle-store'; | ||||||
| import TagStore from '../../db/tag-store'; | import TagStore from '../../db/tag-store'; | ||||||
| import TagTypeStore from '../tag-type/tag-type-store'; | import TagTypeStore from '../tag-type/tag-type-store'; | ||||||
| import ProjectStore from '../../db/project-store'; | import ProjectStore from '../project/project-store'; | ||||||
| import FeatureTagStore from '../../db/feature-tag-store'; | import FeatureTagStore from '../../db/feature-tag-store'; | ||||||
| import StrategyStore from '../../db/strategy-store'; | import StrategyStore from '../../db/strategy-store'; | ||||||
| import ContextFieldStore from '../../db/context-field-store'; | import ContextFieldStore from '../../db/context-field-store'; | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ import { | |||||||
| import FeatureStrategiesStore from './feature-toggle-strategies-store'; | import FeatureStrategiesStore from './feature-toggle-strategies-store'; | ||||||
| import FeatureToggleStore from './feature-toggle-store'; | import FeatureToggleStore from './feature-toggle-store'; | ||||||
| import FeatureToggleClientStore from '../client-feature-toggles/client-feature-toggle-store'; | import FeatureToggleClientStore from '../client-feature-toggles/client-feature-toggle-store'; | ||||||
| import ProjectStore from '../../db/project-store'; | import ProjectStore from '../project/project-store'; | ||||||
| import { FeatureEnvironmentStore } from '../../db/feature-environment-store'; | import { FeatureEnvironmentStore } from '../../db/feature-environment-store'; | ||||||
| import ContextFieldStore from '../../db/context-field-store'; | import ContextFieldStore from '../../db/context-field-store'; | ||||||
| import GroupStore from '../../db/group-store'; | import GroupStore from '../../db/group-store'; | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ import { IUnleashConfig } from '../../types'; | |||||||
| import { Db } from '../../db/db'; | import { Db } from '../../db/db'; | ||||||
| import FeatureToggleStore from '../feature-toggle/feature-toggle-store'; | import FeatureToggleStore from '../feature-toggle/feature-toggle-store'; | ||||||
| import UserStore from '../../db/user-store'; | import UserStore from '../../db/user-store'; | ||||||
| import ProjectStore from '../../db/project-store'; | import ProjectStore from '../project/project-store'; | ||||||
| import EnvironmentStore from '../project-environments/environment-store'; | import EnvironmentStore from '../project-environments/environment-store'; | ||||||
| import StrategyStore from '../../db/strategy-store'; | import StrategyStore from '../../db/strategy-store'; | ||||||
| import ContextFieldStore from '../../db/context-field-store'; | import ContextFieldStore from '../../db/context-field-store'; | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ import { IContextFieldStore } from '../../types/stores/context-field-store'; | |||||||
| import { IEnvironmentStore } from '../project-environments/environment-store-type'; | import { IEnvironmentStore } from '../project-environments/environment-store-type'; | ||||||
| import { IFeatureToggleStore } from '../feature-toggle/types/feature-toggle-store-type'; | import { IFeatureToggleStore } from '../feature-toggle/types/feature-toggle-store-type'; | ||||||
| import { IGroupStore } from '../../types/stores/group-store'; | import { IGroupStore } from '../../types/stores/group-store'; | ||||||
| import { IProjectStore } from '../../types/stores/project-store'; | import { IProjectStore } from '../../features/project/project-store-type'; | ||||||
| import { IStrategyStore } from '../../types/stores/strategy-store'; | import { IStrategyStore } from '../../types/stores/strategy-store'; | ||||||
| import { IUserStore } from '../../types/stores/user-store'; | import { IUserStore } from '../../types/stores/user-store'; | ||||||
| import { ISegmentStore } from '../../types/stores/segment-store'; | import { ISegmentStore } from '../../types/stores/segment-store'; | ||||||
| @ -25,7 +25,7 @@ import { | |||||||
| } from '../../types'; | } from '../../types'; | ||||||
| import { CUSTOM_ROOT_ROLE_TYPE } from '../../util'; | import { CUSTOM_ROOT_ROLE_TYPE } from '../../util'; | ||||||
| import { type GetActiveUsers } from './getActiveUsers'; | import { type GetActiveUsers } from './getActiveUsers'; | ||||||
| import { ProjectModeCount } from '../../db/project-store'; | import { ProjectModeCount } from '../project/project-store'; | ||||||
| import { GetProductionChanges } from './getProductionChanges'; | import { GetProductionChanges } from './getProductionChanges'; | ||||||
| 
 | 
 | ||||||
| export type TimeRange = 'allTime' | '30d' | '7d'; | export type TimeRange = 'allTime' | '30d' | '7d'; | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ import EnvironmentService from './environment-service'; | |||||||
| import EnvironmentStore from './environment-store'; | import EnvironmentStore from './environment-store'; | ||||||
| import FeatureStrategiesStore from '../feature-toggle/feature-toggle-strategies-store'; | import FeatureStrategiesStore from '../feature-toggle/feature-toggle-strategies-store'; | ||||||
| import { FeatureEnvironmentStore } from '../../db/feature-environment-store'; | import { FeatureEnvironmentStore } from '../../db/feature-environment-store'; | ||||||
| import ProjectStore from '../../db/project-store'; | import ProjectStore from '../project/project-store'; | ||||||
| import FakeFeatureEnvironmentStore from '../../../test/fixtures/fake-feature-environment-store'; | import FakeFeatureEnvironmentStore from '../../../test/fixtures/fake-feature-environment-store'; | ||||||
| import FakeProjectStore from '../../../test/fixtures/fake-project-store'; | import FakeProjectStore from '../../../test/fixtures/fake-project-store'; | ||||||
| import FakeFeatureStrategiesStore from '../feature-toggle/fakes/fake-feature-strategies-store'; | import FakeFeatureStrategiesStore from '../feature-toggle/fakes/fake-feature-strategies-store'; | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ import { BadDataError, UNIQUE_CONSTRAINT_VIOLATION } from '../../error'; | |||||||
| import NameExistsError from '../../error/name-exists-error'; | import NameExistsError from '../../error/name-exists-error'; | ||||||
| import { sortOrderSchema } from '../../services/state-schema'; | import { sortOrderSchema } from '../../services/state-schema'; | ||||||
| import NotFoundError from '../../error/notfound-error'; | import NotFoundError from '../../error/notfound-error'; | ||||||
| import { IProjectStore } from '../../types/stores/project-store'; | import { IProjectStore } from '../../features/project/project-store-type'; | ||||||
| import MinimumOneEnvironmentError from '../../error/minimum-one-environment-error'; | import MinimumOneEnvironmentError from '../../error/minimum-one-environment-error'; | ||||||
| import { IFlagResolver } from '../../types/experimental'; | import { IFlagResolver } from '../../types/experimental'; | ||||||
| import { CreateFeatureStrategySchema } from '../../openapi'; | import { CreateFeatureStrategySchema } from '../../openapi'; | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ import { | |||||||
| } from '../../services'; | } from '../../services'; | ||||||
| import FakeGroupStore from '../../../test/fixtures/fake-group-store'; | import FakeGroupStore from '../../../test/fixtures/fake-group-store'; | ||||||
| import FakeEventStore from '../../../test/fixtures/fake-event-store'; | import FakeEventStore from '../../../test/fixtures/fake-event-store'; | ||||||
| import ProjectStore from '../../db/project-store'; | import ProjectStore from './project-store'; | ||||||
| import FeatureToggleStore from '../feature-toggle/feature-toggle-store'; | import FeatureToggleStore from '../feature-toggle/feature-toggle-store'; | ||||||
| import { FeatureEnvironmentStore } from '../../db/feature-environment-store'; | import { FeatureEnvironmentStore } from '../../db/feature-environment-store'; | ||||||
| import ProjectStatsStore from '../../db/project-stats-store'; | import ProjectStatsStore from '../../db/project-stats-store'; | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ import { | |||||||
| import ProjectFeaturesController from '../feature-toggle/feature-toggle-controller'; | import ProjectFeaturesController from '../feature-toggle/feature-toggle-controller'; | ||||||
| import EnvironmentsController from '../project-environments/environments'; | import EnvironmentsController from '../project-environments/environments'; | ||||||
| import ProjectHealthReport from '../../routes/admin-api/project/health-report'; | import ProjectHealthReport from '../../routes/admin-api/project/health-report'; | ||||||
| import ProjectService from '../../services/project-service'; | import ProjectService from './project-service'; | ||||||
| import VariantsController from '../../routes/admin-api/project/variants'; | import VariantsController from '../../routes/admin-api/project/variants'; | ||||||
| import { | import { | ||||||
|     createResponseSchema, |     createResponseSchema, | ||||||
| @ -267,7 +267,7 @@ export default class ProjectController extends Controller { | |||||||
|             200, |             200, | ||||||
|             res, |             res, | ||||||
|             projectApplicationsSchema.$id, |             projectApplicationsSchema.$id, | ||||||
|             applications, |             serializeDates(applications), | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,12 +1,12 @@ | |||||||
| import { subDays } from 'date-fns'; | import { subDays } from 'date-fns'; | ||||||
| import { ValidationError } from 'joi'; | import { ValidationError } from 'joi'; | ||||||
| import { IUser } from '../types/user'; | import { IUser } from '../../types/user'; | ||||||
| import { AccessService, AccessWithRoles } from './access-service'; | import { AccessService, AccessWithRoles } from '../../services/access-service'; | ||||||
| import NameExistsError from '../error/name-exists-error'; | import NameExistsError from '../../error/name-exists-error'; | ||||||
| import InvalidOperationError from '../error/invalid-operation-error'; | import InvalidOperationError from '../../error/invalid-operation-error'; | ||||||
| import { nameType } from '../routes/util'; | import { nameType } from '../../routes/util'; | ||||||
| import { projectSchema } from './project-schema'; | import { projectSchema } from '../../services/project-schema'; | ||||||
| import NotFoundError from '../error/notfound-error'; | import NotFoundError from '../../error/notfound-error'; | ||||||
| import { | import { | ||||||
|     DEFAULT_PROJECT, |     DEFAULT_PROJECT, | ||||||
|     FeatureToggle, |     FeatureToggle, | ||||||
| @ -42,34 +42,32 @@ import { | |||||||
|     IProjectUpdate, |     IProjectUpdate, | ||||||
|     IProjectHealth, |     IProjectHealth, | ||||||
|     SYSTEM_USER, |     SYSTEM_USER, | ||||||
| } from '../types'; |     IProjectApplication, | ||||||
| import { |  | ||||||
|     IProjectQuery, |  | ||||||
|     IProjectEnterpriseSettingsUpdate, |  | ||||||
|     IProjectStore, |     IProjectStore, | ||||||
| } from '../types/stores/project-store'; | } from '../../types'; | ||||||
| import { | import { | ||||||
|     IProjectAccessModel, |     IProjectAccessModel, | ||||||
|     IRoleDescriptor, |     IRoleDescriptor, | ||||||
| } from '../types/stores/access-store'; | } from '../../types/stores/access-store'; | ||||||
| import FeatureToggleService from '../features/feature-toggle/feature-toggle-service'; | import FeatureToggleService from '../feature-toggle/feature-toggle-service'; | ||||||
| import IncompatibleProjectError from '../error/incompatible-project-error'; | import IncompatibleProjectError from '../../error/incompatible-project-error'; | ||||||
| import ProjectWithoutOwnerError from '../error/project-without-owner-error'; | import ProjectWithoutOwnerError from '../../error/project-without-owner-error'; | ||||||
| import { arraysHaveSameItems } from '../util'; | import { arraysHaveSameItems } from '../../util'; | ||||||
| import { GroupService } from './group-service'; | import { GroupService } from '../../services/group-service'; | ||||||
| import { IGroupRole } from '../types/group'; | import { IGroupRole } from '../../types/group'; | ||||||
| import { FavoritesService } from './favorites-service'; | import { FavoritesService } from '../../services/favorites-service'; | ||||||
| import { calculateAverageTimeToProd } from '../features/feature-toggle/time-to-production/time-to-production'; | import { calculateAverageTimeToProd } from '../feature-toggle/time-to-production/time-to-production'; | ||||||
| import { IProjectStatsStore } from '../types/stores/project-stats-store-type'; | import { IProjectStatsStore } from '../../types/stores/project-stats-store-type'; | ||||||
| import { uniqueByKey } from '../util/unique'; | import { uniqueByKey } from '../../util/unique'; | ||||||
| import { BadDataError, PermissionError } from '../error'; | import { BadDataError, PermissionError } from '../../error'; | ||||||
|  | import { ProjectDoraMetricsSchema } from '../../openapi'; | ||||||
|  | import { checkFeatureNamingData } from '../feature-naming-pattern/feature-naming-validation'; | ||||||
|  | import { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType'; | ||||||
|  | import EventService from '../events/event-service'; | ||||||
| import { | import { | ||||||
|     ProjectDoraMetricsSchema, |     IProjectEnterpriseSettingsUpdate, | ||||||
|     ProjectApplicationsSchema, |     IProjectQuery, | ||||||
| } from '../openapi'; | } from './project-store-type'; | ||||||
| import { checkFeatureNamingData } from '../features/feature-naming-pattern/feature-naming-validation'; |  | ||||||
| import { IPrivateProjectChecker } from '../features/private-project/privateProjectCheckerType'; |  | ||||||
| import EventService from '../features/events/event-service'; |  | ||||||
| 
 | 
 | ||||||
| const getCreatedBy = (user: IUser) => user.email || user.username || 'unknown'; | const getCreatedBy = (user: IUser) => user.email || user.username || 'unknown'; | ||||||
| 
 | 
 | ||||||
| @ -903,10 +901,10 @@ export default class ProjectService { | |||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getApplications( |     async getApplications(projectId: string): Promise<IProjectApplication[]> { | ||||||
|         projectId: string, |         const applications = | ||||||
|     ): Promise<ProjectApplicationsSchema> { |             await this.projectStore.getApplicationsByProject(projectId); | ||||||
|         return []; |         return applications; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async changeRole( |     async changeRole( | ||||||
| @ -2,15 +2,16 @@ import { | |||||||
|     IEnvironmentProjectLink, |     IEnvironmentProjectLink, | ||||||
|     IProjectMembersCount, |     IProjectMembersCount, | ||||||
|     ProjectModeCount, |     ProjectModeCount, | ||||||
| } from '../../db/project-store'; | } from './project-store'; | ||||||
| import { | import { | ||||||
|     IEnvironment, |     IEnvironment, | ||||||
|     IFeatureNaming, |     IFeatureNaming, | ||||||
|     IProject, |     IProject, | ||||||
|  |     IProjectApplication, | ||||||
|     IProjectWithCount, |     IProjectWithCount, | ||||||
|     ProjectMode, |     ProjectMode, | ||||||
| } from '../model'; | } from '../../types/model'; | ||||||
| import { Store } from './store'; | import { Store } from '../../types/stores/store'; | ||||||
| import { CreateFeatureStrategySchema } from '../../openapi'; | import { CreateFeatureStrategySchema } from '../../openapi'; | ||||||
| 
 | 
 | ||||||
| export interface IProjectInsert { | export interface IProjectInsert { | ||||||
| @ -121,4 +122,5 @@ export interface IProjectStore extends Store<IProject, string> { | |||||||
|     isFeatureLimitReached(id: string): Promise<boolean>; |     isFeatureLimitReached(id: string): Promise<boolean>; | ||||||
| 
 | 
 | ||||||
|     getProjectModeCounts(): Promise<ProjectModeCount[]>; |     getProjectModeCounts(): Promise<ProjectModeCount[]>; | ||||||
|  |     getApplicationsByProject(projectId: string): Promise<IProjectApplication[]>; | ||||||
| } | } | ||||||
| @ -1,15 +1,17 @@ | |||||||
| import { Knex } from 'knex'; | import { Knex } from 'knex'; | ||||||
| import { Logger, LogProvider } from '../logger'; | import { Logger, LogProvider } from '../../logger'; | ||||||
| 
 | 
 | ||||||
| import NotFoundError from '../error/notfound-error'; | import NotFoundError from '../../error/notfound-error'; | ||||||
| import { | import { | ||||||
|     IEnvironment, |     IEnvironment, | ||||||
|     IFlagResolver, |     IFlagResolver, | ||||||
|     IProject, |     IProject, | ||||||
|  |     IProjectApplication, | ||||||
|  |     IProjectApplicationSdk, | ||||||
|     IProjectUpdate, |     IProjectUpdate, | ||||||
|     IProjectWithCount, |     IProjectWithCount, | ||||||
|     ProjectMode, |     ProjectMode, | ||||||
| } from '../types'; | } from '../../types'; | ||||||
| import { | import { | ||||||
|     IProjectHealthUpdate, |     IProjectHealthUpdate, | ||||||
|     IProjectInsert, |     IProjectInsert, | ||||||
| @ -18,14 +20,14 @@ import { | |||||||
|     IProjectEnterpriseSettingsUpdate, |     IProjectEnterpriseSettingsUpdate, | ||||||
|     IProjectStore, |     IProjectStore, | ||||||
|     ProjectEnvironment, |     ProjectEnvironment, | ||||||
| } from '../types/stores/project-store'; | } from '../../features/project/project-store-type'; | ||||||
| import { DEFAULT_ENV } from '../util'; | import { DEFAULT_ENV } from '../../util'; | ||||||
| import metricsHelper from '../util/metrics-helper'; | import metricsHelper from '../../util/metrics-helper'; | ||||||
| import { DB_TIME } from '../metric-events'; | import { DB_TIME } from '../../metric-events'; | ||||||
| import EventEmitter from 'events'; | import EventEmitter from 'events'; | ||||||
| import { Db } from './db'; | import { Db } from '../../db/db'; | ||||||
| import Raw = Knex.Raw; | import Raw = Knex.Raw; | ||||||
| import { CreateFeatureStrategySchema } from '../openapi'; | import { CreateFeatureStrategySchema } from '../../openapi'; | ||||||
| 
 | 
 | ||||||
| const COLUMNS = [ | const COLUMNS = [ | ||||||
|     'id', |     'id', | ||||||
| @ -112,7 +114,8 @@ class ProjectStore implements IProjectStore { | |||||||
|             `SELECT EXISTS(SELECT 1
 |             `SELECT EXISTS(SELECT 1
 | ||||||
|                            FROM project_settings |                            FROM project_settings | ||||||
|                                     LEFT JOIN features ON project_settings.project = features.project |                                     LEFT JOIN features ON project_settings.project = features.project | ||||||
|              WHERE project_settings.project = ? AND features.archived_at IS NULL |                            WHERE project_settings.project = ? | ||||||
|  |                              AND features.archived_at IS NULL | ||||||
|                            GROUP BY project_settings.project |                            GROUP BY project_settings.project | ||||||
|                            HAVING project_settings.feature_limit <= COUNT(features.project)) AS present`,
 |                            HAVING project_settings.feature_limit <= COUNT(features.project)) AS present`,
 | ||||||
|             [id], |             [id], | ||||||
| @ -254,9 +257,10 @@ class ProjectStore implements IProjectStore { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async updateHealth(healthUpdate: IProjectHealthUpdate): Promise<void> { |     async updateHealth(healthUpdate: IProjectHealthUpdate): Promise<void> { | ||||||
|         await this.db(TABLE) |         await this.db(TABLE).where({ id: healthUpdate.id }).update({ | ||||||
|             .where({ id: healthUpdate.id }) |             health: healthUpdate.health, | ||||||
|             .update({ health: healthUpdate.health, updated_at: new Date() }); |             updated_at: new Date(), | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async create( |     async create( | ||||||
| @ -575,13 +579,45 @@ class ProjectStore implements IProjectStore { | |||||||
|         return Number(members.count); |         return Number(members.count); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     async getApplicationsByProject( | ||||||
|  |         projectId: string, | ||||||
|  |     ): Promise<IProjectApplication[]> { | ||||||
|  |         const query = this.db | ||||||
|  |             .with('applications', (qb) => { | ||||||
|  |                 qb.select('project', 'app_name', 'environment') | ||||||
|  |                     .distinct() | ||||||
|  |                     .from('client_metrics_env as cme') | ||||||
|  |                     .leftJoin('features as f', 'cme.feature_name', 'f.name') | ||||||
|  |                     .where('project', projectId); | ||||||
|  |             }) | ||||||
|  |             .select( | ||||||
|  |                 'a.app_name', | ||||||
|  |                 'a.environment', | ||||||
|  |                 'ci.instance_id', | ||||||
|  |                 'ci.sdk_version', | ||||||
|  |             ) | ||||||
|  |             .from('applications as a') | ||||||
|  |             .leftJoin('client_instances as ci', function () { | ||||||
|  |                 this.on('ci.app_name', 'a.app_name').andOn( | ||||||
|  |                     'ci.environment', | ||||||
|  |                     'a.environment', | ||||||
|  |                 ); | ||||||
|  |             }); | ||||||
|  |         const rows = await query; | ||||||
|  |         const applications = this.getAggregatedApplicationsData(rows); | ||||||
|  |         return applications; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     async getDefaultStrategy( |     async getDefaultStrategy( | ||||||
|         projectId: string, |         projectId: string, | ||||||
|         environment: string, |         environment: string, | ||||||
|     ): Promise<CreateFeatureStrategySchema | null> { |     ): Promise<CreateFeatureStrategySchema | null> { | ||||||
|         const rows = await this.db(PROJECT_ENVIRONMENTS) |         const rows = await this.db(PROJECT_ENVIRONMENTS) | ||||||
|             .select('default_strategy') |             .select('default_strategy') | ||||||
|             .where({ project_id: projectId, environment_name: environment }); |             .where({ | ||||||
|  |                 project_id: projectId, | ||||||
|  |                 environment_name: environment, | ||||||
|  |             }); | ||||||
| 
 | 
 | ||||||
|         return rows.length > 0 ? rows[0].default_strategy : null; |         return rows.length > 0 ? rows[0].default_strategy : null; | ||||||
|     } |     } | ||||||
| @ -595,7 +631,10 @@ class ProjectStore implements IProjectStore { | |||||||
|             .update({ |             .update({ | ||||||
|                 default_strategy: strategy, |                 default_strategy: strategy, | ||||||
|             }) |             }) | ||||||
|             .where({ project_id: projectId, environment_name: environment }) |             .where({ | ||||||
|  |                 project_id: projectId, | ||||||
|  |                 environment_name: environment, | ||||||
|  |             }) | ||||||
|             .returning('default_strategy'); |             .returning('default_strategy'); | ||||||
| 
 | 
 | ||||||
|         return rows[0].default_strategy; |         return rows[0].default_strategy; | ||||||
| @ -680,6 +719,43 @@ class ProjectStore implements IProjectStore { | |||||||
|                     : row.default_strategy, |                     : row.default_strategy, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     getAggregatedApplicationsData(rows): IProjectApplication[] { | ||||||
|  |         const entriesMap: Map<string, IProjectApplication> = new Map(); | ||||||
|  |         const orderedEntries: IProjectApplication[] = []; | ||||||
|  | 
 | ||||||
|  |         const getSdk = (sdkParts: string[]): IProjectApplicationSdk => { | ||||||
|  |             return { | ||||||
|  |                 name: sdkParts[0], | ||||||
|  |                 versions: [sdkParts[1]], | ||||||
|  |             }; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         rows.forEach((row) => { | ||||||
|  |             let entry = entriesMap.get(row.app_name); | ||||||
|  |             const sdkParts = row.sdk_version.split(':'); | ||||||
|  | 
 | ||||||
|  |             if (!entry) { | ||||||
|  |                 entry = { | ||||||
|  |                     name: row.app_name, | ||||||
|  |                     environments: [row.environment], | ||||||
|  |                     instances: [row.instance_id], | ||||||
|  |                     sdks: [getSdk(sdkParts)], | ||||||
|  |                 }; | ||||||
|  |                 entriesMap.set(row.feature_name, entry); | ||||||
|  |                 orderedEntries.push(entry); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             const sdk = entry.sdks.find((sdk) => sdk.name === sdkParts[0]); | ||||||
|  |             if (!sdk) { | ||||||
|  |                 entry.sdks.push(getSdk(sdkParts)); | ||||||
|  |             } else { | ||||||
|  |                 sdk.versions.push(sdkParts[1]); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         return orderedEntries; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default ProjectStore; | export default ProjectStore; | ||||||
| @ -196,6 +196,7 @@ import { segmentStrategiesSchema } from './spec/segment-strategies-schema'; | |||||||
| import { featureDependenciesSchema } from './spec/feature-dependencies-schema'; | import { featureDependenciesSchema } from './spec/feature-dependencies-schema'; | ||||||
| import { projectApplicationsSchema } from './spec/project-applications-schema'; | import { projectApplicationsSchema } from './spec/project-applications-schema'; | ||||||
| import { projectApplicationSchema } from './spec/project-application-schema'; | import { projectApplicationSchema } from './spec/project-application-schema'; | ||||||
|  | import { projectApplicationSdkSchema } from './spec/project-application-sdk-schema'; | ||||||
| 
 | 
 | ||||||
| // Schemas must have an $id property on the form "#/components/schemas/mySchema".
 | // Schemas must have an $id property on the form "#/components/schemas/mySchema".
 | ||||||
| export type SchemaId = (typeof schemas)[keyof typeof schemas]['$id']; | export type SchemaId = (typeof schemas)[keyof typeof schemas]['$id']; | ||||||
| @ -326,6 +327,7 @@ export const schemas: UnleashSchemas = { | |||||||
|     playgroundStrategySchema, |     playgroundStrategySchema, | ||||||
|     profileSchema, |     profileSchema, | ||||||
|     projectApplicationSchema, |     projectApplicationSchema, | ||||||
|  |     projectApplicationSdkSchema, | ||||||
|     projectApplicationsSchema, |     projectApplicationsSchema, | ||||||
|     projectEnvironmentSchema, |     projectEnvironmentSchema, | ||||||
|     projectSchema, |     projectSchema, | ||||||
|  | |||||||
| @ -1,34 +1,45 @@ | |||||||
| import { FromSchema } from 'json-schema-to-ts'; | import { FromSchema } from 'json-schema-to-ts'; | ||||||
|  | import { projectApplicationSdkSchema } from './project-application-sdk-schema'; | ||||||
| 
 | 
 | ||||||
| export const projectApplicationSchema = { | export const projectApplicationSchema = { | ||||||
|     $id: '#/components/schemas/projectApplicationSchema', |     $id: '#/components/schemas/projectApplicationSchema', | ||||||
|     type: 'object', |     type: 'object', | ||||||
|     additionalProperties: false, |     additionalProperties: false, | ||||||
|     required: ['appName', 'instanceId', 'sdkVersion', 'environment'], |     required: ['name', 'environments', 'instances', 'sdks'], | ||||||
|     description: 'A project application instance.', |     description: 'A project application instance.', | ||||||
|     properties: { |     properties: { | ||||||
|         appName: { |         name: { | ||||||
|             type: 'string', |             type: 'string', | ||||||
|             description: |             description: | ||||||
|                 'Name of the application that is using the SDK. This is the same as the appName in the SDK configuration.', |                 'Name of the application that is using the SDK. This is the same as the appName in the SDK configuration.', | ||||||
|         }, |         }, | ||||||
|         instanceId: { |         environments: { | ||||||
|             type: 'string', |  | ||||||
|             description: |             description: | ||||||
|                 'The unique identifier of the application instance. This is the same as the instanceId in the SDK configuration', |                 'The environments that the application is using. This is the same as the environment in the SDK configuration.', | ||||||
|         }, |             type: 'array', | ||||||
|         sdkVersion: { |             items: { | ||||||
|                 type: 'string', |                 type: 'string', | ||||||
|             description: |  | ||||||
|                 'The version of the SDK that is being used by the application', |  | ||||||
|             }, |             }, | ||||||
|         environment: { |             example: ['development', 'production'], | ||||||
|  |         }, | ||||||
|  |         instances: { | ||||||
|  |             description: | ||||||
|  |                 'The instances of the application that are using the SDK.', | ||||||
|  |             type: 'array', | ||||||
|  |             items: { | ||||||
|                 type: 'string', |                 type: 'string', | ||||||
|             description: |             }, | ||||||
|                 'The environment that the application is running in. This is coming from token configured in the SDK configuration.', |             example: ['prod-b4ca', 'prod-ac8a'], | ||||||
|  |         }, | ||||||
|  |         sdks: { | ||||||
|  |             type: 'array', | ||||||
|  |             description: 'The SDKs that the application is using.', | ||||||
|  |             items: { | ||||||
|  |                 $ref: '#/components/schemas/projectApplicationSdkSchema', | ||||||
|             }, |             }, | ||||||
|         }, |         }, | ||||||
|     components: {}, |     }, | ||||||
|  |     components: { projectApplicationSdkSchema }, | ||||||
| } as const; | } as const; | ||||||
| 
 | 
 | ||||||
| export type ProjectApplicationSchema = FromSchema< | export type ProjectApplicationSchema = FromSchema< | ||||||
|  | |||||||
							
								
								
									
										31
									
								
								src/lib/openapi/spec/project-application-sdk-schema.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/lib/openapi/spec/project-application-sdk-schema.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | |||||||
|  | import { FromSchema } from 'json-schema-to-ts'; | ||||||
|  | 
 | ||||||
|  | export const projectApplicationSdkSchema = { | ||||||
|  |     $id: '#/components/schemas/projectApplicationSdkSchema', | ||||||
|  |     type: 'object', | ||||||
|  |     additionalProperties: false, | ||||||
|  |     required: ['name', 'versions'], | ||||||
|  |     description: 'A project application instance SDK.', | ||||||
|  |     properties: { | ||||||
|  |         name: { | ||||||
|  |             type: 'string', | ||||||
|  |             description: | ||||||
|  |                 'Name of the SDK package that the application is using.', | ||||||
|  |             example: 'unleash-client-node', | ||||||
|  |         }, | ||||||
|  |         versions: { | ||||||
|  |             description: | ||||||
|  |                 'The versions of the SDK that the application is using.', | ||||||
|  |             type: 'array', | ||||||
|  |             items: { | ||||||
|  |                 type: 'string', | ||||||
|  |             }, | ||||||
|  |             example: ['4.1.1'], | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|  |     components: {}, | ||||||
|  | } as const; | ||||||
|  | 
 | ||||||
|  | export type ProjectApplicationSdkSchema = FromSchema< | ||||||
|  |     typeof projectApplicationSdkSchema | ||||||
|  | >; | ||||||
| @ -4,10 +4,15 @@ import { ProjectApplicationsSchema } from './project-applications-schema'; | |||||||
| test('projectApplicationsSchema', () => { | test('projectApplicationsSchema', () => { | ||||||
|     const data: ProjectApplicationsSchema = [ |     const data: ProjectApplicationsSchema = [ | ||||||
|         { |         { | ||||||
|             appName: 'my-weba-app', |             name: 'my-weba-app', | ||||||
|             instanceId: 'app1:3:4', |             environments: ['development', 'production'], | ||||||
|             sdkVersion: '4.1.1', |             instances: ['instance-414122'], | ||||||
|             environment: 'production', |             sdks: [ | ||||||
|  |                 { | ||||||
|  |                     name: 'unleash-client-node', | ||||||
|  |                     versions: ['4.1.1'], | ||||||
|  |                 }, | ||||||
|  |             ], | ||||||
|         }, |         }, | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| import { FromSchema } from 'json-schema-to-ts'; | import { FromSchema } from 'json-schema-to-ts'; | ||||||
| import { projectApplicationSchema } from './project-application-schema'; | import { projectApplicationSchema } from './project-application-schema'; | ||||||
|  | import { projectApplicationSdkSchema } from './project-application-sdk-schema'; | ||||||
| 
 | 
 | ||||||
| export const projectApplicationsSchema = { | export const projectApplicationsSchema = { | ||||||
|     $id: '#/components/schemas/projectApplicationsSchema', |     $id: '#/components/schemas/projectApplicationsSchema', | ||||||
| @ -11,6 +12,7 @@ export const projectApplicationsSchema = { | |||||||
|     components: { |     components: { | ||||||
|         schemas: { |         schemas: { | ||||||
|             projectApplicationSchema, |             projectApplicationSchema, | ||||||
|  |             projectApplicationSdkSchema, | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
| } as const; | } as const; | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ import { | |||||||
|     profileSchema, |     profileSchema, | ||||||
|     ProfileSchema, |     ProfileSchema, | ||||||
| } from '../../../openapi/spec/profile-schema'; | } from '../../../openapi/spec/profile-schema'; | ||||||
| import ProjectService from '../../../services/project-service'; | import ProjectService from '../../../features/project/project-service'; | ||||||
| 
 | 
 | ||||||
| class UserController extends Controller { | class UserController extends Controller { | ||||||
|     private accessService: AccessService; |     private accessService: AccessService; | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ import { | |||||||
|     IContextFieldDto, |     IContextFieldDto, | ||||||
|     IContextFieldStore, |     IContextFieldStore, | ||||||
| } from '../types/stores/context-field-store'; | } from '../types/stores/context-field-store'; | ||||||
| import { IProjectStore } from '../types/stores/project-store'; | import { IProjectStore } from '../features/project/project-store-type'; | ||||||
| import { IFeatureStrategiesStore, IUnleashStores } from '../types/stores'; | import { IFeatureStrategiesStore, IUnleashStores } from '../types/stores'; | ||||||
| import { IUnleashConfig } from '../types/option'; | import { IUnleashConfig } from '../types/option'; | ||||||
| import { ContextFieldStrategiesSchema } from '../openapi/spec/context-field-strategies-schema'; | import { ContextFieldStrategiesSchema } from '../openapi/spec/context-field-strategies-schema'; | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ import FeatureTypeService from './feature-type-service'; | |||||||
| import EventService from '../features/events/event-service'; | import EventService from '../features/events/event-service'; | ||||||
| import HealthService from './health-service'; | import HealthService from './health-service'; | ||||||
| 
 | 
 | ||||||
| import ProjectService from './project-service'; | import ProjectService from '../features/project/project-service'; | ||||||
| import StateService from './state-service'; | import StateService from './state-service'; | ||||||
| import ClientInstanceService from '../features/metrics/instance/instance-service'; | import ClientInstanceService from '../features/metrics/instance/instance-service'; | ||||||
| import ClientMetricsServiceV2 from '../features/metrics/client-metrics/metrics-service-v2'; | import ClientMetricsServiceV2 from '../features/metrics/client-metrics/metrics-service-v2'; | ||||||
|  | |||||||
| @ -4,8 +4,8 @@ import { Logger } from '../logger'; | |||||||
| import type { IProject, IProjectHealthReport } from '../types/model'; | import type { IProject, IProjectHealthReport } from '../types/model'; | ||||||
| import type { IFeatureToggleStore } from '../features/feature-toggle/types/feature-toggle-store-type'; | import type { IFeatureToggleStore } from '../features/feature-toggle/types/feature-toggle-store-type'; | ||||||
| import type { IFeatureTypeStore } from '../types/stores/feature-type-store'; | import type { IFeatureTypeStore } from '../types/stores/feature-type-store'; | ||||||
| import type { IProjectStore } from '../types/stores/project-store'; | import type { IProjectStore } from '../features/project/project-store-type'; | ||||||
| import ProjectService from './project-service'; | import ProjectService from '../features/project/project-service'; | ||||||
| import { | import { | ||||||
|     calculateHealthRating, |     calculateHealthRating, | ||||||
|     calculateProjectHealth, |     calculateProjectHealth, | ||||||
|  | |||||||
| @ -36,7 +36,7 @@ import { | |||||||
|     IFeatureTag, |     IFeatureTag, | ||||||
|     IFeatureTagStore, |     IFeatureTagStore, | ||||||
| } from '../types/stores/feature-tag-store'; | } from '../types/stores/feature-tag-store'; | ||||||
| import { IProjectStore } from '../types/stores/project-store'; | import { IProjectStore } from '../features/project/project-store-type'; | ||||||
| import { | import { | ||||||
|     ITagType, |     ITagType, | ||||||
|     ITagTypeStore, |     ITagTypeStore, | ||||||
|  | |||||||
| @ -3,9 +3,9 @@ import { LogProvider } from '../logger'; | |||||||
| import { IRole } from './stores/access-store'; | import { IRole } from './stores/access-store'; | ||||||
| import { IUser } from './user'; | import { IUser } from './user'; | ||||||
| import { ALL_OPERATORS } from '../util'; | import { ALL_OPERATORS } from '../util'; | ||||||
| import { IProjectStats } from '../services/project-service'; | import { IProjectStats } from '../features/project/project-service'; | ||||||
| import { CreateFeatureStrategySchema } from '../openapi'; | import { CreateFeatureStrategySchema } from '../openapi'; | ||||||
| import { ProjectEnvironment } from './stores/project-store'; | import { ProjectEnvironment } from '../features/project/project-store-type'; | ||||||
| 
 | 
 | ||||||
| export type Operator = (typeof ALL_OPERATORS)[number]; | export type Operator = (typeof ALL_OPERATORS)[number]; | ||||||
| 
 | 
 | ||||||
| @ -478,6 +478,18 @@ export interface IProject { | |||||||
|     featureNaming?: IFeatureNaming; |     featureNaming?: IFeatureNaming; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export interface IProjectApplication { | ||||||
|  |     name: string; | ||||||
|  |     environments: string[]; | ||||||
|  |     instances: string[]; | ||||||
|  |     sdks: IProjectApplicationSdk[]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface IProjectApplicationSdk { | ||||||
|  |     name: string; | ||||||
|  |     versions: string[]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // mimics UpdateProjectSchema
 | // mimics UpdateProjectSchema
 | ||||||
| export interface IProjectUpdate { | export interface IProjectUpdate { | ||||||
|     id: string; |     id: string; | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { AccessService } from '../services/access-service'; | import { AccessService } from '../services/access-service'; | ||||||
| import AddonService from '../services/addon-service'; | import AddonService from '../services/addon-service'; | ||||||
| import ProjectService from '../services/project-service'; | import ProjectService from '../features/project/project-service'; | ||||||
| import StateService from '../services/state-service'; | import StateService from '../services/state-service'; | ||||||
| import StrategyService from '../services/strategy-service'; | import StrategyService from '../services/strategy-service'; | ||||||
| import TagTypeService from '../features/tag-type/tag-type-service'; | import TagTypeService from '../features/tag-type/tag-type-service'; | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { IProjectStore } from './stores/project-store'; | import { IProjectStore } from '../features/project/project-store-type'; | ||||||
| import { IEventStore } from './stores/event-store'; | import { IEventStore } from './stores/event-store'; | ||||||
| import { IFeatureTypeStore } from './stores/feature-type-store'; | import { IFeatureTypeStore } from './stores/feature-type-store'; | ||||||
| import { IStrategyStore } from './stores/strategy-store'; | import { IStrategyStore } from './stores/strategy-store'; | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { DoraFeaturesSchema } from '../../openapi'; | import { DoraFeaturesSchema } from '../../openapi'; | ||||||
| import { IProjectStats } from '../../services/project-service'; | import { IProjectStats } from '../../features/project/project-service'; | ||||||
| 
 | 
 | ||||||
| export interface ICreateEnabledDates { | export interface ICreateEnabledDates { | ||||||
|     created: Date; |     created: Date; | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ import { createTestConfig } from '../../config/test-config'; | |||||||
| import { ApiTokenType, IApiToken } from '../../../lib/types/models/api-token'; | import { ApiTokenType, IApiToken } from '../../../lib/types/models/api-token'; | ||||||
| import { DEFAULT_ENV } from '../../../lib/util/constants'; | import { DEFAULT_ENV } from '../../../lib/util/constants'; | ||||||
| import { addDays, subDays } from 'date-fns'; | import { addDays, subDays } from 'date-fns'; | ||||||
| import ProjectService from '../../../lib/services/project-service'; | import ProjectService from '../../../lib/features/project/project-service'; | ||||||
| import { createProjectService } from '../../../lib/features'; | import { createProjectService } from '../../../lib/features'; | ||||||
| import { EventService } from '../../../lib/services'; | import { EventService } from '../../../lib/services'; | ||||||
| import { IUnleashStores } from '../../../lib/types'; | import { IUnleashStores } from '../../../lib/types'; | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import dbInit, { ITestDb } from '../helpers/database-init'; | import dbInit, { ITestDb } from '../helpers/database-init'; | ||||||
| import getLogger from '../../fixtures/no-logger'; | import getLogger from '../../fixtures/no-logger'; | ||||||
| import FeatureToggleService from '../../../lib/features/feature-toggle/feature-toggle-service'; | import FeatureToggleService from '../../../lib/features/feature-toggle/feature-toggle-service'; | ||||||
| import ProjectService from '../../../lib/services/project-service'; | import ProjectService from '../../../lib/features/project/project-service'; | ||||||
| import { AccessService } from '../../../lib/services/access-service'; | import { AccessService } from '../../../lib/services/access-service'; | ||||||
| import { MOVE_FEATURE_TOGGLE } from '../../../lib/types/permissions'; | import { MOVE_FEATURE_TOGGLE } from '../../../lib/types/permissions'; | ||||||
| import { createTestConfig } from '../../config/test-config'; | import { createTestConfig } from '../../config/test-config'; | ||||||
|  | |||||||
| @ -1,12 +1,9 @@ | |||||||
| import { |  | ||||||
|     IProjectInsert, |  | ||||||
|     IProjectStore, |  | ||||||
| } from '../../../lib/types/stores/project-store'; |  | ||||||
| import { IEnvironmentStore } from '../../../lib/features/project-environments/environment-store-type'; | import { IEnvironmentStore } from '../../../lib/features/project-environments/environment-store-type'; | ||||||
| 
 | 
 | ||||||
| import dbInit, { ITestDb } from '../helpers/database-init'; | import dbInit, { ITestDb } from '../helpers/database-init'; | ||||||
| import getLogger from '../../fixtures/no-logger'; | import getLogger from '../../fixtures/no-logger'; | ||||||
| import { IUnleashStores } from '../../../lib/types'; | import { IProjectStore, IUnleashStores } from '../../../lib/types'; | ||||||
|  | import { IProjectInsert } from '../../../lib/features/project/project-store-type'; | ||||||
| 
 | 
 | ||||||
| let stores: IUnleashStores; | let stores: IUnleashStores; | ||||||
| let db: ITestDb; | let db: ITestDb; | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { IProjectStats } from '../../lib/services/project-service'; | import { IProjectStats } from '../../lib/features/project/project-service'; | ||||||
| import { | import { | ||||||
|     ICreateEnabledDates, |     ICreateEnabledDates, | ||||||
|     IProjectStatsStore, |     IProjectStatsStore, | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								src/test/fixtures/fake-project-store.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								src/test/fixtures/fake-project-store.ts
									
									
									
									
										vendored
									
									
								
							| @ -1,17 +1,22 @@ | |||||||
| import { | import { | ||||||
|     IProjectHealthUpdate, |     IEnvironment, | ||||||
|     IProjectInsert, |     IProject, | ||||||
|  |     IProjectApplication, | ||||||
|     IProjectStore, |     IProjectStore, | ||||||
|     ProjectEnvironment, |     IProjectWithCount, | ||||||
| } from '../../lib/types/stores/project-store'; | } from '../../lib/types'; | ||||||
| import { IEnvironment, IProject, IProjectWithCount } from '../../lib/types'; |  | ||||||
| import NotFoundError from '../../lib/error/notfound-error'; | import NotFoundError from '../../lib/error/notfound-error'; | ||||||
| import { | import { | ||||||
|     IEnvironmentProjectLink, |     IEnvironmentProjectLink, | ||||||
|     IProjectMembersCount, |     IProjectMembersCount, | ||||||
|     ProjectModeCount, |     ProjectModeCount, | ||||||
| } from '../../lib/db/project-store'; | } from '../../lib/features/project/project-store'; | ||||||
| import { CreateFeatureStrategySchema } from '../../lib/openapi'; | import { CreateFeatureStrategySchema } from '../../lib/openapi'; | ||||||
|  | import { | ||||||
|  |     IProjectHealthUpdate, | ||||||
|  |     IProjectInsert, | ||||||
|  |     ProjectEnvironment, | ||||||
|  | } from '../../lib/features/project/project-store-type'; | ||||||
| 
 | 
 | ||||||
| export default class FakeProjectStore implements IProjectStore { | export default class FakeProjectStore implements IProjectStore { | ||||||
|     projects: IProject[] = []; |     projects: IProject[] = []; | ||||||
| @ -202,4 +207,10 @@ export default class FakeProjectStore implements IProjectStore { | |||||||
|     updateProjectEnterpriseSettings(update: IProjectInsert): Promise<void> { |     updateProjectEnterpriseSettings(update: IProjectInsert): Promise<void> { | ||||||
|         throw new Error('Method not implemented.'); |         throw new Error('Method not implemented.'); | ||||||
|     } |     } | ||||||
|  |     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|  |     getApplicationsByProject( | ||||||
|  |         projectId: string, | ||||||
|  |     ): Promise<IProjectApplication[]> { | ||||||
|  |         throw new Error('Method not implemented.'); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user