Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit 263a518

Browse files
Merge branch 'main' into rvinnakota/add-ip-address-sentry
2 parents 14e4b69 + f6ed141 commit 263a518

File tree

484 files changed

+12070
-5413
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

484 files changed

+12070
-5413
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@ permissions:
1919
jobs:
2020
lint:
2121
name: Run Lint
22-
uses: codecov/gha-workflows/.github/workflows/[email protected].21
22+
uses: codecov/gha-workflows/.github/workflows/[email protected].23
2323

2424
build:
2525
name: Build API
26-
uses: codecov/gha-workflows/.github/workflows/[email protected].21
26+
uses: codecov/gha-workflows/.github/workflows/[email protected].23
2727
secrets: inherit
2828
with:
2929
repo: ${{ vars.CODECOV_IMAGE_V2 || 'codecov/self-hosted-api' }}
3030

3131
codecovstartup:
3232
name: Codecov Startup
3333
needs: build
34-
uses: codecov/gha-workflows/.github/workflows/[email protected].21
34+
uses: codecov/gha-workflows/.github/workflows/[email protected].23
3535
secrets: inherit
3636

3737
# ats:
@@ -47,15 +47,15 @@ jobs:
4747
test:
4848
name: Test
4949
needs: [build]
50-
uses: codecov/gha-workflows/.github/workflows/[email protected].21
50+
uses: codecov/gha-workflows/.github/workflows/[email protected].23
5151
secrets: inherit
5252
with:
5353
repo: ${{ vars.CODECOV_IMAGE_V2 || 'codecov/self-hosted-api' }}
5454

5555
build-self-hosted:
5656
name: Build Self Hosted API
5757
needs: [build, test]
58-
uses: codecov/gha-workflows/.github/workflows/[email protected].21
58+
uses: codecov/gha-workflows/.github/workflows/[email protected].23
5959
secrets: inherit
6060
with:
6161
repo: ${{ vars.CODECOV_IMAGE_V2 || 'codecov/self-hosted-api' }}
@@ -64,7 +64,7 @@ jobs:
6464
name: Push Staging Image
6565
needs: [build, test]
6666
if: ${{ github.event_name == 'push' && github.event.ref == 'refs/heads/staging' && github.repository_owner == 'codecov' }}
67-
uses: codecov/gha-workflows/.github/workflows/[email protected].21
67+
uses: codecov/gha-workflows/.github/workflows/[email protected].23
6868
secrets: inherit
6969
with:
7070
environment: staging
@@ -74,7 +74,7 @@ jobs:
7474
name: Push Production Image
7575
needs: [build, test]
7676
if: ${{ github.event_name == 'push' && github.event.ref == 'refs/heads/main' && github.repository_owner == 'codecov' }}
77-
uses: codecov/gha-workflows/.github/workflows/[email protected].21
77+
uses: codecov/gha-workflows/.github/workflows/[email protected].23
7878
secrets: inherit
7979
with:
8080
environment: production
@@ -85,7 +85,7 @@ jobs:
8585
needs: [build-self-hosted, test]
8686
secrets: inherit
8787
if: ${{ github.event_name == 'push' && github.event.ref == 'refs/heads/main' && github.repository_owner == 'codecov' }}
88-
uses: codecov/gha-workflows/.github/workflows/[email protected].21
88+
uses: codecov/gha-workflows/.github/workflows/[email protected].23
8989
with:
9090
push_rolling: true
9191
repo: ${{ vars.CODECOV_IMAGE_V2 || 'codecov/self-hosted-api' }}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: Detect dep version changes
2+
3+
on:
4+
pull_request:
5+
6+
permissions:
7+
pull-requests: "write"
8+
9+
jobs:
10+
shared-change-checker:
11+
name: See if shared changed
12+
uses: codecov/gha-workflows/.github/workflows/diff-dep.yml@main
13+
with:
14+
dep: 'shared'

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
24.6.1
1+
24.9.1

api/internal/branch/views.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
from django.db.models import F, OuterRef, Subquery
2-
from django_filters.rest_framework import DjangoFilterBackend
3-
from rest_framework import filters, mixins, viewsets
1+
from django.db.models import OuterRef, Subquery
2+
from rest_framework import mixins
43

54
from api.shared.branch.mixins import BranchViewSetMixin
6-
from api.shared.mixins import RepoPropertyMixin
7-
from api.shared.permissions import RepositoryArtifactPermissions
8-
from core.models import Branch, Commit
5+
from core.models import Commit
96

107
from .serializers import BranchSerializer
118

api/internal/chart/helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
from cerberus import Validator
44
from dateutil import parser
55
from django.db import connection
6-
from django.db.models import Case, F, FloatField, Value, When
6+
from django.db.models import Case, FloatField, Value, When
77
from django.db.models.fields.json import KeyTextTransform
88
from django.db.models.functions import Cast, Trunc
99
from django.utils import timezone
1010
from django.utils.functional import cached_property
1111
from rest_framework.exceptions import ValidationError
1212

1313
from codecov_auth.models import Owner
14-
from core.models import Commit, Repository
14+
from core.models import Repository
1515

1616

