Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
74 changes: 45 additions & 29 deletions packages/adp-flp-config-sub-generator/src/app/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { FlpConfigOptions } from './types';
import type { Question } from 'inquirer';
import Generator from 'yeoman-generator';
import path, { join } from 'node:path';
import { join, basename } from 'node:path';
import { type AxiosError, type AbapServiceProvider, isAxiosError } from '@sap-ux/axios-extension';
import {
getVariant,
Expand All @@ -23,9 +23,11 @@ import {
getAdpFlpInboundsWriterConfig,
getTileSettingsQuestions,
type FLPConfigAnswers,
type TileSettingsAnswers
type TileSettingsAnswers,
tileActions,
tilePromptNames
} from '@sap-ux/flp-config-inquirer';
import { AppWizard, Prompts, MessageType } from '@sap-devx/yeoman-ui-types';
import { AppWizard, Prompts, MessageType, type IPrompt } from '@sap-devx/yeoman-ui-types';
import {
DefaultLogger,
TelemetryHelper,
Expand Down Expand Up @@ -106,8 +108,6 @@ export default class AdpFlpConfigGenerator extends Generator {
(this.env as unknown as YeomanEnvironment).conflicter.force = this.options.force ?? true;
}

this._setupFLPConfigPage();

if (!this.launchAsSubGen) {
await this._initializeStandAloneGenerator();
if (this.abort || this.authenticationRequired) {
Expand Down Expand Up @@ -143,6 +143,10 @@ export default class AdpFlpConfigGenerator extends Generator {
}

this.tileSettingsAnswers = await this._promptTileActions();
if (this.tileSettingsAnswers?.[tilePromptNames.tileHandlingAction] === tileActions.REPLACE) {
return;
}

const prompts: Question<FLPConfigAnswers>[] = await getPrompts(
this.inbounds,
getAdpFlpConfigPromptOptions(this.tileSettingsAnswers as TileSettingsAnswers, this.inbounds, this.variant)
Expand All @@ -158,9 +162,10 @@ export default class AdpFlpConfigGenerator extends Generator {
const config = getAdpFlpInboundsWriterConfig(
this.answers,
this.layer,
this.tileSettingsAnswers as TileSettingsAnswers
this.tileSettingsAnswers as TileSettingsAnswers,
this.inbounds
);
await generateInboundConfig(this.projectRootPath, config as InternalInboundNavigation, this.fs);
await generateInboundConfig(this.projectRootPath, config as InternalInboundNavigation[], this.fs);
} catch (error) {
this.logger.error(`Writing phase failed: ${error}`);
throw new Error(t('error.updatingApp'));
Expand Down Expand Up @@ -258,11 +263,14 @@ export default class AdpFlpConfigGenerator extends Generator {
* Adds navigations steps and callback function for the generator prompts.
*/
private _setupFLPConfigPage(): void {
const pages: IPrompt[] = this.prompts['items'];
const tileSettingsPageIndex = pages.findIndex((p) => p.name === t('yuiNavSteps.tileSettingsName'));

// if launched as a sub-generator, the navigation steps will be set by the parent generator
if (!this.launchAsSubGen) {
this.prompts.splice(0, 0, [
this.prompts.splice(tileSettingsPageIndex + 1, 0, [
{
name: t('yuiNavSteps.flpConfigName', { projectName: path.basename(this.projectRootPath) }),
name: t('yuiNavSteps.flpConfigName'),
description: ''
}
]);
Expand All @@ -275,7 +283,14 @@ export default class AdpFlpConfigGenerator extends Generator {
private _setupFLPConfigPrompts(): void {
// If launched as a sub-generator, the prompts will be set by the parent generator
if (!this.launchAsSubGen) {
this.prompts = new Prompts([]);
this.prompts = new Prompts([
{
name: t('yuiNavSteps.tileSettingsName'),
description: t('yuiNavSteps.tileSettingsDescr', {
projectName: basename(this.projectRootPath)
})
}
]);
this.setPromptsCallback = (fn): void => {
if (this.prompts) {
this.prompts.setCallback(fn);
Expand Down Expand Up @@ -367,33 +382,34 @@ export default class AdpFlpConfigGenerator extends Generator {
if (!this.inbounds) {
return undefined;
}
this._setTileSettingsPrompts();
const existingFlpConfig = !this.launchAsSubGen && flpConfigurationExists(this.variant);
const promptOptions = {
existingFlpConfigInfo: {
hide: !existingFlpConfig
}
};
const tileSettingsPrompts = getTileSettingsQuestions(promptOptions);
return this.prompt(tileSettingsPrompts);
}
const tileSettingsPrompts = getTileSettingsQuestions(this.inbounds, promptOptions, async (answer) => {
if (!answer) {
return true;
}

/**
* Sets the tile settings prompts based on the current state of the generator.
*/
private _setTileSettingsPrompts(): void {
if (this.launchAsSubGen) {
return;
}
const promptsIndex = this.prompts.size() === 1 ? 0 : 1;
this.prompts.splice(promptsIndex, 0, [
{
name: t('yuiNavSteps.tileSettingsName'),
description: t('yuiNavSteps.tileSettingsDescr', {
projectName: path.basename(this.projectRootPath)
})
const pages: IPrompt[] = this.prompts['items'];
const hasExistingFlpConfigPage = pages.some((p) => p.name === t('yuiNavSteps.flpConfigName'));

if (answer === tileActions.ADD && !hasExistingFlpConfigPage) {
this._setupFLPConfigPage();
return true;
}
]);

if (!hasExistingFlpConfigPage) {
return true;
}

this.prompts.splice(this.prompts.size() - 1, 1, []);
return true;
});

return this.prompt(tileSettingsPrompts);
}

/**
Expand Down
26 changes: 16 additions & 10 deletions packages/adp-tooling/src/writer/inbound-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import { enhanceManifestChangeContentWithFlpConfig as enhanceInboundConfig } fro
* Generates and writes the inbound configuration to the manifest.appdescr_variant file.
*
* @param basePath - The base path of the project.
* @param config - The inbound configuration properties.
* @param configs - The inbound configuration properties.
* @param fs - Optional mem-fs editor instance.
* @returns The mem-fs editor instance.
*/
export async function generateInboundConfig(
basePath: string,
config: InternalInboundNavigation,
configs: InternalInboundNavigation[],
fs?: Editor
): Promise<Editor> {
if (!fs) {
Expand All @@ -29,14 +29,17 @@ export async function generateInboundConfig(

variant.content = removeInboundChangeTypes(variant.content);

if (!config?.inboundId) {
config.inboundId = `${variant.id}.InboundID`;
}
// Set default inbound IDs if missing
configs.forEach((config) => {
if (!config?.inboundId) {
config.inboundId = `${variant.id}.InboundID`;
}
});

enhanceInboundConfig(config, variant.id, variant.content as Content[]);
enhanceInboundConfig(configs, variant.id, variant.content as Content[]);

await updateVariant(basePath, variant, fs);
await updateI18n(basePath, variant.id, config, fs);
await updateI18n(basePath, variant.id, configs, fs);

return fs;
}
Expand Down Expand Up @@ -73,17 +76,20 @@ export function getFlpI18nKeys(config: InternalInboundNavigation, appId: string)
*
* @param {string} basePath - The base path of the project.
* @param {string} appId - The application ID used to generate i18n keys.
* @param {InternalInboundNavigation} config - The inbound configuration properties.
* @param {InternalInboundNavigation[]} configs - The inbound configuration properties.
* @param {Editor} fs - The mem-fs editor instance for file operations.
* @returns {Promise<void>} A promise that resolves when the i18n file is updated.
*/
export async function updateI18n(
basePath: string,
appId: string,
config: InternalInboundNavigation,
configs: InternalInboundNavigation[],
fs: Editor
): Promise<void> {
const newEntries = getFlpI18nKeys(config, appId);
let newEntries: NewI18nEntry[] = [];
configs.forEach((config) => {
newEntries = newEntries.concat(getFlpI18nKeys(config, appId));
});
const i18nPath = path.join(basePath, 'webapp', 'i18n', 'i18n.properties');
const keysToRemove = [`${appId}_sap.app.crossNavigation.inbounds`];
await removeAndCreateI18nEntries(i18nPath, newEntries, keysToRemove, basePath, fs);
Expand Down
7 changes: 2 additions & 5 deletions packages/adp-tooling/src/writer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,8 @@ function setDefaults(config: AdpWriterConfig): AdpWriterConfig {
}

if (configWithDefaults.customConfig?.adp.environment === 'C' && configWithDefaults.flp) {
enhanceManifestChangeContentWithFlpConfig(
configWithDefaults.flp as InternalInboundNavigation,
configWithDefaults.app.id,
configWithDefaults.app.content
);
const configs = [configWithDefaults.flp];
enhanceManifestChangeContentWithFlpConfig(configs, configWithDefaults.app.id, configWithDefaults.app.content);
}

return configWithDefaults;
Expand Down
36 changes: 22 additions & 14 deletions packages/adp-tooling/src/writer/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,33 +305,41 @@ function getInboundChangeContentWithNewInboundID(
/**
* Generate Inbound change content required for manifest.appdescriptor.
*
* @param flpConfiguration FLP cloud project configuration
* @param flpConfigurations FLP cloud project configuration
* @param appId Application variant id
* @param manifestChangeContent Application variant change content
*/
export function enhanceManifestChangeContentWithFlpConfig(
flpConfiguration: InternalInboundNavigation,
flpConfigurations: InternalInboundNavigation[],
appId: string,
manifestChangeContent: Content[] = []
): void {
const inboundChangeContent = getInboundChangeContentWithNewInboundID(flpConfiguration, appId);
if (inboundChangeContent) {
flpConfigurations.forEach((flpConfig, index) => {
const inboundChangeContent = getInboundChangeContentWithNewInboundID(flpConfig, appId);
if (!inboundChangeContent) {
return;
}
const addInboundChange = {
changeType: 'appdescr_app_addNewInbound',
content: inboundChangeContent,
texts: {
'i18n': 'i18n/i18n.properties'
}
};
const removeOtherInboundsChange = {
changeType: 'appdescr_app_removeAllInboundsExceptOne',
content: {
'inboundId': flpConfiguration.inboundId
},
texts: {}
};

manifestChangeContent.push(addInboundChange);
manifestChangeContent.push(removeOtherInboundsChange);
}

// Remove all inbounds except one should be only after the first inbound is added
// This is implemented this way to avoid issues with the merged on ABAP side
if (index === 0) {
const removeOtherInboundsChange = {
changeType: 'appdescr_app_removeAllInboundsExceptOne',
content: {
'inboundId': flpConfig.inboundId
},
texts: {}
};

manifestChangeContent.push(removeOtherInboundsChange);
}
});
}
42 changes: 33 additions & 9 deletions packages/create/src/cli/add/navigation-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
type DescriptorVariant
} from '@sap-ux/adp-tooling';
import type { ToolsLogger } from '@sap-ux/logger';
import { getPrompts } from '@sap-ux/flp-config-inquirer';
import { getPrompts, tileActions } from '@sap-ux/flp-config-inquirer';
import { FileName, getAppType } from '@sap-ux/project-access';
import { createAbapServiceProvider } from '@sap-ux/system-access';
import type { Manifest, ManifestNamespace } from '@sap-ux/project-access';
Expand Down Expand Up @@ -82,22 +82,17 @@ async function addInboundNavigationConfig(basePath: string, simulate: boolean, y
const inbounds = await getInbounds(basePath, yamlPath, fs, logger, variant);
let tileSettingsAnswers: TileSettingsAnswers | undefined;
if (inbounds && isAdp) {
tileSettingsAnswers = await promptYUIQuestions(getTileSettingsQuestions(), false);
tileSettingsAnswers = await promptYUIQuestions(getTileSettingsQuestions(inbounds), false);
}

const answers = await getUserAnswers(inbounds, isAdp, tileSettingsAnswers);

if (!answers) {
if (!answers && tileSettingsAnswers?.tileHandlingAction !== tileActions.REPLACE) {
logger.info('User chose not to overwrite existing inbound navigation configuration.');
return;
}

if (variant.isAdp) {
const config = getAdpFlpInboundsWriterConfig(answers, variant.content.layer, tileSettingsAnswers);
await generateInboundConfig(basePath, config as InternalInboundNavigation, fs);
} else {
await generateInboundNavigationConfig(basePath, answers, true, fs);
}
await generateConfig(basePath, answers as FLPConfigAnswers, variant, fs, tileSettingsAnswers);

if (!simulate) {
fs.commit(() => logger.info(`Inbound navigation configuration complete.`));
Expand Down Expand Up @@ -174,6 +169,10 @@ async function getUserAnswers(
promptOptions = getAdpFlpConfigPromptOptions(tileSettingsAnswers as TileSettingsAnswers, inbounds);
}

if (isAdp && tileSettingsAnswers?.tileHandlingAction === tileActions.REPLACE) {
return undefined;
}

const prompts = await filterLabelTypeQuestions<FLPConfigAnswers>(await getPrompts(inbounds, promptOptions));
const config = await promptYUIQuestions(prompts, false);

Expand All @@ -183,3 +182,28 @@ async function getUserAnswers(

return config?.overwrite === false ? undefined : config;
}

/**
* Generates the inbound navigation configuration for the given project.
*
* @param {string} basePath - The path to the application root.
* @param {FLPConfigAnswers} answers - The user-provided configuration answers.
* @param {Variant} variant - The descriptor variant information.
* @param {Editor} fs - The mem-fs editor instance.
* @param {TileSettingsAnswers} [tileSettingsAnswers] - The answers for tile settings.
* @returns {Promise<void>} A promise that resolves when the configuration is generated.
*/
async function generateConfig(
basePath: string,
answers: FLPConfigAnswers,
variant: Variant,
fs: Editor,
tileSettingsAnswers?: TileSettingsAnswers
): Promise<void> {
if (variant.isAdp) {
const config = getAdpFlpInboundsWriterConfig(answers, variant.content.layer, tileSettingsAnswers);
await generateInboundConfig(basePath, config as InternalInboundNavigation[], fs);
} else {
await generateInboundNavigationConfig(basePath, answers as FLPConfigAnswers, true, fs);
}
}
4 changes: 3 additions & 1 deletion packages/flp-config-inquirer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { ManifestNamespace } from '@sap-ux/project-access';
import type { InquirerAdapter, PromptDefaultValue } from '@sap-ux/inquirer-common';

import { initI18n, addi18nResourceBundle } from './i18n';
import { promptNames } from './types';
import { promptNames, tileActions, tilePromptNames } from './types';
import { getExistingFlpConfigInfoPrompt } from './prompts/questions';
import { getQuestions, getTileSettingsQuestions } from './prompts';
import type { FLPConfigAnswers, FLPConfigQuestion, FLPConfigPromptOptions, TileSettingsAnswers } from './types';
Expand Down Expand Up @@ -109,6 +109,8 @@ export {
type FLPConfigPromptOptions,
type FLPConfigQuestion,
type TileSettingsAnswers,
tileActions,
tilePromptNames,
getAdpFlpConfigPromptOptions,
getAdpFlpInboundsWriterConfig,
getTileSettingsQuestions
Expand Down
Loading
Loading