Skip to content

Commit

Permalink
[DEV-1234] Forward account IDs from the top-level handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
vmarkovtsev committed Nov 26, 2020
1 parent 2ce2eff commit 745acea
Show file tree
Hide file tree
Showing 58 changed files with 907 additions and 664 deletions.
1 change: 1 addition & 0 deletions DEPLOYMENT.md
Expand Up @@ -17,6 +17,7 @@ docker run -it --rm --entrypoint python3 athenian/api -m athenian.api.models.sta
The server requires:

- (optional) `SENTRY_KEY`, `SENTRY_PROJECT` and `SENTRY_ENV` environment variables to enable error logging.
- (optional) `ATHENIAN_MAX_CLIENT_SIZE` to limit the maximum request body size (256KB by default).
- `AUTH0_DOMAIN`, `AUTH0_AUDIENCE`, `AUTH0_CLIENT_ID`, `AUTH0_CLIENT_SECRET` environment variables to enable authorization.
- `ATHENIAN_DEFAULT_USER` environment variable that points to the Auth0 user ID used for unauthorized public requests.
- `ATHENIAN_INVITATION_KEY` environment variable with the passphrase for encrypting invitation URLs.
Expand Down
372 changes: 10 additions & 362 deletions server/athenian/api/__init__.py

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion server/athenian/api/auth.py
Expand Up @@ -24,6 +24,7 @@

from athenian.api.async_utils import gather
from athenian.api.cache import cached
from athenian.api.controllers.account import get_user_account_status
from athenian.api.kms import AthenianKMS
from athenian.api.models.state.models import God, UserToken
from athenian.api.models.web import GenericError
Expand Down Expand Up @@ -197,7 +198,12 @@ async def wrapper(request: ConnexionRequest):
token_info = get_authorization_info(auth_funcs, request, required_scopes)
# token_info = {"token": <token>, "method": "bearer" or "apikey"}
await self._set_user(request.context, **token_info)
# nothing important afterward, finish the auth processing
# check whether the user may access the specified account
if request.json and (account := request.json.get("account")) is not None:
assert isinstance(account, int)
await get_user_account_status(
request.context.uid, account, request.context.sdb, request.context.cache)
# finish the auth processing and chain forward
return await function(request)
return wrapper

