Add information about whether a file is minified to the review… (#13661)

* Add information about whether a file is minified to the reviewers API.

Fixes #13589

* Add documentation

* experiment, don't delete all validation objects

* Fix how we're fetching the FileValidation, use parent.current_file.pk

* Increase num queries check for now

* Avoid that extra query
This commit is contained in:
Christopher Grebs 2020-03-16 14:50:40 +01:00 коммит произвёл GitHub
Родитель 4b740c4bd9
Коммит e3e846a086
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 113 добавлений и 3 удалений

Просмотреть файл

@ -169,6 +169,7 @@ This endpoint allows you to browse through the contents of an Add-on version.
:>json string file.content: Raw content of the requested file. :>json string file.content: Raw content of the requested file.
:>json string file.selected_file: The selected file, either from the ``file`` parameter or the default (manifest.json, install.rdf or package.json for Add-ons as well as the XML file for search engines). :>json string file.selected_file: The selected file, either from the ``file`` parameter or the default (manifest.json, install.rdf or package.json for Add-ons as well as the XML file for search engines).
:>json string|null file.download_url: The download url of the selected file or ``null`` in case of a directory. :>json string|null file.download_url: The download url of the selected file or ``null`` in case of a directory.
:>json boolean uses_unknown_minified_code: Indicates that the selected file could be using minified code.
:>json array file.entries[]: The complete file-tree of the extracted XPI. :>json array file.entries[]: The complete file-tree of the extracted XPI.
:>json int file.entries[].depth: Level of folder-tree depth, starting with 0. :>json int file.entries[].depth: Level of folder-tree depth, starting with 0.
:>json string file.entries[].filename: The filename of the file. :>json string file.entries[].filename: The filename of the file.
@ -214,6 +215,7 @@ This endpoint allows you to compare two Add-on versions with each other.
:>json int|null file.entries[].size: The size in bytes. :>json int|null file.entries[].size: The size in bytes.
:>json string|null file.entries[].modified: The exact time of the commit, should be equivalent with ``created``. :>json string|null file.entries[].modified: The exact time of the commit, should be equivalent with ``created``.
:>json object|null diff: See the following output with inline comments for a complete description. :>json object|null diff: See the following output with inline comments for a complete description.
:>json boolean uses_unknown_minified_code: Indicates that the selected file in either the current or the parent version could be using minified code.
Git patch we're talking about: Git patch we're talking about:

Просмотреть файл

@ -2,6 +2,7 @@ import io
import os import os
import mimetypes import mimetypes
import pathlib import pathlib
import json
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime from datetime import datetime
@ -27,7 +28,7 @@ from olympia.addons.models import AddonReviewerFlags
from olympia.api.fields import ReverseChoiceField, SplitField from olympia.api.fields import ReverseChoiceField, SplitField
from olympia.users.models import UserProfile from olympia.users.models import UserProfile
from olympia.files.utils import get_sha256 from olympia.files.utils import get_sha256
from olympia.files.models import File from olympia.files.models import File, FileValidation
from olympia.reviewers.models import CannedResponse from olympia.reviewers.models import CannedResponse
from olympia.versions.models import Version from olympia.versions.models import Version
from olympia.lib.git import AddonGitRepository, get_mime_type_for_blob from olympia.lib.git import AddonGitRepository, get_mime_type_for_blob
@ -50,13 +51,15 @@ class AddonReviewerFlagsSerializer(serializers.ModelSerializer):
class FileEntriesSerializer(FileSerializer): class FileEntriesSerializer(FileSerializer):
content = serializers.SerializerMethodField() content = serializers.SerializerMethodField()
uses_unknown_minified_code = serializers.SerializerMethodField()
download_url = serializers.SerializerMethodField() download_url = serializers.SerializerMethodField()
entries = serializers.SerializerMethodField() entries = serializers.SerializerMethodField()
selected_file = serializers.SerializerMethodField() selected_file = serializers.SerializerMethodField()
class Meta: class Meta:
fields = FileSerializer.Meta.fields + ( fields = FileSerializer.Meta.fields + (
'content', 'entries', 'selected_file', 'download_url' 'content', 'entries', 'selected_file', 'download_url',
'uses_unknown_minified_code'
) )
model = File model = File
@ -213,6 +216,20 @@ class FileEntriesSerializer(FileSerializer):
# more explanation. # more explanation.
return '' return ''
def get_uses_unknown_minified_code(self, obj):
try:
validation = obj.validation
except FileValidation.DoesNotExist:
# We don't have any idea about whether it could be minified or not
# so let's assume it's not for now.
return False
validation_data = json.loads(validation.validation)
prop = 'unknownMinifiedFiles'
minified_files = validation_data.get('metadata', {}).get(prop, [])
return self.get_selected_file(obj) in minified_files
def get_download_url(self, obj): def get_download_url(self, obj):
commit = self._get_commit(obj) commit = self._get_commit(obj)
tree = self.repo.get_root_tree(commit) tree = self.repo.get_root_tree(commit)
@ -285,10 +302,12 @@ class FileEntriesDiffSerializer(FileEntriesSerializer):
entries = serializers.SerializerMethodField() entries = serializers.SerializerMethodField()
selected_file = serializers.SerializerMethodField() selected_file = serializers.SerializerMethodField()
download_url = serializers.SerializerMethodField() download_url = serializers.SerializerMethodField()
uses_unknown_minified_code = serializers.SerializerMethodField()
class Meta: class Meta:
fields = FileSerializer.Meta.fields + ( fields = FileSerializer.Meta.fields + (
'diff', 'entries', 'selected_file', 'download_url' 'diff', 'entries', 'selected_file', 'download_url',
'uses_unknown_minified_code'
) )
model = File model = File
@ -384,6 +403,22 @@ class FileEntriesDiffSerializer(FileEntriesSerializer):
return entries return entries
def get_uses_unknown_minified_code(self, obj):
parent = self.context['parent_version']
selected_file = self.get_selected_file(obj)
for file in [parent.current_file, obj]:
try:
data = json.loads(file.validation.validation)
except FileValidation.DoesNotExist:
continue
prop = 'unknownMinifiedFiles'
minified_files = data.get('metadata', {}).get(prop, [])
if selected_file in minified_files:
return True
return False
class AddonCompareVersionSerializer(AddonBrowseVersionSerializer): class AddonCompareVersionSerializer(AddonBrowseVersionSerializer):
file = FileEntriesDiffSerializer(source='current_file') file = FileEntriesDiffSerializer(source='current_file')

Просмотреть файл

@ -1,4 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import json
from datetime import datetime from datetime import datetime
from django.core.cache import cache from django.core.cache import cache
@ -11,6 +13,7 @@ from olympia import amo
from olympia.amo.templatetags.jinja_helpers import absolutify from olympia.amo.templatetags.jinja_helpers import absolutify
from olympia.amo.tests import TestCase, addon_factory, version_factory from olympia.amo.tests import TestCase, addon_factory, version_factory
from olympia.amo.urlresolvers import reverse from olympia.amo.urlresolvers import reverse
from olympia.files.models import FileValidation
from olympia.lib.git import AddonGitRepository from olympia.lib.git import AddonGitRepository
from olympia.lib.tests.test_git import apply_changes from olympia.lib.tests.test_git import apply_changes
from olympia.reviewers.models import CannedResponse from olympia.reviewers.models import CannedResponse
@ -68,6 +71,7 @@ class TestFileEntriesSerializer(TestCase):
'filename': 'manifest.json' 'filename': 'manifest.json'
} }
)) ))
assert not data['uses_unknown_minified_code']
assert set(data['entries'].keys()) == { assert set(data['entries'].keys()) == {
'README.md', 'README.md',
@ -211,6 +215,24 @@ class TestFileEntriesSerializer(TestCase):
'71d4122c0f2f78e089136602f88dbf590f2fa04bb5bc417454bf21446d6cb4f0') '71d4122c0f2f78e089136602f88dbf590f2fa04bb5bc417454bf21446d6cb4f0')
assert data['entries']['icons/LICENSE']['sha256'] is None assert data['entries']['icons/LICENSE']['sha256'] is None
def test_uses_unknown_minified_code(self):
validation_data = {
'metadata': {
'unknownMinifiedFiles': ['content-script.js']
}
}
fobj = self.addon.current_version.current_file
FileValidation.objects.create(
file=fobj, validation=json.dumps(validation_data))
data = self.serialize(fobj, file='content-script.js')
assert data['uses_unknown_minified_code']
data = self.serialize(fobj, file='background-script.js')
assert not data['uses_unknown_minified_code']
class TestFileEntriesDiffSerializer(TestCase): class TestFileEntriesDiffSerializer(TestCase):
def setUp(self): def setUp(self):
@ -294,6 +316,7 @@ class TestFileEntriesDiffSerializer(TestCase):
'filename': 'manifest.json' 'filename': 'manifest.json'
} }
)) ))
assert not data['uses_unknown_minified_code']
assert set(data['entries'].keys()) == { assert set(data['entries'].keys()) == {
'manifest.json', 'README.md', 'test.txt'} 'manifest.json', 'README.md', 'test.txt'}
@ -301,6 +324,8 @@ class TestFileEntriesDiffSerializer(TestCase):
# The API always renders a diff, even for unmodified files. # The API always renders a diff, even for unmodified files.
assert data['diff'] is not None assert data['diff'] is not None
assert not data['uses_unknown_minified_code']
# Unmodified file # Unmodified file
manifest_data = data['entries']['manifest.json'] manifest_data = data['entries']['manifest.json']
assert manifest_data['depth'] == 0 assert manifest_data['depth'] == 0
@ -531,6 +556,54 @@ class TestFileEntriesDiffSerializer(TestCase):
assert data['diff'] is not None assert data['diff'] is not None
def test_uses_unknown_minified_code(self):
parent_version = self.addon.current_version
new_version = version_factory(
addon=self.addon, file_kw={
'filename': 'webextension_no_id.xpi',
'is_webextension': True,
}
)
AddonGitRepository.extract_and_commit_from_version(new_version)
file = self.addon.current_version.current_file
validation_data = {
'metadata': {
'unknownMinifiedFiles': ['README.md']
}
}
# Let's create a validation for the parent but not the current file
# which will result in us notifying the frontend of a minified file
# as well
current_validation = FileValidation.objects.create(
file=file, validation=json.dumps(validation_data))
data = self.serialize(
file, parent_version=parent_version, file='README.md')
assert data['uses_unknown_minified_code']
data = self.serialize(
file, parent_version=parent_version, file='manifest.json')
assert not data['uses_unknown_minified_code']
current_validation.delete()
# Creating a validation object for the current one works as well
FileValidation.objects.create(
file=parent_version.current_file,
validation=json.dumps(validation_data))
data = self.serialize(
file, parent_version=parent_version, file='README.md')
assert data['uses_unknown_minified_code']
data = self.serialize(
file, parent_version=parent_version, file='manifest.json')
assert not data['uses_unknown_minified_code']
class TestAddonBrowseVersionSerializer(TestCase): class TestAddonBrowseVersionSerializer(TestCase):
def setUp(self): def setUp(self):