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

pass a UserClientSet into SDKMethodRunner #296

Merged
merged 12 commits into from
Feb 4, 2021

Conversation

MrCreosote
Copy link
Member

@MrCreosote MrCreosote commented Jan 28, 2021

Description of PR purpose/changes

UCS contains the clients that need to be instantiated on a per user basis.
UCS is passed in to SDKMR to 1) simplify the SDKMR constructor, and 2) allow for eventual unit testing of SDKMR, since the UCS and workspace client can be mocked via create_autospec.

Also add instructions for running pre-commit hooks.

Jira Ticket / Github Issue

  • [n/a] Added the Jira Ticket to the title of the PR e.g. (DATAUP-69 Adds a PR template)

Testing Instructions

  • Details for how to test the PR:
  • Tests pass in travis and locally
  • Changes available by spinning up a local test suite

Dev Checklist:

  • My code follows the guidelines at https://sites.google.com/truss.works/kbasetruss/development
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • [?] My changes generate no new warnings - too many warnings to feasibly check
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • [n/a] Any dependent changes have been merged and published in downstream modules
  • I have run Black and Flake8 on changed Python Code manually or with git precommit (and the travis build passes)

Updating Version and Release Notes (if applicable)

UCS contains the clients that need to be instantiated on a per user basis.
UCS is passed in to SDKMR to allow for eventual unit testing of SDKMR,
since the UCS and workspace client can be mocked via create_autospec.
@MrCreosote MrCreosote requested a review from bio-boris January 28, 2021 22:32
@MrCreosote
Copy link
Member Author

Huh. The failing tests were marked as failing immediately, and failing after [2, 8]m, even thought they couldn't have started yet

