Skip to content

feat(apiclient): add CRUD methods using cattrs #133

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

Merged
merged 1 commit into from
Dec 23, 2021
Merged

Conversation

gegnew
Copy link
Contributor

@gegnew gegnew commented Dec 14, 2021

  • feat(apiclient): add CRUD methods for instantiated entities

Depends on #132

@gegnew gegnew changed the title feat(apiclient): add CRUD methods for entities feat(apiclient): add CRUD methods using cattrs Dec 14, 2021
@gegnew gegnew force-pushed the ge/cattrs2 branch 3 times, most recently from d6c3a5e to 11bf3c4 Compare December 14, 2021 12:20
@gegnew gegnew requested a review from zbjornson December 14, 2021 13:38
@gegnew gegnew changed the base branch from master to ge/cattrs1 December 14, 2021 14:28
Base automatically changed from ge/cattrs1 to master December 14, 2021 16:49
Copy link
Member

@zbjornson zbjornson left a comment

Choose a reason for hiding this comment

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

Two easy suggestions and two questions. Otherwise looks good.

@gegnew gegnew force-pushed the ge/cattrs2 branch 4 times, most recently from b24cd7b to 46b80e8 Compare December 14, 2021 22:25
@gegnew gegnew requested a review from zbjornson December 14, 2021 22:29
Copy link
Member

@zbjornson zbjornson left a comment

Choose a reason for hiding this comment

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

Can you also rebase on master to drop the commits that are already merged, please?

Comment on lines 183 to 196
body = [unstructure_and_clean(thing) for thing in entity]
# group [(class), (path), (body)]
unzipped = list(zip(*body))
# remove duplicates to ensure all entities are of the same type
(cls, path, body) = (set(unzipped[0]), set(unzipped[1]), unzipped[2])

if len(cls) == 1 and len(path) == 1:
# then post to bulk endpoint
res = post_and_restructure(
List[cls.pop()], path.pop(), list(body) # type: ignore
)
else:
# post each item individually
res = [post_and_restructure(*thing) for thing in body]
Copy link
Member

Choose a reason for hiding this comment

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

Sorry to drag this out. This still seems a bit overly complicated, but maybe it's because I don't know Python well. Ideas:

  • Group entity's elements by class, then iterate over that loop even if it's length-1. Think that avoids the if/else here and avoids ever iterating over each item.

  • Have an explicit create_many method and throw if all entities are not gates. That's the only bulk creation API that we have anyway. Otherwise it's just as easy for the user to call create in a loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Went with option 1; let me know what you think

Copy link
Member

Choose a reason for hiding this comment

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

Bug noted in comment below. I like explicit and simple code so generally lean toward the second option, but am fine with the first if it's tidy.

To make sure I understand: this "create many" logic is only used for creating multiple gates, right?

Copy link
Member

Choose a reason for hiding this comment

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

One more consideration as far as preparing for the future: we have bulk APIs for updating multiple FCS files and deleting multiple FCS files also. That makes me think (a) explicit verb_many functions are the way to go and (b) these local functions could be moved to file-level functions and likely reused when updating multiple FCS files, at least.


if isinstance(entity, list):
body = [unstructure_and_clean(thing) for thing in entity]
things = defaultdict(list)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this works. list is an argument to the function, but here you're intending to use the list class.

(Needs tests!)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is how you're supposed to create a defaultdict, in this case where a non-present key will return a default []

Copy link
Contributor Author

@gegnew gegnew Dec 16, 2021

Choose a reason for hiding this comment

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

This is tested in test_gates.py, but actually only in the ge/cattrs-gates branch (since this is only really relevant to gates). Tests aren't really possible until the changes in ge/cattrs-<entity type> land. However, each of those branches do have tests for this method.

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 just pushed a change that uses create_many at a file-level; I'm not sure it's necessarily better, but it also works 😄

Copy link
Member

Choose a reason for hiding this comment

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

This is how you're supposed to create a defaultdict, in this case where a non-present key will return a default []

I'm confused...

>>> from collections import defaultdict
>>> x = [1,2]
>>> defaultdict(list)
defaultdict(<class 'list'>, {})
>>> defaultdict(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: first argument must be callable or None

Moot question now though since that code is gone.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

defaultdict wants a callable, not an instantiated class; this is because it contains a __missing__() method, which is called if you request a key that doesn't exist. So defaultdict(list)['notAKey'] will return list()

https://docs.python.org/3/library/collections.html

@gegnew gegnew force-pushed the ge/cattrs2 branch 2 times, most recently from 8579276 to 7148f83 Compare December 16, 2021 11:30
@gegnew gegnew requested a review from zbjornson December 16, 2021 11:30
Copy link
Member

@zbjornson zbjornson left a comment

Choose a reason for hiding this comment

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

Looks good, just two types things.

Copy link
Member

@zbjornson zbjornson left a comment

Choose a reason for hiding this comment

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

Sorry for the delay, looks good.

@zbjornson zbjornson merged commit c0743c4 into master Dec 23, 2021
@zbjornson zbjornson deleted the ge/cattrs2 branch December 23, 2021 17:56
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