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:
Родитель
4b740c4bd9
Коммит
e3e846a086
|
@ -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):
|
||||||
|
|
Загрузка…
Ссылка в новой задаче