Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/big-cups-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sap-ux/odata-service-inquirer': patch
'@sap-ux/inquirer-common': patch
---

Check Transformations for Analytical default table.
22 changes: 3 additions & 19 deletions packages/inquirer-common/src/prompts/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ export const transformationsRequiredForAnalyticalTable = [
'filter',
'identity',
'orderby',
'search',
'skip',
'top',
'groupby',
Expand Down Expand Up @@ -213,13 +212,9 @@ export function filterAggregateTransformations(entitySets: EntitySet[]): EntityS
*/
export function hasAggregateTransformationsForEntity(
metadata: ConvertedMetadata,
entitySetName?: string,
entitySetName: string,
requiredTransformations?: readonly string[]
): boolean {
if (!entitySetName) {
return false;
}

const entitySet = findEntitySetByName(metadata, entitySetName);
if (!entitySet) {
return false;
Expand All @@ -235,11 +230,7 @@ export function hasAggregateTransformationsForEntity(
* @param entitySetName The entity set name to check for recursive hierarchy annotation.
* @returns true if the entity set has Hierarchy.RecursiveHierarchy annotation, false otherwise.
*/
export function hasRecursiveHierarchyForEntity(metadata: ConvertedMetadata, entitySetName?: string): boolean {
if (!entitySetName) {
return false;
}

export function hasRecursiveHierarchyForEntity(metadata: ConvertedMetadata, entitySetName: string): boolean {
const entitySet = findEntitySetByName(metadata, entitySetName);
if (!entitySet) {
return false;
Expand All @@ -255,14 +246,7 @@ export function hasRecursiveHierarchyForEntity(metadata: ConvertedMetadata, enti
* @param entitySetName The entity set name to check for recursive hierarchy annotation.
* @returns The qualifier string if found, undefined otherwise.
*/
export function getRecursiveHierarchyQualifier(
metadata: ConvertedMetadata,
entitySetName?: string
): string | undefined {
if (!entitySetName) {
return undefined;
}

export function getRecursiveHierarchyQualifier(metadata: ConvertedMetadata, entitySetName: string): string | undefined {
const entitySet = findEntitySetByName(metadata, entitySetName);
if (!entitySet) {
return undefined;
Expand Down
65 changes: 0 additions & 65 deletions packages/inquirer-common/test/unit/prompts/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -695,23 +695,6 @@ describe('helpers', () => {
).toBe(false);
});

it('should return false when entitySetName is not provided', () => {
const mockMetadata: any = {
version: '4.0',
namespace: 'Test.Service',
entitySets: []
};

expect(hasAggregateTransformationsForEntity(mockMetadata)).toBe(false);
expect(
hasAggregateTransformationsForEntity(
mockMetadata,
undefined,
transformationsRequiredForAnalyticalTable
)
).toBe(false);
});

it('should work with custom transformation requirements', () => {
const mockMetadata: any = {
version: '4.0',
Expand Down Expand Up @@ -858,15 +841,6 @@ describe('helpers', () => {
).toBe(false);
});

it('hasAggregateTransformationsForEntity should return false if entitySetName is not provided', () => {
expect(
hasAggregateTransformationsForEntity(metadata, undefined, transformationsRequiredForAnalyticalTable)
).toBe(false);
expect(
hasAggregateTransformationsForEntity(metadata, undefined, transformationsRequiredForAnalyticalTable)
).toBe(false);
});

it('hasAggregateTransformationsForEntity should return false for non-existent entity sets', () => {
expect(
hasAggregateTransformationsForEntity(
Expand Down Expand Up @@ -997,19 +971,6 @@ describe('helpers', () => {
expect(hasRecursiveHierarchyForEntity(mockMetadata, 'TestEntity')).toBe(false);
});

it('hasRecursiveHierarchyForEntity should return false if entitySetName is not provided', () => {
const mockMetadata: any = {
version: '4.0',
namespace: 'Test.Service',
entitySets: [],
entityTypes: [],
entityContainer: {}
};

expect(hasRecursiveHierarchyForEntity(mockMetadata)).toBe(false);
expect(hasRecursiveHierarchyForEntity(mockMetadata, undefined)).toBe(false);
});

it('hasRecursiveHierarchyForEntity should return false for non-existent entity set', () => {
const mockMetadata: any = {
version: '4.0',
Expand Down Expand Up @@ -1183,19 +1144,6 @@ describe('helpers', () => {

expect(getRecursiveHierarchyQualifier(mockMetadata, 'NonExistentEntity')).toBeUndefined();
});

it('should return undefined when entitySetName is not provided', () => {
const mockMetadata: any = {
version: '4.0',
namespace: 'Test.Service',
entitySets: [],
entityTypes: [],
entityContainer: {}
};

expect(getRecursiveHierarchyQualifier(mockMetadata)).toBeUndefined();
expect(getRecursiveHierarchyQualifier(mockMetadata, undefined)).toBeUndefined();
});
});

describe('hasAggregateTransformationsForEntity', () => {
Expand Down Expand Up @@ -1326,19 +1274,6 @@ describe('helpers', () => {

expect(hasAggregateTransformationsForEntity(mockMetadata, 'NonExistentEntity')).toBe(false);
});

it('should return false when entitySetName is not provided', () => {
const mockMetadata: any = {
version: '4.0',
namespace: 'Test.Service',
entitySets: [],
entityTypes: [],
entityContainer: {}
};

expect(hasAggregateTransformationsForEntity(mockMetadata)).toBe(false);
expect(hasAggregateTransformationsForEntity(mockMetadata, undefined)).toBe(false);
});
});

describe('findEntitySetByName', () => {
Expand Down
76 changes: 35 additions & 41 deletions packages/odata-service-inquirer/src/prompts/edmx/entity-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@ export function filterDraftEnabledEntities(entitySets: EntitySet[]): EntitySet[]
/**
* Determines if AnalyticalTable should be used based on entity annotations and service type.
*
* AnalyticalTable is used when entity has hierarchical and analytical data together, for CAP services with analytical data,
* or for non-CAP services with complete analytical transformations.
* AnalyticalTable is used when entity has hierarchical and analytical data together with complete transformations,
* for CAP services with analytical data, or for non-CAP services with complete analytical transformations.
*
* @param entitySet The entity set to check for annotations.
* @param isCapService Whether the service is a CAP service (affects analytical requirements).
Expand All @@ -261,9 +261,14 @@ function shouldUseAnalyticalTable(entitySet: EntitySet, isCapService: boolean):
return false;
}

// If entity has both analytical and hierarchical data, always use AnalyticalTable
// If entity has both analytical and hierarchical data, check requirements based on service type
if (hasHierarchy) {
return true;
// For CAP services, any analytical annotations are sufficient even with hierarchy
if (isCapService) {
return true;
}
// For non-CAP services, require complete analytical transformations
return hasAggregateTransformationsForEntitySet(entitySet, transformationsRequiredForAnalyticalTable);
}

// For CAP services, analytical annotations are sufficient
Expand Down Expand Up @@ -294,50 +299,39 @@ export function getDefaultTableType(
mainEntitySetName?: string,
currentTableType?: TableType
): { tableType: TableType; setAnalyticalTableDefault: boolean } {
let tableType: TableType;
let setAnalyticalTableDefault = false;
const setAnalyticalTableDefault = false;

// Find the entity set once for all annotation checks
const entitySet = mainEntitySetName ? findEntitySetByName(metadata, mainEntitySetName) : undefined;

if (entitySet) {
// Early return for user-selected table type
if (currentTableType) {
return { tableType: currentTableType, setAnalyticalTableDefault };
}

// Handle ALP template with OData v2 - always use AnalyticalTable
if (templateType === 'alp' && odataVersion === OdataVersion.v2) {
return { tableType: 'AnalyticalTable', setAnalyticalTableDefault };
}

// Handle OData v4 specific logic
if (odataVersion === OdataVersion.v4 && entitySet) {
// Check for analytical data requirements
if (
(templateType === 'lrop' || templateType === 'worklist') &&
odataVersion === OdataVersion.v4 &&
(templateType === 'lrop' || templateType === 'worklist' || templateType === 'alp') &&
shouldUseAnalyticalTable(entitySet, isCapService)
) {
// Use AnalyticalTable for entities with analytical data based on optimized annotation evaluation
tableType = 'AnalyticalTable';
setAnalyticalTableDefault = true;
} else if (
(templateType === 'lrop' || templateType === 'worklist') &&
odataVersion === OdataVersion.v4 &&
hasRecursiveHierarchyForEntitySet(entitySet)
) {
// If the main entity type is annotated with Hierarchy.RecursiveHierarchy, use TreeTable as default
tableType = 'TreeTable';
} else if (templateType === 'alp') {
// For ALP, use AnalyticalTable as default
tableType = 'AnalyticalTable';
} else if (currentTableType) {
// If the user has already selected a table type use it
tableType = currentTableType;
} else {
// Default to ResponsiveTable for other cases
tableType = 'ResponsiveTable';
// Use AnalyticalTable for entities with analytical data
return { tableType: 'AnalyticalTable', setAnalyticalTableDefault: true };
}

// Check for hierarchical data requirements
if ((templateType === 'lrop' || templateType === 'worklist') && hasRecursiveHierarchyForEntitySet(entitySet)) {
// Use TreeTable for entities with recursive hierarchy
return { tableType: 'TreeTable', setAnalyticalTableDefault };
}
} else if (templateType === 'alp') {
// For ALP, use AnalyticalTable as default even if entity set is not found
tableType = 'AnalyticalTable';
} else if (currentTableType) {
// If the user has already selected a table type use it
tableType = currentTableType;
} else {
// Default to ResponsiveTable for other cases
tableType = 'ResponsiveTable';
}
return {
tableType,
setAnalyticalTableDefault
};

// Default to ResponsiveTable for all other cases
return { tableType: 'ResponsiveTable', setAnalyticalTableDefault };
}
Original file line number Diff line number Diff line change
Expand Up @@ -323,12 +323,22 @@ describe('Test entity helper functions', () => {
expect(result.setAnalyticalTableDefault).toBe(true);
});

test('should return AnalyticalTable for ALP template type', () => {
test('should return ResponsiveTable for ALP template when analytical requirements are not met', () => {
const parsedEdmx = parse(metadataV4WithDraftEntities);
const convertedMetadata = convert(parsedEdmx);

const result = getDefaultTableType('alp', convertedMetadata, OdataVersion.v4, false, 'Travel');

expect(result.tableType).toBe('ResponsiveTable');
expect(result.setAnalyticalTableDefault).toBe(false);
});

test('should return AnalyticalTable for ALP template with OData v2', () => {
const parsedEdmx = parse(metadataV4WithDraftEntities);
const convertedMetadata = convert(parsedEdmx);

const result = getDefaultTableType('alp', convertedMetadata, OdataVersion.v2, false, 'Travel');

expect(result.tableType).toBe('AnalyticalTable');
expect(result.setAnalyticalTableDefault).toBe(false);
});
Expand Down Expand Up @@ -370,6 +380,16 @@ describe('Test entity helper functions', () => {
expect(result.setAnalyticalTableDefault).toBe(false);
});

test('should return AnalyticalTable for ALP template with OData v2 when no entitySetName provided', () => {
const parsedEdmx = parse(metadataV4WithDraftEntities);
const convertedMetadata = convert(parsedEdmx);

const result = getDefaultTableType('alp', convertedMetadata, OdataVersion.v2, false, undefined);

expect(result.tableType).toBe('AnalyticalTable');
expect(result.setAnalyticalTableDefault).toBe(false);
});

test('should handle non-existent entitySetName gracefully', () => {
const parsedEdmx = parse(metadataV4WithDraftEntities);
const convertedMetadata = convert(parsedEdmx);
Expand Down Expand Up @@ -436,10 +456,10 @@ describe('Test entity helper functions', () => {
expect(result.setAnalyticalTableDefault).toBe(false);
});

test('should use AnalyticalTable for entities with recursive hierarchy and any aggregate transformations', () => {
test('should use TreeTable for entities with recursive hierarchy and incomplete aggregate transformations', () => {
// Integration test using the actual metadataV4WithHierarchyRecursiveHierarchy.xml file
// This entity has both recursive hierarchy and partial aggregate transformations (only 5 of 9)
// Since both analytical and hierarchical annotations are present, AnalyticalTable takes priority
// This entity has both recursive hierarchy and partial aggregate transformations (only 5 transformations)
// Since analytical transformations are incomplete, TreeTable should be used for hierarchy
const parsedEdmx = parse(metadataV4WithHierarchyRecursiveHierarchy);
const convertedMetadata = convert(parsedEdmx);

Expand All @@ -450,9 +470,9 @@ describe('Test entity helper functions', () => {
false,
'P_SADL_HIER_UUID_D_COMPNY_ROOT'
);
// When both analytical and hierarchical annotations are present, AnalyticalTable takes priority
expect(result.tableType).toBe('AnalyticalTable');
expect(result.setAnalyticalTableDefault).toBe(true);
// When both annotations are present but analytical transformations are incomplete, use TreeTable
expect(result.tableType).toBe('TreeTable');
expect(result.setAnalyticalTableDefault).toBe(false);
});

test('should prioritize complete aggregate transformations over recursive hierarchy', () => {
Expand Down Expand Up @@ -976,14 +996,5 @@ describe('Test entity helper functions', () => {

expect(qualifier).toBeUndefined();
});

test('should handle undefined entity set name gracefully', () => {
const parsedEdmx = parse(metadataV4WithHierarchyRecursiveHierarchy);
const convertedMetadata = convert(parsedEdmx);

const qualifier = getRecursiveHierarchyQualifier(convertedMetadata, undefined);

expect(qualifier).toBeUndefined();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -384,10 +384,17 @@ describe('Test entity prompts', () => {
// If no prevAnswers, default to ResponsiveTable
expect((tableType.default as Function)()).toEqual('ResponsiveTable');

// For ALP, use AnalyticalTable as default
// For ALP with entity that has complete analytical transformations, use AnalyticalTable as default
questions = getEntitySelectionQuestions(metadataV4WithAggregateTransforms, 'alp', false);
tableType = questions.find((question) => question.name === EntityPromptNames.tableType) as ListQuestion;
expect((tableType.default as Function)({})).toEqual('AnalyticalTable');
expect(
(tableType.default as Function)({
[EntityPromptNames.mainEntity]: {
entitySetName: 'SalesOrderItem',
entitySetType: 'com.c_salesordermanage_sd_aggregate.SalesOrderItemType'
}
})
).toEqual('AnalyticalTable');

const hierarchyQualifier = questions.find(
(question) => question.name === EntityPromptNames.hierarchyQualifier
Expand Down
Loading