diff --git a/python/rpdk/java/codegen.py b/python/rpdk/java/codegen.py index 0c1c2fe0..4a8db722 100644 --- a/python/rpdk/java/codegen.py +++ b/python/rpdk/java/codegen.py @@ -8,13 +8,22 @@ from xml.etree.ElementTree import ParseError # nosec from rpdk.core.data_loaders import resource_stream -from rpdk.core.exceptions import InternalError, SysExitRecommendedError -from rpdk.core.init import input_with_validation +from rpdk.core.exceptions import ( + InternalError, + SysExitRecommendedError, + WizardValidationError, +) +from rpdk.core.init import input_with_validation, print_error from rpdk.core.jsonutils.resolver import resolve_models from rpdk.core.plugin_base import LanguagePlugin from .resolver import translate_type -from .utils import safe_reserved, validate_codegen_model, validate_namespace +from .utils import ( + get_default_namespace, + safe_reserved, + validate_codegen_model, + validate_namespace, +) LOG = logging.getLogger(__name__) @@ -76,7 +85,7 @@ def __init__(self): self.env = self._setup_jinja_env( trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True ) - self.codegen_template_path = None + self.codegen_model = None self.env.filters["translate_type"] = translate_type self.env.filters["safe_reserved"] = safe_reserved self.namespace = None @@ -95,42 +104,52 @@ def _namespace_from_project(self, project): self.package_name = ".".join(self.namespace) def _prompt_for_namespace(self, project): - if project.type_info[0] == "AWS": - namespace = ("software", "amazon") + project.type_info[1:] - else: - namespace = ("com",) + project.type_info - - namespace = tuple(safe_reserved(s.lower()) for s in namespace) + default_namespace = get_default_namespace(project) + settings_namespace = project.settings.get("namespace") prompt = "Enter a package name (empty for default '{}'): ".format( - ".".join(namespace) + ".".join(default_namespace) ) - self.namespace = input_with_validation(prompt, validate_namespace(namespace)) + namespace_validator = validate_namespace(default_namespace) + + if settings_namespace == "default": + self.namespace = default_namespace + elif settings_namespace: + try: + self.namespace = namespace_validator(settings_namespace) + except WizardValidationError as error: + print_error(error) + self.namespace = input_with_validation(prompt, namespace_validator) + else: + self.namespace = input_with_validation(prompt, namespace_validator) + project.settings["namespace"] = self.namespace self.package_name = ".".join(self.namespace) @staticmethod def _prompt_for_codegen_model(project): - prompt = "Choose codegen model - 1 (default) or 2 (guided-aws): " + codegen_model = project.settings.get("codegen_model") + if not codegen_model: + prompt = "Choose codegen model - 1 (default) or 2 (guided-aws): " - codegen_model = input_with_validation( - prompt, validate_codegen_model(CODEGEN.default_code) - ) + codegen_model_code = input_with_validation( + prompt, validate_codegen_model(CODEGEN.default_code) + ) - project.settings["codegen_template_path"] = CODEGEN.default + project.settings["codegen_model"] = CODEGEN.default - if codegen_model == CODEGEN.guided_code: - project.settings["codegen_template_path"] = CODEGEN.guided + if codegen_model_code == CODEGEN.guided_code: + project.settings["codegen_model"] = CODEGEN.guided def _get_template(self, project, stage, name): return self.env.get_template( - stage + "/" + project.settings["codegen_template_path"] + "/" + name + stage + "/" + project.settings["codegen_model"] + "/" + name ) @staticmethod def _is_aws_guided(project: object) -> bool: - return project.settings["codegen_template_path"] == CODEGEN.guided + return project.settings["codegen_model"] == CODEGEN.guided @logdebug def _writing_component( diff --git a/python/rpdk/java/parser.py b/python/rpdk/java/parser.py new file mode 100644 index 00000000..e632c0fa --- /dev/null +++ b/python/rpdk/java/parser.py @@ -0,0 +1,25 @@ +def setup_subparser(subparsers, parents): + parser = subparsers.add_parser( + "java", + description="This sub command generates IDE and build files for Java", + parents=parents, + ) + parser.set_defaults(language="java") + + parser.add_argument( + "-n", + "--namespace", + nargs="?", + const="default", + help="""Select the name of the Java namespace. + Passing the flag without argument select the default namespace.""", + ) + + parser.add_argument( + "-c", + "--codegen-model", + choices=["default", "guided_aws"], + help="Select a codegen model.", + ) + + return parser diff --git a/python/rpdk/java/utils.py b/python/rpdk/java/utils.py index 4ab2a84f..7ccebdc2 100644 --- a/python/rpdk/java/utils.py +++ b/python/rpdk/java/utils.py @@ -64,6 +64,16 @@ def safe_reserved(token): return token +def get_default_namespace(project): + if project.type_info[0] == "AWS": + namespace = ("software", "amazon") + project.type_info[1:] + else: + namespace = ("com",) + project.type_info + + namespace = tuple(safe_reserved(s.lower()) for s in namespace) + return namespace + + def validate_namespace(default): pattern = r"^[_a-z][_a-z0-9]+$" diff --git a/setup.py b/setup.py index e13a4474..9869de23 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,10 @@ def find_version(*file_paths): zip_safe=True, install_requires=["cloudformation-cli>=0.1.4,<0.2"], python_requires=">=3.6", - entry_points={"rpdk.v1.languages": ["java = rpdk.java.codegen:JavaLanguagePlugin"]}, + entry_points={ + "rpdk.v1.languages": ["java = rpdk.java.codegen:JavaLanguagePlugin"], + "rpdk.v1.parsers": ["java = rpdk.java.parser:setup_subparser"], + }, license="Apache License 2.0", classifiers=[ "Development Status :: 5 - Production/Stable", diff --git a/tests/test_codegen.py b/tests/test_codegen.py index c30b11ea..a6521a5e 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -35,7 +35,11 @@ def mock_input_with_validation(prompt, validate): # pylint: disable=unused-argu {"test": lambda: JavaLanguagePlugin}, clear=True, ), patch("rpdk.java.codegen.input_with_validation", new=mock_cli): - project.init("AWS::Foo::{}".format(RESOURCE), "test") + project.init( + "AWS::Foo::{}".format(RESOURCE), + "test", + {"namespace": None, "codegen_model": None}, + ) return project @@ -201,7 +205,7 @@ def test_package(project): def test__prompt_for_namespace_aws_default(): - project = Mock(type_info=("AWS", "Clown", "Service"), settings={}) + project = Mock(type_info=("AWS", "Clown", "Service"), settings={"namespace": None}) plugin = JavaLanguagePlugin() with patch("rpdk.core.init.input", return_value="") as mock_input: @@ -213,7 +217,7 @@ def test__prompt_for_namespace_aws_default(): def test__prompt_for_namespace_aws_overwritten(): - project = Mock(type_info=("AWS", "Clown", "Service"), settings={}) + project = Mock(type_info=("AWS", "Clown", "Service"), settings={"namespace": None}) plugin = JavaLanguagePlugin() with patch( @@ -227,7 +231,9 @@ def test__prompt_for_namespace_aws_overwritten(): def test__prompt_for_namespace_other_default(): - project = Mock(type_info=("Balloon", "Clown", "Service"), settings={}) + project = Mock( + type_info=("Balloon", "Clown", "Service"), settings={"namespace": None} + ) plugin = JavaLanguagePlugin() with patch("rpdk.core.init.input", return_value="") as mock_input: @@ -239,7 +245,55 @@ def test__prompt_for_namespace_other_default(): def test__prompt_for_namespace_other_overwritten(): - project = Mock(type_info=("Balloon", "Clown", "Service"), settings={}) + project = Mock( + type_info=("Balloon", "Clown", "Service"), settings={"namespace": None} + ) + plugin = JavaLanguagePlugin() + + with patch( + "rpdk.core.init.input", return_value="com.ball.clown.service" + ) as mock_input: + plugin._prompt_for_namespace(project) + + mock_input.assert_called_once() + + assert project.settings == {"namespace": ("com", "ball", "clown", "service")} + + +def test__prompt_for_namespace_default_provided(): + project = Mock( + type_info=("Balloon", "Clown", "Service"), settings={"namespace": "default"} + ) + plugin = JavaLanguagePlugin() + + with patch("rpdk.core.init.input") as mock_input: + plugin._prompt_for_namespace(project) + + mock_input.assert_not_called() + + assert project.settings == {"namespace": ("com", "balloon", "clown", "service")} + + +def test__prompt_for_namespace_valid_provided(): + project = Mock( + type_info=("Balloon", "Clown", "Service"), + settings={"namespace": "com.valid.namespace"}, + ) + plugin = JavaLanguagePlugin() + + with patch("rpdk.core.init.input") as mock_input: + plugin._prompt_for_namespace(project) + + mock_input.assert_not_called() + + assert project.settings == {"namespace": ("com", "valid", "namespace")} + + +def test__prompt_for_namespace_invalid_provided(): + project = Mock( + type_info=("Balloon", "Clown", "Service"), + settings={"namespace": "com.Invalid.Namespace"}, + ) plugin = JavaLanguagePlugin() with patch( @@ -271,8 +325,24 @@ def test__namespace_from_project_old_settings(): assert plugin.package_name == "com.balloon.clown.service" +def test__prompt_for_codegen_model_provided(): + project = Mock( + type_info=("AWS", "Clown", "Service"), settings={"codegen_model": "default"} + ) + plugin = JavaLanguagePlugin() + + with patch("rpdk.core.init.input") as mock_input: + plugin._prompt_for_codegen_model(project) + + mock_input.assert_not_called() + + assert project.settings == {"codegen_model": "default"} + + def test__prompt_for_codegen_model_no_selection(): - project = Mock(type_info=("AWS", "Clown", "Service"), settings={}) + project = Mock( + type_info=("AWS", "Clown", "Service"), settings={"codegen_model": None} + ) plugin = JavaLanguagePlugin() with patch("rpdk.core.init.input", return_value="") as mock_input: @@ -280,11 +350,13 @@ def test__prompt_for_codegen_model_no_selection(): mock_input.assert_called_once() - assert project.settings == {"codegen_template_path": "default"} + assert project.settings == {"codegen_model": "default"} def test__prompt_for_codegen_model_default(): - project = Mock(type_info=("AWS", "Clown", "Service"), settings={}) + project = Mock( + type_info=("AWS", "Clown", "Service"), settings={"codegen_model": None} + ) plugin = JavaLanguagePlugin() with patch("rpdk.core.init.input", return_value="1") as mock_input: @@ -292,11 +364,13 @@ def test__prompt_for_codegen_model_default(): mock_input.assert_called_once() - assert project.settings == {"codegen_template_path": "default"} + assert project.settings == {"codegen_model": "default"} def test__prompt_for_codegen_model_guided_aws(): - project = Mock(type_info=("AWS", "Clown", "Service"), settings={}) + project = Mock( + type_info=("AWS", "Clown", "Service"), settings={"codegen_model": None} + ) plugin = JavaLanguagePlugin() with patch("rpdk.core.init.input", return_value="2") as mock_input: @@ -304,7 +378,7 @@ def test__prompt_for_codegen_model_guided_aws(): mock_input.assert_called_once() - assert project.settings == {"codegen_template_path": "guided_aws"} + assert project.settings == {"codegen_model": "guided_aws"} def test_generate_image_build_config(project): diff --git a/tests/test_parser.py b/tests/test_parser.py new file mode 100644 index 00000000..f682020e --- /dev/null +++ b/tests/test_parser.py @@ -0,0 +1,16 @@ +import argparse + +from rpdk.java.parser import setup_subparser + + +def test_setup_subparser(): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="subparser_name") + + sub_parser = setup_subparser(subparsers, []) + + args = sub_parser.parse_args(["--namespace", "com.foo.bar", "-c", "default"]) + + assert args.language == "java" + assert args.namespace == "com.foo.bar" + assert args.codegen_model == "default"