-
Notifications
You must be signed in to change notification settings - Fork 42
feat(generator-adp): Make possible to configure the Adaptation project generator from a json. #3188
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
base: main
Are you sure you want to change the base?
Conversation
|
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.
@avasilev-sap, I've added a few suggestions.
|
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.
Overall the code looks good. There are 4 issues in the Sonar that need to be fixed (1 of them should be accepted though). Also, the text on which Louise requested changes.
private async _validateJsonInput({ | ||
projectName, | ||
targetFolder, | ||
namespace, | ||
system | ||
}: { | ||
projectName: string; | ||
targetFolder: string; | ||
namespace: string; | ||
system: string; | ||
}): Promise<void> { | ||
let validationResult = validateProjectName(projectName, targetFolder, this.isCustomerBase); | ||
if (isString(validationResult)) { | ||
throw new Error(validationResult); | ||
} | ||
|
||
validationResult = validateNamespaceAdp(namespace, projectName, this.isCustomerBase); | ||
if (isString(validationResult)) { | ||
throw new Error(validationResult); | ||
} | ||
|
||
const systemEndpoint = await this.systemLookup.getSystemByName(system); | ||
if (!systemEndpoint) { | ||
throw new Error(t('error.systemNotFound', { systemName: system })); | ||
} | ||
} |
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.
I think this part of the code could be extracted into the existing validators.ts
file to not clutter the generator code here. This will be better for tests as I will explain below in the in my other comment.
I think it would be fine instead of an object to pass all of the properties inline, since they are 4-5. We also need to pass the systemLookup instance and fix the JSDoc afterwards.
/**
* Validates input fields for project name, namespace, and system.
*
* @param {string} projectName - Name of the project.
* @param {string} targetFolder - Path to the target folder.
* @param {string} namespace - Namespace of the application.
* @param {string} system - Selected system name.
* @param {SystemLookup} systemLookup - Instance of system lookup service.
*/
export function validateJsonInput(projectName: string, targetFolder: string, namespace: string, system: string, systemLookup: SystemLookup) {}
Also, suggesting to modify the translation parameter systemName
to just system
, that way you will just do this:
throw new Error(t('error.systemNotFound', { system }));
// Application config | ||
application: baseApplicationName, | ||
applicationTitle, | ||
applicationComponentHierarchy = '', |
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.
This variable is never used in this code, so it could be removed for the time being, at least until this PR goes in: #3146. It should also be removed from ConfigAnswers
interface.
} | ||
|
||
const { | ||
// System config |
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.
These comments are not needed.
export function parseJsonInput(jsonString: string): Record<string, string> | undefined { | ||
try { | ||
const parsed = JSON.parse(jsonString); | ||
|
||
if (!isRecordOfStrings(parsed)) { | ||
return; | ||
} | ||
|
||
return parsed; | ||
} catch (error) { | ||
// Do nothing. | ||
} | ||
} |
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.
I think it would be good to have an interface for this instead of using a Record<string, string>
so we know exactly what could come in this JSON? Also, we should explicitly return undefined
.
/** | ||
* Returns the first argument from a list of CLI arguments. If the first argument | ||
* is not a string returns empty string. | ||
* | ||
* @param args The list of CLI command arguments. | ||
* @returns The first parameter in the argument's list as string. | ||
*/ | ||
export function getFirstArgAsString(args: string | string[]): string { |
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.
All of the JSDocs should look the same or be similar in terms of structure.
Although this is optional and the benefits are not so much, I try to put the types for the properties in the JSDoc (see args).
Also, after the @param {type} property
usually comes a dash ("-").
All of the above make the comments more sophisticated, helping both humans and machines understand your code.
/** | |
* Returns the first argument from a list of CLI arguments. If the first argument | |
* is not a string returns empty string. | |
* | |
* @param args The list of CLI command arguments. | |
* @returns The first parameter in the argument's list as string. | |
*/ | |
export function getFirstArgAsString(args: string | string[]): string { | |
/** | |
* Returns the first argument from a list of CLI arguments. If the first argument | |
* is not a string returns empty string. | |
* | |
* @param {string | string[]} args - The list of CLI command arguments. | |
* @returns {string} The first parameter in the argument's list as string. | |
*/ | |
export function getFirstArgAsString(args: string | string[]): string { |
it('should throw an error when the project name in the json input is not valid', async () => { | ||
const jsonInput = { | ||
system: 'urlA', | ||
username: 'user1', | ||
password: 'pass1', | ||
client: '010', | ||
application: 'sap.ui.demoapps.f1', | ||
projectName: 'Invalid project name', | ||
targetFolder: testOutputDir | ||
}; | ||
const jsonInputString = JSON.stringify(jsonInput); | ||
|
||
const runContext = yeomanTest | ||
.create(adpGenerator, { resolved: generatorPath }, { cwd: testOutputDir }) | ||
.withArguments([jsonInputString]); | ||
|
||
await expect(runContext.run()).rejects.toThrow( | ||
t('adp.projectNameUppercaseError', { ns: PROJECT_INPUT_VALIDATOR_NS }) | ||
); | ||
}); | ||
|
||
it('should throw an error when the namespace in the json input is not valid', async () => { | ||
const jsonInput = { | ||
system: 'urlA', | ||
username: 'user1', | ||
password: 'pass1', | ||
client: '010', | ||
application: 'sap.ui.demoapps.f1', | ||
projectName: 'failure', | ||
namespace: 'customer.invalid namespace', | ||
targetFolder: testOutputDir | ||
}; | ||
const jsonInputString = JSON.stringify(jsonInput); | ||
|
||
const runContext = yeomanTest | ||
.create(adpGenerator, { resolved: generatorPath }, { cwd: testOutputDir }) | ||
.withArguments([jsonInputString]); | ||
|
||
await expect(runContext.run()).rejects.toThrow( | ||
t('adp.namespaceValidationError', { ns: PROJECT_INPUT_VALIDATOR_NS }) | ||
); | ||
}); | ||
|
||
it('should throw an error when the system is not found in the json workflow', async () => { | ||
const jsonInput = { | ||
system: 'nonexistent system', | ||
username: 'user1', | ||
password: 'pass1', | ||
client: '010', | ||
application: 'sap.ui.demoapps.f1', | ||
projectName: 'failure', | ||
namespace: 'customer.my.app', | ||
applicationTitle: 'My app title', | ||
targetFolder: testOutputDir | ||
}; | ||
const jsonInputString = JSON.stringify(jsonInput); | ||
|
||
const runContext = yeomanTest | ||
.create(adpGenerator, { resolved: generatorPath }, { cwd: testOutputDir }) | ||
.withArguments([jsonInputString]); | ||
|
||
await expect(runContext.run()).rejects.toThrow(t('error.systemNotFound', { systemName: jsonInput.system })); | ||
}); |
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.
When you extract the validations in a separate file, you will no longer need these tests. Just mock the validateJsonInput function. There should not be so many integration tests just to check if something breaks in the writing phase. We already have one test above which checks if there is an error thrown during the writing phase, no need for more in my opinion. To make sure the test works, you have added a successful integration test that generates a project from JSON inpu,t and that is enough.
import { getFirstArgAsString, parseJsonInput } from '../utils/parse-json-arg'; | ||
import type { AbapServiceProvider } from '@sap-ux/axios-extension'; | ||
import { getDefaultNamespace, getDefaultProjectName } from './questions/helper/default-values'; | ||
import { validateNamespaceAdp, validateProjectName } from '@sap-ux/project-input-validator'; | ||
import { isString } from '../utils/type-guards'; |
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.
Feat for #3015 .
Testing done:
yo @sap-ux/adp '{ "targetFolder": "/Users/I762682/Workspace/adaptations", "application": "sap.ui.demoapps.rta.fe", "system": "U1Y_099", "client": "099", "username": "VASILEVATA", "password": "...", "projectName": "test.json.flow", "applicationTitle": "Test json flow", "namespace": "customer.test.json.flow" }'
. After the project creation the adaptation editor is started successfully.