Fix: Get rid of subgroups in admin/env-variables (#10105)

closes https://github.com/twentyhq/core-team-issues/issues/379

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
nitin
2025-02-10 21:24:37 +05:30
committed by GitHub
parent c07f43fcb1
commit 5948bc2685
14 changed files with 354 additions and 554 deletions

View File

@ -7,13 +7,12 @@ import {
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
import { EnvironmentVariablesGroup } from 'src/engine/core-modules/environment/enums/environment-variables-group.enum';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { User } from 'src/engine/core-modules/user/user.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
const UserFindOneMock = jest.fn();
const WorkspaceFindOneMock = jest.fn();
@ -34,31 +33,23 @@ jest.mock(
);
jest.mock(
'src/engine/core-modules/environment/constants/environment-variables-group-metadata',
'../../environment/constants/environment-variables-group-metadata',
() => ({
ENVIRONMENT_VARIABLES_GROUP_METADATA: {
GROUP_1: {
SERVER_CONFIG: {
position: 100,
description: '',
description: 'Server config description',
isHiddenOnLoad: false,
},
GROUP_2: {
RATE_LIMITING: {
position: 200,
description: '',
description: 'Rate limiting description',
isHiddenOnLoad: false,
},
VISIBLE_GROUP: {
OTHER: {
position: 300,
description: '',
},
},
}),
);
jest.mock(
'src/engine/core-modules/environment/constants/environment-variables-sub-group-metadata',
() => ({
ENVIRONMENT_VARIABLES_SUB_GROUP_METADATA: {
SUBGROUP_1: {
description: '',
description: 'Other description',
isHiddenOnLoad: true,
},
},
}),
@ -269,29 +260,35 @@ describe('AdminPanelService', () => {
});
describe('getEnvironmentVariablesGrouped', () => {
it('should correctly group environment variables', () => {
it('should correctly group and sort environment variables', () => {
EnvironmentServiceGetAllMock.mockReturnValue({
VAR_1: {
value: 'value1',
SERVER_URL: {
value: 'http://localhost',
metadata: {
group: 'GROUP_1',
description: 'Description 1',
group: 'SERVER_CONFIG',
description: 'Server URL',
},
},
VAR_2: {
value: 'value2',
RATE_LIMIT_TTL: {
value: '60',
metadata: {
group: 'GROUP_1',
subGroup: 'SUBGROUP_1',
description: 'Description 2',
group: 'RATE_LIMITING',
description: 'Rate limit TTL',
},
},
API_KEY: {
value: 'secret-key',
metadata: {
group: 'SERVER_CONFIG',
description: 'API Key',
sensitive: true,
},
},
VAR_3: {
value: 'value3',
OTHER_VAR: {
value: 'other',
metadata: {
group: 'GROUP_2',
description: 'Description 3',
group: 'OTHER',
description: 'Other var',
},
},
});
@ -301,74 +298,56 @@ describe('AdminPanelService', () => {
expect(result).toEqual({
groups: [
{
name: 'GROUP_1',
description: '',
name: 'SERVER_CONFIG',
description: 'Server config description',
isHiddenOnLoad: false,
variables: [
{
name: 'VAR_1',
value: 'value1',
description: 'Description 1',
sensitive: false,
name: 'API_KEY',
value: 'secret-key',
description: 'API Key',
sensitive: true,
},
],
subgroups: [
{
name: 'SUBGROUP_1',
description: '',
variables: [
{
name: 'VAR_2',
value: 'value2',
description: 'Description 2',
sensitive: true,
},
],
name: 'SERVER_URL',
value: 'http://localhost',
description: 'Server URL',
sensitive: false,
},
],
},
{
name: 'GROUP_2',
description: '',
name: 'RATE_LIMITING',
description: 'Rate limiting description',
isHiddenOnLoad: false,
variables: [
{
name: 'VAR_3',
value: 'value3',
description: 'Description 3',
name: 'RATE_LIMIT_TTL',
value: '60',
description: 'Rate limit TTL',
sensitive: false,
},
],
},
{
name: 'OTHER',
description: 'Other description',
isHiddenOnLoad: true,
variables: [
{
name: 'OTHER_VAR',
value: 'other',
description: 'Other var',
sensitive: false,
},
],
subgroups: [],
},
],
});
});
it('should sort groups by position and variables alphabetically', () => {
EnvironmentServiceGetAllMock.mockReturnValue({
Z_VAR: {
value: 'valueZ',
metadata: {
group: 'GROUP_1',
description: 'Description Z',
},
},
A_VAR: {
value: 'valueA',
metadata: {
group: 'GROUP_1',
description: 'Description A',
},
},
});
const result = service.getEnvironmentVariablesGrouped();
const group = result.groups.find(
(g) => g.name === ('GROUP_1' as EnvironmentVariablesGroup),
);
expect(group?.variables[0].name).toBe('A_VAR');
expect(group?.variables[1].name).toBe('Z_VAR');
expect(result.groups[0].name).toBe('SERVER_CONFIG');
expect(result.groups[1].name).toBe('RATE_LIMITING');
expect(result.groups[2].name).toBe('OTHER');
});
it('should handle empty environment variables', () => {
@ -380,5 +359,25 @@ describe('AdminPanelService', () => {
groups: [],
});
});
it('should handle variables with undefined metadata fields', () => {
EnvironmentServiceGetAllMock.mockReturnValue({
TEST_VAR: {
value: 'test',
metadata: {
group: 'SERVER_CONFIG',
},
},
});
const result = service.getEnvironmentVariablesGrouped();
expect(result.groups[0].variables[0]).toEqual({
name: 'TEST_VAR',
value: 'test',
description: undefined,
sensitive: false,
});
});
});
});

View File

@ -12,10 +12,9 @@ import {
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
import { ENVIRONMENT_VARIABLES_GROUP_METADATA } from 'src/engine/core-modules/environment/constants/environment-variables-group-metadata';
import { ENVIRONMENT_VARIABLES_SUB_GROUP_METADATA } from 'src/engine/core-modules/environment/constants/environment-variables-sub-group-metadata';
import { EnvironmentVariablesGroup } from 'src/engine/core-modules/environment/enums/environment-variables-group.enum';
import { EnvironmentVariablesSubGroup } from 'src/engine/core-modules/environment/enums/environment-variables-sub-group.enum';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
@ -28,7 +27,6 @@ import { User } from 'src/engine/core-modules/user/user.entity';
import { userValidator } from 'src/engine/core-modules/user/user.validate';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Injectable()
export class AdminPanelService {
@ -175,14 +173,11 @@ export class AdminPanelService {
const rawEnvVars = this.environmentService.getAll();
const groupedData = new Map<
EnvironmentVariablesGroup,
{
variables: EnvironmentVariable[];
subgroups: Map<EnvironmentVariablesSubGroup, EnvironmentVariable[]>;
}
EnvironmentVariable[]
>();
for (const [varName, { value, metadata }] of Object.entries(rawEnvVars)) {
const { group, subGroup, description } = metadata;
const { group, description } = metadata;
const envVar: EnvironmentVariable = {
name: varName,
@ -191,27 +186,11 @@ export class AdminPanelService {
sensitive: metadata.sensitive ?? false,
};
let currentGroup = groupedData.get(group);
if (!currentGroup) {
currentGroup = {
variables: [],
subgroups: new Map(),
};
groupedData.set(group, currentGroup);
if (!groupedData.has(group)) {
groupedData.set(group, []);
}
if (subGroup) {
let subgroupVars = currentGroup.subgroups.get(subGroup);
if (!subgroupVars) {
subgroupVars = [];
currentGroup.subgroups.set(subGroup, subgroupVars);
}
subgroupVars.push(envVar);
} else {
currentGroup.variables.push(envVar);
}
groupedData.get(group)?.push(envVar);
}
const groups: EnvironmentVariablesGroupData[] = Array.from(
@ -223,20 +202,12 @@ export class AdminPanelService {
return positionA - positionB;
})
.map(([name, data]) => ({
.map(([name, variables]) => ({
name,
description: ENVIRONMENT_VARIABLES_GROUP_METADATA[name].description,
isHiddenOnLoad:
ENVIRONMENT_VARIABLES_GROUP_METADATA[name].isHiddenOnLoad,
variables: data.variables.sort((a, b) => a.name.localeCompare(b.name)),
subgroups: Array.from(data.subgroups.entries())
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([name, variables]) => ({
name,
description:
ENVIRONMENT_VARIABLES_SUB_GROUP_METADATA[name].description,
variables,
})),
variables: variables.sort((a, b) => a.name.localeCompare(b.name)),
}));
return { groups };

View File

@ -3,7 +3,6 @@ import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
import { EnvironmentVariablesGroup } from 'src/engine/core-modules/environment/enums/environment-variables-group.enum';
import { EnvironmentVariable } from './environment-variable.dto';
import { EnvironmentVariablesSubgroupData } from './environment-variables-subgroup.dto';
registerEnumType(EnvironmentVariablesGroup, {
name: 'EnvironmentVariablesGroup',
@ -14,9 +13,6 @@ export class EnvironmentVariablesGroupData {
@Field(() => [EnvironmentVariable])
variables: EnvironmentVariable[];
@Field(() => [EnvironmentVariablesSubgroupData])
subgroups: EnvironmentVariablesSubgroupData[];
@Field(() => EnvironmentVariablesGroup)
name: EnvironmentVariablesGroup;

View File

@ -1,21 +0,0 @@
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
import { EnvironmentVariablesSubGroup } from 'src/engine/core-modules/environment/enums/environment-variables-sub-group.enum';
import { EnvironmentVariable } from './environment-variable.dto';
registerEnumType(EnvironmentVariablesSubGroup, {
name: 'EnvironmentVariablesSubGroup',
});
@ObjectType()
export class EnvironmentVariablesSubgroupData {
@Field(() => [EnvironmentVariable])
variables: EnvironmentVariable[];
@Field(() => EnvironmentVariablesSubGroup)
name: EnvironmentVariablesSubGroup;
@Field(() => String, { defaultValue: '' })
description: string;
}