Skip to content
This repository was archived by the owner on Jan 19, 2026. It is now read-only.

Commit 7c570e6

Browse files
authored
Merge pull request #195 from angelikatyborska/next-old-root-groups
fix: prevent infinite loop on push component
2 parents 5661a1d + 7e10dcf commit 7c570e6

2 files changed

Lines changed: 89 additions & 18 deletions

File tree

src/commands/components/push/operations.test.ts

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,18 @@ describe('operations', () => {
451451
internal_tags_list: [],
452452
color: null,
453453
},
454+
{
455+
name: 'old-component-inside-old-folder',
456+
id: 126,
457+
display_name: 'Old Component Inside Old Folder',
458+
created_at: '1999-01-01',
459+
updated_at: '1999-01-01',
460+
schema: {},
461+
component_group_uuid: 'group-old-uuid',
462+
internal_tag_ids: [],
463+
internal_tags_list: [],
464+
color: null,
465+
},
454466
],
455467
groups: [
456468
{
@@ -474,6 +486,14 @@ describe('operations', () => {
474486
parent_id: 2,
475487
parent_uuid: 'group-b-uuid',
476488
},
489+
{
490+
name: 'Group Old',
491+
id: 99,
492+
uuid: 'group-old-uuid',
493+
parent_id: null,
494+
// old root groups have parent uuid equal to own uuid
495+
parent_uuid: 'group-old-uuid',
496+
},
477497
],
478498
presets: [
479499
{
@@ -533,6 +553,17 @@ describe('operations', () => {
533553

534554
expect(result.components).toHaveLength(0); // Should not match as it's treated as literal
535555
});
556+
557+
it('should handle old root groups', () => {
558+
const result = filterSpaceDataByPattern(mockSpaceData, 'old-component-inside-old-folder');
559+
560+
expect(result.components).toHaveLength(1);
561+
expect(result.components[0].name).toBe('old-component-inside-old-folder');
562+
expect(result.internalTags).toHaveLength(0);
563+
expect(result.presets).toHaveLength(0);
564+
expect(result.groups).toHaveLength(1);
565+
expect(result.groups[0].uuid).toBe('group-old-uuid');
566+
});
536567
});
537568

538569
describe('filterSpaceDataByComponent', () => {
@@ -562,6 +593,18 @@ describe('operations', () => {
562593
internal_tags_list: [],
563594
color: null,
564595
},
596+
{
597+
name: 'old-component-inside-old-folder',
598+
id: 126,
599+
display_name: 'Old Component Inside Old Folder',
600+
created_at: '1999-01-01',
601+
updated_at: '1999-01-01',
602+
schema: {},
603+
component_group_uuid: 'group-old-uuid',
604+
internal_tag_ids: [],
605+
internal_tags_list: [],
606+
color: null,
607+
},
565608
],
566609
groups: [
567610
{
@@ -578,6 +621,14 @@ describe('operations', () => {
578621
parent_id: 1,
579622
parent_uuid: 'group-a-uuid',
580623
},
624+
{
625+
name: 'Group Old',
626+
id: 99,
627+
uuid: 'group-old-uuid',
628+
parent_id: null,
629+
// old root groups have parent uuid equal to own uuid
630+
parent_uuid: 'group-old-uuid',
631+
},
581632
],
582633
presets: [
583634
{
@@ -656,6 +707,17 @@ describe('operations', () => {
656707
expect(result.presets).toHaveLength(0);
657708
expect(result.groups).toHaveLength(0);
658709
});
710+
711+
it('should handle old root groups', () => {
712+
const result = filterSpaceDataByComponent(mockSpaceData, 'old-component-inside-old-folder');
713+
714+
expect(result.components).toHaveLength(1);
715+
expect(result.components[0].name).toBe('old-component-inside-old-folder');
716+
expect(result.internalTags).toHaveLength(0);
717+
expect(result.presets).toHaveLength(0);
718+
expect(result.groups).toHaveLength(1);
719+
expect(result.groups[0].uuid).toBe('group-old-uuid');
720+
});
659721
});
660722

661723
describe('handleWhitelists', () => {
@@ -704,7 +766,7 @@ describe('operations', () => {
704766
field2: {
705767
type: 'bloks',
706768
restrict_type: 'groups',
707-
component_group_whitelist: ['group-a-uuid', 'group-b-uuid'],
769+
component_group_whitelist: ['group-a-uuid', 'group-b-uuid', 'group-old-uuid'],
708770
},
709771
},
710772
component_group_uuid: 'group-b-uuid',
@@ -728,6 +790,14 @@ describe('operations', () => {
728790
parent_id: 1,
729791
parent_uuid: 'group-a-uuid',
730792
},
793+
{
794+
name: 'Old Group',
795+
id: 99,
796+
uuid: 'group-old-uuid',
797+
parent_id: null,
798+
// old root groups have parent uuid equal to own uuid
799+
parent_uuid: 'group-old-uuid',
800+
},
731801
],
732802
presets: [],
733803
internalTags: [
@@ -772,19 +842,23 @@ describe('operations', () => {
772842

773843
// Groups should be processed next
774844
expect(allCalls[2][1].name).toBe('Group A');
775-
expect(allCalls[3][1].name).toBe('Group B');
845+
expect(allCalls[3][1].name).toBe('Old Group');
846+
expect(allCalls[4][1].name).toBe('Group B');
776847

777848
// Components should be processed last
778-
expect(allCalls[4][1].name).toBe('component-tags');
849+
expect(allCalls[5][1].name).toBe('component-tags');
779850

780851
// Verify ID/UUID mappings
781852
expect(results.tagsIdMap.get(1)).toBe(1001);
782853
expect(results.tagsIdMap.get(2)).toBe(1002);
783854
expect(results.groupsUuidMap.get('group-a-uuid')).toBe('new-group-a-uuid');
784855
expect(results.groupsUuidMap.get('group-b-uuid')).toBe('new-group-b-uuid');
856+
expect(results.groupsUuidMap.get('group-old-uuid')).toBe('new-group-old-uuid');
785857
});
786858

787859
it('should update component schema with new tag IDs and group UUIDs', async () => {
860+
const updatedComponents: Record<number, any> = {};
861+
788862
// Mock successful responses
789863
vi.mocked(upsertComponentInternalTag).mockImplementation(async (space, tag) => ({
790864
...tag,
@@ -798,26 +872,22 @@ describe('operations', () => {
798872
}));
799873

800874
vi.mocked(upsertComponent).mockImplementation(async (space, component) => {
801-
// Verify that the component's schema has been updated with new IDs/UUIDs
802-
if (component.name === 'component-tags') {
803-
const schema = component.schema as {
804-
tab1: { type: 'tab'; display_name: string; keys: string[] };
805-
field1: { type: 'bloks'; restrict_type: string; component_tag_whitelist: number[] };
806-
};
807-
expect(schema.field1.component_tag_whitelist).toEqual([1001, 1002]);
808-
expect(component.component_group_uuid).toBe('new-group-b-uuid');
809-
expect(component.internal_tag_ids).toEqual(['1001', '1002']);
810-
}
811-
return {
875+
updatedComponents[component.id] = {
812876
...component,
813877
id: component.id + 1000,
814878
};
879+
return updatedComponents[component.id];
815880
});
816881

817882
await handleWhitelists(mockSpace, mockPassword, mockRegion, mockSpaceData);
818883

819884
// Verify that upsertComponent was called with updated schemas
820885
expect(vi.mocked(upsertComponent)).toHaveBeenCalled();
886+
887+
expect(updatedComponents[124].schema.field1.component_tag_whitelist).toEqual([1001, 1002]);
888+
expect(updatedComponents[124].schema.field2.component_group_whitelist).toEqual(['new-group-a-uuid', 'new-group-b-uuid', 'new-group-old-uuid']);
889+
expect(updatedComponents[124].component_group_uuid).toBe('new-group-b-uuid');
890+
expect(updatedComponents[124].internal_tag_ids).toEqual(['1001', '1002']);
821891
});
822892

823893
it('should handle circular dependencies between components', async () => {
@@ -945,7 +1015,7 @@ describe('operations', () => {
9451015

9461016
// Verify that each tag and group was only processed once
9471017
expect(vi.mocked(upsertComponentInternalTag)).toHaveBeenCalledTimes(2); // Only two tags
948-
expect(vi.mocked(upsertComponentGroup)).toHaveBeenCalledTimes(2); // Only two groups
1018+
expect(vi.mocked(upsertComponentGroup)).toHaveBeenCalledTimes(3); // Only three groups
9491019
});
9501020
});
9511021
});

src/commands/components/push/operations.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ function findRelatedResources(
124124
relatedGroups.add(currentGroup);
125125

126126
// If the group has a parent, get it from the map
127-
if (currentGroup.parent_uuid && currentGroup.parent_uuid.length > 0) {
127+
if (currentGroup.parent_uuid && currentGroup.parent_uuid.length > 0 && currentGroup.parent_uuid !== currentGroup.uuid) {
128128
currentGroup = groupsMap.get(currentGroup.parent_uuid);
129129
}
130130
else {
@@ -264,7 +264,8 @@ export async function handleComponentGroups(
264264
: spaceData;
265265

266266
// First, process groups without parents
267-
const rootGroups = groupsToProcess.filter(group => !group.parent_uuid && !group.parent_id);
267+
// This conditional handles a strange scenario where group (folders) ids are equal to their parents
268+
const rootGroups = groupsToProcess.filter(group => (!group.parent_uuid || group.parent_uuid === group.uuid) && !group.parent_id);
268269
for (const group of rootGroups) {
269270
const spinner = new Spinner({
270271
verbose: !isVitest,
@@ -453,7 +454,7 @@ function getGroupHierarchy(group: SpaceComponentGroup, allGroups: SpaceComponent
453454
const hierarchy: SpaceComponentGroup[] = [group];
454455
let currentGroup = group;
455456

456-
while (currentGroup.parent_uuid && currentGroup.parent_uuid.length > 0) {
457+
while (currentGroup.parent_uuid && currentGroup.parent_uuid.length > 0 && currentGroup.parent_uuid !== currentGroup.uuid) {
457458
const parentGroup = allGroups.find(g => g.uuid === currentGroup.parent_uuid);
458459
if (parentGroup) {
459460
hierarchy.unshift(parentGroup); // Add parent to the start of the array

0 commit comments

Comments
 (0)