mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-27 11:02:16 +01:00
chore: extract UI config logic into its own service (#10704)
https://linear.app/unleash/issue/2-3921/extract-ui-config-logic-into-its-own-service Extracts UI config logic into its own service. This is the first step to accomplish resource limits and license key resources alignment.
This commit is contained in:
parent
b865ee44f3
commit
e46f8881d1
@ -8,7 +8,7 @@ import EventController from './event.js';
|
|||||||
import PlaygroundController from '../../features/playground/playground.js';
|
import PlaygroundController from '../../features/playground/playground.js';
|
||||||
import MetricsController from './metrics.js';
|
import MetricsController from './metrics.js';
|
||||||
import UserController from './user/user.js';
|
import UserController from './user/user.js';
|
||||||
import ConfigController from './config.js';
|
import UiConfigController from '../../ui-config/ui-config-controller.js';
|
||||||
import { ContextController } from '../../features/context/context.js';
|
import { ContextController } from '../../features/context/context.js';
|
||||||
import ClientMetricsController from '../../features/metrics/client-metrics/client-metrics.js';
|
import ClientMetricsController from '../../features/metrics/client-metrics/client-metrics.js';
|
||||||
import TagController from './tag.js';
|
import TagController from './tag.js';
|
||||||
@ -90,7 +90,7 @@ export class AdminApi extends Controller {
|
|||||||
|
|
||||||
this.app.use(
|
this.app.use(
|
||||||
'/ui-config',
|
'/ui-config',
|
||||||
new ConfigController(config, services).router,
|
new UiConfigController(config, services).router,
|
||||||
);
|
);
|
||||||
this.app.use(
|
this.app.use(
|
||||||
'/context',
|
'/context',
|
||||||
|
|||||||
@ -170,6 +170,7 @@ import type { IPrivateProjectChecker } from '../features/private-project/private
|
|||||||
import { UnknownFlagsService } from '../features/metrics/unknown-flags/unknown-flags-service.js';
|
import { UnknownFlagsService } from '../features/metrics/unknown-flags/unknown-flags-service.js';
|
||||||
import type FeatureLinkService from '../features/feature-links/feature-link-service.js';
|
import type FeatureLinkService from '../features/feature-links/feature-link-service.js';
|
||||||
import { createUserService } from '../features/users/createUserService.js';
|
import { createUserService } from '../features/users/createUserService.js';
|
||||||
|
import { UiConfigService } from '../ui-config/ui-config-service.js';
|
||||||
|
|
||||||
export const createServices = (
|
export const createServices = (
|
||||||
stores: IUnleashStores,
|
stores: IUnleashStores,
|
||||||
@ -441,6 +442,15 @@ export const createServices = (
|
|||||||
? withTransactional(createUserSubscriptionsService(config), db)
|
? withTransactional(createUserSubscriptionsService(config), db)
|
||||||
: withFakeTransactional(createFakeUserSubscriptionsService(config));
|
: withFakeTransactional(createFakeUserSubscriptionsService(config));
|
||||||
|
|
||||||
|
const uiConfigService = new UiConfigService(config, {
|
||||||
|
versionService,
|
||||||
|
settingService,
|
||||||
|
emailService,
|
||||||
|
frontendApiService,
|
||||||
|
maintenanceService,
|
||||||
|
sessionService,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transactionalAccessService,
|
transactionalAccessService,
|
||||||
accessService,
|
accessService,
|
||||||
@ -514,6 +524,7 @@ export const createServices = (
|
|||||||
transactionalFeatureLinkService,
|
transactionalFeatureLinkService,
|
||||||
featureLinkService,
|
featureLinkService,
|
||||||
unknownFlagsService,
|
unknownFlagsService,
|
||||||
|
uiConfigService,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -645,4 +656,5 @@ export interface IUnleashServices {
|
|||||||
transactionalFeatureLinkService: WithTransactional<FeatureLinkService>;
|
transactionalFeatureLinkService: WithTransactional<FeatureLinkService>;
|
||||||
featureLinkService: FeatureLinkService;
|
featureLinkService: FeatureLinkService;
|
||||||
unknownFlagsService: UnknownFlagsService;
|
unknownFlagsService: UnknownFlagsService;
|
||||||
|
uiConfigService: UiConfigService;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,7 +59,8 @@ export type IFlagKey =
|
|||||||
| 'fetchMode'
|
| 'fetchMode'
|
||||||
| 'optimizeLifecycle'
|
| 'optimizeLifecycle'
|
||||||
| 'newStrategyModal'
|
| 'newStrategyModal'
|
||||||
| 'globalChangeRequestList';
|
| 'globalChangeRequestList'
|
||||||
|
| 'newUiConfigService';
|
||||||
|
|
||||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
||||||
|
|
||||||
@ -273,6 +274,10 @@ const flags: IFlags = {
|
|||||||
process.env.UNLEASH_EXPERIMENTAL_GLOBAL_CHANGE_REQUEST_LIST,
|
process.env.UNLEASH_EXPERIMENTAL_GLOBAL_CHANGE_REQUEST_LIST,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
newUiConfigService: parseEnvVarBoolean(
|
||||||
|
process.env.UNLEASH_EXPERIMENTAL_NEW_UI_CONFIG_SERVICE,
|
||||||
|
false,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultExperimentalOptions: IExperimentalOptions = {
|
export const defaultExperimentalOptions: IExperimentalOptions = {
|
||||||
|
|||||||
@ -1,37 +1,34 @@
|
|||||||
import type { Response } from 'express';
|
import type { Response } from 'express';
|
||||||
import type { AuthedRequest } from '../../types/core.js';
|
import type { AuthedRequest } from '../types/core.js';
|
||||||
import type { IUnleashServices } from '../../services/index.js';
|
import type { IUnleashServices } from '../services/index.js';
|
||||||
import { IAuthType, type IUnleashConfig } from '../../types/option.js';
|
import { IAuthType, type IUnleashConfig } from '../types/option.js';
|
||||||
import version from '../../util/version.js';
|
import version from '../util/version.js';
|
||||||
import Controller from '../controller.js';
|
import Controller from '../routes/controller.js';
|
||||||
import type VersionService from '../../services/version-service.js';
|
import type VersionService from '../services/version-service.js';
|
||||||
import type SettingService from '../../services/setting-service.js';
|
import type SettingService from '../services/setting-service.js';
|
||||||
import {
|
import {
|
||||||
type SimpleAuthSettings,
|
type SimpleAuthSettings,
|
||||||
simpleAuthSettingsKey,
|
simpleAuthSettingsKey,
|
||||||
} from '../../types/settings/simple-auth-settings.js';
|
} from '../types/settings/simple-auth-settings.js';
|
||||||
import { ADMIN, NONE, UPDATE_CORS } from '../../types/permissions.js';
|
import { ADMIN, NONE, UPDATE_CORS } from '../types/permissions.js';
|
||||||
import { createResponseSchema } from '../../openapi/util/create-response-schema.js';
|
import { createResponseSchema } from '../openapi/util/create-response-schema.js';
|
||||||
import {
|
import {
|
||||||
uiConfigSchema,
|
uiConfigSchema,
|
||||||
type UiConfigSchema,
|
type UiConfigSchema,
|
||||||
} from '../../openapi/spec/ui-config-schema.js';
|
} from '../openapi/spec/ui-config-schema.js';
|
||||||
import type { OpenApiService } from '../../services/openapi-service.js';
|
import type { OpenApiService } from '../services/openapi-service.js';
|
||||||
import type { EmailService } from '../../services/email-service.js';
|
import type { EmailService } from '../services/email-service.js';
|
||||||
import { emptyResponse } from '../../openapi/util/standard-responses.js';
|
import { emptyResponse } from '../openapi/util/standard-responses.js';
|
||||||
import type { IAuthRequest } from '../unleash-types.js';
|
import type { IAuthRequest } from '../routes/unleash-types.js';
|
||||||
import NotFoundError from '../../error/notfound-error.js';
|
import NotFoundError from '../error/notfound-error.js';
|
||||||
import type { SetCorsSchema } from '../../openapi/spec/set-cors-schema.js';
|
import type { SetCorsSchema } from '../openapi/spec/set-cors-schema.js';
|
||||||
import { createRequestSchema } from '../../openapi/util/create-request-schema.js';
|
import { createRequestSchema } from '../openapi/util/create-request-schema.js';
|
||||||
import type {
|
import type { FrontendApiService, SessionService } from '../services/index.js';
|
||||||
FrontendApiService,
|
import type MaintenanceService from '../features/maintenance/maintenance-service.js';
|
||||||
SessionService,
|
import type { IFlagResolver } from '../types/index.js';
|
||||||
} from '../../services/index.js';
|
import type { UiConfigService } from './ui-config-service.js';
|
||||||
import type MaintenanceService from '../../features/maintenance/maintenance-service.js';
|
|
||||||
import type ClientInstanceService from '../../features/metrics/instance/instance-service.js';
|
|
||||||
import type { IFlagResolver } from '../../types/index.js';
|
|
||||||
|
|
||||||
class ConfigController extends Controller {
|
class UiConfigController extends Controller {
|
||||||
private versionService: VersionService;
|
private versionService: VersionService;
|
||||||
|
|
||||||
private settingService: SettingService;
|
private settingService: SettingService;
|
||||||
@ -40,14 +37,14 @@ class ConfigController extends Controller {
|
|||||||
|
|
||||||
private emailService: EmailService;
|
private emailService: EmailService;
|
||||||
|
|
||||||
private clientInstanceService: ClientInstanceService;
|
|
||||||
|
|
||||||
private sessionService: SessionService;
|
private sessionService: SessionService;
|
||||||
|
|
||||||
private maintenanceService: MaintenanceService;
|
private maintenanceService: MaintenanceService;
|
||||||
|
|
||||||
private flagResolver: IFlagResolver;
|
private flagResolver: IFlagResolver;
|
||||||
|
|
||||||
|
private uiConfigService: UiConfigService;
|
||||||
|
|
||||||
private readonly openApiService: OpenApiService;
|
private readonly openApiService: OpenApiService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -59,8 +56,8 @@ class ConfigController extends Controller {
|
|||||||
openApiService,
|
openApiService,
|
||||||
frontendApiService,
|
frontendApiService,
|
||||||
maintenanceService,
|
maintenanceService,
|
||||||
clientInstanceService,
|
|
||||||
sessionService,
|
sessionService,
|
||||||
|
uiConfigService,
|
||||||
}: Pick<
|
}: Pick<
|
||||||
IUnleashServices,
|
IUnleashServices,
|
||||||
| 'versionService'
|
| 'versionService'
|
||||||
@ -71,18 +68,20 @@ class ConfigController extends Controller {
|
|||||||
| 'maintenanceService'
|
| 'maintenanceService'
|
||||||
| 'clientInstanceService'
|
| 'clientInstanceService'
|
||||||
| 'sessionService'
|
| 'sessionService'
|
||||||
|
| 'uiConfigService'
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
super(config);
|
super(config);
|
||||||
|
this.flagResolver = config.flagResolver;
|
||||||
|
this.openApiService = openApiService;
|
||||||
|
this.uiConfigService = uiConfigService;
|
||||||
this.versionService = versionService;
|
this.versionService = versionService;
|
||||||
this.settingService = settingService;
|
this.settingService = settingService;
|
||||||
this.emailService = emailService;
|
this.emailService = emailService;
|
||||||
this.openApiService = openApiService;
|
|
||||||
this.frontendApiService = frontendApiService;
|
this.frontendApiService = frontendApiService;
|
||||||
this.maintenanceService = maintenanceService;
|
this.maintenanceService = maintenanceService;
|
||||||
this.clientInstanceService = clientInstanceService;
|
|
||||||
this.sessionService = sessionService;
|
this.sessionService = sessionService;
|
||||||
this.flagResolver = config.flagResolver;
|
|
||||||
this.route({
|
this.route({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
path: '',
|
path: '',
|
||||||
@ -125,6 +124,17 @@ class ConfigController extends Controller {
|
|||||||
req: AuthedRequest,
|
req: AuthedRequest,
|
||||||
res: Response<UiConfigSchema>,
|
res: Response<UiConfigSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
if (this.flagResolver.isEnabled('newUiConfigService')) {
|
||||||
|
const uiConfig = await this.uiConfigService.getUiConfig(req.user);
|
||||||
|
|
||||||
|
return this.openApiService.respondWithValidation(
|
||||||
|
200,
|
||||||
|
res,
|
||||||
|
uiConfigSchema.$id,
|
||||||
|
uiConfig,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const getMaxSessionsCount = async () => {
|
const getMaxSessionsCount = async () => {
|
||||||
if (this.flagResolver.isEnabled('showUserDeviceCount')) {
|
if (this.flagResolver.isEnabled('showUserDeviceCount')) {
|
||||||
return this.sessionService.getMaxSessionsCount();
|
return this.sessionService.getMaxSessionsCount();
|
||||||
@ -207,4 +217,4 @@ class ConfigController extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConfigController;
|
export default UiConfigController;
|
||||||
127
src/lib/ui-config/ui-config-service.ts
Normal file
127
src/lib/ui-config/ui-config-service.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import type { IUnleashConfig } from '../types/option.js';
|
||||||
|
import type { UiConfigSchema } from '../openapi/index.js';
|
||||||
|
import {
|
||||||
|
IAuthType,
|
||||||
|
type EmailService,
|
||||||
|
type FrontendApiService,
|
||||||
|
type IFlagResolver,
|
||||||
|
type IUnleashServices,
|
||||||
|
type SessionService,
|
||||||
|
type SettingService,
|
||||||
|
type User,
|
||||||
|
type VersionService,
|
||||||
|
} from '../server-impl.js';
|
||||||
|
import type MaintenanceService from '../features/maintenance/maintenance-service.js';
|
||||||
|
import {
|
||||||
|
type SimpleAuthSettings,
|
||||||
|
simpleAuthSettingsKey,
|
||||||
|
} from '../types/settings/simple-auth-settings.js';
|
||||||
|
import version from '../util/version.js';
|
||||||
|
|
||||||
|
export class UiConfigService {
|
||||||
|
private config: IUnleashConfig;
|
||||||
|
|
||||||
|
private versionService: VersionService;
|
||||||
|
|
||||||
|
private settingService: SettingService;
|
||||||
|
|
||||||
|
private frontendApiService: FrontendApiService;
|
||||||
|
|
||||||
|
private emailService: EmailService;
|
||||||
|
|
||||||
|
private sessionService: SessionService;
|
||||||
|
|
||||||
|
private maintenanceService: MaintenanceService;
|
||||||
|
|
||||||
|
private flagResolver: IFlagResolver;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
config: IUnleashConfig,
|
||||||
|
{
|
||||||
|
versionService,
|
||||||
|
settingService,
|
||||||
|
emailService,
|
||||||
|
frontendApiService,
|
||||||
|
maintenanceService,
|
||||||
|
sessionService,
|
||||||
|
}: Pick<
|
||||||
|
IUnleashServices,
|
||||||
|
| 'versionService'
|
||||||
|
| 'settingService'
|
||||||
|
| 'emailService'
|
||||||
|
| 'frontendApiService'
|
||||||
|
| 'maintenanceService'
|
||||||
|
| 'sessionService'
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
this.config = config;
|
||||||
|
this.flagResolver = config.flagResolver;
|
||||||
|
this.versionService = versionService;
|
||||||
|
this.settingService = settingService;
|
||||||
|
this.emailService = emailService;
|
||||||
|
this.frontendApiService = frontendApiService;
|
||||||
|
this.maintenanceService = maintenanceService;
|
||||||
|
this.sessionService = sessionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMaxSessionsCount(): Promise<number> {
|
||||||
|
if (this.flagResolver.isEnabled('showUserDeviceCount')) {
|
||||||
|
return this.sessionService.getMaxSessionsCount();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUiConfig(user: User): Promise<UiConfigSchema> {
|
||||||
|
const [
|
||||||
|
frontendSettings,
|
||||||
|
simpleAuthSettings,
|
||||||
|
maintenanceMode,
|
||||||
|
maxSessionsCount,
|
||||||
|
] = await Promise.all([
|
||||||
|
this.frontendApiService.getFrontendSettings(false),
|
||||||
|
this.settingService.get<SimpleAuthSettings>(simpleAuthSettingsKey),
|
||||||
|
this.maintenanceService.isMaintenanceMode(),
|
||||||
|
this.getMaxSessionsCount(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const disablePasswordAuth =
|
||||||
|
simpleAuthSettings?.disabled ||
|
||||||
|
this.config.authentication.type === IAuthType.NONE;
|
||||||
|
|
||||||
|
const expFlags = this.config.flagResolver.getAll({
|
||||||
|
email: user.email,
|
||||||
|
});
|
||||||
|
|
||||||
|
const flags = {
|
||||||
|
...this.config.ui.flags,
|
||||||
|
...expFlags,
|
||||||
|
};
|
||||||
|
|
||||||
|
const unleashContext = {
|
||||||
|
...this.flagResolver.getStaticContext(),
|
||||||
|
email: user.email,
|
||||||
|
userId: user.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uiConfig: UiConfigSchema = {
|
||||||
|
...this.config.ui,
|
||||||
|
flags,
|
||||||
|
version,
|
||||||
|
emailEnabled: this.emailService.isEnabled(),
|
||||||
|
unleashUrl: this.config.server.unleashUrl,
|
||||||
|
baseUriPath: this.config.server.baseUriPath,
|
||||||
|
authenticationType: this.config.authentication?.type,
|
||||||
|
frontendApiOrigins: frontendSettings.frontendApiOrigins,
|
||||||
|
versionInfo: await this.versionService.getVersionInfo(),
|
||||||
|
prometheusAPIAvailable: this.config.prometheusApi !== undefined,
|
||||||
|
resourceLimits: this.config.resourceLimits,
|
||||||
|
disablePasswordAuth,
|
||||||
|
maintenanceMode,
|
||||||
|
feedbackUriPath: this.config.feedbackUriPath,
|
||||||
|
maxSessionsCount,
|
||||||
|
unleashContext: unleashContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
return uiConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +1,15 @@
|
|||||||
import supertest, { type Test } from 'supertest';
|
import supertest, { type Test } from 'supertest';
|
||||||
import { createTestConfig } from '../../../test/config/test-config.js';
|
import { createTestConfig } from '../../test/config/test-config.js';
|
||||||
|
|
||||||
import createStores from '../../../test/fixtures/store.js';
|
import createStores from '../../test/fixtures/store.js';
|
||||||
import getApp from '../../app.js';
|
import getApp from '../app.js';
|
||||||
import { createServices } from '../../services/index.js';
|
import { createServices } from '../services/index.js';
|
||||||
import {
|
import {
|
||||||
DEFAULT_SEGMENT_VALUES_LIMIT,
|
DEFAULT_SEGMENT_VALUES_LIMIT,
|
||||||
DEFAULT_STRATEGY_SEGMENTS_LIMIT,
|
DEFAULT_STRATEGY_SEGMENTS_LIMIT,
|
||||||
} from '../../util/segments.js';
|
} from '../util/segments.js';
|
||||||
import type TestAgent from 'supertest/lib/agent.d.ts';
|
import type TestAgent from 'supertest/lib/agent.d.ts';
|
||||||
import type { IUnleashStores } from '../../types/index.js';
|
import type { IUnleashStores } from '../types/index.js';
|
||||||
|
|
||||||
const uiConfig = {
|
const uiConfig = {
|
||||||
headerBackground: 'red',
|
headerBackground: 'red',
|
||||||
@ -55,6 +55,7 @@ process.nextTick(async () => {
|
|||||||
lifecycleGraphs: true,
|
lifecycleGraphs: true,
|
||||||
newStrategyModal: true,
|
newStrategyModal: true,
|
||||||
globalChangeRequestList: true,
|
globalChangeRequestList: true,
|
||||||
|
newUiConfigService: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
authentication: {
|
authentication: {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user