Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option to open source FE writer generate to enable writing annotations. #2849

Open
wants to merge 46 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b9dfe11
add enable npm workspaces to appOptions
kjose90 Jan 16, 2025
3dd9c4c
removing unecessary console logs
kjose90 Jan 16, 2025
d246d0e
removing debugger
kjose90 Jan 16, 2025
560cb17
Merge branch 'main' into moveCapUpdates
kjose90 Jan 16, 2025
3f89f43
lint issues
kjose90 Jan 16, 2025
d763ff1
Linting auto fix commit
github-actions[bot] Jan 16, 2025
901a6e5
unit tests
kjose90 Jan 17, 2025
82bd7e2
Merge branch 'moveCapUpdates' of https://github.com/SAP/open-ux-tools…
kjose90 Jan 17, 2025
3d88289
Linting auto fix commit
github-actions[bot] Jan 17, 2025
a120f48
Merge branch 'main' into moveCapUpdates
kjose90 Jan 17, 2025
87020f2
remove unused reference to package type
kjose90 Jan 17, 2025
811810b
sonar qube fix
kjose90 Jan 17, 2025
dea250e
add documentation to enable npm workspaces type
kjose90 Jan 22, 2025
cee24bb
add enableNPMWorkspaces doc to f-f-w
kjose90 Jan 22, 2025
b2fed1e
add comment
kjose90 Jan 22, 2025
e1851b8
Move and types from to
kjose90 Jan 22, 2025
9a5d436
remve old changeset
kjose90 Jan 22, 2025
20a39b7
merge main and fix conflicts
kjose90 Jan 22, 2025
3490ac7
Linting auto fix commit
github-actions[bot] Jan 22, 2025
da1f726
Merge branch 'main' into updateCapService
kjose90 Jan 22, 2025
ca59e47
enable npm workspace comment
kjose90 Jan 22, 2025
b01a596
js doc fix
kjose90 Jan 23, 2025
e4546f7
Merge branch 'updateCapService' of https://github.com/SAP/open-ux-too…
kjose90 Jan 23, 2025
110da8c
Linting auto fix commit
github-actions[bot] Jan 23, 2025
1bb7159
js doc fix
kjose90 Jan 23, 2025
b01bef7
js doc fix
kjose90 Jan 23, 2025
200aed4
Linting auto fix commit
github-actions[bot] Jan 23, 2025
4596f1f
Merge branch 'main' into updateCapService
kjose90 Jan 23, 2025
b33329b
Merge branch 'main' into updateCapService
kjose90 Jan 28, 2025
6e359be
remove enable npm workspace options from ff and fe appOptions
kjose90 Jan 28, 2025
8328245
resolve merge conflict
kjose90 Jan 28, 2025
ffcfe4e
move annotation writing logic to generate function of FE
kjose90 Jan 28, 2025
ce2e16a
move annotations generation to writers
kjose90 Jan 29, 2025
f059df7
fix merge conflicts
kjose90 Jan 29, 2025
bfc3c55
add annotations logic to FE generate and tests
kjose90 Jan 30, 2025
7263e0a
Merge branch 'main' into moveWriteAnnotationsLogic
kjose90 Jan 30, 2025
3d058c9
add changeset
kjose90 Jan 30, 2025
288b612
delete old changeset
kjose90 Jan 30, 2025
e1709f3
add js docs
kjose90 Jan 30, 2025
e1563f5
sonar issues fix
kjose90 Jan 30, 2025
2e249a1
improise annotation wirting logic
kjose90 Jan 31, 2025
137d521
Merge branch 'main' into moveWriteAnnotationsLogic
kjose90 Jan 31, 2025
7dc70dc
add logger to f-e-w
kjose90 Jan 31, 2025
2bfb2c8
fix test for write annotations
kjose90 Jan 31, 2025
de66d3c
sonar issues
kjose90 Jan 31, 2025
c3e53c6
move writeAnnotations and improvise
kjose90 Jan 31, 2025
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
5 changes: 5 additions & 0 deletions .changeset/sweet-games-raise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sap-ux/fiori-elements-writer': minor
---

Add an option to open source FE writer to enable writing annotations.
2 changes: 2 additions & 0 deletions packages/fiori-elements-writer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
"@sap-ux/ui5-test-writer": "workspace:*",
"@sap-ux/fiori-generator-shared": "workspace:*",
"@sap-ux/cap-config-writer": "workspace:*",
"@sap-ux/annotation-generator": "workspace:*",
"@sap-ux/logger": "workspace:*",
"ejs": "3.1.10",
"i18next": "20.6.1",
"lodash": "4.17.21",
Expand Down
102 changes: 102 additions & 0 deletions packages/fiori-elements-writer/src/data/writeAnnotations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { sep } from 'path';
Copy link
Contributor

