def validate_upgrade(self, provider, product_spec): if 'version' in product_spec and 'productSpecCharacteristic' in product_spec: # Extract product needed characteristics asset_t, media_type, url, asset_id = self.parse_characteristics(product_spec) is_digital = asset_t is not None and media_type is not None and url is not None if is_digital: asset, lock = self._get_upgrading_asset(asset_t, url, product_spec['id']) self._to_downgrade = asset self._validate_product_characteristics(asset, provider, asset_t, media_type) # Check product version if not is_valid_version(product_spec['version']): raise ProductError('The field version does not have a valid format') if not is_lower_version(asset.old_versions[-1].version, product_spec['version']): raise ProductError('The provided version is not higher that the previous one') # Attach new info asset.version = product_spec['version'] asset.save() # Release asset lock lock.unlock_document()
def _get_upgrading_asset(self, asset_t, url, product_id): asset_type, assets = self._get_asset_resources(asset_t, url, None, product_id) if not len(assets): raise ProductError( 'The URL specified in the location characteristic does not point to a valid digital asset' ) asset = assets[0] # Lock the access to the asset lock = DocumentLock('wstore_resource', asset.pk, 'asset') lock.wait_document() asset = Resource.objects.get(pk=asset.pk) # Check that the asset is in upgrading state if asset.state != 'upgrading': raise ProductError( 'There is not a new version of the specified digital asset') if asset.product_id != product_id: raise ProductError( 'The specified digital asset is included in other product spec' ) return asset, lock
def parse_characteristics(self, product_spec): expected_chars = { 'asset type': [], 'media type': [], 'location': [], 'asset': [] } asset_type = None media_type = None location = None asset_id = None if 'productSpecCharacteristic' in product_spec: terms = [] # Extract the needed characteristics for processing digital assets is_digital = False for char in product_spec['productSpecCharacteristic']: if char['name'].lower() in expected_chars: is_digital = True expected_chars[char['name'].lower()].append( self._get_characteristic_value(char)) if char['name'].lower() == 'license': terms.append(self._get_characteristic_value(char)) for char_name in expected_chars: # Validate the existence of the characteristic if not len(expected_chars[char_name]) and is_digital: raise ProductError( 'Digital product specifications must contain a ' + char_name + ' characteristic') # Validate that only a value has been provided if len(expected_chars[char_name]) > 1: raise ProductError( 'The product specification must not contain more than one ' + char_name + ' characteristic') if len(terms) > 1: raise ProductError( 'The product specification must not contain more than one license characteristic' ) self._has_terms = len(terms) > 0 if is_digital: asset_type = expected_chars['asset type'][0] media_type = expected_chars['media type'][0] location = expected_chars['location'][0] asset_id = expected_chars['asset'][0] return asset_type, media_type, location, asset_id
def _validate_product(self, provider, asset_t, media_type, url): asset_type, assets = self._get_asset_resouces(asset_t, url) if len(assets): # The asset is already registered asset = assets[0] if asset.product_id is not None: raise ConflictError( 'There is already an existing product specification defined for the given digital asset' ) self._validate_product_characteristics(asset, provider, asset_t, media_type) asset.has_terms = self._has_terms asset.save() else: # The asset is not yet included, this option is only valid for URL assets without metadata site = settings.SITE if 'FILE' in asset_type.formats and ( ('URL' not in asset_type.formats) or ('URL' in asset_type.formats and url.startswith(site))): raise ProductError( 'The URL specified in the location characteristic does not point to a valid digital asset' ) if asset_type.form: raise ProductError( 'Automatic creation of digital assets with expected metadata is not supported' ) # Validate media type if len(asset_type.media_types) and media_type.lower() not in [ media.lower() for media in asset_type.media_types ]: raise ProductError( 'The media type characteristic included in the product specification is not valid for the given asset type' ) # Create the new asset model asset = Resource.objects.create(has_terms=self._has_terms, resource_path='', download_link=url, provider=provider, content_type=media_type) # The asset model is included to the rollback list so if an exception is raised in the plugin post validation # the asset model would be deleted self.rollback_logger['models'].append(asset) return asset
def _validate_product_characteristics(self, asset, provider, asset_t, media_type): if asset.provider != provider: raise PermissionDenied('You are not authorized to use the digital asset specified in the location characteristic') if asset.resource_type != asset_t: raise ProductError('The specified asset type is different from the asset one') if asset.content_type.lower() != media_type.lower(): raise ProductError('The provided media type characteristic is different from the asset one') if asset.is_public: raise ProductError('It is not allowed to create products with public assets')
def _get_asset_resources(self, asset_t, url, provider_id=None, prod_id=None): # Search the asset type asset_type = ResourcePlugin.objects.get(name=asset_t) # Validate location format if not is_valid_url(url): raise ProductError( 'The location characteristic included in the product specification is not a valid URL' ) # Use the location to retrieve the attached asset if not (provider_id is None): assets = Resource.objects.filter(download_link=url, provider=provider_id) elif not (prod_id is None): assets = Resource.objects.filter(download_link=url, product_id=prod_id) else: assets = Resource.objects.filter(download_link=url) return asset_type, assets
def _build_bundle(self, provider, product_spec): if 'bundledProductSpecification' not in product_spec or not len(product_spec['bundledProductSpecification']) > 1: raise ProductError('A product spec bundle must contain at least two bundled product specs') assets = self._extract_digital_assets(product_spec['bundledProductSpecification']) if len(assets) and len(assets) != len(product_spec['bundledProductSpecification']): raise ProductError('Mixed product bundles are not allowed. All bundled products must be digital or physical') if len(assets): Resource.objects.create( has_terms=self._has_terms, resource_path='', download_link='', provider=provider, content_type='bundle', bundled_assets=assets )
def _get_plugin_model(name): try: plugin_model = ResourcePlugin.objects.get(name=name) except: # Validate resource type raise ProductError( 'The given product specification contains a not supported asset type: ' + name) return plugin_model
def validate_creation(self, provider, product_spec): # Extract product needed characteristics asset_t, media_type, url, asset_id = self.parse_characteristics(product_spec) is_digital = asset_t is not None and media_type is not None and url is not None # Product spec bundles are intended for creating composed products, it cannot contain its own asset if product_spec['isBundle'] and is_digital: raise ProductError('Product spec bundles cannot define digital assets') if not product_spec['isBundle'] and is_digital: # Process the new digital product self._validate_product(provider, asset_t, media_type, url, asset_id) elif product_spec['isBundle'] and not is_digital: # The product bundle may contain digital products already registered self._build_bundle(provider, product_spec)
def _prod_val_product_error(self): self.validator_instance.validate.side_effect = ProductError('Missing product information')
def _get_characteristic_value(self, characteristic): if len(characteristic['productSpecCharacteristicValue']) > 1: raise ProductError('The characteristic ' + characteristic['name'] + ' must not contain multiple values') return characteristic['productSpecCharacteristicValue'][0]['value']
def _validate_product(self, provider, asset_t, media_type, url): # Search the asset type asset_type = ResourcePlugin.objects.get(name=asset_t) # Validate media type if len(asset_type.media_types ) and media_type not in asset_type.media_types: raise ProductError( 'The media type characteristic included in the product specification is not valid for the given asset type' ) # Validate location format if not is_valid_url(url): raise ProductError( 'The location characteristic included in the product specification is not a valid URL' ) site = Context.objects.all()[0].site # If the asset is a file it must have been uploaded if 'FILE' in asset_type.formats and ( ('URL' not in asset_type.formats) or ('URL' in asset_type.formats and url.startswith(site.domain))): try: asset = Resource.objects.get(download_link=url) except: raise ProductError( 'The URL specified in the location characteristic does not point to a valid digital asset' ) if asset.provider != provider: raise PermissionDenied( 'You are not authorized to use the digital asset specified in the location characteristic' ) if asset.content_type != media_type.lower(): raise ProductError( 'The specified media type characteristic is different from the one of the provided digital asset' ) asset.has_terms = self._has_terms asset.save() else: # If the asset is an URL and the resource model is created, that means that # the asset have been already included in another product resources = Resource.objects.filter(download_link=url) error = False for res in resources: # The asset has been attached so it already exists if res.product_id: error = True else: res.delete() if error: raise ProductError( 'There is already an existing product specification defined for the given digital asset' ) # Create the new asset model asset = Resource.objects.create(has_terms=self._has_terms, resource_path='', download_link=url, provider=provider, content_type=media_type) self.rollback_logger['models'].append(asset) return asset