Skip to content

Add support for GPG keys #886

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
Aug 19, 2018
Merged

Conversation

jacquerie
Copy link
Collaborator

Implement the CRUD API for users' GPG keys described
in https://developer.github.com/v3/users/gpg_keys/.

Closes #704

if 'primary' in email:
self.primary = email['primary']
if 'visibility' in email:
self.visibility = email['visibility']
Copy link
Collaborator Author

@jacquerie jacquerie Aug 13, 2018

Choose a reason for hiding this comment

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

This is because the emails returned by the GPG key API only have the email and the verified attributes (see: https://developer.github.com/v3/users/gpg_keys/#response-2). An alternative implementation is to split the Email model into ShortEmail and Email; I chose the above solution for simplicity, but I'm open to suggestions!

Copy link
Owner

Choose a reason for hiding this comment

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

I'd rather consistency over simplicity. That means creating ShortEmail and Email classes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure thing! Will fix.

@jacquerie jacquerie changed the title Add support for GPG keys [WIP] Add support for GPG keys Aug 13, 2018
self.emails = [Email(email, self) for email in key['emails']]
try:
self.expires_at = self._strptime(key['expires_at'])
except ValueError:
Copy link
Owner

Choose a reason for hiding this comment

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

Is this not valid in practice?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What can happen here is that key['expires_at'] might be null, for example https://developer.github.com/v3/users/gpg_keys/#response-2. I'm not sure what github3.py usually does when a value might be null, I originally had self.expires_at = None...

Copy link
Owner

Choose a reason for hiding this comment

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

I thought that _strptime() handled that case automagically. If not, it should.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Whoops, you are right! See:

@classmethod
def _strptime(cls, time_str):
"""Convert an ISO 8601 formatted string to a datetime object.
We assume that the ISO 8601 formatted string is in UTC and we create
the datetime object so that it is timezone-aware.
:param str time_str: ISO 8601 formatted string
:returns: timezone-aware datetime object
:rtype: datetime or None
"""
if time_str:
return dateutil.parser.parse(time_str)
return None

I was probably thinking of datetime's strptime. Will fix this too!

if 'primary' in email:
self.primary = email['primary']
if 'visibility' in email:
self.visibility = email['visibility']
Copy link
Owner

Choose a reason for hiding this comment

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

I'd rather consistency over simplicity. That means creating ShortEmail and Email classes.

@jacquerie jacquerie force-pushed the add-gpg-keys branch 6 times, most recently from f46b683 to 9f08f95 Compare August 18, 2018 10:41
@jacquerie jacquerie changed the title [WIP] Add support for GPG keys Add support for GPG keys Aug 18, 2018
@jacquerie
Copy link
Collaborator Author

@sigmavirus24 I did what you asked in the review and I filled in the missing documentation TODOs. I think this is ready for another round of review!

Copy link
Owner

@sigmavirus24 sigmavirus24 left a comment

Choose a reason for hiding this comment

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

Just a couple small changes to the tests. I really need to write better/updated documentation for the testing in this library

self.token_login()
cassette_name = self.cassette_name('create_gpg_key')
with self.recorder.use_cassette(cassette_name):
gpg_key = self.gh.create_gpg_key(GPG_KEY)
Copy link
Owner

Choose a reason for hiding this comment

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

In the past we've been a bit sloppy. Going forward I've started writing tests like so:

def test_create...(self):
    self.token_login()
    cassette_name = self.cassette_name('...')
    with self.recorder.use_cassette(...):
        gpg_key = self.gh.create_gpg_key(GPG_KEY)
        assert isinstance(gpg_key, github3.users.GPGKey)
        assert gpg_key.delete()

This way there's no out-of-band, manual clean-up required. Then for the

def test_gpg_key(self):
    ...
    with ...:
        created_gpg_key = self.gh.create_gpg_key(...)
        assert isinstance(created_gpg_key, ...)
        retrieved_gpg_key = self.gh.gpg_key(created_gpg_key.id)
        assert isinstance(retrieved_gpg_key, ...)
        assert created_gpg_key == retrieved_gpg_key
        assert created_gpg_key.delete(0

And the same goes for the last method too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Uh, I like the idea of having each test responsible for its cleanup. Another benefit is that anybody can re-record the cassette if need arises. Done!

@@ -73,6 +88,17 @@ def test_following(self):
for person in following:
assert isinstance(person, github3.users.ShortUser)

def test_gpg_keys(self):
Copy link
Owner

Choose a reason for hiding this comment

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

👏

Copy link
Collaborator Author

@jacquerie jacquerie Aug 18, 2018

Choose a reason for hiding this comment

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

Err... I can't decipher this comment! Do you want me to delete this test? It makes sense to me: it shows that an unauthenticated user can retrieve another user and ask for their GPG keys.

Copy link
Owner

Choose a reason for hiding this comment

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

No, sorry. I was clapping as in "this is great"

self.token_login()
cassette_name = self.cassette_name('delete')
with self.recorder.use_cassette(cassette_name):
gpg_key = self.gh.gpg_key(407238)
Copy link
Owner

Choose a reason for hiding this comment

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

And this test can look a lot like the create_gpg_key test. It's good to have it anyway.

Implement the CRUD API for users' GPG keys described
in https://developer.github.com/v3/users/gpg_keys/.

Closes sigmavirus24#704
@sigmavirus24 sigmavirus24 merged commit 5eff8c6 into sigmavirus24:master Aug 19, 2018
@jacquerie jacquerie deleted the add-gpg-keys branch August 19, 2018 13:09
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