def from_upload(cls, upload, addon, platforms, send_signal=True): data = utils.parse_addon(upload, addon) try: license = addon.versions.latest().license_id except Version.DoesNotExist: license = None max_len = cls._meta.get_field_by_name('_developer_name')[0].max_length developer = data.get('developer_name', '')[:max_len] v = cls.objects.create(addon=addon, version=data['version'], license_id=license, _developer_name=developer) log.info('New version: %r (%s) from %r' % (v, v.id, upload)) AV = ApplicationsVersions for app in data.get('apps', []): AV(version=v, min=app.min, max=app.max, application_id=app.id).save() if addon.type == amo.ADDON_SEARCH: # Search extensions are always for all platforms. platforms = [Platform.objects.get(id=amo.PLATFORM_ALL.id)] else: platforms = cls._make_safe_platform_files(platforms) for platform in platforms: File.from_upload(upload, v, platform, parse_data=data) v.disable_old_files() # After the upload has been copied to all platforms, remove the upload. storage.delete(upload.path) if send_signal: version_uploaded.send(sender=v) return v
def from_upload(cls, upload, addon, platforms): data = utils.parse_addon(upload.path, addon) try: license = addon.versions.latest().license_id except Version.DoesNotExist: license = None v = cls.objects.create(addon=addon, version=data['version'], license_id=license) log.info('New version: %r (%s) from %r' % (v, v.id, upload)) # appversions AV = ApplicationsVersions for app in data.get('apps', []): AV(version=v, min=app.min, max=app.max, application_id=app.id).save() if addon.type == amo.ADDON_SEARCH: # Search extensions are always for all platforms. platforms = [Platform.objects.get(id=amo.PLATFORM_ALL.id)] else: platforms = cls._make_safe_platform_files(platforms) for platform in platforms: File.from_upload(upload, v, platform, parse_data=data) v.disable_old_files() # After the upload has been copied to all # platforms, remove the upload. upload.path.unlink() version_uploaded.send(sender=v) return v
def from_upload(cls, upload, addon, platforms, send_signal=True): data = utils.parse_addon(upload, addon) try: license = addon.versions.latest().license_id except Version.DoesNotExist: license = None max_len = cls._meta.get_field_by_name('_developer_name')[0].max_length developer = data.get('developer_name', '')[:max_len] v = cls.objects.create(addon=addon, version=data['version'], license_id=license, _developer_name=developer) log.info('New version: %r (%s) from %r' % (v, v.id, upload)) AV = ApplicationsVersions for app in data.get('apps', []): AV(version=v, min=app.min, max=app.max, application_id=app.id).save() if addon.type in [amo.ADDON_SEARCH, amo.ADDON_WEBAPP]: # Search extensions and webapps are always for all platforms. platforms = [Platform.objects.get(id=amo.PLATFORM_ALL.id)] else: platforms = cls._make_safe_platform_files(platforms) if addon.is_webapp(): from mkt.webapps.models import AppManifest # Create AppManifest if we're a Webapp. # Note: This must happen before we call `File.from_upload`. manifest = utils.WebAppParser().get_json_data(upload) AppManifest.objects.create( version=v, manifest=json.dumps(manifest)) for platform in platforms: File.from_upload(upload, v, platform, parse_data=data) if addon.is_webapp(): # Update supported locales from manifest. # Note: This needs to happen after we call `File.from_upload`. update_supported_locales_single.apply_async( args=[addon.id], kwargs={'latest': True}, eta=datetime.datetime.now() + datetime.timedelta(seconds=settings.NFS_LAG_DELAY)) v.disable_old_files() # After the upload has been copied to all platforms, remove the upload. storage.delete(upload.path) if send_signal: version_uploaded.send(sender=v) # If packaged app and app is blocked, put in escalation queue. if (addon.is_webapp() and addon.is_packaged and addon.status == amo.STATUS_BLOCKED): # To avoid circular import. from editors.models import EscalationQueue EscalationQueue.objects.create(addon=addon) return v
def from_upload(cls, upload, addon, platforms, send_signal=True, source=None, is_beta=False): data = utils.parse_addon(upload, addon) try: license = addon.versions.latest().license_id except Version.DoesNotExist: license = None max_len = cls._meta.get_field_by_name('_developer_name')[0].max_length developer = data.get('developer_name', '')[:max_len] v = cls.objects.create( addon=addon, version=data['version'], license_id=license, _developer_name=developer, source=source ) log.info('New version: %r (%s) from %r' % (v, v.id, upload)) AV = ApplicationsVersions for app in data.get('apps', []): AV(version=v, min=app.min, max=app.max, application=app.id).save() if addon.type == amo.ADDON_SEARCH: # Search extensions are always for all platforms. platforms = [amo.PLATFORM_ALL.id] else: platforms = cls._make_safe_platform_files(platforms) for platform in platforms: File.from_upload(upload, v, platform, parse_data=data, is_beta=is_beta) v.disable_old_files() # After the upload has been copied to all platforms, remove the upload. storage.delete(upload.path) if send_signal: version_uploaded.send(sender=v) # Track the time it took from first upload through validation # (and whatever else) until a version was created. upload_start = utc_millesecs_from_epoch(upload.created) now = datetime.datetime.now() now_ts = utc_millesecs_from_epoch(now) upload_time = now_ts - upload_start log.info('Time for version {version} creation from upload: {delta}; ' 'created={created}; now={now}' .format(delta=upload_time, version=v, created=upload.created, now=now)) statsd.timing('devhub.version_created_from_upload', upload_time) return v
def test_beta_version_non_public(self): # Only public add-ons can get beta versions. upload = self.upload('beta-extension') data = parse_addon(upload.path) self.addon.update(status=amo.STATUS_LITE) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.status, amo.STATUS_UNREVIEWED)
def test_jetpack_version(self): upload = self.upload('jetpack') f = File.from_upload(upload, self.version, self.platform) file_ = File.objects.get(id=f.id) eq_(file_.jetpack_version, '1.0b4') eq_(file_.builder_version, None) eq_(['jetpack'], [t.tag_text for t in self.addon.tags.all()])
def test_jetpack_with_invalid_json(self): upload = self.upload('jetpack_invalid') f = File.from_upload(upload, self.version, self.platform) file_ = File.objects.get(id=f.id) eq_(file_.jetpack_version, None) eq_(file_.builder_version, None) assert not self.addon.tags.exists()
def test_lite_to_unreviewed(self): upload = self.upload('extension') data = parse_addon(upload.path) self.addon.update(status=amo.STATUS_LITE) eq_(self.addon.status, amo.STATUS_LITE) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.status, amo.STATUS_UNREVIEWED)
def test_trusted_lite_to_lite(self): upload = self.upload('extension') data = parse_addon(upload.path) self.addon.update(status=amo.STATUS_LITE, trusted=True) eq_(self.addon.status, amo.STATUS_LITE) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.status, amo.STATUS_LITE)
def test_public_to_beta(self): upload = self.upload('beta-extension') data = parse_addon(upload.path) self.addon.update(status=amo.STATUS_PUBLIC) eq_(self.addon.status, amo.STATUS_PUBLIC) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.status, amo.STATUS_BETA)
def test_public_to_unreviewed(self): upload = self.upload('extension') d = parse_addon(upload.path) self.addon.update(status=amo.STATUS_PUBLIC) eq_(self.addon.status, amo.STATUS_PUBLIC) f = File.from_upload(upload, self.version, self.platform, parse_data=d) eq_(f.status, amo.STATUS_UNREVIEWED)
def test_trusted_public_to_public(self): upload = self.upload('extension') d = parse_addon(upload.path) self.addon.update(status=amo.STATUS_PUBLIC, trusted=True) eq_(self.addon.status, amo.STATUS_PUBLIC) f = File.from_upload(upload, self.version, self.platform, parse_data=d) eq_(f.status, amo.STATUS_PUBLIC)
def test_trusted_litenominated_to_litenominated(self): upload = self.upload("extension") data = parse_addon(upload.path) with mock.patch("addons.models.Addon.update_status"): # mock update_status because it doesn't like Addons without files. self.addon.update(status=amo.STATUS_LITE_AND_NOMINATED, trusted=True) eq_(self.addon.status, amo.STATUS_LITE_AND_NOMINATED) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.status, amo.STATUS_LITE_AND_NOMINATED)
def test_litenominated_to_unreviewed(self): upload = self.upload('extension') data = parse_addon(upload.path) with mock.patch('addons.models.Addon.update_status'): # mock update_status because it doesn't like Addons without files. self.addon.update(status=amo.STATUS_LITE_AND_NOMINATED) eq_(self.addon.status, amo.STATUS_LITE_AND_NOMINATED) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.status, amo.STATUS_UNREVIEWED)
def test_file_validation(self): upload = self.upload('jetpack') file = File.from_upload(upload, self.version, self.platform) fv = FileValidation.objects.get(file=file) eq_(fv.validation, upload.validation) eq_(fv.valid, True) eq_(fv.errors, 0) eq_(fv.warnings, 1) eq_(fv.notices, 2)
def from_upload(cls, upload, addon, platforms, send_signal=True): data = utils.parse_addon(upload, addon) try: license = addon.versions.latest().license_id except Version.DoesNotExist: license = None v = cls.objects.create(addon=addon, version=data['version'], license_id=license) log.info('New version: %r (%s) from %r' % (v, v.id, upload)) # appversions AV = ApplicationsVersions for app in data.get('apps', []): AV(version=v, min=app.min, max=app.max, application_id=app.id).save() if addon.type in [amo.ADDON_SEARCH, amo.ADDON_WEBAPP]: # Search extensions and webapps are always for all platforms. platforms = [Platform.objects.get(id=amo.PLATFORM_ALL.id)] else: platforms = cls._make_safe_platform_files(platforms) for platform in platforms: File.from_upload(upload, v, platform, parse_data=data) if addon.type == amo.ADDON_WEBAPP: update_supported_locales_single.apply_async( args=[addon.id], kwargs={'latest': True}, eta=datetime.datetime.now() + datetime.timedelta(seconds=settings.NFS_LAG_DELAY)) v.disable_old_files() # After the upload has been copied to all platforms, remove the upload. storage.delete(upload.path) if send_signal: version_uploaded.send(sender=v) # If packaged app and app is blocked, put in escalation queue. if (addon.is_webapp() and addon.is_packaged and addon.status == amo.STATUS_BLOCKED): # To avoid circular import. from editors.models import EscalationQueue EscalationQueue.objects.create(addon=addon) return v
def test_trusted_litenominated_to_litenominated(self): upload = self.upload('extension') d = parse_addon(upload.path) with mock.patch('addons.models.Addon.update_status'): # mock update_status because it doesn't like Addons without files. self.addon.update(status=amo.STATUS_LITE_AND_NOMINATED, trusted=True) assert self.addon.status == amo.STATUS_LITE_AND_NOMINATED f = File.from_upload(upload, self.version, self.platform, parse_data=d) assert f.status == amo.STATUS_LITE
def version_add_file(request, addon_id, addon, version_id): version = get_object_or_404(Version, pk=version_id, addon=addon) form = forms.NewFileForm(request.POST, addon=addon, version=version) if not form.is_valid(): return json_view.error(form.errors) upload = form.cleaned_data['upload'] new_file = File.from_upload(upload, version, form.cleaned_data['platform']) upload.path.unlink() file_form = forms.FileFormSet(prefix='files', queryset=version.files.all()) form = [f for f in file_form.forms if f.instance == new_file] return jingo.render(request, 'devhub/includes/version_file.html', {'form': form[0], 'addon': addon})
def from_upload(cls, upload, addon, platforms): data = utils.parse_addon(upload.path, addon) try: license = addon.versions.latest().license_id except Version.DoesNotExist: license = None v = cls.objects.create(addon=addon, version=data['version'], license_id=license) log.debug('New version: %r (%s) from %r' % (v, v.id, upload)) # appversions AV = ApplicationsVersions for app in data.get('apps', []): AV(version=v, min=app.min, max=app.max, application_id=app.id).save() if addon.type == amo.ADDON_SEARCH: # Search extensions are always for all platforms. platforms = [Platform.objects.get(id=amo.PLATFORM_ALL.id)] else: new_plats = [] # Transform PLATFORM_ALL_MOBILE into specific mobile platform # files (e.g. Android, Maemo). # TODO(Kumar) Stop doing this when allmobile is supported # for downloads. See bug 646268. for p in platforms: if p.id == amo.PLATFORM_ALL_MOBILE.id: for mobi_p in (set(amo.MOBILE_PLATFORMS.keys()) - set([amo.PLATFORM_ALL_MOBILE.id])): new_plats.append(Platform.objects.get(id=mobi_p)) else: new_plats.append(p) platforms = new_plats for platform in platforms: File.from_upload(upload, v, platform, parse_data=data) v.disable_old_files() # After the upload has been copied to all # platforms, remove the upload. upload.path.unlink() return v
def test_validator_sets_require_chrome(self): validation = json.dumps( { "errors": 0, "success": True, "warnings": 0, "notices": 0, "message_tree": {}, "messages": [], "metadata": {"version": "1.0", "name": "gK0Bes Bot", "id": "gkobes@gkobes", "requires_chrome": True}, } ) upload = self.get_upload(filename="extension.xpi", validation=validation) version = Version.objects.filter(addon__pk=3615)[0] file_ = File.from_upload(upload, version, amo.PLATFORM_LINUX.id) eq_(file_.requires_chrome, True)
def test_validator_sets_binary_via_content(self): validation = json.dumps({ "errors": 0, "success": True, "warnings": 0, "notices": 0, "message_tree": {}, "messages": [], "metadata": { "contains_binary_content": True, "version": "1.0", "name": "gK0Bes Bot", "id": "gkobes@gkobes", } }) upload = self.get_upload(filename='extension.xpi', validation=validation) version = Version.objects.filter(addon__pk=3615)[0] file_ = File.from_upload(upload, version, amo.PLATFORM_LINUX.id) eq_(file_.binary, True)
def test_file_hash_paranoia(self): upload = self.upload('extension') f = File.from_upload(upload, self.version, self.platform) assert f.hash.startswith('sha256:035ae07b4988711')
def test_trusted_litenominated_to_litenominated(self): upload = self.upload('extension') data = parse_addon(upload.path) self.addon.update(status=amo.STATUS_LITE_AND_NOMINATED, trusted=True) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.status, amo.STATUS_LITE_AND_NOMINATED)
def test_size_small(self): upload = self.upload('alt-rdf') f = File.from_upload(upload, self.version, self.platform) eq_(f.size, 675)
def test_utf8(self): upload = self.upload(u'jétpack') self.version.addon.name = u'jéts!' f = File.from_upload(upload, self.version, self.platform) eq_(f.filename, u'jets-0.1-mac.xpi')
def test_jetpack_builder_version(self): upload = self.upload('jetpack_builder') f = File.from_upload(upload, self.version, self.platform) file_ = File.objects.get(id=f.id) eq_(file_.builder_version, '1.1.1.1')
def test_no_restart_dictionary(self): upload = self.upload('dictionary-explicit-type-test') d = parse_addon(upload.path) f = File.from_upload(upload, self.version, self.platform, parse_data=d) assert f.no_restart
def test_trusted_public_to_public(self): upload = self.upload('extension') data = parse_addon(upload.path) self.addon.update(status=amo.STATUS_PUBLIC, trusted=True) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.status, amo.STATUS_PUBLIC)
def test_langpack_extension(self): upload = self.upload('langpack.xpi') f = File.from_upload(upload, self.version, self.platform) eq_(f.filename.endswith('.xpi'), True)
def test_extension_extension(self): upload = self.upload('extension.xpi') f = File.from_upload(upload, self.version, self.platform) eq_(f.filename.endswith('.xpi'), True) assert not self.addon.tags.exists()
def test_theme_extension(self): upload = self.upload('theme.jar') f = File.from_upload(upload, self.version, self.platform) eq_(f.filename.endswith('.xpi'), True)
def test_strict_compat(self): upload = self.upload('strict-compat') data = parse_addon(upload.path) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.strict_compatibility, True)
def test_beta_version(self): upload = self.upload('beta-extension') data = parse_addon(upload.path) self.addon.status = amo.STATUS_PUBLIC f = File.from_upload(upload, self.version, self.platform, data) eq_(f.status, amo.STATUS_BETA)
def test_theme_extension(self): upload = self.upload('theme.jar') f = File.from_upload(upload, self.version, self.platform) eq_(f.filename.endswith('.xpi'), True)
def test_utf8(self): upload = self.upload(u'jétpack') self.version.addon.name = u'jéts!' f = File.from_upload(upload, self.version, self.platform) eq_(f.filename, u'jets-0.1-mac.xpi')
def test_size_small(self): upload = self.upload('alt-rdf') f = File.from_upload(upload, self.version, self.platform) eq_(f.size, 1)
def test_no_restart_false(self): upload = self.upload('extension') d = parse_addon(upload.path) f = File.from_upload(upload, self.version, self.platform, parse_data=d) assert not f.no_restart
def test_file_hash(self): upload = self.upload('jetpack') f = File.from_upload(upload, self.version, self.platform) assert f.hash.startswith('sha256:') assert len(f.hash) == 64 + 7 # 64 for hash, 7 for 'sha256:'
def test_no_restart_true(self): upload = self.upload('jetpack') d = parse_addon(upload.path) f = File.from_upload(upload, self.version, self.platform, parse_data=d) assert f.no_restart
def test_filename_no_extension(self): upload = self.upload('jetpack') # Remove the exension. upload.name = upload.name.rsplit('.', 1)[0] f = File.from_upload(upload, self.version, self.platform) eq_(f.filename, 'xxx-0.1-mac.xpi')
def test_file_hash(self): upload = self.upload('jetpack') f = File.from_upload(upload, self.version, self.platform) eq_(f.hash, upload.hash)
def test_not_multi_package(self): upload = self.upload('extension') file_ = File.from_upload(upload, self.version, self.platform) assert not file_.is_multi_package
def test_filename(self): upload = self.upload('jetpack') f = File.from_upload(upload, self.version, self.platform) eq_(f.filename, 'xxx-0.1-mac.xpi')
def test_size(self): upload = self.upload('extension') f = File.from_upload(upload, self.version, self.platform) eq_(f.size, 2264)
def test_is_jetpack(self): upload = self.upload('jetpack') f = File.from_upload(upload, self.version, self.platform) assert File.objects.get(id=f.id).jetpack
def test_file_hash_no_paranoia(self): upload = self.upload('extension') upload.hash = 'oops' f = File.from_upload(upload, self.version, self.platform) assert f.hash.startswith('oops')
def test_search_extension(self): upload = self.upload('search.xml') f = File.from_upload(upload, self.version, self.platform) eq_(f.filename.endswith('.xml'), True)
def test_lite_to_unreviewed(self): upload = self.upload('extension') data = parse_addon(upload.path) self.addon.update(status=amo.STATUS_LITE) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.status, amo.STATUS_UNREVIEWED)
def test_langpack_extension(self): upload = self.upload('langpack.xpi') f = File.from_upload(upload, self.version, self.platform) eq_(f.filename.endswith('.xpi'), True)
def test_strict_compat(self): upload = self.upload('strict-compat') data = parse_addon(upload.path) f = File.from_upload(upload, self.version, self.platform, data) eq_(f.strict_compatibility, True)
def test_file_hash_paranoia(self): upload = self.upload('extension') f = File.from_upload(upload, self.version, self.platform) assert f.hash.startswith('sha256:035ae07b4988711')
def test_extension_extension(self): upload = self.upload('extension.xpi') f = File.from_upload(upload, self.version, self.platform) eq_(f.filename.endswith('.xpi'), True) assert not self.addon.tags.exists()
def test_size(self): upload = self.upload('extension') f = File.from_upload(upload, self.version, self.platform) eq_(f.size, 2)
def test_search_extension(self): upload = self.upload('search.xml') f = File.from_upload(upload, self.version, self.platform) eq_(f.filename.endswith('.xml'), True)
def test_jetpack_with_invalid_json(self): upload = self.upload('jetpack_invalid') f = File.from_upload(upload, self.version, self.platform) file_ = File.objects.get(id=f.id) eq_(file_.jetpack_version, None) eq_(file_.builder_version, None)