Expand Down
416 changes: 416 additions & 0 deletions server/athenian/api/connexion.py

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions server/athenian/api/controllers/account.py
Expand Up @@ -54,11 +54,11 @@ async def get_metadata_account_ids(account: int,
)
async def get_user_account_status(user: str,
account: int,
sdb_conn: DatabaseLike,
sdb: DatabaseLike,
cache: Optional[aiomcache.Client],
) -> bool:
"""Return the value indicating whether the given user is an admin of the given account."""
status = await sdb_conn.fetch_val(
status = await sdb.fetch_val(
select([UserAccount.is_admin])
.where(and_(UserAccount.user_id == user, UserAccount.account_id == account)))
if status is None:
Expand Down
12 changes: 9 additions & 3 deletions server/athenian/api/controllers/contributors_controller.py
@@ -1,7 +1,8 @@
from aiohttp import web
from sqlalchemy import and_, select

from athenian.api.controllers.account import get_account_repositories
from athenian.api.async_utils import gather
from athenian.api.controllers.account import get_account_repositories, get_metadata_account_ids
from athenian.api.controllers.miners.github.contributors import mine_contributors
from athenian.api.controllers.settings import Settings
from athenian.api.models.metadata import PREFIXES
Expand All @@ -26,12 +27,17 @@ async def get_contributors(request: AthenianWebRequest, id: int) -> web.Response
"is not a member."
)
return ResponseError(NotFoundError(detail=err_detail)).response
repos = await get_account_repositories(id, sdb_conn)
tasks = [
get_account_repositories(id, sdb_conn),
# not sdb_conn! we must go parallel
get_metadata_account_ids(id, request.sdb, request.cache),
]
repos, meta_ids = await gather(*tasks)
release_settings = \
await Settings.from_request(request, account_id).list_release_matches(repos)
repos = [r.split("/", 1)[1] for r in repos]
users = await mine_contributors(
repos, None, None, False, [], release_settings,
meta_ids, repos, None, None, False, [], release_settings,
request.mdb, request.pdb, request.cache)
prefix = PREFIXES["github"]
contributors = [
Expand Down
2 changes: 1 addition & 1 deletion server/athenian/api/controllers/datetime_utils.py
@@ -1,8 +1,8 @@
from datetime import date, datetime, timedelta, timezone
from typing import List, Optional, Tuple, Union

from athenian.api import ResponseError
from athenian.api.models.web import Granularity, InvalidRequestError
from athenian.api.response import ResponseError


def coarsen_time_interval(time_from: datetime, time_to: datetime) -> Tuple[date, date]:
Expand Down
42 changes: 27 additions & 15 deletions server/athenian/api/controllers/features/entries.py
Expand Up @@ -70,7 +70,8 @@
),
version=2,
)
async def calc_pull_request_facts_github(time_from: datetime,
async def calc_pull_request_facts_github(accounts: Tuple[int, ...],
time_from: datetime,
time_to: datetime,
repositories: Set[str],
participants: PRParticipants,
Expand All @@ -86,6 +87,8 @@ async def calc_pull_request_facts_github(time_from: datetime,
"""
Calculate facts about pull request on GitHub.
:param account: Metadata (GitHub) account IDs (*not the state DB account*) that own the repos.
:param exclude_inactive: Do not load PRs without events between `time_from` and `time_to`.
:param fresh: If the number of done PRs for the time period and filters exceeds \
`unfresh_mode_threshold`, force querying mdb instead of pdb only.
:return: Map repository name -> list of PR facts.
Expand Down Expand Up @@ -205,7 +208,8 @@ async def calc_pull_request_facts_github(time_from: datetime,
release_settings,
),
)
async def calc_pull_request_metrics_line_github(metrics: Sequence[str],
async def calc_pull_request_metrics_line_github(accounts: Tuple[int, ...],
metrics: Sequence[str],
time_intervals: Sequence[Sequence[datetime]],
quantiles: Sequence[float],
lines: Sequence[int],
Expand All @@ -231,13 +235,14 @@ async def calc_pull_request_metrics_line_github(metrics: Sequence[str],
metrics, quantiles, lines, exclude_inactive=exclude_inactive)
time_from, time_to = time_intervals[0][0], time_intervals[0][-1]
mined_facts = await calc_pull_request_facts_github(
time_from, time_to, all_repositories, participants, labels, jira, exclude_inactive,
release_settings, fresh, mdb, pdb, cache)
accounts, time_from, time_to, all_repositories, participants, labels, jira,
exclude_inactive, release_settings, fresh, mdb, pdb, cache)
return calc(mined_facts, time_intervals, repositories)


@sentry_span
async def calc_code_metrics_github(prop: FilterCommitsProperty,
async def calc_code_metrics_github(accounts: Tuple[int, ...],
prop: FilterCommitsProperty,
time_intervals: Sequence[datetime],
repos: Collection[str],
with_author: Optional[Collection[str]],
Expand All @@ -248,10 +253,11 @@ async def calc_code_metrics_github(prop: FilterCommitsProperty,
"""Filter code pushed on GitHub according to the specified criteria."""
time_from, time_to = time_intervals[0], time_intervals[-1]
x_commits = await extract_commits(
prop, time_from, time_to, repos, with_author, with_committer, db, cache)
accounts, prop, time_from, time_to, repos, with_author, with_committer, db, cache)
all_commits = await extract_commits(
FilterCommitsProperty.NO_PR_MERGES, time_from, time_to, repos, with_author, with_committer,
db, cache, columns=[PushCommit.committed_date, PushCommit.additions, PushCommit.deletions])
accounts, FilterCommitsProperty.NO_PR_MERGES, time_from, time_to, repos,
with_author, with_committer, db, cache,
columns=[PushCommit.committed_date, PushCommit.additions, PushCommit.deletions])
return calc_code_stats(x_commits, all_commits, time_intervals)


Expand All @@ -272,7 +278,8 @@ async def calc_code_metrics_github(prop: FilterCommitsProperty,
release_settings,
),
)
async def calc_pull_request_histogram_github(defs: Dict[HistogramParameters, List[str]],
async def calc_pull_request_histogram_github(accounts: Tuple[int, ...],
defs: Dict[HistogramParameters, List[str]],
time_from: datetime,
time_to: datetime,
quantiles: Sequence[float],
Expand All @@ -288,15 +295,19 @@ async def calc_pull_request_histogram_github(defs: Dict[HistogramParameters, Lis
pdb: Database,
cache: Optional[aiomcache.Client],
) -> List[List[Tuple[str, Histogram]]]:
"""Calculate the pull request histograms on GitHub."""
"""
Calculate the pull request histograms on GitHub.
:return: len(defs) x len(repositories) x (metric ID, Histogram).
"""
all_repositories = set(chain.from_iterable(repositories))
try:
calc = PullRequestBinnedHistogramCalculator(defs.values(), quantiles, lines)
except KeyError as e:
raise ValueError("Unsupported metric: %s" % e)
mined_facts = await calc_pull_request_facts_github(
time_from, time_to, all_repositories, participants, labels, jira, exclude_inactive,
release_settings, fresh, mdb, pdb, cache)
accounts, time_from, time_to, all_repositories, participants, labels, jira,
exclude_inactive, release_settings, fresh, mdb, pdb, cache)
hists = calc(mined_facts, [[time_from, time_to]], repositories, [k.__dict__ for k in defs])
result = [[] for _ in range(len(repositories) * (len(lines or [None] * 2) - 1))]
for defs_hists, metrics in zip(hists, defs.values()):
Expand All @@ -323,7 +334,8 @@ async def calc_pull_request_histogram_github(defs: Dict[HistogramParameters, Lis
release_settings,
),
)
async def calc_release_metrics_line_github(metrics: Sequence[str],
async def calc_release_metrics_line_github(accounts: Tuple[int, ...],
metrics: Sequence[str],
time_intervals: Sequence[Sequence[datetime]],
quantiles: Sequence[float],
repositories: Sequence[Collection[str]],
Expand All @@ -342,8 +354,8 @@ async def calc_release_metrics_line_github(metrics: Sequence[str],
branches, default_branches = await extract_branches(all_repositories, mdb, cache)
all_participants = merge_release_participants(participants)
releases, _, matched_bys = await mine_releases(
all_repositories, all_participants, branches, default_branches, time_from, time_to, jira,
release_settings, mdb, pdb, cache)
accounts, all_repositories, all_participants, branches, default_branches,
time_from, time_to, jira, release_settings, mdb, pdb, cache)
mined_facts = defaultdict(list)
for i, f in releases:
mined_facts[i[Release.repository_full_name.key].split("/", 1)[1]].append(f)
Expand Down
Expand Up @@ -355,7 +355,8 @@ def __iter__(self) -> Generator[PullRequestListItem, None, None]:


@sentry_span
async def filter_pull_requests(events: Set[PullRequestEvent],
async def filter_pull_requests(accounts: Tuple[int, ...],
events: Set[PullRequestEvent],
stages: Set[PullRequestStage],
time_from: datetime,
time_to: datetime,
Expand Down

0 comments on commit 745acea

Please sign in to comment.