mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
feat: only display oss included projects/environments when install is oss (#8896)
Trying again, now with a tested function for resolvingIsOss. Still want to test this on a pro instance in sandbox before we deploy this to our customers to avoid what happened Friday. --------- Co-authored-by: Gastón Fournier <gaston@getunleash.io>
This commit is contained in:
parent
b701fec75d
commit
66907bfd8e
@ -85,6 +85,7 @@ exports[`should create default config 1`] = `
|
||||
},
|
||||
"inlineSegmentConstraints": true,
|
||||
"isEnterprise": false,
|
||||
"isOss": false,
|
||||
"listen": {
|
||||
"host": undefined,
|
||||
"port": 4242,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createConfig } from './create-config';
|
||||
import { createConfig, resolveIsOss } from './create-config';
|
||||
import { ApiTokenType } from './types/models/api-token';
|
||||
|
||||
test('should create default config', async () => {
|
||||
@ -498,3 +498,36 @@ test('Config with enterpriseVersion set and not pro environment should set isEnt
|
||||
});
|
||||
expect(config.isEnterprise).toBe(true);
|
||||
});
|
||||
|
||||
describe('isOSS', () => {
|
||||
test('Config with pro environment should set isOss to false regardless of pro casing', async () => {
|
||||
const isOss = resolveIsOss(false, false, 'Pro');
|
||||
expect(isOss).toBe(false);
|
||||
const lowerCase = resolveIsOss(false, false, 'pro');
|
||||
expect(lowerCase).toBe(false);
|
||||
const strangeCase = resolveIsOss(false, false, 'PrO');
|
||||
expect(strangeCase).toBe(false);
|
||||
});
|
||||
test('Config with enterpriseVersion set should set isOss to false', async () => {
|
||||
const isOss = resolveIsOss(true, false, 'Enterprise');
|
||||
expect(isOss).toBe(false);
|
||||
});
|
||||
test('Config with no enterprise version and any other environment than pro should have isOss as true', async () => {
|
||||
const isOss = resolveIsOss(false, false, 'my oss environment');
|
||||
expect(isOss).toBe(true);
|
||||
});
|
||||
test('Config with enterprise false and isOss option set to false should return false in test mode', async () => {
|
||||
const isOss = resolveIsOss(false, false, 'my environment', true);
|
||||
expect(isOss).toBe(false);
|
||||
});
|
||||
test('Config with isOss option set to true should return true when test environment is active', async () => {
|
||||
let isOss = resolveIsOss(false, true, 'Pro', true);
|
||||
expect(isOss).toBe(true);
|
||||
|
||||
isOss = resolveIsOss(true, true, 'Pro', true);
|
||||
expect(isOss).toBe(true);
|
||||
|
||||
isOss = resolveIsOss(false, true, 'some environment', true);
|
||||
expect(isOss).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@ -496,6 +496,17 @@ const parseFrontendApiOrigins = (options: IUnleashOptions): string[] => {
|
||||
return frontendApiOrigins;
|
||||
};
|
||||
|
||||
export function resolveIsOss(
|
||||
isEnterprise: boolean,
|
||||
isOssOption?: boolean,
|
||||
uiEnvironment?: string,
|
||||
testEnvironmentActive: boolean = false,
|
||||
): boolean {
|
||||
return testEnvironmentActive
|
||||
? (isOssOption ?? false)
|
||||
: !isEnterprise && uiEnvironment?.toLowerCase() !== 'pro';
|
||||
}
|
||||
|
||||
export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
||||
let extraDbOptions = {};
|
||||
|
||||
@ -620,6 +631,13 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
||||
Boolean(options.enterpriseVersion) &&
|
||||
ui.environment?.toLowerCase() !== 'pro';
|
||||
|
||||
const isTest = process.env.NODE_ENV === 'test';
|
||||
const isOss = resolveIsOss(
|
||||
isEnterprise,
|
||||
options.isOss,
|
||||
ui.environment,
|
||||
isTest,
|
||||
);
|
||||
const metricsRateLimiting = loadMetricsRateLimitingConfig(options);
|
||||
|
||||
const rateLimiting = loadRateLimitingConfig(options);
|
||||
@ -760,6 +778,7 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
||||
publicFolder: options.publicFolder,
|
||||
disableScheduler: options.disableScheduler,
|
||||
isEnterprise: isEnterprise,
|
||||
isOss: isOss,
|
||||
metricsRateLimiting,
|
||||
rateLimiting,
|
||||
feedbackUriPath,
|
||||
@ -771,5 +790,6 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
||||
|
||||
module.exports = {
|
||||
createConfig,
|
||||
resolveIsOss,
|
||||
authTypeFromString,
|
||||
};
|
||||
|
@ -3,13 +3,14 @@ import type {
|
||||
FeatureEnvironmentKey,
|
||||
IFeatureEnvironmentStore,
|
||||
} from '../types/stores/feature-environment-store';
|
||||
import type { Logger, LogProvider } from '../logger';
|
||||
import type { Logger } from '../logger';
|
||||
import metricsHelper from '../util/metrics-helper';
|
||||
import { DB_TIME } from '../metric-events';
|
||||
import type { IFeatureEnvironment, IVariant } from '../types/model';
|
||||
import NotFoundError from '../error/notfound-error';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import type { Db } from './db';
|
||||
import type { IUnleashConfig } from '../types';
|
||||
|
||||
const T = {
|
||||
featureEnvs: 'feature_environments',
|
||||
@ -36,7 +37,12 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
||||
|
||||
private readonly timer: Function;
|
||||
|
||||
constructor(db: Db, eventBus: EventEmitter, getLogger: LogProvider) {
|
||||
private readonly isOss: boolean;
|
||||
constructor(
|
||||
db: Db,
|
||||
eventBus: EventEmitter,
|
||||
{ getLogger, isOss }: Pick<IUnleashConfig, 'getLogger' | 'isOss'>,
|
||||
) {
|
||||
this.db = db;
|
||||
this.logger = getLogger('feature-environment-store.ts');
|
||||
this.timer = (action) =>
|
||||
@ -44,6 +50,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
||||
store: 'feature-environments',
|
||||
action,
|
||||
});
|
||||
this.isOss = isOss;
|
||||
}
|
||||
|
||||
async delete({
|
||||
@ -96,11 +103,30 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
||||
);
|
||||
}
|
||||
|
||||
addOssFilterIfNeeded(queryBuilder) {
|
||||
if (this.isOss) {
|
||||
return queryBuilder
|
||||
.join(
|
||||
'environments',
|
||||
'environments.name',
|
||||
'=',
|
||||
`${T.featureEnvs}.environment`,
|
||||
)
|
||||
.whereIn('environments.name', [
|
||||
'default',
|
||||
'development',
|
||||
'production',
|
||||
]);
|
||||
}
|
||||
return queryBuilder;
|
||||
}
|
||||
|
||||
async getAll(query?: Object): Promise<IFeatureEnvironment[]> {
|
||||
let rows = this.db(T.featureEnvs);
|
||||
if (query) {
|
||||
rows = rows.where(query);
|
||||
}
|
||||
this.addOssFilterIfNeeded(rows);
|
||||
return (await rows).map((r) => ({
|
||||
enabled: r.enabled,
|
||||
featureName: r.feature_name,
|
||||
@ -119,6 +145,7 @@ export class FeatureEnvironmentStore implements IFeatureEnvironmentStore {
|
||||
if (environment) {
|
||||
rows = rows.where({ environment });
|
||||
}
|
||||
this.addOssFilterIfNeeded(rows);
|
||||
return (await rows).map((r) => ({
|
||||
enabled: r.enabled,
|
||||
featureName: r.feature_name,
|
||||
|
@ -94,12 +94,7 @@ export const createStores = (
|
||||
settingStore: new SettingStore(db, getLogger),
|
||||
userStore: new UserStore(db, getLogger, config.flagResolver),
|
||||
accountStore: new AccountStore(db, getLogger),
|
||||
projectStore: new ProjectStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
config.flagResolver,
|
||||
),
|
||||
projectStore: new ProjectStore(db, eventBus, config),
|
||||
tagStore: new TagStore(db, eventBus, getLogger),
|
||||
tagTypeStore: new TagTypeStore(db, eventBus, getLogger),
|
||||
addonStore: new AddonStore(db, eventBus, getLogger),
|
||||
@ -122,15 +117,14 @@ export const createStores = (
|
||||
clientFeatureToggleStore: new FeatureToggleClientStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
config.flagResolver,
|
||||
config,
|
||||
),
|
||||
environmentStore: new EnvironmentStore(db, eventBus, getLogger),
|
||||
environmentStore: new EnvironmentStore(db, eventBus, config),
|
||||
featureTagStore: new FeatureTagStore(db, eventBus, getLogger),
|
||||
featureEnvironmentStore: new FeatureEnvironmentStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
config,
|
||||
),
|
||||
userSplashStore: new UserSplashStore(db, eventBus, getLogger),
|
||||
roleStore: new RoleStore(db, eventBus, getLogger),
|
||||
|
@ -25,7 +25,7 @@ export const createAccessService = (
|
||||
const groupStore = new GroupStore(db);
|
||||
const accountStore = new AccountStore(db, getLogger);
|
||||
const roleStore = new RoleStore(db, eventBus, getLogger);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, getLogger);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, config);
|
||||
const accessStore = new AccessStore(db, eventBus, getLogger);
|
||||
const eventService = createEventsService(db, config);
|
||||
const groupService = new GroupService(
|
||||
|
@ -21,7 +21,7 @@ export const createApiTokenService = (
|
||||
getLogger,
|
||||
config.flagResolver,
|
||||
);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, getLogger);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, config);
|
||||
const eventService = createEventsService(db, config);
|
||||
|
||||
return new ApiTokenService(
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Knex } from 'knex';
|
||||
import metricsHelper from '../../util/metrics-helper';
|
||||
import { DB_TIME } from '../../metric-events';
|
||||
import type { Logger, LogProvider } from '../../logger';
|
||||
import type { Logger } from '../../logger';
|
||||
import type {
|
||||
IFeatureToggleClient,
|
||||
IFeatureToggleClientStore,
|
||||
@ -9,6 +9,7 @@ import type {
|
||||
IFlagResolver,
|
||||
IStrategyConfig,
|
||||
ITag,
|
||||
IUnleashConfig,
|
||||
PartialDeep,
|
||||
} from '../../types';
|
||||
import {
|
||||
@ -49,8 +50,10 @@ export default class FeatureToggleClientStore
|
||||
constructor(
|
||||
db: Db,
|
||||
eventBus: EventEmitter,
|
||||
getLogger: LogProvider,
|
||||
flagResolver: IFlagResolver,
|
||||
{
|
||||
getLogger,
|
||||
flagResolver,
|
||||
}: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
|
||||
) {
|
||||
this.db = db;
|
||||
this.logger = getLogger('feature-toggle-client-store.ts');
|
||||
@ -72,7 +75,6 @@ export default class FeatureToggleClientStore
|
||||
const isPlayground = requestType === 'playground';
|
||||
const environment = featureQuery?.environment || DEFAULT_ENV;
|
||||
const stopTimer = this.timer(`getAllBy${requestType}`);
|
||||
|
||||
let selectColumns = [
|
||||
'features.name as name',
|
||||
'features.description as description',
|
||||
|
@ -11,13 +11,10 @@ export const createClientFeatureToggleService = (
|
||||
db: Db,
|
||||
config: IUnleashConfig,
|
||||
): ClientFeatureToggleService => {
|
||||
const { getLogger, eventBus, flagResolver } = config;
|
||||
|
||||
const featureToggleClientStore = new FeatureToggleClientStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
flagResolver,
|
||||
config.eventBus,
|
||||
config,
|
||||
);
|
||||
|
||||
const segmentReadModel = new SegmentReadModel(db);
|
||||
@ -30,7 +27,7 @@ export const createClientFeatureToggleService = (
|
||||
},
|
||||
segmentReadModel,
|
||||
clientFeatureToggleCache,
|
||||
{ getLogger, flagResolver },
|
||||
config,
|
||||
);
|
||||
|
||||
return clientFeatureToggleService;
|
||||
@ -39,8 +36,6 @@ export const createClientFeatureToggleService = (
|
||||
export const createFakeClientFeatureToggleService = (
|
||||
config: IUnleashConfig,
|
||||
): ClientFeatureToggleService => {
|
||||
const { getLogger, flagResolver } = config;
|
||||
|
||||
const fakeClientFeatureToggleStore = new FakeClientFeatureToggleStore();
|
||||
|
||||
const fakeSegmentReadModel = new FakeSegmentReadModel();
|
||||
@ -51,7 +46,7 @@ export const createFakeClientFeatureToggleService = (
|
||||
},
|
||||
fakeSegmentReadModel,
|
||||
null,
|
||||
{ getLogger, flagResolver },
|
||||
config,
|
||||
);
|
||||
|
||||
return clientFeatureToggleService;
|
||||
|
@ -150,12 +150,7 @@ export const deferredExportImportTogglesService = (
|
||||
);
|
||||
const tagStore = new TagStore(db, eventBus, getLogger);
|
||||
const tagTypeStore = new TagTypeStore(db, eventBus, getLogger);
|
||||
const projectStore = new ProjectStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
flagResolver,
|
||||
);
|
||||
const projectStore = new ProjectStore(db, eventBus, config);
|
||||
const featureTagStore = new FeatureTagStore(db, eventBus, getLogger);
|
||||
const strategyStore = new StrategyStore(db, getLogger);
|
||||
const contextFieldStore = new ContextFieldStore(
|
||||
@ -172,7 +167,7 @@ export const deferredExportImportTogglesService = (
|
||||
const featureEnvironmentStore = new FeatureEnvironmentStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
config,
|
||||
);
|
||||
const eventStore = new EventStore(db, getLogger);
|
||||
const accessService = createAccessService(db, config);
|
||||
|
@ -19,11 +19,11 @@ export const createFeatureLifecycleService =
|
||||
const { eventBus, getLogger } = config;
|
||||
const eventStore = new EventStore(db, getLogger);
|
||||
const featureLifecycleStore = new FeatureLifecycleStore(db);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, getLogger);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, config);
|
||||
const featureEnvironmentStore = new FeatureEnvironmentStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
config,
|
||||
);
|
||||
const eventService = createEventsService(db, config);
|
||||
const featureLifecycleService = new FeatureLifecycleService(
|
||||
|
@ -80,19 +80,13 @@ export const createFeatureToggleService = (
|
||||
const featureToggleClientStore = new FeatureToggleClientStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
flagResolver,
|
||||
);
|
||||
const projectStore = new ProjectStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
flagResolver,
|
||||
config,
|
||||
);
|
||||
const projectStore = new ProjectStore(db, eventBus, config);
|
||||
const featureEnvironmentStore = new FeatureEnvironmentStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
config,
|
||||
);
|
||||
const contextFieldStore = new ContextFieldStore(
|
||||
db,
|
||||
@ -105,7 +99,7 @@ export const createFeatureToggleService = (
|
||||
const accessStore = new AccessStore(db, eventBus, getLogger);
|
||||
const featureTagStore = new FeatureTagStore(db, eventBus, getLogger);
|
||||
const roleStore = new RoleStore(db, eventBus, getLogger);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, getLogger);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, config);
|
||||
const eventService = createEventsService(db, config);
|
||||
const groupService = new GroupService(
|
||||
{ groupStore, accountStore },
|
||||
|
@ -58,13 +58,8 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
||||
flagResolver,
|
||||
);
|
||||
const userStore = new UserStore(db, getLogger, flagResolver);
|
||||
const projectStore = new ProjectStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
flagResolver,
|
||||
);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, getLogger);
|
||||
const projectStore = new ProjectStore(db, eventBus, config);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, config);
|
||||
const strategyStore = new StrategyStore(db, getLogger);
|
||||
const contextFieldStore = new ContextFieldStore(
|
||||
db,
|
||||
|
@ -21,21 +21,16 @@ export const createEnvironmentService =
|
||||
const featureEnvironmentStore = new FeatureEnvironmentStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
);
|
||||
const projectStore = new ProjectStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
flagResolver,
|
||||
config,
|
||||
);
|
||||
const projectStore = new ProjectStore(db, eventBus, config);
|
||||
const featureStrategiesStore = new FeatureStrategiesStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
flagResolver,
|
||||
);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, getLogger);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, config);
|
||||
const eventService = createEventsService(db, config);
|
||||
return new EnvironmentService(
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type EventEmitter from 'events';
|
||||
import type { Db } from '../../db/db';
|
||||
import type { Logger, LogProvider } from '../../logger';
|
||||
import type { Logger } from '../../logger';
|
||||
import metricsHelper from '../../util/metrics-helper';
|
||||
import { DB_TIME } from '../../metric-events';
|
||||
import type {
|
||||
@ -12,6 +12,7 @@ import NotFoundError from '../../error/notfound-error';
|
||||
import type { IEnvironmentStore } from './environment-store-type';
|
||||
import { snakeCaseKeys } from '../../util/snakeCase';
|
||||
import type { CreateFeatureStrategySchema } from '../../openapi';
|
||||
import type { IUnleashConfig } from '../../types';
|
||||
|
||||
interface IEnvironmentsTable {
|
||||
name: string;
|
||||
@ -104,11 +105,18 @@ export default class EnvironmentStore implements IEnvironmentStore {
|
||||
|
||||
private db: Db;
|
||||
|
||||
private isOss: boolean;
|
||||
|
||||
private timer: (string) => any;
|
||||
|
||||
constructor(db: Db, eventBus: EventEmitter, getLogger: LogProvider) {
|
||||
constructor(
|
||||
db: Db,
|
||||
eventBus: EventEmitter,
|
||||
{ getLogger, isOss }: Pick<IUnleashConfig, 'getLogger' | 'isOss'>,
|
||||
) {
|
||||
this.db = db;
|
||||
this.logger = getLogger('db/environment-store.ts');
|
||||
this.isOss = isOss;
|
||||
this.timer = (action) =>
|
||||
metricsHelper.wrapTimer(eventBus, DB_TIME, {
|
||||
store: 'environment',
|
||||
@ -148,9 +156,15 @@ export default class EnvironmentStore implements IEnvironmentStore {
|
||||
|
||||
async get(key: string): Promise<IEnvironment> {
|
||||
const stopTimer = this.timer('get');
|
||||
const row = await this.db<IEnvironmentsTable>(TABLE)
|
||||
.where({ name: key })
|
||||
.first();
|
||||
let keyQuery = this.db<IEnvironmentsTable>(TABLE).where({ name: key });
|
||||
if (this.isOss) {
|
||||
keyQuery = keyQuery.whereIn('name', [
|
||||
'default',
|
||||
'development',
|
||||
'production',
|
||||
]);
|
||||
}
|
||||
const row = await keyQuery.first();
|
||||
stopTimer();
|
||||
if (row) {
|
||||
return mapRow(row);
|
||||
@ -169,6 +183,9 @@ export default class EnvironmentStore implements IEnvironmentStore {
|
||||
if (query) {
|
||||
qB = qB.where(query);
|
||||
}
|
||||
if (this.isOss) {
|
||||
qB = qB.whereIn('name', ['default', 'development', 'production']);
|
||||
}
|
||||
const rows = await qB;
|
||||
stopTimer();
|
||||
return rows.map(mapRow);
|
||||
@ -196,6 +213,9 @@ export default class EnvironmentStore implements IEnvironmentStore {
|
||||
if (query) {
|
||||
qB = qB.where(query);
|
||||
}
|
||||
if (this.isOss) {
|
||||
qB = qB.whereIn('name', ['default', 'development', 'production']);
|
||||
}
|
||||
const rows = await qB;
|
||||
stopTimer();
|
||||
return rows.map(mapRowWithCounts);
|
||||
@ -230,6 +250,13 @@ export default class EnvironmentStore implements IEnvironmentStore {
|
||||
if (query) {
|
||||
qB = qB.where(query);
|
||||
}
|
||||
if (this.isOss) {
|
||||
qB = qB.whereIn('environments.name', [
|
||||
'default',
|
||||
'production',
|
||||
'development',
|
||||
]);
|
||||
}
|
||||
|
||||
const rows = await qB;
|
||||
stopTimer();
|
||||
|
@ -16,12 +16,7 @@ export const createProjectInsightsService = (
|
||||
config: IUnleashConfig,
|
||||
): ProjectInsightsService => {
|
||||
const { eventBus, getLogger, flagResolver } = config;
|
||||
const projectStore = new ProjectStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
flagResolver,
|
||||
);
|
||||
const projectStore = new ProjectStore(db, eventBus, config);
|
||||
const featureToggleStore = new FeatureToggleStore(
|
||||
db,
|
||||
eventBus,
|
||||
|
@ -24,12 +24,7 @@ export const createProjectStatusService = (
|
||||
config: IUnleashConfig,
|
||||
): ProjectStatusService => {
|
||||
const eventStore = new EventStore(db, config.getLogger);
|
||||
const projectStore = new ProjectStore(
|
||||
db,
|
||||
config.eventBus,
|
||||
config.getLogger,
|
||||
config.flagResolver,
|
||||
);
|
||||
const projectStore = new ProjectStore(db, config.eventBus, config);
|
||||
const apiTokenStore = new ApiTokenStore(
|
||||
db,
|
||||
config.eventBus,
|
||||
|
@ -63,12 +63,7 @@ export const createProjectService = (
|
||||
): ProjectService => {
|
||||
const { eventBus, getLogger, flagResolver } = config;
|
||||
const eventStore = new EventStore(db, getLogger);
|
||||
const projectStore = new ProjectStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
flagResolver,
|
||||
);
|
||||
const projectStore = new ProjectStore(db, eventBus, config);
|
||||
const projectOwnersReadModel = new ProjectOwnersReadModel(db);
|
||||
const projectFlagCreatorsReadModel = new ProjectFlagCreatorsReadModel(db);
|
||||
const groupStore = new GroupStore(db);
|
||||
@ -79,11 +74,11 @@ export const createProjectService = (
|
||||
flagResolver,
|
||||
);
|
||||
const accountStore = new AccountStore(db, getLogger);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, getLogger);
|
||||
const environmentStore = new EnvironmentStore(db, eventBus, config);
|
||||
const featureEnvironmentStore = new FeatureEnvironmentStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
config,
|
||||
);
|
||||
const projectStatsStore = new ProjectStatsStore(db, eventBus, getLogger);
|
||||
const accessService: AccessService = createAccessService(db, config);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { Logger, LogProvider } from '../../logger';
|
||||
import type { Logger } from '../../logger';
|
||||
|
||||
import NotFoundError from '../../error/notfound-error';
|
||||
import type {
|
||||
@ -8,6 +8,7 @@ import type {
|
||||
IProjectApplication,
|
||||
IProjectApplications,
|
||||
IProjectUpdate,
|
||||
IUnleashConfig,
|
||||
ProjectMode,
|
||||
} from '../../types';
|
||||
import type {
|
||||
@ -72,11 +73,16 @@ class ProjectStore implements IProjectStore {
|
||||
|
||||
private timer: Function;
|
||||
|
||||
private isOss: boolean;
|
||||
|
||||
constructor(
|
||||
db: Db,
|
||||
eventBus: EventEmitter,
|
||||
getLogger: LogProvider,
|
||||
flagResolver: IFlagResolver,
|
||||
{
|
||||
getLogger,
|
||||
flagResolver,
|
||||
isOss,
|
||||
}: Pick<IUnleashConfig, 'getLogger' | 'flagResolver' | 'isOss'>,
|
||||
) {
|
||||
this.db = db;
|
||||
this.logger = getLogger('project-store.ts');
|
||||
@ -86,6 +92,7 @@ class ProjectStore implements IProjectStore {
|
||||
action,
|
||||
});
|
||||
this.flagResolver = flagResolver;
|
||||
this.isOss = isOss;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
@ -122,6 +129,9 @@ class ProjectStore implements IProjectStore {
|
||||
.orderBy('name', 'asc');
|
||||
|
||||
projects = projects.where(`${TABLE}.archived_at`, null);
|
||||
if (this.isOss) {
|
||||
projects = projects.where('id', 'default');
|
||||
}
|
||||
|
||||
const rows = await projects;
|
||||
|
||||
|
@ -33,7 +33,7 @@ export function findParam(
|
||||
}
|
||||
|
||||
const rbacMiddleware = (
|
||||
config: Pick<IUnleashConfig, 'getLogger'>,
|
||||
config: Pick<IUnleashConfig, 'getLogger' | 'isOss'>,
|
||||
{
|
||||
featureToggleStore,
|
||||
segmentStore,
|
||||
@ -98,6 +98,24 @@ const rbacMiddleware = (
|
||||
) {
|
||||
projectId = 'default';
|
||||
}
|
||||
if (config.isOss) {
|
||||
if (projectId !== undefined && projectId !== 'default') {
|
||||
logger.error(
|
||||
'OSS is only allowed to work with default project.',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
const ossEnvs = ['default', 'development', 'production'];
|
||||
if (
|
||||
environment !== undefined &&
|
||||
!ossEnvs.includes(environment)
|
||||
) {
|
||||
logger.error(
|
||||
`OSS is only allowed to work with ${ossEnvs} environments.`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE segment does not include information about the segment's project
|
||||
// This is needed to check if the user has the right permissions on a project level
|
||||
|
@ -143,6 +143,7 @@ export interface IUnleashOptions {
|
||||
metricsRateLimiting?: Partial<IMetricsRateLimiting>;
|
||||
dailyMetricsStorageDays?: number;
|
||||
rateLimiting?: Partial<IRateLimiting>;
|
||||
isOss?: boolean;
|
||||
resourceLimits?: Partial<
|
||||
Pick<
|
||||
ResourceLimitsSchema,
|
||||
@ -273,6 +274,7 @@ export interface IUnleashConfig {
|
||||
publicFolder?: string;
|
||||
disableScheduler?: boolean;
|
||||
isEnterprise: boolean;
|
||||
isOss: boolean;
|
||||
rateLimiting: IRateLimiting;
|
||||
feedbackUriPath?: string;
|
||||
openAIAPIKey?: string;
|
||||
|
68
src/test/e2e/api/admin/environment-oss.e2e.test.ts
Normal file
68
src/test/e2e/api/admin/environment-oss.e2e.test.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import {
|
||||
type IUnleashTest,
|
||||
setupAppWithCustomConfig,
|
||||
} from '../../helpers/test-helper';
|
||||
import dbInit, { type ITestDb } from '../../helpers/database-init';
|
||||
import getLogger from '../../../fixtures/no-logger';
|
||||
|
||||
let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('environment_api_is_oss_serial', getLogger, {
|
||||
isOss: true,
|
||||
});
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
experimental: {
|
||||
flags: {
|
||||
strictSchemaValidation: true,
|
||||
},
|
||||
},
|
||||
isOss: true,
|
||||
},
|
||||
db.rawDatabase,
|
||||
);
|
||||
await db.stores.environmentStore.create({
|
||||
name: 'development',
|
||||
type: 'development',
|
||||
enabled: true,
|
||||
});
|
||||
await db.stores.environmentStore.create({
|
||||
name: 'production',
|
||||
type: 'production',
|
||||
enabled: true,
|
||||
});
|
||||
await db.stores.environmentStore.create({
|
||||
name: 'customenvironment',
|
||||
type: 'production',
|
||||
enabled: true,
|
||||
});
|
||||
await db.stores.environmentStore.create({
|
||||
name: 'customenvironment2',
|
||||
type: 'production',
|
||||
enabled: true,
|
||||
});
|
||||
await db.stores.environmentStore.create({
|
||||
name: 'customenvironment3',
|
||||
type: 'production',
|
||||
enabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.destroy();
|
||||
await db.destroy();
|
||||
});
|
||||
|
||||
test('querying environments in OSS only returns environments that are included in oss', async () => {
|
||||
await app.request
|
||||
.get('/api/admin/environments')
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.environments).toHaveLength(3);
|
||||
const names = res.body.environments.map((env) => env.name);
|
||||
expect(names).toEqual(['default', 'development', 'production']);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user