Skip to content

Commit

Permalink
Implement addon PUT as full-update or create (#19342)
Browse files Browse the repository at this point in the history
* Implement addon PUT as full-update or create

* refactor serializers and views to use common code

* update self.action when we create from a PUT

* Enforce a GUID in the manifest for PUT
  • Loading branch information
eviljeff authored Jun 13, 2022
1 parent ca25610 commit a0476b7
Show file tree
Hide file tree
Showing 8 changed files with 407 additions and 273 deletions.
21 changes: 21 additions & 0 deletions docs/topics/api/addons.rst
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,27 @@ Note: as form-data can not include objects, and creating an add-on requires the
:reqheader Content-Type: multipart/form-data


--------------------
Put - Create or Edit
--------------------

.. _addon-put:

This endpoint allows a submission of an upload, which will either update an existing add-on and create a new version if the guid already exists, or will create a new add-on if the guid does not exist.
See the :ref:`Add-on Create <addon-create>` documentation for details of the request and restrictions.

.. note::
This API requires :doc:`authentication <auth>`, and for the user to be an author of the add-on if the add-on exists already.

.. note::
The guid in the url must match a guid specified in the manifest.

.. note::
A submission that results in a new add-on will have metadata defaults taken from the manifest (e.g. name), but a submission that updates an existing listing will not use data from the manifest.

.. http:put:: /api/v5/addons/addon/(string:guid)/
------
Delete
------
Expand Down
1 change: 1 addition & 0 deletions docs/topics/api/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ These are `v5` specific changes - `v4` changes apply also.
* 2022-05-05: added the ability to delete add-on authors. https://github.com/mozilla/addons-server/issues/19163
* 2022-05-12: added the ability to add new add-on authors, as pending authors. https://github.com/mozilla/addons-server/issues/19164
* 2022-06-02: enabled setting ``default_locale`` via addon submission and edit endpoints. https://github.com/mozilla/addons-server/issues/18235
* 2022-06-16: added the ability to "PUT" an add-on upload to either create or update an add-on. https://github.com/mozilla/addons-server/issues/15353

.. _`#11380`: https://github.com/mozilla/addons-server/issues/11380/
.. _`#11379`: https://github.com/mozilla/addons-server/issues/11379/
Expand Down
28 changes: 22 additions & 6 deletions src/olympia/addons/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from olympia.amo.validators import (
CreateOnlyValidator,
OneOrMoreLetterOrNumberCharacterValidator,
PreventPartialUpdateValidator,
)
from olympia.api.fields import (
EmailTranslationField,
Expand Down Expand Up @@ -76,6 +77,7 @@
from .validators import (
AddonMetadataValidator,
AddonDefaultLocaleValidator,
MatchingGuidValidator,
VersionAddonMetadataValidator,
VersionLicenseValidator,
VerifyMozillaTrademark,
Expand Down Expand Up @@ -859,7 +861,11 @@ class AddonSerializer(serializers.ModelSerializer):
version = DeveloperVersionSerializer(
write_only=True,
# Note: we're purposefully omitting VersionAddonMetadataValidator
validators=(CreateOnlyValidator(), VersionLicenseValidator()),
validators=(
PreventPartialUpdateValidator(),
VersionLicenseValidator(),
MatchingGuidValidator(),
),
)
versions_url = serializers.SerializerMethodField()

Expand Down Expand Up @@ -995,15 +1001,17 @@ def get_ratings(self, obj):
def get_is_source_public(self, obj):
return False

def run_validation(self, *args, **kwargs):
def run_validation(self, data=serializers.empty):
# We want name and summary to be required fields so they're not cleared, but
# *only* if this is an existing add-on with listed versions.
# - see AddonMetadataValidator for new add-ons/versions.
if self.instance and self.instance.has_listed_versions():
self.fields['name'].required = True
self.fields['summary'].required = True
self.fields['categories'].required = True
return super().run_validation(*args, **kwargs)
for field in ('name', 'summary', 'categories'):
if field in data:
self.fields[field].required = True
if self.instance:
self.fields['version'].addon = self.instance
return super().run_validation(data)

def validate_slug(self, value):
slug_validator(value)
Expand Down Expand Up @@ -1080,6 +1088,9 @@ def log(self, instance, validated_data):
if 'tag_list' in validated_data:
# Tag.add_tag and Tag.remove_tag have their own logging so don't repeat it.
validated_data.pop('tag_list')
if 'version' in validated_data:
# version is always a new object, and not a property either
validated_data.pop('version')

if validated_data:
ActivityLog.create(
Expand Down Expand Up @@ -1137,6 +1148,11 @@ def update(self, instance, validated_data):
if 'tag_list' in validated_data:
del instance.tag_list # super.update will have set it.
instance.set_tag_list(validated_data['tag_list'])
if 'version' in validated_data:
self.fields['version'].create(
{**validated_data.get('version', {}), 'addon': instance}
)

self.log(instance, validated_data)
return instance

Expand Down
Loading

0 comments on commit a0476b7

Please sign in to comment.