Skip to content
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

Wizard feature v2 #220

Closed
wants to merge 16 commits into from
Closed

Wizard feature v2 #220

wants to merge 16 commits into from

Conversation

Jacksonzhang34
Copy link
Contributor

Issue #, if available:

Description of changes:
Version 2 of the wizard:

  • revision of the wizard, prefilled editable command line prompts
  • modularity: code is broken up into setter, getter, checker, Wizard, and data classes
  • code safety with the introduction of field enum class
    Why is this change necessary:
  • change was necessary in order to address the feedback from the previous PR and ensure code safety, readability, and readiness for future changes
    How was this change tested:
    change will be tested

Any additional information or context required to review the change:

Checklist:

  • Updated the README if applicable
  • Updated or added new unit tests
  • Updated or added new integration tests
  • Updated or added new end-to-end tests
  • If your code makes a remote network call, it was tested with a proxy
  • You confirm that the change is backwards compatible

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@codecov
Copy link

codecov bot commented Jul 31, 2023

Codecov Report

Patch coverage: 51.61% and project coverage change: +13.06% 🎉

Comparison is base (5d87492) 77.24% compared to head (0eff94a) 90.30%.
Report is 2 commits behind head on development.

Additional details and impacted files
@@               Coverage Diff                @@
##           development     #220       +/-   ##
================================================
+ Coverage        77.24%   90.30%   +13.06%     
================================================
  Files               40       45        +5     
  Lines             1472     1753      +281     
================================================
+ Hits              1137     1583      +446     
+ Misses             335      170      -165     
Flag Coverage Δ
integ 74.78% <0.35%> (?)
uat 65.03% <0.35%> (-12.22%) ⬇️
unit 86.76% <51.61%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Changed Coverage Δ
gdk/wizard/commands/prompt.py 0.00% <0.00%> (ø)
gdk/commands/component/component.py 86.66% <33.33%> (-13.34%) ⬇️
gdk/wizard/commons/checkers.py 70.58% <70.58%> (ø)
gdk/wizard/commands/data.py 81.81% <81.81%> (ø)
gdk/wizard/commons/model.py 83.54% <83.54%> (ø)
gdk/wizard/commons/fields.py 100.00% <100.00%> (ø)

... and 27 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment on lines 128 to 129
# with open(self.project_config_file, "w") as config_file:
# json.dump(self.field_map, config_file, indent=2)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove if not required anymore

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you have separate getter and setter files? In Java, bean class with attributes have getters and setters defined in same class. Is this is standard practice in python to have separate classes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure if it is standard practice to have them in separate file. My thought process for to minimize the size of each file and separate out distinct task to hopefully make the code easier to read

Copy link
Member

@jcosentino11 jcosentino11 Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to jump in unannounced...I get email notifications and thought I could contribute an opinion here:

Echoing Urvashi's point, I suggest designing around your data model rather than language concepts. For example, get author is really a behavior of project config. Wizard_getter#get_author vs ProjectConfig#get_author.

IMO, you get more discoverability and self-documentation with the latter approach. ProjectConfig clearly shows everything you can do with config, rather than having to inspect the whole Wizard_getter class to figure that out.

"then",
}

if isinstance(schema, dict):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use camel case for isInstance

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Python isinstance is a built-in function

elif key not in ignore_fields:
traverse_schema(value, field_name)

elif isinstance(schema, list):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Comment on lines 56 to 58
# args = self.parser.parse_args(
# [f'--{field}', input(f"Current value of the {require}{field} is {value}: ")]
# )

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove if not required

[
f"--change_{build_or_publish}",
input(
f"Do you want to change the {build_or_publish} configurations? (y/n) "

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you want input from Customer? Even if they select "n" the fields will still be editable..right? Or you would simply exit the wizard? What is the behavior in each scenario?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea was to allow users to be prompted in an hierarchical manner. 'custom_build_command' and 'build_system' fall under the umbrella of build and "bucket", "region", and "options" all fall under the umbrella of publish, so if we first ask the user if they would like to change the build config or regions at all before prompting to change specific fields under their respective umbrellas. If the user respond 'n' the prompter would simply skip over the fields in build or publish.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this file along with the static and the integration folders.


class Wizard_data:
def __init__(self):
self.project_config_file = "/Users/jacksozh/Desktop/aws-greengrass-gdk-cli/gdk/wizard/static/default_config.json"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if file exists? Also, you are specifying your local path which will not be present on customer's system.

Comment on lines 21 to 22
getter: Wizard_getter object
setter: Wizard_setter object

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update this.

-------
string: the value for the field key "field"
"""
require = "required " if required else "OPTIONAL "

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason why required is in small case and optional is in upper case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My apologize, I should have been more consistent. I will change them both to upper case for readability and consistency.

Comment on lines 11 to 20
def test_init(self):
# self.project_config_file = _get_project_config_file()
# self.field_map = get_configuration()
# self.project_config = self.field_map["component"]
# self.component_name = next(iter(self.project_config))
# self.schema = self.get_schema()
pass

def test_get_schema():
pass

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand this. Are you defaulting it to pass?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My apologizes, the test cases for this section is not yet complete. I just used pass for now

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment then to inform reviewers that it will be updated in future CR.

import json


class Wizard_data:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename all the class names to camel case - WizardData

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it

Comment on lines 14 to 15
# self.project_config_file = _get_project_config_file()
# self.field_map = get_configuration()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove these.

Copy link
Member

@saranyailla saranyailla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only need one module for Wizard. So, remove commands and commons modules and merge them into existing ones. We also don't need separate ones just for the wizard functionality testing. Do the same for them as well.

return isinstance(input_value, str) and input_value in allowed_build_systems

def check_custom_build_command(self, input_value):
# input must be a non-empty string or a non-empty list of strings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit : Use """ """ for multi-line comments.

""" 
Line 1
Line 2
....
""""


def get_author(self):
try:
return self.project_config[self.component_name][Fields.AUTHOR.value.key]
Copy link
Member

@saranyailla saranyailla Aug 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use return self.project_config.get(self.component_name, default_value) instead, here and at every other place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotcha, I'm assuming that you mean that I should replace return self.project_config[self.component_name] with return self.project_config.get(self.component_name, default_value) and have a default value for component_name ?

print("Exceeded maximum attempts. Assuming default response.")
return value

def change_build_or_publish(self, build_or_publish, max_attempts=3):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we asking for these two together?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh its not asking both questions at once. It's asking about build or publish depending on the value of build_or_publish

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that nothing changes on the customer side but I don't think we need to club these two together in the code. Build and publish are two independent configs so coupling these two will make it hard for us to maintain and test the code in future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it I will separate them out

@Jacksonzhang34 Jacksonzhang34 changed the base branch from main to development August 9, 2023 20:13
@saranyailla saranyailla closed this Sep 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants