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

Draft design #18

Open
jooola opened this issue Mar 22, 2020 · 4 comments
Open

Draft design #18

jooola opened this issue Mar 22, 2020 · 4 comments

Comments

@jooola
Copy link
Collaborator

@jooola jooola commented Mar 22, 2020

In order to share a common idea on the direction of this package, we should draft/discuss on the design of this package.

Some details are bothering me with the current implementation:

  • We do not use the power of the requests.Session object
  • We are heading toward a huge god object wrapping all the api calls, we could avoid this using some refactor / mixins / dataclasses shared arguments.
  • We should require strict type hinting on the public facing api definition.
  • We should split the package in multiple api ressource object, in order to simplify any future contribution (goes with the god object problem)

What should we implement first, what should be open for contribution ?

@vvatelot Any thoughts on this one ?

@vvatelot
Copy link
Contributor

@vvatelot vvatelot commented Mar 23, 2020

Yes, we can use requests.Session that seems really fitting our needs here.

I like Mixins pattern to split "modules" into separated files. I make a proposal this way.

In my opinion, we can try to refact some common parameters that we get in requests (pagination, offset, limit... One dataclass? fields, sort, filter, query... One dataclass?).

I think we should try to have a consistent way to deal with input / output objects -> I make a GET request to retrieve a collection, I get an Item object. I want to update this Item object, I update the object itself, then make the PATCH request. I want to delete this object, I make a DELETE request with this Object...

My guess: Start first with modules split (Mixins), then refact common parameters, then deal with consistency

@jooola
Copy link
Collaborator Author

@jooola jooola commented Mar 23, 2020

I like Mixins pattern to split "modules" into separated files. I make a proposal this way.

Here are 2 ways I would see this:

  1. Mixin approach
    • Define a abstract base API class that provide everything we need to build each endpoints such a the requests.session object, some helpers (base_url)
    • Each API Mixins (Can be run individually for testing) are derived from this base API and each call uses the previously defined session object.
    • Build a entry-point that wraps all our mixins and might redefine vars such as project or auth which is created at Client creation.
  2. REST Object approach (since we work with a REST api)
    • Define a base REST Object, with common list(), get(), create(), update(), delete()
    • Each API object represent a RESTobject and any additional custom request are implement here
    • The entry point embed all the RESTObjects (might be factories so we don't create each objects at Client creation) and we can browse the SDK like this: client.collections().list(...)

Maybe both strategies can be used together ?

In my opinion, we can try to refact some common parameters that we get in requests (pagination, offset, limit... One dataclass? fields, sort, filter, query... One dataclass?).

Yes, this is how I was thinking it too. Most api call will only be really simple, so we can wraps some args that serves a specific purpose in dataclasses.

I already started to work on this part, but we might need to think a bit more before coding.

@dataclass
class Pagination:
    limit: int = 100
    offset: int = 0
    page: Optional[int] = None

    def __dict__(self):
        result = {"limit": self.limit}
        if self.page:
            result.update({"page": self.page})
        else:
            result.update({"offset": self.offset})
        return result


# Here is an API call with Pagination
def list(self, pagination: Pagination = None):
	pass

I say this https://www.python.org/dev/peps/pep-0589/#class-based-syntax, but sadly it is really recent, so we would have to drop support for 3.7 which is not acceptable for now. We might move to this once 3.8 is the most used version across all distributions. But the above code should work nicely.

@jooola
Copy link
Collaborator Author

@jooola jooola commented Mar 24, 2020

@vvatelot Here is a small snapshot of what I had in mind regarding solution 1:

https://github.com/directus/sdk-python/tree/rewrite

@vvatelot
Copy link
Contributor

@vvatelot vvatelot commented Mar 30, 2020

@vvatelot Here is a small snapshot of what I had in mind regarding solution 1:

https://github.com/directus/sdk-python/tree/rewrite

I like this approach. This is really straightforward, clean and simple.
The only thing I regret is that you lose the ease of use of auto completion in the IDE while using TypedDict or dataclass. But it is a detail, and I think we have to focus mainly on maintanability. So having a generic solution with an abstract API Class and Mixins is great.
Why not naming Mixins class like SomethingMixin?
TypedDict is only supported from python 3.8, but we can use dataclass instead? It is kind of the same?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.