Skip to content

Commit

Permalink
fix: static webapp path (#2931)
Browse files Browse the repository at this point in the history
* get rid of static webapp path

* get rid of static webapp path (2)

* fix readme

* fixes

* fixes

* refactoring

* fix unit test

* fix unit test

* adjust snapshots

* adjust snapshots

* adjust snapshots

* backward compatibility

* force posix path

* adjust snapshots

* review comments + refactoring

* adjust JSDoc

* add missing await + add missing return types to functions

* refactoring

---------

Co-authored-by: Austin Devine <[email protected]>
Co-authored-by: Johannes Kolbe <[email protected]>
Co-authored-by: mmilko01 <[email protected]>
Co-authored-by: Klaus Keller <[email protected]>
  • Loading branch information
5 people authored Feb 27, 2025
1 parent 334d7b0 commit 4b8577f
Show file tree
Hide file tree
Showing 28 changed files with 170 additions and 122 deletions.
11 changes: 11 additions & 0 deletions .changeset/orange-donkeys-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@sap-ux/adp-flp-config-sub-generator': patch
'@sap-ux/mockserver-config-writer': patch
'@sap-ux/odata-service-writer': patch
'@sap-ux/adp-tooling': patch
'@sap-ux/telemetry': patch
'@sap-ux/create': patch
'@sap-ux/i18n': patch
---

fix: usage of static webapp path
2 changes: 1 addition & 1 deletion packages/adp-flp-config-sub-generator/src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export default class extends Generator {
requestOptions['auth'] = { username: this.credentials.username, password: this.credentials.password };
}
const provider = await createAbapServiceProvider(target, requestOptions, false, this.toolsLogger);
const variant = getVariant(this.projectRootPath);
const variant = await getVariant(this.projectRootPath);
const manifestService = await ManifestService.initMergedManifest(
provider,
this.projectRootPath,
Expand Down
2 changes: 1 addition & 1 deletion packages/adp-tooling/src/base/abap/manifest-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export class ManifestService {
*/
private async fetchMergedManifest(basePath: string, descriptorVariantId: string): Promise<void> {
const zip = new ZipFile();
const files = getWebappFiles(basePath);
const files = await getWebappFiles(basePath);
for (const file of files) {
zip.addFile(file.relativePath, Buffer.from(file.content, 'utf-8'));
}
Expand Down
49 changes: 28 additions & 21 deletions packages/adp-tooling/src/base/helper.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Editor } from 'mem-fs-editor';
import { existsSync, readdirSync, readFileSync } from 'fs';
import { join, isAbsolute, relative } from 'path';

import { UI5Config } from '@sap-ux/ui5-config';
import { join, isAbsolute, relative, basename, dirname } from 'path';
import { getWebappPath, FileName, readUi5Yaml } from '@sap-ux/project-access';
import type { UI5Config } from '@sap-ux/ui5-config';

import type { DescriptorVariant, AdpPreviewConfig } from '../types';

Expand All @@ -11,13 +11,14 @@ import type { DescriptorVariant, AdpPreviewConfig } from '../types';
*
* @param {string} basePath - The path to the adaptation project.
* @param {Editor} fs - The mem-fs editor instance.
* @returns {DescriptorVariant} The app descriptor variant.
* @returns {Promise<DescriptorVariant>} The app descriptor variant.
*/
export function getVariant(basePath: string, fs?: Editor): DescriptorVariant {
export async function getVariant(basePath: string, fs?: Editor): Promise<DescriptorVariant> {
const webappPath = await getWebappPath(basePath);
if (fs) {
return fs.readJSON(join(basePath, 'webapp', 'manifest.appdescr_variant')) as unknown as DescriptorVariant;
return fs.readJSON(join(webappPath, FileName.ManifestAppDescrVar)) as unknown as DescriptorVariant;
}
return JSON.parse(readFileSync(join(basePath, 'webapp', 'manifest.appdescr_variant'), 'utf-8'));
return JSON.parse(readFileSync(join(webappPath, FileName.ManifestAppDescrVar), 'utf-8'));
}

/**
Expand All @@ -27,8 +28,8 @@ export function getVariant(basePath: string, fs?: Editor): DescriptorVariant {
* @param {DescriptorVariant} variant - The descriptor variant object.
* @param {Editor} fs - The mem-fs editor instance.
*/
export function updateVariant(basePath: string, variant: DescriptorVariant, fs: Editor) {
fs.writeJSON(join(basePath, 'webapp', 'manifest.appdescr_variant'), variant);
export async function updateVariant(basePath: string, variant: DescriptorVariant, fs: Editor): Promise<void> {
fs.writeJSON(join(await getWebappPath(basePath), FileName.ManifestAppDescrVar), variant);
}

/**
Expand All @@ -38,12 +39,12 @@ export function updateVariant(basePath: string, variant: DescriptorVariant, fs:
* or `appdescr_app_addNewInbound` present in the content of the descriptor variant.
*
* @param {string} basePath - The base path of the project where the manifest.appdescr_variant is located.
* @returns {boolean} Returns `true` if FLP configuration changes exist, otherwise `false`.
* @returns {Promise<boolean>} Returns `true` if FLP configuration changes exist, otherwise `false`.
* @throws {Error} Throws an error if the variant could not be retrieved.
*/
export function flpConfigurationExists(basePath: string): boolean {
export async function flpConfigurationExists(basePath: string): Promise<boolean> {
try {
const variant = getVariant(basePath);
const variant = await getVariant(basePath);
return variant.content?.some(
({ changeType }) =>
changeType === 'appdescr_app_changeInbound' || changeType === 'appdescr_app_addNewInbound'
Expand Down Expand Up @@ -74,13 +75,19 @@ export function isTypescriptSupported(basePath: string, fs?: Editor): boolean {
*/
export async function getAdpConfig(basePath: string, yamlPath: string): Promise<AdpPreviewConfig> {
const ui5ConfigPath = isAbsolute(yamlPath) ? yamlPath : join(basePath, yamlPath);
const ui5Conf = await UI5Config.newInstance(readFileSync(ui5ConfigPath, 'utf-8'));
const customMiddlerware =
ui5Conf.findCustomMiddleware<{ adp: AdpPreviewConfig }>('fiori-tools-preview') ??
ui5Conf.findCustomMiddleware<{ adp: AdpPreviewConfig }>('preview-middleware');
const adp = customMiddlerware?.configuration?.adp;
let ui5Conf: UI5Config;
let adp: AdpPreviewConfig | undefined;
try {
ui5Conf = await readUi5Yaml(dirname(ui5ConfigPath), basename(ui5ConfigPath));
const customMiddleware =
ui5Conf.findCustomMiddleware<{ adp: AdpPreviewConfig }>('fiori-tools-preview') ??
ui5Conf.findCustomMiddleware<{ adp: AdpPreviewConfig }>('preview-middleware');
adp = customMiddleware?.configuration?.adp;
} catch (error) {
// do nothing here
}
if (!adp) {
throw new Error('No system configuration found in ui5.yaml');
throw new Error(`No system configuration found in ${basename(ui5ConfigPath)}`);
}
return adp;
}
Expand All @@ -89,10 +96,10 @@ export async function getAdpConfig(basePath: string, yamlPath: string): Promise<
* Get all files in the webapp folder.
*
* @param {string} basePath - The path to the adaptation project.
* @returns {Array<{ relativePath: string; content: string }>} The files in the webapp folder.
* @returns {Promise<{ relativePath: string; content: string }[]>} The files in the webapp folder.
*/
export function getWebappFiles(basePath: string): { relativePath: string; content: string }[] {
const dir = join(basePath, 'webapp');
export async function getWebappFiles(basePath: string): Promise<{ relativePath: string; content: string }[]> {
const dir = await getWebappPath(basePath);
const files: { relativePath: string; content: string }[] = [];

const getFilesRecursivelySync = (directory: string): void => {
Expand Down
4 changes: 2 additions & 2 deletions packages/adp-tooling/src/preview/change-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export async function addAnnotationFile(
serviceUrl: datasoruces[dataSourceId].uri,
fileName: basename(dataSource[annotationDataSourceKey].uri)
},
variant: getVariant(projectRoot),
variant: await getVariant(projectRoot),
isCommand: false
},
fs
Expand All @@ -289,7 +289,7 @@ export async function addAnnotationFile(
* @returns Promise<ManifestService>
*/
async function getManifestService(basePath: string, logger: Logger): Promise<ManifestService> {
const variant = getVariant(basePath);
const variant = await getVariant(basePath);
const { target, ignoreCertErrors = false } = await getAdpConfig(basePath, join(basePath, FileName.Ui5Yaml));
const provider = await createAbapServiceProvider(target, { ignoreCertErrors }, true, logger);
return await ManifestService.initMergedManifest(provider, basePath, variant, logger as unknown as ToolsLogger);
Expand Down
36 changes: 22 additions & 14 deletions packages/adp-tooling/src/preview/routes-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default class RoutesHandler {
* @param data Data that is sent to the client
* @param contentType Content type, defaults to json
*/
private sendFilesResponse(res: Response, data: object | string, contentType: string = 'application/json') {
private sendFilesResponse(res: Response, data: object | string, contentType: string = 'application/json'): void {
res.status(HttpStatusCodes.OK).contentType(contentType).send(data);
}

Expand All @@ -100,12 +100,12 @@ export default class RoutesHandler {
* @param res Response
* @param next Next Function
*/
public handleReadAllFragments = async (_: Request, res: Response, next: NextFunction) => {
public handleReadAllFragments = async (_: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const files = await this.readAllFilesByGlob('/**/changes/fragments/*.fragment.xml');

const fileNames = files.map((f) => ({
fragmentName: f.getName()
const fileNames = files.map((file) => ({
fragmentName: file.getName()
}));

this.sendFilesResponse(res, {
Expand All @@ -125,12 +125,12 @@ export default class RoutesHandler {
* @param res Response
* @param next Next Function
*/
public handleReadAllControllers = async (_: Request, res: Response, next: NextFunction) => {
public handleReadAllControllers = async (_: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const files = await this.readAllFilesByGlob('/**/changes/coding/*.js');

const fileNames = files.map((f) => ({
controllerName: f.getName()
const fileNames = files.map((file) => ({
controllerName: file.getName()
}));

this.sendFilesResponse(res, {
Expand All @@ -150,7 +150,11 @@ export default class RoutesHandler {
* @param res Response
* @param next Next Function
*/
public handleGetControllerExtensionData = async (req: Request, res: Response, next: NextFunction) => {
public handleGetControllerExtensionData = async (
req: Request,
res: Response,
next: NextFunction
): Promise<void> => {
try {
const params = req.params as { controllerName: string };
const controllerName = sanitize(params.controllerName);
Expand Down Expand Up @@ -219,7 +223,7 @@ export default class RoutesHandler {
* @param res Response
* @param next Next Function
*/
public handleWriteControllerExt = async (req: Request, res: Response, next: NextFunction) => {
public handleWriteControllerExt = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const data = req.body as WriteControllerBody;

Expand Down Expand Up @@ -249,7 +253,7 @@ export default class RoutesHandler {
return;
}

generateControllerFile(rootPath, filePath, name);
await generateControllerFile(rootPath, filePath, name);

const message = 'Controller extension created!';
res.status(HttpStatusCodes.CREATED).send(message);
Expand All @@ -269,7 +273,11 @@ export default class RoutesHandler {
* @param res Response
* @param next Next Function
*/
public handleGetAllAnnotationFilesMappedByDataSource = async (_req: Request, res: Response, next: NextFunction) => {
public handleGetAllAnnotationFilesMappedByDataSource = async (
_req: Request,
res: Response,
next: NextFunction
): Promise<void> => {
try {
const isRunningInBAS = isAppStudio();

Expand Down Expand Up @@ -344,7 +352,7 @@ export default class RoutesHandler {
private async getManifestService(): Promise<ManifestService> {
const project = this.util.getProject();
const basePath = project.getRootPath();
const variant = getVariant(basePath);
const variant = await getVariant(basePath);
const { target, ignoreCertErrors = false } = await getAdpConfig(
basePath,
path.join(basePath, FileName.Ui5Yaml)
Expand All @@ -365,8 +373,8 @@ export default class RoutesHandler {
* @param {string} name - The name of the controller extension (used in TypeScript templates).
* @throws {Error} Throws an error if rendering the template fails.
*/
function generateControllerFile(rootPath: string, filePath: string, name: string): void {
const id = getVariant(rootPath)?.id;
async function generateControllerFile(rootPath: string, filePath: string, name: string): Promise<void> {
const id = (await getVariant(rootPath))?.id;
const isTsSupported = isTypescriptSupported(rootPath);
const tmplFileName = isTsSupported ? TemplateFileName.TSController : TemplateFileName.Controller;
const tmplPath = path.join(__dirname, '../../templates/rta', tmplFileName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import type { ListQuestion, FileBrowserQuestion, YUIQuestion } from '@sap-ux/inq
import type { ManifestNamespace } from '@sap-ux/project-access';
import { AnnotationFileSelectType, type AddAnnotationsAnswers } from '../../types';
import { t } from '../../i18n';
import { filterDataSourcesByType } from '@sap-ux/project-access';
import { filterDataSourcesByType, getWebappPath, DirName } from '@sap-ux/project-access';
import { existsSync } from 'fs';
import { validateEmptyString } from '@sap-ux/project-input-validator';
import { join, isAbsolute, sep } from 'path';
import { join, isAbsolute, basename } from 'path';

/**
* Gets the prompts for adding annotations to OData service.
Expand Down Expand Up @@ -60,7 +60,7 @@ export function getPrompts(
default: '',
when: (answers: AddAnnotationsAnswers) =>
answers.id !== '' && answers.fileSelectOption === AnnotationFileSelectType.ExistingFile,
validate: (value) => {
validate: async (value: string) => {
const validationResult = validateEmptyString(value);
if (typeof validationResult === 'string') {
return validationResult;
Expand All @@ -71,8 +71,11 @@ export function getPrompts(
return t('validators.fileDoesNotExist');
}

const fileName = filePath.split(sep).pop();
if (existsSync(join(basePath, 'webapp', 'changes', 'annotations', fileName))) {
if (
existsSync(
join(await getWebappPath(basePath), DirName.Changes, DirName.Annotations, basename(filePath))
)
) {
return t('validators.annotationFileAlreadyExists');
}

Expand Down
4 changes: 2 additions & 2 deletions packages/adp-tooling/src/writer/inbound-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export async function generateInboundConfig(
fs = create(createStorage());
}

const variant = getVariant(basePath, fs);
const variant = await getVariant(basePath, fs);

if (!config?.inboundId) {
config.addInboundId = true;
Expand All @@ -34,7 +34,7 @@ export async function generateInboundConfig(

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

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

return fs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ describe('ManifestService', () => {
describe('initMergedManifest', () => {
it('should initialize and fetch the merged manifest', async () => {
const variant = { id: 'descriptorVariantId', reference: 'referenceAppId' };
(getWebappFiles as jest.MockedFunction<typeof getWebappFiles>).mockReturnValue([
{ relativePath: 'path', content: 'content' }
]);
(getWebappFiles as jest.MockedFunction<typeof getWebappFiles>).mockReturnValue(
Promise.resolve([{ relativePath: 'path', content: 'content' }])
);
manifestService = await ManifestService.initMergedManifest(
provider,
'basePath',
Expand Down
Loading

0 comments on commit 4b8577f

Please sign in to comment.