@@ -0,0 +1,5 @@
'''
Copy link
Member Author

Choose a reason for hiding this comment

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

I had to pull the constants into a separate file to avoid an import cycle

return self._workspace_auth


class ClientSet:
Copy link
Member Author

Choose a reason for hiding this comment

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

Currently unused, but is the next step in the unit-testability work

@lgtm-com
Copy link

lgtm-com bot commented Jan 28, 2021

This pull request introduces 3 alerts when merging 5c01612 into 441293c - view on LGTM.com

new alerts:

  • 3 for Unused import

Also added pre-commit instructions
Surprised flake8 didn't highlight these
... a 1 line triple quoted comment unless the quotes are one the same line.
@MrCreosote
Copy link
Member Author

I see a fix I can make for the workspace-url check in the client set, give me a sec

@MrCreosote
Copy link
Member Author

@bio-boris ok, all set, ready for re-review

self._workspace = Workspace(ws_url, token=token)
self._workspace_auth = WorkspaceAuth(user_id, self._workspace)

# create_autospec can't mock instance variables, so we add some boilerplate
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems we just need to pass spec_set=True to magicmocks to achieve the same functionality as createautospec and then there is no need to create setters and getters for instance variables just for testing purposes?

Copy link
Member Author

Choose a reason for hiding this comment

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

Have you verified that with a @patch decorator with spec_set=True that it detects instance variables? My understanding is that the spec_set mocks can't see instance variables regardless of how the mock is created. I'll update the comment to that effect.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, No, instance variables are not detected at all, so nevermind.

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed getters, no longer needed

@@ -41,6 +42,10 @@ class execution_engine2:

ADMIN_ROLES_CACHE_SIZE = 500
ADMIN_ROLES_CACHE_EXPIRE_TIME = 300 # seconds

def get_user_clients(self, ctx) -> UserClientSet:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there an advantage of having this here instead of inside the SDKMethodRunner?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, instantiating the client set outside of SDKMR is what allows for passing mock objects into SDKMR rather than monkey patching (although you can still do that if you want) and enables constructor-based dependency injection. The major change for this PR is exactly that - making the code that creates SDKMR responsible for the creation of SDKMR's dependencies rather than SDKMR.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I should have said inside the SDKMethodRunner file, defined outside of the SDKMethodRunner class, or moving this function to its own file. We should probably move out everything from the BEGIN_CLASS_HEADER section to its own file, as there are often issues with editing code in the impl file, and we have the common KBase pattern of doing as little as possible in the IMPL file itself.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh I see. I'll make an APIHelper class that holds a reference to the config so we don't have to pass it around all over place and put the method there.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

self.auth_admin = AdminAuthUtil(auth_url, [ADMIN_READ_ROLE, ADMIN_WRITE_ROLE])

# KafkaClient has a nice error message when the arg is None
self.kafka_client = KafkaClient(cfg.get("kafka-host"))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we should follow the fail fast with Keyerror way to handling dictionaries by doing the following

cfg['key'] == required value
cfg.get('key') == optionally None
cfg.get('key','default') == explicitly set default

Copy link
Member Author

@MrCreosote MrCreosote Feb 3, 2021

Choose a reason for hiding this comment

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

If we wanted to do something like this the way it'd do it is something like:

if not cfg.get('key') or not cfg.get('key').strip():
    raise ValueError('configuration is missing key "key"')

Just getting a KeyError("key") is not very helpful to someone trying to figure out why the service won't start, especially if they're not familiar with it.

I actually did use your first approach in cases where the client I was instantiating ignored missing arguments, although there could still be issues if the argument was whitespace or a bad url, etc. Where the clients had readable error messages (like Kafka and Slack) I went with those.

In most of my repos all the config args are checked for correctness, and urls used to make requests to the service and check the results, before the service starts. The checking is mostly delegated to the clients / objects that use them since they understand the semantics.

Copy link
Collaborator

@bio-boris bio-boris Feb 4, 2021

Choose a reason for hiding this comment

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

Add dict['key'] vs dict.get('') to the style guide at #303

# Could make these properties but then they return MagicMocks which don't update with API
# changes

def user_id(self):
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems strange to do it this way. Are other kbase codebases doing it this way?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so. You only need to do this when you have a mock where you need to mock the instance variables, which doesn't happen all that often in my experience.

A better way to do this might be to make a constructor that takes a workspace client, user id, and token, and then you can just mock the workspace client and you don't need to mock the client set. I think I'll do that, then this becomes a non-issue.

Copy link
Member Author

Choose a reason for hiding this comment

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

Did that, this is now moot, no more getters

# Regular User
lowly_user = "Access Denied: You are not an administrator"
runner = self.getRunner()
ucs, wsa = self.get_mocks()
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would help with readability to write out
user_client_set and ws_auth instead of these 3 letter variables names

Copy link
Member Author

Choose a reason for hiding this comment

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

done

Effectively makes it a value class which doesn't need to be mocked. Just
pass mocks into the UCS instead.
Keeping as much code as possible out of the Impl file is good practice
I'm looking at my console right now, and I see a black run with no issues,
and then the current black run with this problem. I don't understand why
black didn't catch this the first time.
It's needed in a couple of places, most notably EE2RunJob
Copy link
Collaborator

@bio-boris bio-boris left a comment

Choose a reason for hiding this comment

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

Created new issues, questions answered, LGTM

@MrCreosote
Copy link
Member Author

Travis CI should be deleted per Boris

@MrCreosote MrCreosote merged commit de6cbab into dev-require_resources Feb 4, 2021
bio-boris added a commit that referenced this pull request Feb 4, 2021
* Remove cruft.py (#279)

I suspect it may contain some cruft.

* Correct spec, improve documentation (#284)

* Remove cruft.py

I suspect it may contain some cruft.

* Correct spec, improve documentation

Corrected / improved spec and spec documentation for date range methods

* Fix spec

typo. Also clarify what needs to be done to run tests after making code
changes

* make WorkspaceAuth and authstrategy unit-testable (#288)

* Remove cruft.py

I suspect it may contain some cruft.

* Correct spec, improve documentation

Corrected / improved spec and spec documentation for date range methods

* Fix spec

typo. Also clarify what needs to be done to run tests after making code
changes

* make WorkspaceAuth and authstrategy unit-testable

Inject dependencies so they can be mocked.

* Fix flake8

* python formatting rules are stupid

* more flake8 fixes

really, flake8? really?

* Make token and user id required for sdkmr (#294)

Always provided, so no reason to be optional, and makes the code easier to understand.

* pass a UserClientSet into SDKMethodRunner (#296)

* pass a UserClientSet into SDKMethodRunner

UCS contains the clients that need to be instantiated on a per user basis.
UCS is passed in to SDKMR to allow for eventual unit testing of SDKMR,
since the UCS and workspace client can be mocked via create_autospec

* remove extraneous period

Co-authored-by: MrCreosote <[email protected]>
Co-authored-by: Gavin <[email protected]>
@MrCreosote MrCreosote deleted the dev-user-client-set branch February 8, 2021 03:39
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.

2 participants