1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-31 00:16:47 +01:00
unleash.unleash/src/lib/features/export-import-toggles/import-permissions-service.ts
Christopher Kolstad 53354224fc
chore: Bump biome and configure husky (#6589)
Upgrades biome to 1.6.1, and updates husky pre-commit hook.

Most changes here are making type imports explicit.
2024-03-18 13:58:05 +01:00

160 lines
5.1 KiB
TypeScript

import type { IImportTogglesStore } from './import-toggles-store-type';
import type {
AccessService,
ContextService,
TagTypeService,
} from '../../services';
import type { ContextFieldSchema, ImportTogglesSchema } from '../../openapi';
import type { ITagType } from '../tag-type/tag-type-store-type';
import type { IUser } from '../../types/user';
import {
CREATE_CONTEXT_FIELD,
CREATE_FEATURE,
CREATE_FEATURE_STRATEGY,
DELETE_FEATURE_STRATEGY,
UPDATE_FEATURE,
UPDATE_FEATURE_ENVIRONMENT_VARIANTS,
CREATE_TAG_TYPE,
} from '../../types';
import { PermissionError } from '../../error';
export type Mode = 'regular' | 'change_request';
export class ImportPermissionsService {
private importTogglesStore: IImportTogglesStore;
private accessService: AccessService;
private tagTypeService: TagTypeService;
private contextService: ContextService;
private async getNewTagTypes(
dto: ImportTogglesSchema,
): Promise<ITagType[]> {
const existingTagTypes = (await this.tagTypeService.getAll()).map(
(tagType) => tagType.name,
);
const newTagTypes = dto.data.tagTypes?.filter(
(tagType) => !existingTagTypes.includes(tagType.name),
);
return [
...new Map(newTagTypes.map((item) => [item.name, item])).values(),
];
}
private async getNewContextFields(
dto: ImportTogglesSchema,
): Promise<ContextFieldSchema[]> {
const availableContextFields = await this.contextService.getAll();
return dto.data.contextFields?.filter(
(contextField) =>
!availableContextFields.some(
(availableField) =>
availableField.name === contextField.name,
),
);
}
constructor(
importTogglesStore: IImportTogglesStore,
accessService: AccessService,
tagTypeService: TagTypeService,
contextService: ContextService,
) {
this.importTogglesStore = importTogglesStore;
this.accessService = accessService;
this.tagTypeService = tagTypeService;
this.contextService = contextService;
}
async getMissingPermissions(
dto: ImportTogglesSchema,
user: IUser,
mode: Mode,
): Promise<string[]> {
const [
newTagTypes,
newContextFields,
strategiesExistForFeatures,
featureEnvsWithVariants,
existingFeatures,
] = await Promise.all([
this.getNewTagTypes(dto),
this.getNewContextFields(dto),
this.importTogglesStore.strategiesExistForFeatures(
dto.data.features.map((feature) => feature.name),
dto.environment,
),
dto.data.featureEnvironments?.filter(
(featureEnvironment) =>
Array.isArray(featureEnvironment.variants) &&
featureEnvironment.variants.length > 0,
) || Promise.resolve([]),
this.importTogglesStore.getExistingFeatures(
dto.data.features.map((feature) => feature.name),
),
]);
const permissions = [UPDATE_FEATURE];
if (newTagTypes.length > 0) {
permissions.push(CREATE_TAG_TYPE);
}
if (Array.isArray(newContextFields) && newContextFields.length > 0) {
permissions.push(CREATE_CONTEXT_FIELD);
}
if (strategiesExistForFeatures && mode === 'regular') {
permissions.push(DELETE_FEATURE_STRATEGY);
}
if (dto.data.featureStrategies.length > 0 && mode === 'regular') {
permissions.push(CREATE_FEATURE_STRATEGY);
}
if (featureEnvsWithVariants.length > 0 && mode === 'regular') {
permissions.push(UPDATE_FEATURE_ENVIRONMENT_VARIANTS);
}
if (existingFeatures.length < dto.data.features.length) {
permissions.push(CREATE_FEATURE);
}
const displayPermissions =
await this.importTogglesStore.getDisplayPermissions(permissions);
const results = await Promise.all(
displayPermissions.map((permission) =>
this.accessService
.hasPermission(
user,
permission.name,
dto.project,
dto.environment,
)
.then(
(hasPermission) => [permission, hasPermission] as const,
),
),
);
return results
.filter(([, hasAccess]) => !hasAccess)
.map(([permission]) => permission.displayName);
}
async verifyPermissions(
dto: ImportTogglesSchema,
user: IUser,
mode: Mode,
): Promise<void> {
const missingPermissions = await this.getMissingPermissions(
dto,
user,
mode,
);
if (missingPermissions.length > 0) {
throw new PermissionError(missingPermissions);
}
}
}