mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-31 00:16:47 +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',
|
||||||
@ -110,11 +112,12 @@ class ProjectStore implements IProjectStore {
|
|||||||
async isFeatureLimitReached(id: string): Promise<boolean> {
|
async isFeatureLimitReached(id: string): Promise<boolean> {
|
||||||
const result = await this.db.raw(
|
const result = await this.db.raw(
|
||||||
`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 = ?
|
||||||
GROUP BY project_settings.project
|
AND features.archived_at IS NULL
|
||||||
HAVING project_settings.feature_limit <= COUNT(features.project)) AS present`,
|
GROUP BY project_settings.project
|
||||||
|
HAVING project_settings.feature_limit <= COUNT(features.project)) AS present`,
|
||||||
[id],
|
[id],
|
||||||
);
|
);
|
||||||
const { present } = result.rows[0];
|
const { present } = result.rows[0];
|
||||||
@ -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',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
example: ['development', 'production'],
|
||||||
},
|
},
|
||||||
sdkVersion: {
|
instances: {
|
||||||
type: 'string',
|
|
||||||
description:
|
description:
|
||||||
'The version of the SDK that is being used by the application',
|
'The instances of the application that are using the SDK.',
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
example: ['prod-b4ca', 'prod-ac8a'],
|
||||||
},
|
},
|
||||||
environment: {
|
sdks: {
|
||||||
type: 'string',
|
type: 'array',
|
||||||
description:
|
description: 'The SDKs that the application is using.',
|
||||||
'The environment that the application is running in. This is coming from token configured in the SDK configuration.',
|
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