-
Notifications
You must be signed in to change notification settings - Fork 33
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
Updating methods for custom field choices #115
Updating methods for custom field choices #115
Conversation
Add method `custom_field_choices()` in `App` class. Rename method `custom_choices()` to `custom_fields()`. Update docstings for both methods according to returning data.
Add test case `AppCustomFieldChoicesTestCase` for `custom_field_choices()` method. Rename test case for `custom_fields()` method. Add using fixtures for mentioned test cases.
Add missing JSON files with fixtures for tests for getting custom fields.
IDK why tests are failing on URL assertion, when i run it locally, those tests are passed well.
or via invoke
Working on it... |
a89d82f
to
49c500f
Compare
Fix test cases `AppCustomFieldsTestCase` and `AppCustomFieldChoicesTestCase` for python 3.7 stable. Update logic for checking passed arguments of mock's `call_args`.
49c500f
to
b09543a
Compare
Done, tests are fixed.
However That's why tests were failing only on 3.7. |
@pszulczewski with what you have been seeing, this look good? |
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.
Good work, just some cosmetic changes around examples, mocking and variable names.
pynautobot/core/app.py
Outdated
>>> custom_fields_list = nb.extras.custom_fields() | ||
>>> print(custom_fields_list[0]['label']) | ||
Test custom field for rack | ||
>>> print(custom_fields_list[0]['content_types']) | ||
['dcim.rack'] |
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.
>>> custom_fields_list = nb.extras.custom_fields() | |
>>> print(custom_fields_list[0]['label']) | |
Test custom field for rack | |
>>> print(custom_fields_list[0]['content_types']) | |
['dcim.rack'] | |
>>> nb.extras.custom_fields() | |
[ | |
{ | |
"id": "5b39ba88-e5ab-4be2-89f5-5a016473b53c", | |
"display": "Test custom field", | |
"url": "http://localhost:8000/api/extras/custom-fields/5b39ba88-e5ab-4be2-89f5-5a016473b53c/", | |
"content_types": ["dcim.rack"], | |
"type": {"value": "integer", "label": "Integer"}, | |
"label": "Test custom field", | |
"name": "test_custom_field", | |
"slug": "test_custom_field", | |
"description": "", | |
"required": False, | |
"filter_logic": {"value": "loose", "label": "Loose"}, | |
"default": None, | |
"weight": 100, | |
"validation_minimum": None, | |
"validation_maximum": None, | |
"validation_regex": "", | |
"created": "2023-04-15", | |
"last_updated": "2023-04-15T17:45:11.839431Z", | |
"notes_url": "http://localhost:8000/api/extras/custom-fields/5b39ba88-e5ab-4be2-89f5-5a016473b53c/notes/", | |
}, | |
] |
Examples should show what is really returned from the method.
Think about someone who is looking at this for the first time.
pynautobot/core/app.py
Outdated
>>> print(custom_fields_list[0]['label']) | ||
Test custom field for rack | ||
>>> print(custom_fields_list[0]['content_types']) | ||
['dcim.rack'] | ||
""" | ||
custom_fields = Request( | ||
base="{}/{}/custom-fields/".format( |
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.
Would be good to convert that to f-string. Refactor is welcomed when it simplifies the code.
pynautobot/core/app.py
Outdated
|
||
|
||
>>> custom_field_choices_list = nb.extras.custom_field_choices() | ||
>>> print(custom_field_choices_list[0]['value']) | ||
First option | ||
>>> print(custom_field_choices_list[0]['field']['name']) | ||
test_custom_field |
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.
>>> custom_field_choices_list = nb.extras.custom_field_choices() | |
>>> print(custom_field_choices_list[0]['value']) | |
First option | |
>>> print(custom_field_choices_list[0]['field']['name']) | |
test_custom_field | |
>>> nb.extras.custom_field_choices() | |
[ | |
{ | |
"id": "5b39ba88-e5ab-4be2-89f5-5a016473b53c", | |
"display": "First option", | |
"url": "http://localhost:8000/api/extras/custom-field-choices/5b39ba88-e5ab-4be2-89f5-5a016473b53c/", | |
"field": { | |
"display": "Test custom field 2", | |
"id": "5b39ba88-e5ab-4be2-89f5-5a016473b53c", | |
"url": "http://localhost:8000/api/extras/custom-fields/5b39ba88-e5ab-4be2-89f5-5a016473b53c/", | |
"name": "test_custom_field_2" | |
}, | |
"value": "First option", | |
"weight": 100, | |
"created": "2023-04-15", | |
"last_updated": "2023-04-15T18:11:57.163237Z" | |
}, | |
] |
Same here, give full example of what this method is returning.
pynautobot/core/app.py
Outdated
custom_fields = Request( | ||
base="{}/{}/custom-field-choices/".format( | ||
self.api.base_url, | ||
self.name, | ||
), | ||
token=self.api.token, | ||
http_session=self.api.http_session, | ||
).get() | ||
return custom_fields |
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.
custom_fields = Request( | |
base="{}/{}/custom-field-choices/".format( | |
self.api.base_url, | |
self.name, | |
), | |
token=self.api.token, | |
http_session=self.api.http_session, | |
).get() | |
return custom_fields | |
cf_choices = Request( | |
base=f"{self.api.base_url}/{self.name}/custom-field-choices/", | |
token=self.api.token, | |
http_session=self.api.http_session, | |
).get() | |
return cf_choices |
Change custom_fields
to cf_choices
+ refactor to f-string.
tests/test_app.py
Outdated
api = pynautobot.api(host, **def_kwargs) | ||
choices = api.extras.custom_choices() | ||
choices = api.extras.custom_fields() |
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.
choices = api.extras.custom_fields() | |
cfs = api.extras.custom_fields() |
tests/test_app.py
Outdated
"pynautobot.core.query.Request.get", | ||
return_value={"Testfield1": {"TF1_1": 1, "TF1_2": 2}, "Testfield2": {"TF2_1": 3, "TF2_2": 4}}, | ||
"requests.sessions.Session.get", | ||
side_effect=[Response(fixture="extras/custom_fields.json")], |
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.
side_effect=[Response(fixture="extras/custom_fields.json")], | |
return_value=Response(fixture="extras/custom_fields.json"), |
return_value
is good enough, side_effect is for rising exceptions or iterables.
tests/test_app.py
Outdated
self.assertIsInstance(choices, list) | ||
self.assertEqual(len(choices), 2) |
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.
self.assertIsInstance(choices, list) | |
self.assertEqual(len(choices), 2) | |
self.assertIsInstance(cfs, list) | |
self.assertEqual(len(cfs), 2) |
tests/test_app.py
Outdated
self.assertEqual(len(choices), 2) | ||
self.assertEqual(sorted(choices.keys()), ["Testfield1", "Testfield2"]) | ||
for field in choices: |
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.
for field in choices: | |
for field in cfs: |
tests/test_app.py
Outdated
class AppCustomFieldChoicesTestCase(unittest.TestCase): | ||
@patch( | ||
"requests.sessions.Session.get", | ||
side_effect=[Response(fixture="extras/custom_field_choices.json")], |
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.
side_effect=[Response(fixture="extras/custom_field_choices.json")], | |
return_value=Response(fixture="extras/custom_field_choices.json"), |
* (docs) update docstrings for `custom_fields()` and `custom_field_choices()` methods * (refactor): use f-strings instead of `.format()` in `custom_fields()` and `custom_field_choices()` * (tests): update naming in tests * (tests): use `return_value` instead of `side_effect` for mocks
@pszulczewski I have uploaded a commit with all the changes you've suggested.
All CI checks are passed. p. s. about docstrings: i had inteded to make them as you described at first, but i was afraid that would look too huge for a docstring =) |
@@ -82,22 +82,73 @@ def choices(self): | |||
|
|||
return self._choices | |||
|
|||
def custom_choices(self): |
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.
Should we have .custom_choices()
call self.custom_fields()
so it is backwards compatible?
@jvanderaa @pszulczewski do we have any specific patterns of notifying the end user the method is deprecated?
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.
BTW, this is exactly the question that i had gotten, but i thought it depends on versioning. I guess if it's ok to wait new minor feature release (x.y.z) for this update, it can be as is. Otherwise if it's better to keep as it was, i'm ready to upload a new commit, containing the original method implemetation and both of new, but renamed. Just let me know.
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 don't think we have a specific pattern today @joewesch. I think we should add in something for this.
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 don't think we need to have it straight for here.
If there is a breaking change, we will do a major release, following SemVer. There is an up coming major release for supporting Nautobot 2.0 if we want to get that in with this.
On top of it we should start to look at adding the policy in with 2.0 release.
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 add .custom_choices()
to call self.custom_fields()
as @joewesch suggested and we will keep it in 1.x release train and remove in 2.0.
We can also add logging
and call logger.warning()
that the method will be deprecated in 2.x.
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 add
.custom_choices()
to callself.custom_fields()
as @joewesch suggested and we will keep it in 1.x release train and remove in 2.0. We can also addlogging
and calllogger.warning()
that the method will be deprecated in 2.x.
Done
I've restored original method as it was, except adding deprecation warning log. Original tests are left removed for obvious reasons i presume (or should they be restored as well?)
* (fix): restore original method `custom_choices()` for application class to provide a compatibility with existing client code * (enhance): add logging a deprecation warning
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.
Brilliant! Thanks @nautics889
pynautobot/core/app.py
Outdated
@@ -103,6 +118,78 @@ def custom_choices(self): | |||
).get() | |||
return custom_fields |
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.
return custom_fields | |
return self.custom_fields() |
You need just that after logger
.
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 ✓
* (refactor): call `custom_fields()` in `custom_choices()` since they represent essentially identical requests to Nautobot
@pszulczewski thank you for the feedback! I've uploaded the last minor changes you've mentioned
p. s. there was some problem with CI runners (or rather with dependencies being installed for tests as seems to me according to logs). Failed job: https://github.com/nautics889/pynautobot/actions/runs/5660539569/job/15336640442 |
Those are the actions in your fork. They are passing in this repo: https://github.com/nautobot/pynautobot/actions/runs/5660540032/job/15336460260 Your branch is 20 commits behind develop, so may be worth pulling in the latest changes....or just disable and ignore your own CI. |
Indeed, didn't notice, thank you |
Since Nautobot API has two different endpoints for custom fields and custom field choices (/api/extras/custom-fields/ and /api/extras/custom-field-choices/ respectively), it might seem some ambiguous that we have a method
custom_choices()
which actually sends a request to /custom-fields/.Rename:
App.custom_choices()
→App.custom_fields()
(sends a request to /extras/custom-fields/)AppCustomChoicesTestCase
→AppCustomFieldsTestCase
(tests for sending a request to /extras/custom-fields/)Add:
App.custom_field_choices()
(sends a request to /extras/custom-field-choices/)AppCustomFieldChoicesTestCase
(tests for sending a request to /extras/custom-field-choices/)I also add checking of called URL in the tests. I presume it's a point to be discussed, because without this checking there's a possibility that tests don't display an error when the method being tested sends request to wrong URL. Considering we have mocks all over test module. So as for me, it'd be good to add such assertions in other places of test modules.