@IainSAP IainSAP Jan 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if the data folder is the correct place as this is for static settings but I guess it's ok for now, not sure what data means actually in this context :)

import { type CapServiceCdsInfo } from '@sap-ux/cap-config-writer';
import { getCapFolderPathsSync } from '@sap-ux/fiori-generator-shared';
import { TemplateType } from '../types';
import {
generateAnnotations,
type AnnotationServiceParameters,
type GenerateAnnotationsOptions
} from '@sap-ux/annotation-generator';
import type { Editor } from 'mem-fs-editor';
import type { OdataVersion } from '@sap-ux/odata-service-writer';

/**
* The list of template types that support generation of annotations.
*/
export const annotationSupportedTemplateTypes: TemplateType[] = [
TemplateType.ListReportObjectPage,
TemplateType.Worklist,
TemplateType.FormEntryObjectPage
];

/**
* Generates the annotation file path based on whether the CAP service is available.
*
* @param {string} [appName] - The name of the application.
* @param {CapServiceCdsInfo} [capService] - The CAP service info.
* @returns {string} The annotation file path based on whether the CAP service is available.
*/
function getAnnotationFilePath(appName?: string, capService?: CapServiceCdsInfo): string {
if (capService) {
const appPath = capService.appPath ?? getCapFolderPathsSync(capService.projectPath).app;
return `${appPath}${sep}${appName}${sep}annotations.cds`;
}
return `webapp${sep}annotations${sep}annotation.xml`;
}

/**
* Determines if annotations can be generated for a given template.
*
* @param {OdataVersion} odataServiceVersion - The version of the OData service being used.
* @param {TemplateType} templateType - The type of the template being used by app.
* @param {boolean} [addAnnotations] - An optional flag indicating whether annotations should be enabled.
* @returns {boolean} - Returns `true` if annotations can be generated, otherwise `false`.
*/
export function canGenerateAnnotationsForTemplate(
Copy link
Contributor

@IainSAP IainSAP Jan 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to use the template settings for this information, as thats its purpose, to have all template related information in one place.

e.g.

export const TemplateTypeAttributes: TemplateAttributes = {
    [TemplateType.Worklist]: {
        supportedODataVersions: [OdataVersion.v2, OdataVersion.v4],
        minimumUi5Version: {
            [OdataVersion.v2]: minSupportedUI5Version,
            [OdataVersion.v4]: '1.99.0'
        },
        annotationGenerationSupport: {
            [OdataVersion.v4]: true
    },

Then you dont need this function: https://github.com/SAP/open-ux-tools/pull/2849/files#diff-ac5073f87858b510da65484577f6afc172f304cfec03c196415cb096ce0ebf14R45
or this def:
https://github.com/SAP/open-ux-tools/pull/2849/files#diff-ac5073f87858b510da65484577f6afc172f304cfec03c196415cb096ce0ebf14R16

odataServiceVersion: OdataVersion,
templateType: TemplateType,
addAnnotations: boolean = false
): boolean {
return addAnnotations && annotationSupportedTemplateTypes.includes(templateType) && odataServiceVersion === '4';
}

/**
* Writes annotation files for the given application configuration.
*
* @param {string} basePath - The base directory path of the project.
* @param {object} appInfo - Information about the application.
* @param {Template} appInfo.templateType - The template type of the application.
* @param {string} appInfo.packageName - The package name of the application.
* @param {string} appInfo.entitySetName - The entity set name associated with the annotations.
* @param {CapServiceCdsInfo} [appInfo.capService] - Optional CAP service information.
* @param {Editor} fs - The file system editor instance.
* @returns {Promise<void>} A promise that resolves when the annotation files are successfully generated.
*/
export async function writeAnnotations(
basePath: string,
appInfo: {
templateType: TemplateType;
packageName?: string;
entitySetName: string;
capService?: CapServiceCdsInfo;
},
fs: Editor
): Promise<void> {
let serviceName = 'mainService';
let projectPath = basePath;

// Determine whether to add line items
const addLineItems =
appInfo.templateType === TemplateType.ListReportObjectPage || appInfo.templateType === TemplateType.Worklist;

if (appInfo.capService) {
serviceName = appInfo.capService.serviceName;
projectPath = appInfo.capService.projectPath;
}

const options: GenerateAnnotationsOptions = {
entitySetName: appInfo.entitySetName,
annotationFilePath: getAnnotationFilePath(appInfo.packageName, appInfo.capService),
addFacets: true,
addLineItems,
addValueHelps: !!appInfo.capService
};

const serviceParameters: AnnotationServiceParameters = {
serviceName,
appName: appInfo.packageName,
project: projectPath
};

await generateAnnotations(fs, serviceParameters, options);
}
46 changes: 43 additions & 3 deletions packages/fiori-elements-writer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { generate as generateUi5Project } from '@sap-ux/ui5-application-writer';
import { generate as addOdataService, OdataVersion, ServiceType } from '@sap-ux/odata-service-writer';
import { generateOPAFiles } from '@sap-ux/ui5-test-writer';
import cloneDeep from 'lodash/cloneDeep';
import type { FioriElementsApp } from './types';
import type { FioriElementsApp, EntityConfig } from './types';
import { TemplateType } from './types';
import { validateApp, validateRequiredProperties } from './validate';
import { setAppDefaults, setDefaultTemplateSettings, getTemplateOptions } from './data/defaults';
Expand All @@ -18,10 +18,16 @@ import {
} from './data/templateAttributes';
import { extendManifestJson } from './data/manifestSettings';
import semVer from 'semver';
import { initI18n } from './i18n';
import { initI18n, t } from './i18n';
import { getBootstrapResourceUrls, getPackageScripts } from '@sap-ux/fiori-generator-shared';
import { generateFpmConfig } from './fpmConfig';
import { applyCAPUpdates, type CapProjectSettings } from '@sap-ux/cap-config-writer';
import type { Logger } from '@sap-ux/logger';
import {
writeAnnotations,
canGenerateAnnotationsForTemplate,
annotationSupportedTemplateTypes
} from './data/writeAnnotations';

export const V2_FE_TYPES_AVAILABLE = '1.108.0';
/**
Expand Down Expand Up @@ -50,15 +56,22 @@ function getTypeScriptIgnoreGlob<T extends {}>(feApp: FioriElementsApp<T>, coerc
}
return ignore;
}

/**
* Generate a UI5 application based on the specified Fiori Elements floorplan template.
*
* @param basePath - the absolute target path where the application will be generated
* @param data - configuration to generate the Fiori elements application
* @param fs - an optional reference to a mem-fs editor
* @param log - optional logger instance
* @returns Reference to a mem-fs-editor
*/
async function generate<T extends {}>(basePath: string, data: FioriElementsApp<T>, fs?: Editor): Promise<Editor> {
async function generate<T extends {}>(
basePath: string,
data: FioriElementsApp<T>,
fs?: Editor,
log?: Logger
): Promise<Editor> {
// Load i18n translations asynchronously to ensure proper initialization.
// This addresses occasional issues where i18n is not initialized in time, causing tests to fail.
await initI18n();
Expand Down Expand Up @@ -223,6 +236,33 @@ async function generate<T extends {}>(basePath: string, data: FioriElementsApp<T
await applyCAPUpdates(fs, feApp.service.capService, settings);
}

// Handle annotation writing
if (
Copy link
Contributor

@IainSAP IainSAP Jan 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this whole block to the writeAnnotions file :

if (feApp.appOptions.addAnnotations) {
writeAnnotations(...)
}

The index file is getting too busy and the function is too long now! Would be nicer to hide all annotation writing code to that file now?

canGenerateAnnotationsForTemplate(feApp.service.version, feApp.template.type, feApp.appOptions?.addAnnotations)
) {
const { settings } = feApp.template;
const { capService } = feApp.service;
const { name: packageName } = feApp.package ?? {};
const entitySetName = (settings as T & { entityConfig?: EntityConfig })?.entityConfig?.mainEntityName ?? '';

await writeAnnotations(
basePath,
{
templateType: feApp.template.type,
packageName: packageName,
entitySetName: entitySetName,
capService: capService
},
fs
);
} else {
log?.warn(
t('warn.invalidTypeForAnnotationGeneration', {
templateType: feApp.template.type,
supportedTypes: annotationSupportedTemplateTypes
})
);
}
return fs;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"info": {
"mockOnlyWarning": "This application was generated with a local metadata file and does not reference a live server. Please add the required server configuration or start this application with mock data using the target: npm run start-mock"
},
"warn": {
"invalidTypeForAnnotationGeneration": "Invalid template type '{{ templateType }}' for annotation generation. Supported types are: {{ supportedTypes }}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add additional information...

"The provided option addAnnotations is not supported for the specified template and odata version. Generation will continue but additional annotations will not added."

},
"error": {
"unsupportedOdataVersion": "OData Version of the specified service: {{ serviceVersion }}, is not supported by the template type: {{ templateType }}",
"unsupportedUI5Version": "Specified UI5 property '{{ versionProperty }}': {{ ui5Version }}, is not supported by the template type: {{ templateType }}. Please specify {{minRequiredUI5Version}} or above.",
Expand Down
5 changes: 5 additions & 0 deletions packages/fiori-elements-writer/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ export interface FioriElementsApp<T> extends Ui5App {
* This will eventually move up to {@link Ui5App.appOptions}
*/
addTests?: boolean;
/**
* If addAnnotations is true, annotations are enabled.
* However, annotations will only be written if the template type is lrop/worklist or formEntryObject; annotation genration is not uspported for other project types.
*/
addAnnotations?: boolean;
};
}

Expand Down
44 changes: 43 additions & 1 deletion packages/fiori-elements-writer/test/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import {
type FEOPSettings,
type FioriElementsApp,
type LROPSettings,
type WorklistSettings
type WorklistSettings,
TemplateType
} from '../src/types';
import { promisify } from 'util';
import { exec as execCP } from 'child_process';
const exec = promisify(execCP);
import { ServiceType } from '@sap-ux/odata-service-writer';
import { type CapServiceCdsInfo } from '@sap-ux/cap-config-writer';

export const testOutputDir = join(__dirname, 'test-output');

Expand Down Expand Up @@ -201,3 +203,43 @@ export const projectChecks = async (
expect(error).toBeUndefined();
}
};

export const sampleCapService: CapServiceCdsInfo = {
cdsUi5PluginInfo: {
isCdsUi5PluginEnabled: true,
hasMinCdsVersion: true,
isWorkspaceEnabled: true,
hasCdsUi5Plugin: true
},
projectPath: join('test'),
serviceName: 'mainService',
capType: 'Node.js',
appPath: join('test', 'path')
};

/**
*
* @param name name of the app
* @param templateType template type of the app
* @returns a Fiori Elements App of provided template type
*/
export const applyBaseConfigToFEApp = (name: string, templateType: TemplateType) => {
const addUi5Config = templateType === TemplateType.Worklist;
const appInfo = feBaseConfig(name, !addUi5Config);
return {
...Object.assign(appInfo, {
template: {
type: templateType,
settings: v4TemplateSettings
}
}),
service: {
version: OdataVersion.v4,
capService: sampleCapService
},
package: {
...appInfo.package,
sapuxLayer: 'CUSTOMER_BASE'
}
} as FioriElementsApp<LROPSettings>;
};
63 changes: 62 additions & 1 deletion packages/fiori-elements-writer/test/feop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,27 @@ import {
v4TemplateSettings,
v4Service,
projectChecks,
updatePackageJSONDependencyToUseLocalPath
updatePackageJSONDependencyToUseLocalPath,
applyBaseConfigToFEApp,
sampleCapService
} from './common';
import type { FEOPSettings } from '../src/types';
import type { CapServiceCdsInfo } from '@sap-ux/cap-config-writer';
import { OdataVersion } from '@sap-ux/odata-service-writer';
import { create as createStorage } from 'mem-fs';
import { create } from 'mem-fs-editor';
import { generateAnnotations } from '@sap-ux/annotation-generator';

const TEST_NAME = 'feopTemplate';
if (debug?.enabled) {
jest.setTimeout(360000);
}

jest.mock('@sap-ux/annotation-generator', () => ({
...jest.requireActual('@sap-ux/annotation-generator'),
generateAnnotations: jest.fn()
}));

describe(`Fiori Elements template: ${TEST_NAME}`, () => {
const curTestOutPath = join(testOutputDir, TEST_NAME);

Expand Down Expand Up @@ -73,3 +85,52 @@ describe(`Fiori Elements template: ${TEST_NAME}`, () => {
});
});
});

describe('Should generate annotations correctly for FEOP projects', () => {
const curTestOutPath = join(testOutputDir, TEST_NAME);
const fs = create(createStorage());

afterEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});

test('Should generate annotations for FEOP projects when addAnnotations is enabled, regardless of service availability', async () => {
const fioriElementsApp = {
...applyBaseConfigToFEApp('fefeop1', TemplateType.FormEntryObjectPage),
appOptions: {
addAnnotations: true
}
};
await generate(curTestOutPath, fioriElementsApp, fs);
expect(generateAnnotations).toBeCalledTimes(1);

// ensure addLineItems is false for feop project
expect(generateAnnotations).toBeCalledWith(
fs,
{
serviceName: sampleCapService.serviceName,
appName: fioriElementsApp.package.name,
project: sampleCapService.projectPath
},
{
entitySetName: v4TemplateSettings?.entityConfig?.mainEntityName,
annotationFilePath: join('test', 'path', 'fefeop1', 'annotations.cds'),
addFacets: true,
addLineItems: false,
addValueHelps: true
}
);
});

test('Should generate annotations for FEOP projects when addAnnotations is disabled', async () => {
const fioriElementsApp = {
...applyBaseConfigToFEApp('fefeop1', TemplateType.FormEntryObjectPage),
appOptions: {
addAnnotations: false
}
};
await generate(curTestOutPath, fioriElementsApp, fs);
expect(generateAnnotations).not.toBeCalled();
});
});
Loading
Loading