From 6c4eecefde3ff2bbb4d02b2fa06b89c6e39e2a36 Mon Sep 17 00:00:00 2001 From: Jesus Cabrera Date: Fri, 11 Sep 2020 12:21:26 -0400 Subject: [PATCH 1/6] Add --namespace flag --- python/rpdk/java/codegen.py | 41 +++++++++++++++++++------ tests/test_codegen.py | 60 +++++++++++++++++++++++++++++++++---- 2 files changed, 87 insertions(+), 14 deletions(-) diff --git a/python/rpdk/java/codegen.py b/python/rpdk/java/codegen.py index 3f5ab270..88c40b41 100644 --- a/python/rpdk/java/codegen.py +++ b/python/rpdk/java/codegen.py @@ -6,8 +6,13 @@ from collections import namedtuple from xml.etree.ElementTree import ParseError # nosec +from colorama import Fore, Style from rpdk.core.data_loaders import resource_stream -from rpdk.core.exceptions import InternalError, SysExitRecommendedError +from rpdk.core.exceptions import ( + InternalError, + SysExitRecommendedError, + WizardValidationError, +) from rpdk.core.init import input_with_validation from rpdk.core.jsonutils.resolver import resolve_models from rpdk.core.plugin_base import LanguagePlugin @@ -62,6 +67,16 @@ class InvalidMavenPOMError(SysExitRecommendedError): pass +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 + + class JavaLanguagePlugin(LanguagePlugin): MODULE_NAME = __name__ RUNTIME = "java8" @@ -92,18 +107,26 @@ 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["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(Style.BRIGHT, Fore.RED, str(error), Style.RESET_ALL, sep="") + 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) diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 0d8926c8..c8221b81 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -35,7 +35,7 @@ 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}) return project @@ -201,7 +201,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 +213,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 +227,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 +241,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( From 3ac81dffa006d50d9f2b781061d10ae1894f42ba Mon Sep 17 00:00:00 2001 From: Jesus Cabrera Date: Fri, 11 Sep 2020 14:22:03 -0400 Subject: [PATCH 2/6] Add --codegen_model flag --- python/rpdk/java/codegen.py | 16 +++++++++------- tests/test_codegen.py | 33 +++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/python/rpdk/java/codegen.py b/python/rpdk/java/codegen.py index 88c40b41..4ef34664 100644 --- a/python/rpdk/java/codegen.py +++ b/python/rpdk/java/codegen.py @@ -132,16 +132,18 @@ def _prompt_for_namespace(self, project): @staticmethod def _prompt_for_codegen_model(project): - prompt = "Choose codegen model - 1 (default) or 2 (guided-aws): " + codegen_model = project.settings["codegen_template_path"] + 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_template_path"] = 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_template_path"] = CODEGEN.guided def _get_template(self, project, stage, name): return self.env.get_template( diff --git a/tests/test_codegen.py b/tests/test_codegen.py index c8221b81..41807311 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", {"namespace": None}) + project.init( + "AWS::Foo::{}".format(RESOURCE), + "test", + {"namespace": None, "codegen_template_path": None}, + ) return project @@ -322,7 +326,9 @@ def test__namespace_from_project_old_settings(): 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_template_path": None} + ) plugin = JavaLanguagePlugin() with patch("rpdk.core.init.input", return_value="") as mock_input: @@ -334,7 +340,9 @@ def test__prompt_for_codegen_model_no_selection(): def test__prompt_for_codegen_model_default(): - project = Mock(type_info=("AWS", "Clown", "Service"), settings={}) + project = Mock( + type_info=("AWS", "Clown", "Service"), settings={"codegen_template_path": None} + ) plugin = JavaLanguagePlugin() with patch("rpdk.core.init.input", return_value="1") as mock_input: @@ -346,7 +354,9 @@ def test__prompt_for_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_template_path": None} + ) plugin = JavaLanguagePlugin() with patch("rpdk.core.init.input", return_value="2") as mock_input: @@ -355,3 +365,18 @@ def test__prompt_for_codegen_model_guided_aws(): mock_input.assert_called_once() assert project.settings == {"codegen_template_path": "guided_aws"} + + +def test__prompt_for_codegen_model_provided(): + project = Mock( + type_info=("AWS", "Clown", "Service"), + settings={"codegen_template_path": "guided_aws"}, + ) + 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_template_path": "guided_aws"} From 82c148c73380be4a7ad21c8debbb12df9274a9e5 Mon Sep 17 00:00:00 2001 From: Jesus Cabrera Date: Fri, 11 Sep 2020 18:38:32 -0400 Subject: [PATCH 3/6] Clean up code --- python/rpdk/java/codegen.py | 17 ++++++----------- python/rpdk/java/utils.py | 10 ++++++++++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/python/rpdk/java/codegen.py b/python/rpdk/java/codegen.py index 4ef34664..fb79f29e 100644 --- a/python/rpdk/java/codegen.py +++ b/python/rpdk/java/codegen.py @@ -18,7 +18,12 @@ 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__) @@ -67,16 +72,6 @@ class InvalidMavenPOMError(SysExitRecommendedError): pass -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 - - class JavaLanguagePlugin(LanguagePlugin): MODULE_NAME = __name__ RUNTIME = "java8" 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]+$" From e9a0de8a66a91de164a312f322867668bf731fef Mon Sep 17 00:00:00 2001 From: Jesus Cabrera Date: Fri, 25 Sep 2020 11:57:08 -0400 Subject: [PATCH 4/6] Add plugin subparser --- python/rpdk/java/codegen.py | 14 +++++++------- python/rpdk/java/parser.py | 25 +++++++++++++++++++++++++ setup.py | 5 ++++- tests/test_codegen.py | 18 +++++++++--------- tests/test_parser.py | 16 ++++++++++++++++ 5 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 python/rpdk/java/parser.py create mode 100644 tests/test_parser.py diff --git a/python/rpdk/java/codegen.py b/python/rpdk/java/codegen.py index fb79f29e..83c37b0f 100644 --- a/python/rpdk/java/codegen.py +++ b/python/rpdk/java/codegen.py @@ -83,7 +83,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 @@ -103,7 +103,7 @@ def _namespace_from_project(self, project): def _prompt_for_namespace(self, project): default_namespace = get_default_namespace(project) - settings_namespace = project.settings["namespace"] + settings_namespace = project.settings.get("namespace") prompt = "Enter a package name (empty for default '{}'): ".format( ".".join(default_namespace) @@ -127,7 +127,7 @@ def _prompt_for_namespace(self, project): @staticmethod def _prompt_for_codegen_model(project): - codegen_model = project.settings["codegen_template_path"] + codegen_model = project.settings.get("codegen_model") if not codegen_model: prompt = "Choose codegen model - 1 (default) or 2 (guided-aws): " @@ -135,19 +135,19 @@ def _prompt_for_codegen_model(project): prompt, validate_codegen_model(CODEGEN.default_code) ) - project.settings["codegen_template_path"] = CODEGEN.default + project.settings["codegen_model"] = CODEGEN.default if codegen_model_code == CODEGEN.guided_code: - project.settings["codegen_template_path"] = CODEGEN.guided + 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/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 41807311..a47e1100 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -38,7 +38,7 @@ def mock_input_with_validation(prompt, validate): # pylint: disable=unused-argu project.init( "AWS::Foo::{}".format(RESOURCE), "test", - {"namespace": None, "codegen_template_path": None}, + {"namespace": None, "codegen_model": None}, ) return project @@ -327,7 +327,7 @@ def test__namespace_from_project_old_settings(): def test__prompt_for_codegen_model_no_selection(): project = Mock( - type_info=("AWS", "Clown", "Service"), settings={"codegen_template_path": None} + type_info=("AWS", "Clown", "Service"), settings={"codegen_model": None} ) plugin = JavaLanguagePlugin() @@ -336,12 +336,12 @@ 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={"codegen_template_path": None} + type_info=("AWS", "Clown", "Service"), settings={"codegen_model": None} ) plugin = JavaLanguagePlugin() @@ -350,12 +350,12 @@ 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={"codegen_template_path": None} + type_info=("AWS", "Clown", "Service"), settings={"codegen_model": None} ) plugin = JavaLanguagePlugin() @@ -364,13 +364,13 @@ 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__prompt_for_codegen_model_provided(): project = Mock( type_info=("AWS", "Clown", "Service"), - settings={"codegen_template_path": "guided_aws"}, + settings={"codegen_model": "guided_aws"}, ) plugin = JavaLanguagePlugin() @@ -379,4 +379,4 @@ def test__prompt_for_codegen_model_provided(): mock_input.assert_not_called() - assert project.settings == {"codegen_template_path": "guided_aws"} + assert project.settings == {"codegen_model": "guided_aws"} 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" From 5280633486cde613b717cc28a90867b4662e29b9 Mon Sep 17 00:00:00 2001 From: Jesus Cabrera Date: Fri, 25 Sep 2020 15:20:39 -0400 Subject: [PATCH 5/6] Clean up code --- python/rpdk/java/codegen.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python/rpdk/java/codegen.py b/python/rpdk/java/codegen.py index 83c37b0f..51e40b39 100644 --- a/python/rpdk/java/codegen.py +++ b/python/rpdk/java/codegen.py @@ -6,14 +6,13 @@ from collections import namedtuple from xml.etree.ElementTree import ParseError # nosec -from colorama import Fore, Style from rpdk.core.data_loaders import resource_stream from rpdk.core.exceptions import ( InternalError, SysExitRecommendedError, WizardValidationError, ) -from rpdk.core.init import input_with_validation +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 @@ -117,7 +116,7 @@ def _prompt_for_namespace(self, project): try: self.namespace = namespace_validator(settings_namespace) except WizardValidationError as error: - print(Style.BRIGHT, Fore.RED, str(error), Style.RESET_ALL, sep="") + print_error(error) self.namespace = input_with_validation(prompt, namespace_validator) else: self.namespace = input_with_validation(prompt, namespace_validator) From 97051769bd905ce533bbbce97b67fc9ea0a7d4dd Mon Sep 17 00:00:00 2001 From: Jesus Cabrera Date: Thu, 15 Oct 2020 13:28:02 -0400 Subject: [PATCH 6/6] Fix test coverage --- tests/test_codegen.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_codegen.py b/tests/test_codegen.py index bda72523..a6521a5e 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -325,6 +325,20 @@ 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={"codegen_model": None}