From 8c821d94397e9e69d8e6513cdfcdb9adf2d0fd83 Mon Sep 17 00:00:00 2001 From: Spencer Murray Date: Tue, 25 Mar 2025 09:32:12 -0400 Subject: [PATCH 1/4] Add bundle badge api --- graphs/urls.py | 10 +++++++ graphs/views.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/graphs/urls.py b/graphs/urls.py index bc0e0d45c8..3e926a40d5 100644 --- a/graphs/urls.py +++ b/graphs/urls.py @@ -13,6 +13,16 @@ BadgeHandler.as_view(), name="default-badge", ), + re_path( + "branch/(?P.+)/(graph|graphs)/bundle/badge.(?P[^/]+)", + BadgeHandler.as_view(), + "branch-bundle-badge", + ), + re_path( + "(graph|graphs)/bundle/badge.(?P[^/]+)", + BadgeHandler.as_view(), + "default-bundle-badge", + ), re_path( "pull/(?P[^/]+)/(graph|graphs)/(?Ptree|icicle|sunburst|commits).(?P[^/]+)", GraphHandler.as_view(), diff --git a/graphs/views.py b/graphs/views.py index 9eb9c52714..9209ce5378 100644 --- a/graphs/views.py +++ b/graphs/views.py @@ -13,10 +13,17 @@ from api.shared.mixins import RepoPropertyMixin from core.models import Branch, Pull +from graphql_api.dataloader.bundle_analysis import load_bundle_analysis_report from graphs.settings import settings +from services.bundle_analysis import BundleAnalysisReport from services.components import commit_components -from .helpers.badge import format_coverage_precision, get_badge +from .helpers.badge import ( + format_bundle_bytes, + format_coverage_precision, + get_badge, + get_bundle_badge, +) from .helpers.graphs import icicle, sunburst, tree from .mixins import GraphBadgeAPIMixin @@ -193,6 +200,72 @@ def component_coverage(self, component_identifier: str, commit: Commit): return filtered_report.totals.coverage +class BundleBadgeHandler(APIView, RepoPropertyMixin, GraphBadgeAPIMixin): + content_negotiation_class = IgnoreClientContentNegotiation + + permission_classes = [AllowAny] + + extensions = ["svg", "txt"] + precisions = ["0", "1", "2"] + filename = "bundle-badge" + + def get_object(self, request, *args, **kwargs): + # Validate coverage precision + precision = self.request.query_params.get("precision", "2") + if precision not in self.precisions: + raise NotFound("Bundle size precision should be one of [ 0 || 1 || 2 ]") + precision = int(precision) + + bundle_size_bytes = self.get_bundle_size() + + if self.kwargs.get("ext") == "txt": + return format_bundle_bytes(bundle_size_bytes, precision) + + return get_bundle_badge(bundle_size_bytes, precision) + + def get_bundle_size(self) -> int | None: + try: + repo = self.repo + except Http404: + log.warning("Repo not found", extra=dict(repo=self.kwargs.get("repo_name"))) + return None + + if repo.private and repo.image_token != self.request.query_params.get("token"): + log.warning( + "Token provided does not match repo's image token", + extra=dict(repo=repo), + ) + return None + + branch_name = self.kwargs.get("branch") or repo.branch + branch = Branch.objects.filter( + name=branch_name, repository_id=repo.repoid + ).first() + + if branch is None: + log.warning( + "Branch not found", extra=dict(branch_name=branch_name, repo=repo) + ) + return None + + try: + commit: Commit = repo.commits.filter(commitid=branch.head).first() + except ObjectDoesNotExist: + log.warning("Commit not found", extra=dict(commit=branch.head)) + return None + + bundle_report = load_bundle_analysis_report(commit) + + if not isinstance(bundle_report, BundleAnalysisReport): + log.warning( + "Bundle analysis report not found for commit", + extra=dict(commit=branch.head), + ) + return None + + return bundle_report.size_total + + class GraphHandler(APIView, RepoPropertyMixin, GraphBadgeAPIMixin): permission_classes = [AllowAny] From 7192a46f1805d149e89db965fd43a45c0829ed75 Mon Sep 17 00:00:00 2001 From: Spencer Murray Date: Tue, 25 Mar 2025 09:54:26 -0400 Subject: [PATCH 2/4] Use load_report instead of gql dataloader version --- graphs/views.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/graphs/views.py b/graphs/views.py index 9209ce5378..dd8e083b1f 100644 --- a/graphs/views.py +++ b/graphs/views.py @@ -13,9 +13,8 @@ from api.shared.mixins import RepoPropertyMixin from core.models import Branch, Pull -from graphql_api.dataloader.bundle_analysis import load_bundle_analysis_report from graphs.settings import settings -from services.bundle_analysis import BundleAnalysisReport +from services.bundle_analysis import BundleAnalysisReport, load_report from services.components import commit_components from .helpers.badge import ( @@ -254,15 +253,17 @@ def get_bundle_size(self) -> int | None: log.warning("Commit not found", extra=dict(commit=branch.head)) return None - bundle_report = load_bundle_analysis_report(commit) + shared_bundle_report = load_report(commit) - if not isinstance(bundle_report, BundleAnalysisReport): + if shared_bundle_report is None: log.warning( "Bundle analysis report not found for commit", extra=dict(commit=branch.head), ) return None + bundle_report = BundleAnalysisReport(shared_bundle_report) + return bundle_report.size_total From 1692987af724eb770dc608529d4b3bf4c3bed03f Mon Sep 17 00:00:00 2001 From: Spencer Murray Date: Tue, 25 Mar 2025 10:52:19 -0400 Subject: [PATCH 3/4] Fix urls --- graphs/urls.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/graphs/urls.py b/graphs/urls.py index 3e926a40d5..d79480baea 100644 --- a/graphs/urls.py +++ b/graphs/urls.py @@ -1,6 +1,6 @@ from django.urls import re_path -from .views import BadgeHandler, GraphHandler +from .views import BadgeHandler, BundleBadgeHandler, GraphHandler urlpatterns = [ re_path( @@ -15,13 +15,13 @@ ), re_path( "branch/(?P.+)/(graph|graphs)/bundle/badge.(?P[^/]+)", - BadgeHandler.as_view(), - "branch-bundle-badge", + BundleBadgeHandler.as_view(), + name="branch-bundle-badge", ), re_path( "(graph|graphs)/bundle/badge.(?P[^/]+)", - BadgeHandler.as_view(), - "default-bundle-badge", + BundleBadgeHandler.as_view(), + name="default-bundle-badge", ), re_path( "pull/(?P[^/]+)/(graph|graphs)/(?Ptree|icicle|sunburst|commits).(?P[^/]+)", From 6f4df0bda4ddfb5dd3cf1888726b1e4e04948e9d Mon Sep 17 00:00:00 2001 From: Spencer Murray Date: Tue, 25 Mar 2025 11:37:30 -0400 Subject: [PATCH 4/4] Require bundle name in badge url --- graphs/urls.py | 4 ++-- graphs/views.py | 26 +++++++++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/graphs/urls.py b/graphs/urls.py index d79480baea..cf069e9a42 100644 --- a/graphs/urls.py +++ b/graphs/urls.py @@ -14,12 +14,12 @@ name="default-badge", ), re_path( - "branch/(?P.+)/(graph|graphs)/bundle/badge.(?P[^/]+)", + "branch/(?P.+)/(graph|graphs)/bundle/(?P.+)/badge.(?P[^/]+)", BundleBadgeHandler.as_view(), name="branch-bundle-badge", ), re_path( - "(graph|graphs)/bundle/badge.(?P[^/]+)", + "(graph|graphs)/bundle/(?P.+)/badge.(?P[^/]+)", BundleBadgeHandler.as_view(), name="default-bundle-badge", ), diff --git a/graphs/views.py b/graphs/views.py index dd8e083b1f..788b7c8012 100644 --- a/graphs/views.py +++ b/graphs/views.py @@ -14,7 +14,7 @@ from api.shared.mixins import RepoPropertyMixin from core.models import Branch, Pull from graphs.settings import settings -from services.bundle_analysis import BundleAnalysisReport, load_report +from services.bundle_analysis import load_report from services.components import commit_components from .helpers.badge import ( @@ -209,7 +209,7 @@ class BundleBadgeHandler(APIView, RepoPropertyMixin, GraphBadgeAPIMixin): filename = "bundle-badge" def get_object(self, request, *args, **kwargs): - # Validate coverage precision + # Validate precision query param precision = self.request.query_params.get("precision", "2") if precision not in self.precisions: raise NotFound("Bundle size precision should be one of [ 0 || 1 || 2 ]") @@ -218,7 +218,11 @@ def get_object(self, request, *args, **kwargs): bundle_size_bytes = self.get_bundle_size() if self.kwargs.get("ext") == "txt": - return format_bundle_bytes(bundle_size_bytes, precision) + return ( + "unknown" + if bundle_size_bytes is None + else format_bundle_bytes(bundle_size_bytes, precision) + ) return get_bundle_badge(bundle_size_bytes, precision) @@ -253,18 +257,26 @@ def get_bundle_size(self) -> int | None: log.warning("Commit not found", extra=dict(commit=branch.head)) return None - shared_bundle_report = load_report(commit) + commit_bundles = load_report(commit) - if shared_bundle_report is None: + if commit_bundles is None: log.warning( "Bundle analysis report not found for commit", extra=dict(commit=branch.head), ) return None - bundle_report = BundleAnalysisReport(shared_bundle_report) + bundle_name = str(self.kwargs.get("bundle")) + bundle = commit_bundles.bundle_report(bundle_name) + + if bundle is None: + log.warning( + "Bundle with provided name not found for commit", + extra=dict(commit=branch.head), + ) + return None - return bundle_report.size_total + return bundle.total_size() class GraphHandler(APIView, RepoPropertyMixin, GraphBadgeAPIMixin):