-
Notifications
You must be signed in to change notification settings - Fork 31
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
feat: Switch Adaptation Project's generator/wizard to use Open Source writers #1775
base: main
Are you sure you want to change the base?
Changes from 15 commits
d207584
bb4a0a0
363a702
2cffde0
774f0ac
a9ae35a
ef58dd5
9aa7edc
0d0bef9
464b534
5ad6495
82b0238
32a3214
5956a34
8a25655
5d6d4cb
004b5cb
f28ca6e
43ca69a
4af85b2
8ac89f4
09d5cf4
d42387b
0e31e07
2507b01
cb495b2
0ca6004
3dba791
6c21e2f
0516fc4
35a6347
a2c2b71
1da5255
3bed8d2
edb43db
b970461
31153cf
196fc44
0e12110
4158c84
113e2af
513b0f7
328b15d
c05b0dd
8f5b365
bce2d5c
db1a539
a6ac36a
78f7a82
c190b64
a933d59
5e74a2a
be4d7e1
515d984
114591d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@sap-ux/adp-tooling': minor | ||
'@sap-ux/preview-middleware': patch | ||
--- | ||
|
||
Adds writer functionality for generation of adaptation projects |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
import { join } from 'path'; | ||
import type { Editor } from 'mem-fs-editor'; | ||
import { AdpWriterConfig, CfAdpConfig, CfModuleData, ManifestAppdescr, ProjectType } from '../types'; | ||
import { | ||
enhanceUI5DeployYaml, | ||
enhanceUI5Yaml, | ||
hasDeployConfig, | ||
enhanceUI5YamlWithCustomConfig, | ||
enhanceUI5YamlWithCustomTask | ||
} from '../writer/options'; | ||
import { UI5Config } from '@sap-ux/ui5-config'; | ||
|
||
/** | ||
* Writes a given project template files within a specified folder in the project directory. | ||
* | ||
* @param {string} templatePath - The root path of the project template. | ||
* @param {string} projectPath - The root path of the project. | ||
* @param {CfModuleData | AdpWriterConfig} data - The data to be populated in the template file. | ||
* @param {Editor} fs - The `mem-fs-editor` instance used for file operations. | ||
* @param {string[]} exclude - Files to be ignored when writing. | ||
* @returns {void} | ||
*/ | ||
export function writeTemplateToFolder( | ||
templatePath: string, | ||
projectPath: string, | ||
data: CfModuleData | AdpWriterConfig, | ||
fs: Editor, | ||
exclude: string[] | [] = [] | ||
nikmace marked this conversation as resolved.
Show resolved
Hide resolved
|
||
): void { | ||
try { | ||
fs.copyTpl(templatePath, projectPath, data, undefined, { | ||
globOptions: { dot: true, ignore: exclude }, | ||
processDestinationPath: (filePath: string) => filePath.replace(/gitignore.tmpl/g, '.gitignore') | ||
}); | ||
} catch (e) { | ||
throw new Error(`Could not write template files to folder. Reason: ${e.message}`); | ||
} | ||
} | ||
|
||
/** | ||
* Writes a manifest.adpescr file within a specified folder in the project directory. | ||
* | ||
* @param {string} templatePath - The root path of the project template. | ||
* @param {string} projectPath - The root path of the project. | ||
* @param {ManifestAppdescr} data - The data to be populated in the template file. | ||
* @param {Editor} fs - The `mem-fs-editor` instance used for file operations. | ||
* @returns {void} | ||
*/ | ||
export function writeManifestAppdecr( | ||
IvoSG marked this conversation as resolved.
Show resolved
Hide resolved
|
||
templatePath: string, | ||
projectPath: string, | ||
data: ManifestAppdescr, | ||
fs: Editor | ||
): void { | ||
try { | ||
const appdescrTplPath = join(templatePath, 'webapp/manifest.appdescr_variant'); | ||
const appdescrPath = join(projectPath, 'webapp/manifest.appdescr_variant'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please use the enum from project access for different folders |
||
const baseAppdescrContent: ManifestAppdescr = JSON.parse(fs.read(appdescrTplPath)); | ||
data.content = [...baseAppdescrContent.content, ...data.content]; | ||
Object.assign(baseAppdescrContent, data); | ||
|
||
fs.writeJSON(appdescrPath, baseAppdescrContent); | ||
} catch (e) { | ||
throw new Error(`Could not write manifest.appdescr_variant file. Reason: ${e.message}`); | ||
} | ||
} | ||
|
||
/** | ||
* Writes a .apd/config.json file within a specified folder in the project directory. | ||
* | ||
* @param {string} projectPath - The root path of the project. | ||
* @param {CfAdpConfig} data - The data to be populated in the template file. | ||
* @param {Editor} fs - The `mem-fs-editor` instance used for file operations. | ||
* @returns {void} | ||
*/ | ||
export function writeAdpConfig(projectPath: string, data: CfAdpConfig, fs: Editor): void { | ||
try { | ||
const adpConfigPath = join(projectPath, '.adp/config.json'); | ||
nikmace marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
fs.writeJSON(adpConfigPath, data); | ||
} catch (e) { | ||
throw new Error(`Could not write .adp/config.json file. Reason: ${e.message}`); | ||
} | ||
} | ||
|
||
/** | ||
* Writes a xs-security.json file within a specified folder in the project directory. | ||
* | ||
* @param {string} projectPath - The root path of the project. | ||
* @param {CfModuleData} data - The data to be populated in the template file. | ||
* @param {Editor} fs - The `mem-fs-editor` instance used for file operations. | ||
* @returns {void} | ||
*/ | ||
export function writeXsSecurity(templatePath: string, data: CfModuleData, fs: Editor): void { | ||
try { | ||
const projectPath = join(data.projectPath, 'xs-security.json'); | ||
if (!fs.exists(projectPath)) { | ||
writeTemplateToFolder( | ||
join(templatePath, 'xs-security.json'), | ||
projectPath, | ||
{ | ||
projectName: data.xsSecurityProjectName | ||
} as CfModuleData, | ||
fs | ||
); | ||
} | ||
} catch (e) { | ||
throw new Error(`Could not write xs-security.json file. Reason: ${e.message}`); | ||
} | ||
} | ||
|
||
/** | ||
* Writes a given approuter template files within a specified folder in the project directory | ||
* | ||
* @param {string} templatePath - The root path of the project template. | ||
* @param {string} projectPath - The root path of the project. | ||
* @param {CfModuleData} data - The data to be populated in the template file. | ||
* @param {Editor} fs - The `mem-fs-editor` instance used for file operations. | ||
* @returns {void} | ||
*/ | ||
export function writeApprouterTemplate( | ||
templatePath: string, | ||
projectPath: string, | ||
data: CfModuleData, | ||
fs: Editor | ||
): void { | ||
try { | ||
if (data.addStandaloneApprouter) { | ||
const approuterProjectPath = join(projectPath, 'approuter'); | ||
const approuterTplPath = join(templatePath, 'approuter/*.*'); | ||
|
||
writeTemplateToFolder(approuterTplPath, approuterProjectPath, data, fs); | ||
} | ||
} catch (e) { | ||
throw new Error(`Could not write approuter template files to folder. Reason: ${e.message}`); | ||
} | ||
} | ||
|
||
/** | ||
* Writes a ui5.yaml file within a specified folder in the project directory. | ||
* | ||
* @param {string} projectPath - The root path of the project. | ||
* @param {AdpWriterConfig} data - The data to be populated in the template file. | ||
* @param {Editor} fs - The `mem-fs-editor` instance used for file operations. | ||
* @returns {void} | ||
*/ | ||
export async function writeUI5Yaml(projectPath: string, data: AdpWriterConfig, fs: Editor): Promise<void> { | ||
try { | ||
const ui5ConfigPath = join(projectPath, 'ui5.yaml'); | ||
const baseUi5ConfigContent = fs.read(ui5ConfigPath); | ||
const ui5Config = await UI5Config.newInstance(baseUi5ConfigContent); | ||
enhanceUI5Yaml(ui5Config, data); | ||
enhanceUI5YamlWithCustomConfig(ui5Config, data?.customConfig); | ||
if (data.customConfig?.adp?.environment === ProjectType.S4) { | ||
enhanceUI5YamlWithCustomTask(ui5Config, data); | ||
} | ||
|
||
fs.write(ui5ConfigPath, ui5Config.toString()); | ||
} catch (e) { | ||
throw new Error(`Could not write ui5.yaml file. Reason: ${e.message}`); | ||
} | ||
} | ||
|
||
/** | ||
* Writes a ui5-deploy.yaml file within a specified folder in the project directory. | ||
* | ||
* @param {string} projectPath - The root path of the project. | ||
* @param {AdpWriterConfig} data - The data to be populated in the template file. | ||
* @param {Editor} fs - The `mem-fs-editor` instance used for file operations. | ||
* @returns {void} | ||
*/ | ||
export async function writeUI5DeployYaml(projectPath: string, data: AdpWriterConfig, fs: Editor): Promise<void> { | ||
try { | ||
if (hasDeployConfig(data)) { | ||
const ui5ConfigPath = join(projectPath, 'ui5.yaml'); | ||
const baseUi5ConfigContent = fs.read(ui5ConfigPath); | ||
const ui5DeployConfig = await UI5Config.newInstance(baseUi5ConfigContent); | ||
enhanceUI5DeployYaml(ui5DeployConfig, data); | ||
|
||
fs.write(join(projectPath, 'ui5-deploy.yaml'), ui5DeployConfig.toString()); | ||
} | ||
} catch (e) { | ||
throw new Error(`Could not write ui5-deploy.yaml file. Reason: ${e.message}`); | ||
} | ||
} | ||
|
||
/** | ||
* Writes a .env file within a specified folder in the project directory. | ||
* | ||
* @param {string} projectPath - The root path of the project. | ||
* @param {AdpWriterConfig} data - The data to be populated in the template file. | ||
* @param {Editor} fs - The `mem-fs-editor` instance used for file operations. | ||
* @returns {void} | ||
*/ | ||
export async function writeEnvFile(projectPath: string, data: AdpWriterConfig, fs: Editor): Promise<void> { | ||
try { | ||
if (data?.flp?.credentials) { | ||
const templateModel = `ABAP_USERNAME: ${data.flp.credentials.username} | ||
ABAP_PASSWORD: ${data.flp.credentials.password}`; | ||
|
||
fs.write(join(projectPath, '.env'), templateModel); | ||
} | ||
} catch (e) { | ||
throw new Error(`Could not write .env file. Reason: ${e.message}`); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import type { UI5FlexLayer } from '@sap-ux/project-access'; | ||
import type { DestinationAbapTarget, UrlAbapTarget } from '@sap-ux/system-access'; | ||
import type { Adp } from '@sap-ux/ui5-config'; | ||
import type { Adp, BspApp } from '@sap-ux/ui5-config'; | ||
import type { Editor } from 'mem-fs-editor'; | ||
|
||
export interface DescriptorVariant { | ||
|
@@ -35,21 +35,41 @@ export interface AdpWriterConfig { | |
target: AbapTarget; | ||
ui5?: { | ||
tobiasqueck marked this conversation as resolved.
Show resolved
Hide resolved
|
||
minVersion?: string; | ||
ui5Version?: string; | ||
ui5EndpointUrl?: string; | ||
nikmace marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
package?: { | ||
name?: string; | ||
description?: string; | ||
}; | ||
flp?: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That object is odd. What is the difference between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, the title, subtitle and inbound id are only used in the i18n file because the appdescr information comes already filtered after the prompting. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed on the phone, please add the change for the inbound communication to the appdescr template file with the condtion that the flp object is provided There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The change for the inbound it's more complex to be in the aprdescr template and because of that it's passed with other changes in the content There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it is too complex for ejs, implement it as a plain function and then add it to the list of changes. Most importantly move the logic here because you have a reference to it in your generated i18n.properties file |
||
flpTitle?: string; | ||
flpSubtitle?: string; | ||
nikmace marked this conversation as resolved.
Show resolved
Hide resolved
|
||
inboundId?: string; | ||
bspName?: string; | ||
languages?: Language[]; | ||
credentials?: { | ||
username: string; | ||
password: string; | ||
}; | ||
}; | ||
customConfig?: AdpCustomConfig; | ||
/** | ||
* Optional: configuration for deployment to ABAP | ||
*/ | ||
deploy?: Adp; | ||
deploy?: Adp | BspApp; | ||
options?: { | ||
/** | ||
* Optional: if set to true then the generated project will be recognized by the SAP Fiori tools | ||
*/ | ||
fioriTools?: boolean; | ||
}; | ||
appdescr?: ManifestAppdescr; | ||
tobiasqueck marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
export interface Language { | ||
sap: string; | ||
i18n: string; | ||
} | ||
|
||
export interface ManifestAppdescr { | ||
|
@@ -178,6 +198,14 @@ export type IWriterData<T extends ChangeType> = IWriter<GeneratorData<T>>; | |
* | ||
* @template T - The specific type of data the writer will handle, determined by the associated ChangeType. | ||
*/ | ||
export type IProjectWriterData<T extends ProjectType> = IWriter<ProjectGeneratorData<T>>; | ||
|
||
/** | ||
* Defines a generic interface for writer classes, specialized by the type of data they handle. | ||
* | ||
* @template T - The specific type of data the writer will handle, determined by the associated ChangeType. | ||
*/ | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this extra empty line as it detaches the jsdoc from the interface. |
||
export interface IWriter<T> { | ||
/** | ||
* Writes the provided data to the project. | ||
|
@@ -199,6 +227,29 @@ export const enum ChangeType { | |
CHANGE_INBOUND = 'appdescr_app_changeInbound' | ||
} | ||
|
||
/** | ||
* Enumerates the types of projects that can be made, each representing a specific kind of project structure. | ||
*/ | ||
export const enum ProjectType { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both are S/4 systems here, so please change it to something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
ON_PREM = 'OnPrem', | ||
S4 = 'S4', | ||
CF = 'CF' | ||
} | ||
|
||
/** | ||
* Maps a ChangeType to the corresponding data structure needed for that type of change. | ||
* This conditional type ensures type safety by linking each change type with its relevant data model. | ||
* | ||
* @template T - A subtype of ChangeType indicating the specific type of change. | ||
*/ | ||
export type ProjectGeneratorData<T extends ProjectType> = T extends ProjectType.ON_PREM | ||
nikmace marked this conversation as resolved.
Show resolved
Hide resolved
|
||
? AdpWriterConfig | ||
: T extends ProjectType.S4 | ||
? AdpWriterConfig | ||
: T extends ProjectType.CF | ||
? CfWriterConfig | ||
: never; | ||
|
||
/** | ||
* Maps a ChangeType to the corresponding data structure needed for that type of change. | ||
* This conditional type ensures type safety by linking each change type with its relevant data model. | ||
|
@@ -347,3 +398,50 @@ export interface AdpProjectData { | |
reference: string; | ||
id: string; | ||
} | ||
|
||
export interface AdpCustomConfig { | ||
adp: { | ||
safeMode: boolean; | ||
nikmace marked this conversation as resolved.
Show resolved
Hide resolved
|
||
environment: ProjectType; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TBD: the environment is now 'OnPrem' but our change editors still have checks for 'ABAP' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nikmace Why would the change editors need to know about the target environment? |
||
}; | ||
} | ||
|
||
export interface CfModuleData { | ||
appHostId: string; | ||
appName: string; | ||
appVersion: string; | ||
appId: string; | ||
module: string; | ||
moduleTitle: string; | ||
appVariantId: string; | ||
projectName: string; | ||
i18nGuid: string; | ||
projectPath: string; | ||
addSecurity: string; | ||
addStandaloneApprouter: boolean; | ||
sapCloudService: string; | ||
xsSecurityProjectName: string; | ||
org: string; | ||
space: string; | ||
html5RepoRuntime: string; | ||
} | ||
|
||
export interface CfAdpConfig { | ||
componentname: string; | ||
nikmace marked this conversation as resolved.
Show resolved
Hide resolved
|
||
safeMode: boolean; | ||
appvariant: string; | ||
layer: string; | ||
isOVPApp: boolean; | ||
isFioriElement: boolean; | ||
nikmace marked this conversation as resolved.
Show resolved
Hide resolved
|
||
environment: string; | ||
ui5Version: string; | ||
cfSpace: string; | ||
cfOrganization: string; | ||
cfApiUrl: string; | ||
} | ||
|
||
export interface CfWriterConfig { | ||
app: CfModuleData; | ||
appdescr: ManifestAppdescr; | ||
adpConfig: CfAdpConfig; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove preview-middleware and axios-extension from the changeset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done