1717
class ChartParamValidator(Validator):

api/internal/chart/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from django.urls import path, re_path
1+
from django.urls import re_path
22

33
from .views import OrganizationChartHandler, RepositoryChartHandler
44

api/internal/chart/views.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from api.shared.mixins import RepositoriesMixin
77
from api.shared.permissions import ChartPermissions
88
from core.models import Commit
9+
from utils import round_decimals_down
910

1011
from .filters import apply_default_filters, apply_simple_filters
1112
from .helpers import (
@@ -102,7 +103,9 @@ def post(self, request, *args, **kwargs):
102103
complexity = [
103104
{
104105
"date": commit.timestamp,
105-
"complexity_ratio": round(commit.complexity_ratio * 100, 2),
106+
"complexity_ratio": round_decimals_down(
107+
commit.complexity_ratio * 100, 2
108+
),
106109
"commitid": commit.commitid,
107110
}
108111
for commit in commits
@@ -136,7 +139,9 @@ def post(self, request, *args, **kwargs):
136139
complexity = [
137140
{
138141
"date": commit.truncated_date,
139-
"complexity_ratio": round(commit.complexity_ratio * 100, 2),
142+
"complexity_ratio": round_decimals_down(
143+
commit.complexity_ratio * 100, 2
144+
),
140145
"commitid": commit.commitid,
141146
}
142147
for commit in complexity_grouped_queryset

api/internal/enterprise_urls.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
from django.urls import include, path
22

3-
from api.internal.self_hosted.views import SettingsViewSet, UserViewSet
4-
from utils.routers import OptionalTrailingSlashRouter, RetrieveUpdateDestroyRouter
3+
from api.internal.self_hosted.views import UserViewSet
4+
from utils.routers import OptionalTrailingSlashRouter
55

66
self_hosted_router = OptionalTrailingSlashRouter()
77
self_hosted_router.register(r"users", UserViewSet, basename="selfhosted-users")
88

9-
settings_router = RetrieveUpdateDestroyRouter()
10-
settings_router.register(r"settings", SettingsViewSet, basename="selfhosted-settings")
11-
129
urlpatterns = [
1310
path("license/", include("api.internal.license.urls")),
14-
path("", include(settings_router.urls)),
1511
path("", include(self_hosted_router.urls)),
1612
]

api/internal/owner/serializers.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,11 @@ def validate_value(self, value):
143143
return value
144144

145145
def validate(self, plan):
146-
owner = self.context["view"].owner
146+
current_org = self.context["view"].owner
147+
if current_org.account:
148+
raise serializers.ValidationError(
149+
detail="You cannot update your plan manually, for help or changes to plan, connect with [email protected]"
150+
)
147151

148152
# Validate quantity here because we need access to whole plan object
149153
if plan["value"] in PAID_PLANS:
@@ -156,16 +160,19 @@ def validate(self, plan):
156160
"Quantity for paid plan must be greater than 1"
157161
)
158162

159-
plan_service = PlanService(current_org=owner)
163+
plan_service = PlanService(current_org=current_org)
160164
is_org_trialing = plan_service.is_org_trialing
161165

162-
if plan["quantity"] < owner.activated_user_count and not is_org_trialing:
166+
if (
167+
plan["quantity"] < current_org.activated_user_count
168+
and not is_org_trialing
169+
):
163170
raise serializers.ValidationError(
164171
"Quantity cannot be lower than currently activated user count"
165172
)
166173
if (
167-
plan["quantity"] == owner.plan_user_count
168-
and plan["value"] == owner.plan
174+
plan["quantity"] == current_org.plan_user_count
175+
and plan["value"] == current_org.plan
169176
and not is_org_trialing
170177
):
171178
raise serializers.ValidationError(
@@ -190,6 +197,9 @@ class SubscriptionDetailSerializer(serializers.Serializer):
190197
current_period_end = serializers.IntegerField()
191198
customer = StripeCustomerSerializer()
192199
collection_method = serializers.CharField()
200+
tax_ids = serializers.ListField(
201+
source="customer.tax_ids.data", read_only=True, allow_null=True
202+
)
193203
trial_end = serializers.IntegerField()
194204

195205

@@ -249,6 +259,10 @@ class AccountDetailsSerializer(serializers.ModelSerializer):
249259
root_organization = RootOrganizationSerializer()
250260
schedule_detail = serializers.SerializerMethodField()
251261
apply_cancellation_discount = serializers.BooleanField(write_only=True)
262+
activated_student_count = serializers.SerializerMethodField()
263+
activated_user_count = serializers.SerializerMethodField()
264+
delinquent = serializers.SerializerMethodField()
265+
uses_invoice = serializers.SerializerMethodField()
252266

253267
class Meta:
254268
model = Owner
@@ -258,21 +272,22 @@ class Meta:
258272
fields = read_only_fields + (
259273
"activated_student_count",
260274
"activated_user_count",
275+
"apply_cancellation_discount",
261276
"checkout_session_id",
277+
"delinquent",
262278
"email",
263279
"inactive_user_count",
264280
"name",
265281
"nb_active_private_repos",
266-
"plan",
267282
"plan_auto_activate",
268283
"plan_provider",
269-
"uses_invoice",
284+
"plan",
270285
"repo_total_credits",
271286
"root_organization",
272287
"schedule_detail",
273288
"student_count",
274289
"subscription_detail",
275-
"apply_cancellation_discount",
290+
"uses_invoice",
276291
)
277292

278293
def _get_billing(self):
@@ -292,6 +307,26 @@ def get_schedule_detail(self, owner):
292307
def get_checkout_session_id(self, _):
293308
return self.context.get("checkout_session_id")
294309

310+
def get_activated_student_count(self, owner):
311+
if owner.account:
312+
return owner.account.activated_student_count
313+
return owner.activated_student_count
314+
315+
def get_activated_user_count(self, owner):
316+
if owner.account:
317+
return owner.account.activated_user_count
318+
return owner.activated_user_count
319+
320+
def get_delinquent(self, owner):
321+
if owner.account:
322+
return owner.account.is_delinquent
323+
return owner.delinquent
324+
325+
def get_uses_invoice(self, owner):
326+
if owner.account:
327+
return owner.account.invoice_billing.filter(is_active=True).exists()
328+
return owner.uses_invoice
329+
295330
def update(self, instance, validated_data):
296331
if "pretty_plan" in validated_data:
297332
desired_plan = validated_data.pop("pretty_plan")

api/internal/owner/views.py

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import logging
2-
from dataclasses import asdict
32

43
from django.db.models import F
54
from django_filters import rest_framework as django_filters
65
from rest_framework import filters, mixins, status, viewsets
76
from rest_framework.decorators import action
8-
from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError
7+
from rest_framework.exceptions import PermissionDenied, ValidationError
98
from rest_framework.response import Response
9+
from shared.django_apps.codecov_auth.models import Owner
1010

1111
from api.shared.mixins import OwnerPropertyMixin
1212
from api.shared.owner.mixins import OwnerViewSetMixin, UserViewSetMixin
@@ -19,7 +19,6 @@
1919
from .serializers import (
2020
AccountDetailsSerializer,
2121
OwnerSerializer,
22-
StripeInvoiceSerializer,
2322
UserSerializer,
2423
)
2524

@@ -30,31 +29,6 @@ class OwnerViewSet(OwnerViewSetMixin, mixins.RetrieveModelMixin):
3029
serializer_class = OwnerSerializer
3130

3231

33-
class InvoiceViewSet(
34-
viewsets.GenericViewSet,
35-
mixins.ListModelMixin,
36-
mixins.RetrieveModelMixin,
37-
OwnerPropertyMixin,
38-
):
39-
serializer_class = StripeInvoiceSerializer
40-
permission_classes = [MemberOfOrgPermissions]
41-
pagination_class = None
42-
43-
def get_queryset(self):
44-
return BillingService(
45-
requesting_user=self.request.current_owner
46-
).list_filtered_invoices(self.owner, 100)
47-
48-
def get_object(self):
49-
invoice_id = self.kwargs.get("pk")
50-
invoice = BillingService(
51-
requesting_user=self.request.current_owner
52-
).get_invoice(self.owner, invoice_id)
53-
if not invoice:
54-
raise NotFound(f"Invoice {invoice_id} does not exist for that account")
55-
return invoice
56-
57-
5832
class AccountDetailsViewSet(
5933
viewsets.GenericViewSet,
6034
mixins.UpdateModelMixin,
@@ -82,6 +56,14 @@ def destroy(self, request, *args, **kwargs):
8256
return Response(status=status.HTTP_204_NO_CONTENT)
8357

8458
def get_object(self):
59+
if self.owner.account:
60+
# gets the related account and invoice_billing objects from db in 1 query
61+
# otherwise, each reference to owner.account would be an additional query
62+
self.owner = (
63+
Owner.objects.filter(pk=self.owner.ownerid)
64+
.select_related("account__invoice_billing")
65+
.first()
66+
)
8567
return self.owner
8668

8769
@action(detail=False, methods=["patch"])
@@ -109,12 +91,25 @@ def update_email(self, request, *args, **kwargs):
10991
@action(detail=False, methods=["patch"])
11092
@stripe_safe
11193
def update_billing_address(self, request, *args, **kwargs):
94+
name = request.data.get("name")
95+
if not name:
96+
raise ValidationError(detail="No name sent")
11297
billing_address = request.data.get("billing_address")
11398
if not billing_address:
11499
raise ValidationError(detail="No billing_address sent")
115100
owner = self.get_object()
101+
102+
formatted_address = {
103+
"line1": billing_address["line_1"],
104+
"line2": billing_address["line_2"],
105+
"city": billing_address["city"],
106+
"state": billing_address["state"],
107+
"postal_code": billing_address["postal_code"],
108+
"country": billing_address["country"],
109+
}
110+
116111
billing = BillingService(requesting_user=request.current_owner)
117-
billing.update_billing_address(owner, billing_address)
112+
billing.update_billing_address(owner, name, billing_address=formatted_address)
118113
return Response(self.get_serializer(owner).data)
119114

120115

0 commit comments

Comments
 (0)