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
Gastón Fournier f16ad4e899
chore: import IUser instead of User for auth (#5269)
## About the changes
A very subtle change in the way we import IUser makes a huge difference
because previously, instead of importing IUser interface we were
importing User and naming it IUser here:

6f8f21fd48/src/lib/routes/unleash-types.ts (L2)
whereas the correct way of importing the interface is:

eec64b119e/src/lib/routes/unleash-types.ts (L2)
2023-11-06 10:46:59 +01:00

156 lines
5.0 KiB
TypeScript

import { IImportTogglesStore } from './import-toggles-store-type';
import { AccessService, ContextService, TagTypeService } from '../../services';
import { ContextFieldSchema, ImportTogglesSchema } from '../../openapi';
import { ITagType } from '../../types/stores/tag-type-store';
import { IUser } from '../../types/user';
import {
CREATE_CONTEXT_FIELD,
CREATE_FEATURE,
CREATE_FEATURE_STRATEGY,
DELETE_FEATURE_STRATEGY,
UPDATE_FEATURE,
UPDATE_FEATURE_ENVIRONMENT_VARIANTS,
UPDATE_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(UPDATE_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);
}
}
}