From eda7153a130042dc24ba6526018d9ddd0b1a61b5 Mon Sep 17 00:00:00 2001 From: Samad Yar Khan Date: Tue, 23 Apr 2024 20:22:11 +0530 Subject: [PATCH 1/5] Add patch_team_repos_mapping to CodeRepoService. --- backend/analytics_server/mhq/store/repos/code.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/backend/analytics_server/mhq/store/repos/code.py b/backend/analytics_server/mhq/store/repos/code.py index 3427d72c9..430bbb4f6 100644 --- a/backend/analytics_server/mhq/store/repos/code.py +++ b/backend/analytics_server/mhq/store/repos/code.py @@ -75,6 +75,17 @@ def update_team_repos(self, team: Team, org_repos: List[OrgRepo]): self._db.session.commit() + @rollback_on_exc + def patch_team_repos_mapping( + self, team: Team, team_repos: List[TeamRepos] + ) -> List[TeamRepos]: + [self._db.session.merge(team_repo) for team_repo in team_repos] + self._db.session.commit() + team_repo_ids = [str(team_repo.org_repo_id) for team_repo in team_repos] + return self._db.session.query(TeamRepos).filter( + TeamRepos.team_id == team.id, TeamRepos.org_repo_id.in_(team_repo_ids) + ) + @rollback_on_exc def get_org_repos_used_across_teams(self, org_id: str) -> List[OrgRepo]: """ From 44e7345442ecb7341dedb9218bd5dabfa2581da6 Mon Sep 17 00:00:00 2001 From: Samad Yar Khan Date: Tue, 23 Apr 2024 20:22:49 +0530 Subject: [PATCH 2/5] Add patch_team_repos_mapping to RepositoryService. --- .../mhq/service/code/repository_service.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/backend/analytics_server/mhq/service/code/repository_service.py b/backend/analytics_server/mhq/service/code/repository_service.py index e2eedbb78..095dc17bd 100644 --- a/backend/analytics_server/mhq/service/code/repository_service.py +++ b/backend/analytics_server/mhq/service/code/repository_service.py @@ -7,7 +7,7 @@ from mhq.service.code.models.org_repo import RawOrgRepo from mhq.store.models.code import OrgRepo from mhq.store.models.core import Team -from mhq.store.repos.code import CodeRepoService +from mhq.store.repos.code import CodeRepoService, TeamRepos class RepositoryService: @@ -100,6 +100,25 @@ def update_org_repos( return self._code_repo_service.update_org_repos(updated_org_repos) + def patch_team_repos_mapping(self, team: Team, team_repos: List[TeamRepos]): + + existing_team_repos = self.get_team_repos(team) + + existing_team_repo_ids = set([str(repo.id) for repo in existing_team_repos]) + + team_repo_ids = set([str(repo.org_repo_id) for repo in team_repos]) + + team_repos_mappings_not_in_db = list( + team_repo_ids.difference(existing_team_repo_ids) + ) + + if team_repos_mappings_not_in_db: + raise Exception( + f"Team Repo Mappings does not exist for team: {str(team.id)} and repos {team_repos_mappings_not_in_db}" + ) + + return self._code_repo_service.patch_team_repos_mapping(team, team_repos) + def _update_team_incident_services(self, team: Team, org_repos: List[OrgRepo]): incident_services = self._update_org_incident_services(team.org_id, org_repos) From f17b129ddde32b6044a1a3f9724b486a7aeb1f7b Mon Sep 17 00:00:00 2001 From: Samad Yar Khan Date: Tue, 23 Apr 2024 20:23:33 +0530 Subject: [PATCH 3/5] Coerce method for TeamRepos --- .../analytics_server/mhq/api/request_utils.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/backend/analytics_server/mhq/api/request_utils.py b/backend/analytics_server/mhq/api/request_utils.py index ed9475595..13dcf014c 100644 --- a/backend/analytics_server/mhq/api/request_utils.py +++ b/backend/analytics_server/mhq/api/request_utils.py @@ -1,11 +1,14 @@ from functools import wraps from typing import Any, Dict, List from uuid import UUID +from datetime import datetime from flask import request from stringcase import snakecase from voluptuous import Invalid from werkzeug.exceptions import BadRequest +from mhq.utils.time import time_now +from mhq.store.models.code.repository import TeamRepos from mhq.service.code.models.org_repo import RawOrgRepo from mhq.store.models.code import WorkflowFilter, CodeProvider @@ -92,3 +95,20 @@ def coerce_org_repo(repo: Dict[str, str]) -> RawOrgRepo: def coerce_org_repos(repos: List[Dict[str, str]]) -> List[RawOrgRepo]: return [coerce_org_repo(repo) for repo in repos] + + +def coerce_team_repo(team_repo: Dict[str, str]) -> TeamRepos: + + assert uuid_validator(team_repo.get("org_repo_id")) + + return TeamRepos( + team_id=team_repo["team_id"], + org_repo_id=team_repo["org_repo_id"], + prod_branch=team_repo.get("prod_branch"), + prod_branches=team_repo.get("prod_branches"), + is_active=team_repo.get("is_active", True), + ) + + +def coerce_team_repos(repos: List[Dict[str, str]]) -> List[TeamRepos]: + return [coerce_team_repo(repo) for repo in repos] From e2718b11012fce7df10ff154ce889d7eb2d15949 Mon Sep 17 00:00:00 2001 From: Samad Yar Khan Date: Tue, 23 Apr 2024 20:24:06 +0530 Subject: [PATCH 4/5] Add adpater for team repos: adapt_team_repos. --- .../mhq/api/resources/code_resouces.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/backend/analytics_server/mhq/api/resources/code_resouces.py b/backend/analytics_server/mhq/api/resources/code_resouces.py index 00684d6f0..7685c065a 100644 --- a/backend/analytics_server/mhq/api/resources/code_resouces.py +++ b/backend/analytics_server/mhq/api/resources/code_resouces.py @@ -1,7 +1,7 @@ from typing import Dict, List from mhq.service.code.models.lead_time import LeadTimeMetrics from mhq.api.resources.core_resources import adapt_user_info -from mhq.store.models.code import PullRequest, OrgRepo +from mhq.store.models.code import PullRequest, OrgRepo, TeamRepos from mhq.store.models.core import Users @@ -123,3 +123,18 @@ def adapt_org_repo(org_repo: OrgRepo) -> Dict[str, any]: "created_at": org_repo.created_at.isoformat(), "updated_at": org_repo.updated_at.isoformat(), } + + +def adapt_team_repos(team_repos: List[TeamRepos]) -> List[Dict[str, any]]: + return [ + { + "team_id": str(team_repo.team_id), + "org_repo_id": str(team_repo.org_repo_id), + "prod_branch": team_repo.prod_branch, + "prod_branches": team_repo.prod_branches, + "is_active": team_repo.is_active, + "created_at": team_repo.created_at.isoformat(), + "updated_at": team_repo.updated_at.isoformat(), + } + for team_repo in team_repos + ] From 77f01a3245b63e9f6ac67fe91bf4a0d1f197df3e Mon Sep 17 00:00:00 2001 From: Samad Yar Khan Date: Tue, 23 Apr 2024 20:24:49 +0530 Subject: [PATCH 5/5] Add API for TeamRepos mapping crud --- backend/analytics_server/mhq/api/teams.py | 30 +++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/backend/analytics_server/mhq/api/teams.py b/backend/analytics_server/mhq/api/teams.py index 35e873926..bcfa00d63 100644 --- a/backend/analytics_server/mhq/api/teams.py +++ b/backend/analytics_server/mhq/api/teams.py @@ -1,14 +1,16 @@ from flask import Blueprint from typing import Any, Dict, List from voluptuous import Required, Schema, Optional, All, Coerce +from werkzeug.exceptions import BadRequest +from mhq.store.models.code.repository import TeamRepos from mhq.service.code.models.org_repo import RawOrgRepo -from mhq.api.resources.code_resouces import adapt_org_repo +from mhq.api.resources.code_resouces import adapt_org_repo, adapt_team_repos from mhq.service.code.repository_service import get_repository_service from mhq.api.resources.core_resources import adapt_team from mhq.store.models.core.teams import Team from mhq.service.core.teams import get_team_service -from mhq.api.request_utils import coerce_org_repos, dataschema +from mhq.api.request_utils import coerce_org_repos, coerce_team_repos, dataschema from mhq.service.query_validator import get_query_validator app = Blueprint("teams", __name__) @@ -109,3 +111,27 @@ def update_team_repos(team_id: str, repos: List[RawOrgRepo]): team_repos = get_repository_service().update_team_repos(team, repos) return [adapt_org_repo(repo) for repo in team_repos] + + +@app.route("/teams//team_repos", methods={"PATCH"}) +@dataschema( + Schema( + { + Required("team_repos_data"): All(list, Coerce(coerce_team_repos)), + } + ), +) +def patch_team_repos_mapping(team_id: str, team_repos_data: List[TeamRepos]): + + query_validator = get_query_validator() + team = query_validator.team_validator(team_id) + + for team_repo in team_repos_data: + if team_repo.team_id != team_id: + raise BadRequest( + f"Team Repo with repo_id: {team_repo.org_repo_id} team_id: {team_repo.team_id} does not match team in request url: {team_id}." + ) + + team_repos_service = get_repository_service() + team_repos = team_repos_service.patch_team_repos_mapping(team, team_repos_data) + return adapt_team_repos(team_repos)