fix(customHomePage): handle fetching deleted template (#14414)

Co-authored-by: Chris Collins <chriscollins3456@gmail.com>
This commit is contained in:
v-tarasevich-blitz-brain 2025-08-27 17:56:45 +03:00 committed by GitHub
parent afd3ab4e06
commit 22341a164b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 79 additions and 6 deletions

View File

@ -21,7 +21,9 @@ import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class PageTemplateMapper implements ModelMapper<EntityResponse, DataHubPageTemplate> {
public static final PageTemplateMapper INSTANCE = new PageTemplateMapper();
@ -41,6 +43,13 @@ public class PageTemplateMapper implements ModelMapper<EntityResponse, DataHubPa
result.setType(EntityType.DATAHUB_PAGE_TEMPLATE);
EnvelopedAspectMap aspectMap = entityResponse.getAspects();
// Handle getting deleted template by broken reference (check if required aspect was fetched)
if (aspectMap.get(DATAHUB_PAGE_TEMPLATE_PROPERTIES_ASPECT_NAME) == null) {
log.warn("Page Template {} doesn't have required aspects", entityUrn);
return null;
}
MappingHelper<DataHubPageTemplate> mappingHelper = new MappingHelper<>(aspectMap, result);
mappingHelper.mapToResult(
DATAHUB_PAGE_TEMPLATE_PROPERTIES_ASPECT_NAME,

View File

@ -113,4 +113,20 @@ public class PageTemplateMapperTest {
result.getProperties().getLastModified().getActor().getUrn(),
lastModified.getActor().toString());
}
@Test
public void testPageTemplateMapperWithoutRequiredAspect() {
// Create test entity response
Urn templateUrn = UrnUtils.getUrn("urn:li:dataHubPageTemplate:test-template");
EntityResponse entityResponse = new EntityResponse();
entityResponse.setUrn(templateUrn);
EnvelopedAspectMap aspectMap = new EnvelopedAspectMap();
entityResponse.setAspects(aspectMap);
// Test mapping
DataHubPageTemplate result = PageTemplateMapper.map(null, entityResponse);
// Verify result
assertNull(result);
}
}

View File

@ -158,8 +158,9 @@ describe('useTemplateState', () => {
const { result } = renderHook(() => useTemplateState());
expect(result.current.personalTemplate).toBe(null);
expect(result.current.globalTemplate).toBe(null);
expect(result.current.template).toBe(null);
// undefined global template should be replaced with fallback default global template
expect(result.current.globalTemplate?.urn).toBe('urn:li:dataHubPageTemplate:home_default_1');
expect(result.current.template?.urn).toBe('urn:li:dataHubPageTemplate:home_default_1');
expect(result.current.isEditingGlobalTemplate).toBe(false);
});
@ -182,8 +183,9 @@ describe('useTemplateState', () => {
const { result } = renderHook(() => useTemplateState());
expect(result.current.personalTemplate).toBe(null);
expect(result.current.globalTemplate).toBe(null);
expect(result.current.template).toBe(null);
// undefined global template should be replaced with fallback default global template
expect(result.current.globalTemplate?.urn).toBe('urn:li:dataHubPageTemplate:home_default_1');
expect(result.current.template?.urn).toBe('urn:li:dataHubPageTemplate:home_default_1');
expect(result.current.isEditingGlobalTemplate).toBe(false);
});

View File

@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from 'react';
import { useGlobalSettings } from '@app/context/GlobalSettingsContext';
import { useUserContext } from '@app/context/useUserContext';
import { filterOutNonExistentModulesFromTemplate } from '@app/homeV3/context/hooks/utils/moduleOperationsUtils';
import { DEFAULT_TEMPLATE } from '@app/homeV3/modules/constants';
import { PageTemplateFragment } from '@graphql/template.generated';
@ -17,7 +18,8 @@ export function useTemplateState() {
useEffect(() => {
if (globalSettingsLoaded && userLoaded && !areTemplatesInitialized) {
setGlobalTemplate(
filterOutNonExistentModulesFromTemplate(settings.globalHomePageSettings?.defaultTemplate) || null,
filterOutNonExistentModulesFromTemplate(settings.globalHomePageSettings?.defaultTemplate) ||
DEFAULT_TEMPLATE,
);
setPersonalTemplate(
filterOutNonExistentModulesFromTemplate(user?.settings?.homePage?.pageTemplate) || null,

View File

@ -1,6 +1,7 @@
import { IconNames } from '@components';
import { DataHubPageModuleType } from '@types';
import { PageTemplateFragment } from '@graphql/template.generated';
import { DataHubPageModuleType, EntityType, PageModuleScope, PageTemplateScope, PageTemplateSurfaceType } from '@types';
// TODO: remove these description once descriptions in modules are implemented
export const MODULE_TYPE_TO_DESCRIPTION: Map<DataHubPageModuleType, string> = new Map([
@ -59,3 +60,46 @@ export const LARGE_MODULE_TYPES: DataHubPageModuleType[] = [
];
export const SMALL_MODULE_TYPES: DataHubPageModuleType[] = [DataHubPageModuleType.Link];
export const DEFAULT_TEMPLATE: PageTemplateFragment = {
urn: DEFAULT_TEMPLATE_URN,
type: EntityType.DatahubPageTemplate,
properties: {
visibility: {
scope: PageTemplateScope.Global,
},
surface: {
surfaceType: PageTemplateSurfaceType.HomePage,
},
rows: [
{
modules: [
{
urn: 'urn:li:dataHubPageModule:your_assets',
type: EntityType.DatahubPageModule,
properties: {
name: 'Your Assets',
type: DataHubPageModuleType.OwnedAssets,
visibility: {
scope: PageModuleScope.Global,
},
params: {},
},
},
{
urn: 'urn:li:dataHubPageModule:top_domains',
type: EntityType.DatahubPageModule,
properties: {
name: 'Domains',
type: DataHubPageModuleType.Domains,
visibility: {
scope: PageModuleScope.Global,
},
params: {},
},
},
],
},
],
},
};