def test_extract_header_with_additional_imgs(): file_obj = os.path.join( settings.ROOT, 'src/olympia/devhub/tests/addons/static_theme_tiled.zip') data = { 'images': { 'headerURL': 'empty.png', 'additional_backgrounds': [ 'transparent.gif', 'missing_&_ignored.png', 'weta_for_tiling.png' ] } } dest_path = tempfile.mkdtemp() header_file = dest_path + '/empty.png' additional_file_1 = dest_path + '/transparent.gif' additional_file_2 = dest_path + '/weta_for_tiling.png' assert not default_storage.exists(header_file) assert not default_storage.exists(additional_file_1) assert not default_storage.exists(additional_file_2) utils.extract_header_img(file_obj, data, dest_path) assert default_storage.exists(header_file) assert default_storage.size(header_file) == 332 assert default_storage.exists(additional_file_1) assert default_storage.size(additional_file_1) == 42 assert default_storage.exists(additional_file_2) assert default_storage.size(additional_file_2) == 93371
def test_extract_header_img_missing(self): data = {'images': {'headerURL': 'missing_file.png'}} dest_path = tempfile.mkdtemp() header_file = dest_path + '/missing_file.png' assert not default_storage.exists(header_file) utils.extract_header_img(self.file_obj, data, dest_path) assert not default_storage.exists(header_file)
def test_extract_header_img(self): data = {'images': {'headerURL': 'weta.png'}} dest_path = tempfile.mkdtemp() header_file = dest_path + '/weta.png' assert not default_storage.exists(header_file) utils.extract_header_img(self.file_obj, data, dest_path) assert default_storage.exists(header_file) assert default_storage.size(header_file) == 126447
def test_extract_header_img_missing(): file_obj = os.path.join( settings.ROOT, 'src/olympia/devhub/tests/addons/static_theme.zip') data = {'images': {'headerURL': 'missing_file.png'}} dest_path = tempfile.mkdtemp() header_file = dest_path + '/missing_file.png' assert not default_storage.exists(header_file) utils.extract_header_img(file_obj, data, dest_path) assert not default_storage.exists(header_file)
def test_extract_header_img_not_image(self): self.file_obj = os.path.join( settings.ROOT, 'src/olympia/devhub/tests/addons/static_theme_non_image.zip') data = {'images': {'headerURL': 'not_an_image.js'}} dest_path = tempfile.mkdtemp() header_file = dest_path + '/not_an_image.js' assert not default_storage.exists(header_file) utils.extract_header_img(self.file_obj, data, dest_path) assert not default_storage.exists(header_file)
def test_extract_header_with_additional_imgs(): file_obj = os.path.join( settings.ROOT, 'src/olympia/devhub/tests/addons/static_theme_tiled.zip') data = {'images': { 'headerURL': 'empty.png', 'additional_backgrounds': [ 'transparent.gif', 'missing_&_ignored.png', 'weta_for_tiling.png'] }} dest_path = tempfile.mkdtemp() header_file = dest_path + '/empty.png' additional_file_1 = dest_path + '/transparent.gif' additional_file_2 = dest_path + '/weta_for_tiling.png' assert not default_storage.exists(header_file) assert not default_storage.exists(additional_file_1) assert not default_storage.exists(additional_file_2) utils.extract_header_img(file_obj, data, dest_path) assert default_storage.exists(header_file) assert default_storage.size(header_file) == 332 assert default_storage.exists(additional_file_1) assert default_storage.size(additional_file_1) == 42 assert default_storage.exists(additional_file_2) assert default_storage.size(additional_file_2) == 93371
def from_upload(cls, upload, addon, platforms, channel, send_signal=True, source=None, is_beta=False, parsed_data=None): from olympia.addons.models import AddonFeatureCompatibility if addon.status == amo.STATUS_DISABLED: raise VersionCreateError( 'Addon is Mozilla Disabled; no new versions are allowed.') if parsed_data is None: parsed_data = utils.parse_addon(upload, addon) license_id = None if channel == amo.RELEASE_CHANNEL_LISTED: previous_version = addon.find_latest_version(channel=channel, exclude=()) if previous_version and previous_version.license_id: license_id = previous_version.license_id version = cls.objects.create( addon=addon, version=parsed_data['version'], license_id=license_id, source=source, channel=channel, ) log.info('New version: %r (%s) from %r' % (version, version.id, upload)) activity.log_create(amo.LOG.ADD_VERSION, version, addon) # Update the add-on e10s compatibility since we're creating a new # version that may change that. e10s_compatibility = parsed_data.get('e10s_compatibility') if e10s_compatibility is not None: feature_compatibility = ( AddonFeatureCompatibility.objects.get_or_create( addon=addon)[0]) feature_compatibility.update(e10s=e10s_compatibility) compatible_apps = {} for app in parsed_data.get('apps', []): compatible_apps[app.appdata] = ApplicationsVersions( version=version, min=app.min, max=app.max, application=app.id) compatible_apps[app.appdata].save() # See #2828: sometimes when we generate the filename(s) below, in # File.from_upload(), cache-machine is confused and has trouble # fetching the ApplicationsVersions that were just created. To work # around this we pre-generate version.compatible_apps and avoid the # queries completely. version._compatible_apps = compatible_apps if addon.type in [amo.ADDON_SEARCH, amo.ADDON_STATICTHEME]: # Search extensions and static themes 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, version, platform, parsed_data=parsed_data, is_beta=(is_beta and waffle.switch_is_active('beta-versions'))) version.inherit_nomination(from_statuses=[amo.STATUS_AWAITING_REVIEW]) version.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=version) # Generate a preview and icon for listed static themes if (addon.type == amo.ADDON_STATICTHEME and channel == amo.RELEASE_CHANNEL_LISTED): from olympia.addons.models import Preview # Avoid circular ref. dst_root = os.path.join(user_media_path('addons'), str(addon.id)) theme_data = parsed_data.get('theme', {}) version_root = os.path.join(dst_root, unicode(version.id)) utils.extract_header_img(version.all_files[0].file_path, theme_data, version_root) preview = Preview.objects.create(addon=addon, caption=unicode(version.version)) generate_static_theme_preview.delay(theme_data, version_root, preview) # 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=version, created=upload.created, now=now)) statsd.timing('devhub.version_created_from_upload', upload_time) return version
def from_upload(cls, upload, addon, selected_apps, channel, parsed_data=None): """ Create a Version instance and corresponding File(s) from a FileUpload, an Addon, a list of compatible app ids, a channel id and the parsed_data generated by parse_addon(). Note that it's the caller's responsability to ensure the file is valid. We can't check for that here because an admin may have overridden the validation results. """ assert parsed_data is not None from olympia.addons.models import AddonFeatureCompatibility if addon.status == amo.STATUS_DISABLED: raise VersionCreateError( 'Addon is Mozilla Disabled; no new versions are allowed.') license_id = None if channel == amo.RELEASE_CHANNEL_LISTED: previous_version = addon.find_latest_version( channel=channel, exclude=()) if previous_version and previous_version.license_id: license_id = previous_version.license_id version = cls.objects.create( addon=addon, version=parsed_data['version'], license_id=license_id, channel=channel, ) log.info( 'New version: %r (%s) from %r' % (version, version.id, upload)) activity.log_create(amo.LOG.ADD_VERSION, version, addon) # Update the add-on e10s compatibility since we're creating a new # version that may change that. e10s_compatibility = parsed_data.get('e10s_compatibility') if e10s_compatibility is not None: feature_compatibility = ( AddonFeatureCompatibility.objects.get_or_create(addon=addon)[0] ) feature_compatibility.update(e10s=e10s_compatibility) compatible_apps = {} for app in parsed_data.get('apps', []): if app.id not in selected_apps: # If the user chose to explicitly deselect Firefox for Android # we're not creating the respective `ApplicationsVersions` # which will have this add-on then be listed only for # Firefox specifically. continue compatible_apps[app.appdata] = ApplicationsVersions( version=version, min=app.min, max=app.max, application=app.id) compatible_apps[app.appdata].save() # See #2828: sometimes when we generate the filename(s) below, in # File.from_upload(), cache-machine is confused and has trouble # fetching the ApplicationsVersions that were just created. To work # around this we pre-generate version.compatible_apps and avoid the # queries completely. version._compatible_apps = compatible_apps # For backwards compatibility. We removed specific platform # support during submission but we don't handle it any different # beyond that yet. That means, we're going to simply set it # to `PLATFORM_ALL` and also have the backend create separate # files for each platform. Cleaning that up is another step. # Given the timing on this, we don't care about updates to legacy # add-ons as well. platforms = [amo.PLATFORM_ALL.id] # Create as many files as we have platforms. Update the all_files # cached property on the Version while we're at it, because we might # need it afterwards. version.all_files = [ File.from_upload( upload, version, platform, parsed_data=parsed_data) for platform in platforms] version.inherit_nomination(from_statuses=[amo.STATUS_AWAITING_REVIEW]) version.disable_old_files() # After the upload has been copied to all platforms, remove the upload. storage.delete(upload.path) version_uploaded.send(sender=version) # Generate a preview and icon for listed static themes if (addon.type == amo.ADDON_STATICTHEME and channel == amo.RELEASE_CHANNEL_LISTED): dst_root = os.path.join(user_media_path('addons'), str(addon.id)) theme_data = parsed_data.get('theme', {}) version_root = os.path.join(dst_root, unicode(version.id)) utils.extract_header_img( version.all_files[0].file_path, theme_data, version_root) generate_static_theme_preview( theme_data, version_root, version.pk) # 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=version, created=upload.created, now=now)) statsd.timing('devhub.version_created_from_upload', upload_time) return version
def from_upload(cls, upload, addon, platforms, channel, source=None, parsed_data=None): """ Create a Version instance and corresponding File(s) from a FileUpload, an Addon, a list of platform ids, a channel id and the parsed_data generated by parse_addon(). Note that it's the caller's responsability to ensure the file is valid. We can't check for that here because an admin may have overridden the validation results. """ assert parsed_data is not None from olympia.addons.models import AddonFeatureCompatibility if addon.status == amo.STATUS_DISABLED: raise VersionCreateError( 'Addon is Mozilla Disabled; no new versions are allowed.') license_id = None if channel == amo.RELEASE_CHANNEL_LISTED: previous_version = addon.find_latest_version( channel=channel, exclude=()) if previous_version and previous_version.license_id: license_id = previous_version.license_id version = cls.objects.create( addon=addon, version=parsed_data['version'], license_id=license_id, source=source, channel=channel, ) log.info( 'New version: %r (%s) from %r' % (version, version.id, upload)) activity.log_create(amo.LOG.ADD_VERSION, version, addon) # Update the add-on e10s compatibility since we're creating a new # version that may change that. e10s_compatibility = parsed_data.get('e10s_compatibility') if e10s_compatibility is not None: feature_compatibility = ( AddonFeatureCompatibility.objects.get_or_create(addon=addon)[0] ) feature_compatibility.update(e10s=e10s_compatibility) compatible_apps = {} for app in parsed_data.get('apps', []): compatible_apps[app.appdata] = ApplicationsVersions( version=version, min=app.min, max=app.max, application=app.id) compatible_apps[app.appdata].save() # See #2828: sometimes when we generate the filename(s) below, in # File.from_upload(), cache-machine is confused and has trouble # fetching the ApplicationsVersions that were just created. To work # around this we pre-generate version.compatible_apps and avoid the # queries completely. version._compatible_apps = compatible_apps if addon.type in [amo.ADDON_SEARCH, amo.ADDON_STATICTHEME]: # Search extensions and static themes are always for all platforms. platforms = [amo.PLATFORM_ALL.id] else: platforms = cls._make_safe_platform_files(platforms) # Create as many files as we have platforms. Update the all_files # cached property on the Version while we're at it, because we might # need it afterwards. version.all_files = [ File.from_upload( upload, version, platform, parsed_data=parsed_data) for platform in platforms] version.inherit_nomination(from_statuses=[amo.STATUS_AWAITING_REVIEW]) version.disable_old_files() # After the upload has been copied to all platforms, remove the upload. storage.delete(upload.path) version_uploaded.send(sender=version) # Generate a preview and icon for listed static themes if (addon.type == amo.ADDON_STATICTHEME and channel == amo.RELEASE_CHANNEL_LISTED): dst_root = os.path.join(user_media_path('addons'), str(addon.id)) theme_data = parsed_data.get('theme', {}) version_root = os.path.join(dst_root, unicode(version.id)) utils.extract_header_img( version.all_files[0].file_path, theme_data, version_root) preview = VersionPreview.objects.create(version=version) generate_static_theme_preview( theme_data, version_root, preview.pk) # 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=version, created=upload.created, now=now)) statsd.timing('devhub.version_created_from_upload', upload_time) return version