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

feat: WIP Github Oauth integration #159

Draft
wants to merge 38 commits into
base: main
Choose a base branch
from

Conversation

alejandrosame
Copy link
Collaborator

No description provided.

logger = logging.getLogger(__name__)


def get_gh_username(user: User) -> str | None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

How the caching is supposed to work? Or is it that everytime we hit those tags, we run a query to GitHub API?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Right now there's no caching, and that's a problem of course. The tags are going away, they just allowed me to quickly work with the Github calls.

My plan is to move the logic to map Github memberships to Django's group membership and do authorization from there. These mappings would act as a cache and then the question will be how often do we want to refresh them.

Copy link
Collaborator

Choose a reason for hiding this comment

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

In that case, it must be very explicit what is the upperbound on the refresh for the authorizations and I suggest to have an systemd timer to refresh them every X and let user figure out what they want.

logger = logging.getLogger(__name__)


def get_team_member_ids(orgname: str, teamname: str) -> set[int]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any reason not to use @functools.lru_cache with an appropriate size?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

When I was mentioning caching I was thinking about database caching and getting the info from there, but I see that you meant memoization.

I'll implement it with functools decorators. I think to make it work the memoized function will have to be triggered by a signal so it's always executed by the running webserver process, otherwise the command will not profit from the memoized cache.

Then the systemd timer can call this command by invalidating first the cache.

As an alternative to the systemd timer, I saw that cachedtools has timed versions of functools caches with TTLCache. I haven't read the implementation yet to see the tradeoffs, and it would also mean configuring the TTL at settings level, which seems less flexible that changing the refresh timer.

social_user.socialaccount_set.filter(provider="github").first() # type: ignore
)
if social_account:
return social_account.extra_data.get("login") # type: ignore
Copy link
Collaborator

Choose a reason for hiding this comment

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

Sounds like what you care about is .values_list('extra_data__login') or something like that, no? You could get rid of the many attributes that could get passed via the SQL query.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Doing

def get_gh_username(user: User) -> str | None:
    """
    Return the Github username of a given Auth.User.
    """
    social_user = User.objects.get(id=user.id)  # type: ignore
    try: 
        extra_login: str = (
            social_user.socialaccount_set.filter(provider="github")
                .values_list('extra_data__login', flat=True)[0]
        )
        return extra_login
    except Exception:
        logger.exception("Failed to get GitHub username for user %s.", user)
        return None

Gives me

django.core.exceptions.FieldError: Unsupported lookup 'login' for JSONField or join on the field not permitted.

For now I'll keep the extra_data.get("login") and give this another spin later.

)
self.assertContains(redirect_response, "1 nixpkgs issue", status_code=200)

def test_committer_cannot_add_non_related_issue_from_admin_site(self) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
def test_committer_cannot_add_non_related_issue_from_admin_site(self) -> None:
def test_maintainer_cannot_add_non_related_issue_from_admin_site(self) -> None:

This only tests what maintainers can do

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also there seems to be no test for actual committers.

Comment on lines +20 to +29
@lru_cache(maxsize=1)
def get_gh_organization(orgname: str) -> Organization | None:
"""
Return the Github Organization instance given an organization name.
"""
try:
return github.get_organization(login=orgname)
except Exception:
logger.exception("Failed to get organization %s", orgname)
return None
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is not needed as we're only caching two teams, so it's at most two calls to get_organization for an application lifetime.

"""
Return whether a given username is a member of a Github team
"""
gh_named_user: NamedUser = cast(NamedUser, github.get_user(login=username))
Copy link
Collaborator

Choose a reason for hiding this comment

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

We can use get_user_by_id here, which returns a NamedUser

logger = logging.getLogger(__name__)


class Command(BaseCommand):
Copy link
Collaborator

Choose a reason for hiding this comment

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

We probably rather want to set up a webhook on team membership events

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.

None yet

3 participants