If you think a bug might affect users in the 57 release, please set the correct tracking and status flags for Release Management.

500 error trying to set a price to a free app that was previously a paid app using the API

RESOLVED WONTFIX

Status

Marketplace
API
P3
normal
RESOLVED WONTFIX
2 years ago
2 years ago

People

(Reporter: mat, Unassigned)

Tracking

Avenir
Points:
---

Details

(Whiteboard: [ktlo])

(Reporter)

Description

2 years ago
STR:
- Create a paid app
- Make it free
- Try to set the "price" for it through the API

Expected result:
- Nothing, price is ignored

Actual result:
- A 500 Error


Sentry:
http://sentry.dmz.phx1.mozilla.com/marketplace-stage/marketplace-stage/group/28927/

IntegrityError: (1062, "Duplicate entry '532283' for key 'addon_id'")

Stacktrace (most recent call last):

  File "rest_framework/views.py", line 400, in dispatch
    response = handler(request, *args, **kwargs)
  File "mkt/webapps/views.py", line 115, in update
    r = super(AppViewSet, self).update(request, *args, **kwargs)
  File "rest_framework/mixins.py", line 122, in update
    if not serializer.is_valid():
  File "rest_framework/serializers.py", line 554, in is_valid
    return not self.errors
  File "rest_framework/serializers.py", line 546, in errors
    ret = self.from_native(data, files)
  File "rest_framework/serializers.py", line 1025, in from_native
    instance = super(ModelSerializer, self).from_native(data, files)
  File "rest_framework/serializers.py", line 379, in from_native
    return self.restore_object(attrs, instance=getattr(self, 'object', None))
  File "mkt/webapps/serializers.py", line 366, in restore_object
    f(instance, v)
  File "mkt/webapps/serializers.py", line 323, in save_price
    premium.save()
  File "django/db/models/base.py", line 589, in save
    force_update=force_update, update_fields=update_fields)
  File "django/db/models/base.py", line 617, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "django/db/models/base.py", line 698, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "django/db/models/base.py", line 731, in _do_insert
    using=using, raw=raw)
  File "django/db/models/manager.py", line 92, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "django/db/models/query.py", line 921, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "django/db/models/sql/compiler.py", line 921, in execute_sql
    cursor.execute(sql, params)
  File "django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
  File "django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
  File "django/db/backends/mysql/base.py", line 129, in execute
    return self.cursor.execute(query, args)
  File "newrelic/hooks/database_dbapi2.py", line 22, in execute
    *args, **kwargs)
  File "MySQLdb/cursors.py", line 205, in execute
    self.errorhandler(self, exc, value)
  File "MySQLdb/connections.py", line 36, in defaulterrorhandler
    raise errorclass, errorvalue
(Reporter)

Comment 1

2 years ago
It happens because of the combination of 2 things:

- When we load one or more Webapps, we go though the Webapp transformer, which looks at the premium_type for each app it's trying to load and sets _premium property accordingly. Crucially, it's set to None if an app premium_type is free, even if an AddonPremium exists, to avoid loading unnecessary objects.
- When we save a Webapp in the API, the serializer does:

    def save_price(self, obj, price):
        premium = obj.premium
        if not premium:
            premium = AddonPremium()
            premium.addon = obj
        premium.price = Price.objects.active().get(price=price)
        premium.save()


Since we set _premium to None, obj.premium (which is just a wrapper around that property) directly returns None, and we try to create an AddonPremium even though one exists.

Possible solutions:
- In save_price(), try to load an existing AddonPremium ignoring the 'premium' property, using get_or_create().
- In the serializer save(), don't call save_price() on free webapps. Possibly even return a 400 error.

Updated

2 years ago
Priority: -- → P3

Updated

2 years ago
Whiteboard: [ktlo]
Because Payments are trending to close.
Status: NEW → RESOLVED
Last Resolved: 2 years ago
Resolution: --- → WONTFIX
You need to log in before you can comment on or make changes to this bug.