CSC/ECE 517 Spring 2020/P2000.Refactor pyrh oauth capabilities to use marshmallow
Introduction
This project contributed to an open-source Python project called pyrh. This is an unofficial python API for the Robinhood trading platform. It allows users to programmatically interact with their specific user accounts.
Pre-Development Documentation
Project Purpose
The goal of this project is to refactor the initial OAuth class that was created to leverage API best practices handling serialization/deserialization. An existing project called marshmallow handles the use case of the custom code that was initially written to manage serialization and deserialization. It would be prudent to adopt that package. Additionally, the internal code that passed around JSON dictionaries likely should use python object models which can more easily be manipulated allowing the business logic to be moved to the models. Additionally, this data model-based framework will make it easy to implement further refactors as more data models are introduced for other parts of the API besides auth.
Project Design
(Please note that this is a custom project and does not have an Expertiza project requirements)
In order to more properly follow object-oriented design practices, the large SessionManager class will be aided by data manager classes that encapsulate both the data and business logic associated with that data to cut on bloat. The *Schema classes will aid in the serialization/deserialization of those data model classes. The use of the existing python package "Marshmallow" will aid in that data serialization/validation process.
New classes:
- BaseModel: a model that any new model will inherit from, provides default data representation functionality and inherit from Python's SimpleNamespace
- BaseSchema: a Schema class that provides default functionality for serializing the object attributes that matter for a particular object
- Challenge: A model that represents the data returned by an OAuth Challenge from that designated endpoint
- ChallengeSchema: the schema and validations associated with the Challenge model
- OAuth: A model that represents the data returned by the OAuth token generation endpoint
- OAuthSchema: the schema and validations associated with the OAuth model
- SessionManager (existing): a class that implements functionality to interact with the Robinhood authorization endpoint API
- SessionManagerSchema: the schema associated with the SessionManager model
Each of the above classes serve a distinct purpose and are more clearly explained in the video linked below. At a high level, the Base models are designed to serve as convenience classes to gather convenience methods that will be used by all future models and schemas. Additionally, they interconnect in that BaseSchema automatically can create any model that is linked to it by the `__model__` class attribute. Next up the Challenge and OAuth objects are the first examples of classes that leverage the new serialization capabilities. They are integrated with SessionManager which previously manipulated the raw JSON itself. Now, the business logic to handle that is encapsulated away by the use of `marshmallow`.
The inheritance diagrams are listed below:
OAuth inheritance diagram
SessionManager inheritance diagram
Testing Plan
The testing plan is quite simple in this case and revolves around these key ideas:
- Test that none of the existing authentication functionality is broken
- Test that the new data models can be serialized/deserialized
- Test that the SessionManager class can be serialized to the custom data cache location
- Update tests to use FreezeGun where DateTime is required
In order to thoroughly test the project, the above points of interest were selected. Firstly, making sure that none of the authentication functionality that was initially implemented in the most recent relevant PR should not be broken as this PR merely tries to make the design of that initial PR a bit more expressive. Next, each of the new data models (OAuth, Challenge, and SessionManager) need to be tested to make sure that they are integrated with marshmallow and can hence be easily serialized and deserialized. Afterward, the testing should confirm that the core functionality of saving login credentials is not broken due to the new serialization scheme by making sure that SessionManager can be reliably recreated using the new scheme. Lastly, the testing framework should be updated so that convoluted mocking methods do not need to be used to deal with the realities of date-time objects in the API code. The use of freeze-gun, a Python package to mock date-time will be used to that end.
Post-Development Documentation
Code Modifications
Note: these are only the important files, for a full list of changes please see the GitHub Pull Request
- .github/workflows/main.yml
- Updated the listing process to install all dependencies so that static type checking can occur using mpy
- Fix cache paths in restore-key
- .pre-commit-config.yaml
- Bump pre-commit config python version
- Let mypy use locally installed version
- Add darglint to pre-commit
- pyproject.toml
- Add darglint for function signature and docstring agreement checking
- Add freezegun to allow for freezing of time when unit testing
- Make sure extras points at toolkit and not toml
- __init__.py
- Patch bug that depended on pyproject.toml being shipped in the wheels for versioning to work
- endpoints
- Use yarl for easy URL management
- exceptions
- refactor to make more pythonic and drop unused
- models folder
- refactor sessionmanager so that it now sits in the models folder
- Add a BaseModel: all models inherit from this and hence SimpleNamepsace
- Add BaseSchema: all schemas inherit from this and are hooked to a particular model
- Add OAuth and Challenge models and schemas to be used in SessionManager
- SessionManager
- Refactor to use new models
- add dump and load functions to allow convenient storing of credentials
- test_*.py
- Add relevant tests
Setting up the project to run the unit tests
Please see the linked documentation page on the installation the project and running tests. The specific command to pay attention to once installation is complete is `pytest` which runs all the tests. Running `coverage html` allows one to view the coverage.
Team Members
- Adithya Balaji