Skip to content

Commit

Permalink
fix: Stop Square Webhook 404 On Confectionary Payments (#672)
Browse files Browse the repository at this point in the history
* Add check to see if same location before 404

* Check all object return types for location ID

* Update config

* Set to latest possible version

* fix test and linting

* Check the damage

* Some config changes

* finally make mypy stop screaming

* Updated schema

* Fix mypy & linting issues after rebase

* Further pylint fixes, thanks CLI

* Fix more pylint issues

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: HappyNTH <[email protected]>
  • Loading branch information
3 people authored Sep 28, 2024
1 parent 6e76184 commit 53eb894
Show file tree
Hide file tree
Showing 38 changed files with 272 additions and 117 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/python-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:

steps:
# Set up cache for pip packages
- uses: actions/cache@v2
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
Expand Down Expand Up @@ -86,7 +86,7 @@ jobs:
- name: set up python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10.6"
python-version: "3.10"

# Install dependencies
- name: Install dependencies
Expand Down
30 changes: 15 additions & 15 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ repos:
- shortuuid>=1.0.1
- django-filter==22.1
- Pillow>=8.1.2
- django-cors-headers==3.13.0
- django-admin-confirm==0.2.5
- django-cors-headers==3.14.0
- django-admin-confirm==1.0.0
- django-configurations==2.4
- django-environ==0.9.0
- xlsxwriter==3.0.3
- xlsxwriter==3.0.8
- html2text==2020.1.16
- django==3.2.15
- django==3.2.18
- django-guardian==2.4.0
- django_tiptap==0.0.10
- djangorestframework==3.13.1
Expand All @@ -69,30 +69,30 @@ repos:
- django-graphql-jwt==0.3.0
- PyJWT==1.7.0
- squareup>=9.0.0.20210226
- sentry-sdk==1.9.6
- whitenoise==6.2.0
- django-anymail[amazon_ses]==8.6
- sentry-sdk==1.14.0
- whitenoise==6.4.0
- django-anymail[amazon_ses]==9.0
- django-inlinecss==0.3.0
- timezonefinder==6.1.1
- pytz==2022.2.1
- timezonefinder==6.1.9
- pytz==2022.7.1
- django-celery-results==2.2.0
- redis==4.3.4
- redis==4.5.1
- pre-commit >= 2.11.0
- black >= 22.3.0
- isort >= 5.10.1
- pylint >= 2.14.0
- django-stubs == 1.10.0
- types-python-dateutil==2.8.19
- django-stubs==1.13.0
- types-python-dateutil==2.8.19.10
- pytest >= 6.2.2
- pytest-sugar >= 0.9.4
- pytest-cov >= 2.11.1
- pytest-django >= 0.2.0
- pytest-factoryboy >= 2.1.0
- coveralls >= 3.0.1
- factory-boy==3.2.1
- faker==14.2.1; python_version >= '3.6'
- jedi==0.18.1; python_version >= '3.6'
- faker==17.6.0; python_version >= '3.6'
- jedi==0.18.2; python_version >= '3.6'
- psycopg2-binary
id: mypy
repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.930
rev: v0.981
6 changes: 3 additions & 3 deletions compose/local/django/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
ARG PYTHON_VERSION=3.10

# define an alias for the specfic python version used in this file.
FROM mcr.microsoft.com/vscode/devcontainers/python:${PYTHON_VERSION} as python
FROM mcr.microsoft.com/vscode/devcontainers/python:${PYTHON_VERSION} AS python

# Python build stage
FROM python as python-build-stage
FROM python AS python-build-stage

ARG BUILD_ENVIRONMENT=local

Expand All @@ -24,7 +24,7 @@ RUN pip wheel --wheel-dir /usr/src/app/wheels \


# Python 'run' stage
FROM python as python-run-stage
FROM python AS python-run-stage

ARG BUILD_ENVIRONMENT=local
ARG APP_HOME=/app/uobtheatre-api
Expand Down
6 changes: 3 additions & 3 deletions compose/production/django/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ ARG PYTHON_VERSION=3.10-slim-buster


# define an alias for the specfic python version used in this file.
FROM python:${PYTHON_VERSION} as python
FROM python:${PYTHON_VERSION} AS python

# Python build stage
FROM python as python-build-stage
FROM python AS python-build-stage

ARG BUILD_ENVIRONMENT=production

Expand All @@ -26,7 +26,7 @@ RUN pip wheel --wheel-dir /usr/src/app/wheels \


# Python 'run' stage
FROM python as python-run-stage
FROM python AS python-run-stage

ARG BUILD_ENVIRONMENT=production
ARG APP_HOME=/app
Expand Down
4 changes: 4 additions & 0 deletions config/settings/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,7 @@
# MEDIA
# ------------------------------------------------------------------------------
MEDIA_ROOT = tempfile.mkdtemp()

# OTHER
# ------------------------------------------------------------------------------
SQUARE_SETTINGS["SQUARE_LOCATION"] = "LMHPTEST"
2 changes: 1 addition & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def mock_square():
"""

@contextmanager
def mock_client( # pylint: disable=too-many-arguments
def mock_client( # pylint: disable=too-many-arguments,too-many-positional-arguments
square_client_api,
method: str,
body: Optional[dict] = None,
Expand Down
6 changes: 6 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ ignore_missing_imports = True
warn_incomplete_stub = True
cache_dir = /dev/null
show_error_codes = True
python_version = 3.10
no_implicit_optional=False
check_untyped_defs=True

[mypy-uobtheatre.*.test.*]
check_untyped_defs = False

[mypy-uobtheatre.*.migrations.*]
ignore_errors = True
Expand Down
2 changes: 1 addition & 1 deletion requirements/local.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pre-commit >= 2.11.0
# formatting
black >= 22.3.0 # Code formatting
isort >= 5.10.1 # Import sorting
pylint >= 2.14.0 # Linting
pylint >= 3.3.1 # Linting

# mypy stuff
mypy >= 1.11.2 # Type checking
Expand Down
14 changes: 7 additions & 7 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ type BookingNode implements Node {
updatedAt: DateTime!
status: BookingStatus!
user: ExtendedUserNode!
reference: String!
creator: ExtendedUserNode!
reference: String!
performance: PerformanceNode!
adminDiscountPercentage: Float!
accessibilityInfo: String
Expand Down Expand Up @@ -274,8 +274,8 @@ type ExtendedUserNode {
dateJoined: DateTime!
id: String
email: String!
createdBookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, reference: String, creator: ID, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection!
bookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, reference: String, creator: ID, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection!
bookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, creator: ID, reference: String, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection!
createdBookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, creator: ID, reference: String, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection!
pk: Int
archived: Boolean
verified: Boolean
Expand Down Expand Up @@ -474,7 +474,7 @@ type PerformanceNode implements Node {
seatGroups(offset: Int, before: String, after: String, first: Int, last: Int): SeatGroupNodeConnection!
capacity: Int
discounts(offset: Int, before: String, after: String, first: Int, last: Int, group: Boolean, id: ID): DiscountNodeConnection!
bookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, reference: String, creator: ID, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection!
bookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, creator: ID, reference: String, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection!
capacityRemaining: Int
ticketOptions: [PerformanceSeatGroupNode]
minSeatPrice: Int
Expand Down Expand Up @@ -664,7 +664,7 @@ input ProductionWarning {
type Query {
images: [ImageNode]
paymentDevices(paymentProvider: PaymentProvider, paired: Boolean): [SquarePaymentDevice]
bookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, reference: String, creator: ID, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection
bookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, creator: ID, reference: String, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection
me: ExtendedUserNode
societies(offset: Int, before: String, after: String, first: Int, last: Int, id: ID, name: String, slug: String, userHasPermission: String): SocietyNodeConnection
society(slug: String!): SocietyNode
Expand Down Expand Up @@ -934,8 +934,8 @@ type UserNode implements Node {
id: ID!
email: String!
societies(offset: Int, before: String, after: String, first: Int, last: Int, id: ID, name: String, slug: String, userHasPermission: String): SocietyNodeConnection!
createdBookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, reference: String, creator: ID, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection!
bookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, reference: String, creator: ID, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection!
bookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, creator: ID, reference: String, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection!
createdBookings(offset: Int, before: String, after: String, first: Int, last: Int, createdAt: DateTime, updatedAt: DateTime, status: String, user: ID, creator: ID, reference: String, performance: ID, adminDiscountPercentage: Float, accessibilityInfo: String, expiresAt: DateTime, id: ID, statusIn: [String], search: String, checkedIn: Boolean, active: Boolean, expired: Boolean, orderBy: String): BookingNodeConnection!
ticketsCheckedInByUser(offset: Int, before: String, after: String, first: Int, last: Int): TicketNodeConnection!
pk: Int
archived: Boolean
Expand Down
3 changes: 2 additions & 1 deletion uobtheatre/addresses/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ def timezone(self):
return pytz.UTC

finder = TimezoneFinder()
return pytz.timezone(finder.timezone_at(lat=self.latitude, lng=self.longitude))
timezone = finder.timezone_at(lat=self.latitude, lng=self.longitude)
return pytz.timezone(timezone) if timezone else pytz.UTC

def __str__(self):
return f"{self.building_name + ', ' if self.building_name else ''}{self.building_number + ', ' if self.building_number else ''}{self.street}, {self.city}, {self.postcode}"
2 changes: 1 addition & 1 deletion uobtheatre/bookings/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class BookingForm(MutationForm):

def clean(self):
"""Validate and clean form data"""
cleaned_data = super().clean()
cleaned_data = super().clean() or {}

if not self.instance.creator_id:
# If the instance has no creater, the current user is the creator
Expand Down
17 changes: 6 additions & 11 deletions uobtheatre/bookings/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import math
from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Tuple, Union
from urllib.parse import urlencode
Expand Down Expand Up @@ -138,7 +139,7 @@ def annotate_checked_in_proportion(self) -> QuerySet:
# Whilst it shouldn't occur a divide by zero is prevented setting to zero when the ticket count is zero
return self.annotate_checked_in_count().annotate(
proportion=Case(
When(Q(count=0), then=Cast(0, FloatField())), # type: ignore
When(Q(count=0), then=Cast(0, FloatField())), # type: ignore
default=Cast(F("checked_in_count"), FloatField())
/ Cast(F("count"), FloatField()),
)
Expand Down Expand Up @@ -201,7 +202,7 @@ def expired(self, bool_val=True) -> QuerySet:

def generate_expires_at():
"""Generates the expires at timestamp for a booking"""
return timezone.now() + timezone.timedelta(minutes=15)
return timezone.now() + datetime.timedelta(minutes=15)


BookingManager = models.Manager.from_queryset(BookingQuerySet)
Expand Down Expand Up @@ -232,14 +233,6 @@ class Meta:
default=create_short_uuid, editable=False, max_length=12, unique=True
)

# Stores who created the booking
# For regular bookings this will be the user
# For boxoffice bookings it will be the logged in boxoffice user
# For admin bookings it will be the logged in admin
creator = models.ForeignKey(
User, on_delete=models.RESTRICT, related_name="created_bookings"
)

performance = models.ForeignKey(
Performance,
on_delete=models.RESTRICT,
Expand Down Expand Up @@ -572,7 +565,9 @@ def web_tickets_path(self):
@property
def is_reservation_expired(self):
"""Returns whether the booking is considered expired"""
return filter_passes_on_model(self, lambda qs: qs.expired())
return filter_passes_on_model(
self, lambda qs: qs.expired() # type:ignore
)

def validate_cant_be_refunded(self) -> Optional[CantBeRefundedException]:
if error := super().validate_cant_be_refunded():
Expand Down
7 changes: 6 additions & 1 deletion uobtheatre/bookings/mutations.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import graphene

from uobtheatre.bookings.abilities import ModifyBooking
Expand All @@ -8,6 +10,7 @@
from uobtheatre.payments.transaction_providers import (
Card,
Cash,
PaymentProvider,
SquareOnline,
SquarePOS,
)
Expand Down Expand Up @@ -173,7 +176,7 @@ class Arguments:
verify_token = graphene.String(required=False)

@classmethod
def resolve_mutation( # pylint: disable=too-many-arguments, too-many-branches
def resolve_mutation( # pylint: disable=too-many-arguments, too-many-branches, too-many-positional-arguments
cls,
_,
info,
Expand Down Expand Up @@ -235,6 +238,8 @@ def resolve_mutation( # pylint: disable=too-many-arguments, too-many-branches
code="missing_required",
)

payment_method: Optional[PaymentProvider] = None

if payment_provider == SquareOnline.name:
if not nonce:
raise GQLException(
Expand Down
4 changes: 2 additions & 2 deletions uobtheatre/bookings/test/test_mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -2255,7 +2255,7 @@ def test_pay_booking_fails_if_already_paid(gql_client):
}


# pylint: disable=too-many-arguments,too-many-locals,too-many-branches
# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-positional-arguments
@pytest.mark.django_db
@pytest.mark.parametrize(
"performance_id, booking_obj, check_in_ticket_id_list, not_check_in_ticket_id_list, non_booking_ticket_id_list",
Expand All @@ -2266,7 +2266,7 @@ def test_pay_booking_fails_if_already_paid(gql_client):
(1, {"booking_id": 4, "performance_id": 1}, [1, 2, 3], [], [4, 5, 6]),
(1, {"booking_id": 4, "performance_id": 1}, [], [], [7, 8, 9]),
(1, {"booking_id": 4, "performance_id": 2}, [1, 2, 3], [], [4, 5, 6]),
], # pylint: disable=too-many-arguments,too-many-locals,too-many-branches
], # pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-positional-arguments
)
def test_check_in_booking(
performance_id,
Expand Down
4 changes: 3 additions & 1 deletion uobtheatre/discounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ def validate_unique(self, *args, **kwargs):
]

discounts_with_same_requirements_names = [
discount.name for discount in discounts_with_same_requirements
discount.name
for discount in discounts_with_same_requirements
if discount.name
]

if len(discounts_with_same_requirements) != 0:
Expand Down
2 changes: 1 addition & 1 deletion uobtheatre/management/commands/number_of_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ class Command(BaseCommand):

def handle(self, *args, **options): # pylint: disable=unused-argument
number_of_users = User.objects.count()
self.stdout.write(str(self.style.SUCCESS(number_of_users)))
self.stdout.write(str(self.style.SUCCESS(str(number_of_users))))
8 changes: 5 additions & 3 deletions uobtheatre/payments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,11 @@ def from_square_status(cls, square_status: str):
"pay_object_type", "pay_object_id"
) # type: ignore

type = models.CharField(
type: TextChoices = models.CharField(
max_length=20,
choices=Type.choices,
default=Type.PAYMENT,
)
) # type: ignore
status: TextChoices = models.CharField(
max_length=20,
choices=Status.choices,
Expand Down Expand Up @@ -176,7 +176,9 @@ def is_refunded(self) -> bool:
def provider(self):
return next(
method
for method in list(TransactionProvider.__all__)
for method in list( # type: ignore[call-overload]
TransactionProvider.__all__
)
if method.name == self.provider_name
)

Expand Down
Loading

0 comments on commit 53eb894

Please sign in to comment.