def run_validation(self, data): """ Validates 'data' dict. Validates that all keys of 'data' are relative paths. Validates that all values of 'data' are URLs for an existing Artifact. Args: data (dict): A dict mapping relative paths inside the Content to the corresponding Artifact URLs. Returns: A dict mapping relative paths inside the Content to the corresponding Artifact instances. Raises: :class:`rest_framework.exceptions.ValidationError`: When one of the Artifacts does not exist or one of the paths is not a relative path. """ ret = {} for relative_path, url in data.items(): if os.path.isabs(relative_path): raise serializers.ValidationError(_("Relative path can't start with '/'. " "{0}").format(relative_path)) artifactfield = RelatedField(view_name='artifacts-detail', queryset=models.Artifact.objects.all(), source='*', initial=url) try: artifact = artifactfield.run_validation(data=url) ret[relative_path] = artifact except serializers.ValidationError as e: # Append the URL of missing Artifact to the error message e.detail[0] = "%s %s" % (e.detail[0], url) raise e return ret
class TaskSerializer(ModelSerializer): _href = IdentityField(view_name='tasks-detail') state = serializers.CharField( help_text=_("The current state of the task. The possible values include:" " 'waiting', 'skipped', 'running', 'completed', 'failed' and 'canceled'."), read_only=True ) started_at = serializers.DateTimeField( help_text=_("Timestamp of the when this task started execution."), read_only=True ) finished_at = serializers.DateTimeField( help_text=_("Timestamp of the when this task stopped execution."), read_only=True ) non_fatal_errors = serializers.JSONField( help_text=_("A JSON Object of non-fatal errors encountered during the execution of this " "task."), read_only=True ) error = serializers.JSONField( help_text=_("A JSON Object of a fatal error encountered during the execution of this " "task."), read_only=True ) worker = RelatedField( help_text=_("The worker associated with this task." " This field is empty if a worker is not yet assigned."), read_only=True, view_name='workers-detail' ) parent = RelatedField( help_text=_("The parent task that spawned this task."), read_only=True, view_name='tasks-detail' ) spawned_tasks = RelatedField( help_text=_("Any tasks spawned by this task."), many=True, read_only=True, view_name='tasks-detail' ) progress_reports = ProgressReportSerializer( many=True, read_only=True ) created_resources = CreatedResourceSerializer( help_text=_('Resources created by this task.'), many=True, read_only=True, view_name='None' # This is a polymorphic field. The serializer does not need a view name. ) class Meta: model = models.Task fields = ModelSerializer.Meta.fields + ('state', 'started_at', 'finished_at', 'non_fatal_errors', 'error', 'worker', 'parent', 'spawned_tasks', 'progress_reports', 'created_resources')
class RepositoryVersionDistributionSerializer(BaseDistributionSerializer): repository = RelatedField( required=False, help_text=_('The latest RepositoryVersion for this Repository will be served.'), queryset=models.Repository.objects.all(), view_name='repositories-detail', allow_null=True ) repository_version = NestedRelatedField( required=False, help_text=_('RepositoryVersion to be served'), queryset=models.RepositoryVersion.objects.exclude(complete=False), view_name='versions-detail', allow_null=True, lookup_field='number', parent_lookup_kwargs={'repository_pk': 'repository__pk'}, ) class Meta: abstract = True fields = BaseDistributionSerializer.Meta.fields + ( 'repository', 'repository_version', ) def validate(self, data): super().validate(data) if 'repository' in data and 'repository_version' in data: msg = _("The attributes 'repository' and 'repository_version' must be used" "exclusively.") raise serializers.ValidationError(msg) return data
class ExportSerializer(ModelSerializer): """ Base serializer for Exports. """ pulp_href = ExportIdentityField() task = RelatedField( help_text=_('A URI of the task that ran the Export.'), queryset=models.Task.objects.all(), view_name='tasks-detail', ) exported_resources = ExportedResourcesSerializer( help_text=_('Resources that were exported.'), read_only=True, many=True, ) params = serializers.JSONField( help_text=_( 'Any additional parameters that were used to create the export.'), required=False, ) class Meta: model = models.Export fields = ModelSerializer.Meta.fields + ('task', 'exported_resources', 'params')
class ProgressReportSerializer(ModelSerializer): message = serializers.CharField( help_text=_("The message shown to the user for the progress report."), read_only=True) state = serializers.CharField(help_text=_( "The current state of the progress report. The possible values are:" " 'waiting', 'skipped', 'running', 'completed', 'failed' and 'canceled'." " The default is 'waiting'."), read_only=True) total = serializers.IntegerField(help_text=_( "The total count of items to be handled by the ProgressBar."), read_only=True) done = serializers.IntegerField( help_text=_("The count of items already processed. Defaults to 0."), read_only=True) suffix = serializers.CharField( help_text=_("The suffix to be shown with the progress report."), read_only=True, allow_blank=True) task = RelatedField( help_text=_("The task associated with this progress report."), read_only=True, view_name='tasks-detail') class Meta: model = models.ProgressReport # this serializer is meant to be nested inside Task serializer, # so it will not have its own endpoint, that's why # we need to explicitly define fields to exclude '_href' field. fields = ('message', 'state', 'total', 'done', 'suffix', 'task')
class ExportSerializer(ModelSerializer): """ Base serializer for Exports. """ pulp_href = ExportIdentityField() task = RelatedField( help_text=_("A URI of the task that ran the Export."), queryset=models.Task.objects.all(), view_name="tasks-detail", required=False, allow_null=True, ) exported_resources = ExportedResourceField( help_text=_("Resources that were exported."), many=True, read_only=True, view_name= "None", # This is a polymorphic field. The serializer does not need a view name. ) params = serializers.JSONField( help_text=_( "Any additional parameters that were used to create the export."), read_only=True, ) class Meta: model = models.Export fields = ModelSerializer.Meta.fields + ("task", "exported_resources", "params")
def run_validation(self, data): """ Validates 'data' dict. Validates that all keys of 'data' are relative paths. Validates that all values of 'data' are URLs for an existing Artifact. Args: data (dict): A dict mapping relative paths inside the Content to the corresponding Artifact URLs. Returns: A dict mapping relative paths inside the Content to the corresponding Artifact instances. Raises: :class:`rest_framework.exceptions.ValidationError`: When one of the Artifacts does not exist or one of the paths is not a relative path or the field is missing. """ ret = {} if data is empty: raise serializers.ValidationError( _("artifacts field must be specified.")) for relative_path, url in data.items(): relative_path_validator(relative_path) artifactfield = RelatedField( view_name="artifacts-detail", queryset=models.Artifact.objects.all(), source="*", initial=url, ) try: artifact = artifactfield.run_validation(data=url) ret[relative_path] = artifact except serializers.ValidationError as e: # Append the URL of missing Artifact to the error message e.detail[0] = "%s %s" % (e.detail[0], url) raise e return ret
class PublicationSerializer(MasterModelSerializer): _href = DetailIdentityField() repository_version = NestedRelatedField( view_name='versions-detail', lookup_field='number', parent_lookup_kwargs={'repository_pk': 'repository__pk'}, queryset=models.RepositoryVersion.objects.all(), required=False, ) repository = RelatedField( help_text=_('A URI of the repository to be published.'), required=False, label=_('Repository'), queryset=models.Repository.objects.all(), view_name='repositories-detail', write_only=True ) def validate(self, data): if hasattr(self, 'initial_data'): validate_unknown_fields(self.initial_data, self.fields) repository = data.pop('repository', None) # not an actual field on publication repository_version = data.get('repository_version') if not repository and not repository_version: raise serializers.ValidationError( _("Either the 'repository' or 'repository_version' need to be specified")) elif not repository and repository_version: return data elif repository and not repository_version: version = models.RepositoryVersion.latest(repository) if version: new_data = {'repository_version': version} new_data.update(data) return new_data else: raise serializers.ValidationError( detail=_('Repository has no version available to create Publication from')) raise serializers.ValidationError( _("Either the 'repository' or 'repository_version' need to be specified " "but not both.") ) class Meta: abstract = True model = models.Publication fields = MasterModelSerializer.Meta.fields + ( 'publisher', 'repository_version', 'repository' )
class ImportSerializer(ModelSerializer): """Serializer for Imports.""" pulp_href = ImportIdentityField() task = RelatedField( help_text=_('A URI of the Task that ran the Import.'), queryset=models.Task.objects.all(), view_name='tasks-detail', ) params = serializers.JSONField( help_text=_('Any parameters that were used to create the import.'), ) class Meta: model = models.Importer fields = ModelSerializer.Meta.fields + ('task', 'params')
class WorkerSerializer(ModelSerializer): pulp_href = IdentityField(view_name="workers-detail") name = serializers.CharField(help_text=_("The name of the worker."), read_only=True) last_heartbeat = serializers.DateTimeField(help_text=_( "Timestamp of the last time the worker talked to the service."), read_only=True) current_task = RelatedField( help_text=_( "The task this worker is currently executing, or empty if the worker is not " "currently assigned to a task."), read_only=True, view_name="tasks-detail", ) class Meta: model = models.Worker fields = ModelSerializer.Meta.fields + ("name", "last_heartbeat", "current_task")
class TaskSerializer(ModelSerializer): pulp_href = IdentityField(view_name="tasks-detail") state = serializers.CharField( help_text=_( "The current state of the task. The possible values include:" " 'waiting', 'skipped', 'running', 'completed', 'failed', 'canceled' and 'canceling'." ), read_only=True, ) name = serializers.CharField(help_text=_("The name of task.")) logging_cid = serializers.CharField( help_text=_("The logging correlation id associated with this task") ) started_at = serializers.DateTimeField( help_text=_("Timestamp of the when this task started execution."), read_only=True ) finished_at = serializers.DateTimeField( help_text=_("Timestamp of the when this task stopped execution."), read_only=True ) error = serializers.DictField( child=serializers.JSONField(), help_text=_( "A JSON Object of a fatal error encountered during the execution of this task." ), read_only=True, ) worker = RelatedField( help_text=_( "The worker associated with this task." " This field is empty if a worker is not yet assigned." ), read_only=True, view_name="workers-detail", ) parent_task = RelatedField( help_text=_("The parent task that spawned this task."), read_only=True, view_name="tasks-detail", ) child_tasks = RelatedField( help_text=_("Any tasks spawned by this task."), many=True, read_only=True, view_name="tasks-detail", ) task_group = RelatedField( help_text=_("The task group that this task is a member of."), read_only=True, view_name="task-groups-detail", ) progress_reports = ProgressReportSerializer(many=True, read_only=True) created_resources = CreatedResourceSerializer( help_text=_("Resources created by this task."), many=True, read_only=True, view_name="None", # This is a polymorphic field. The serializer does not need a view name. ) reserved_resources_record = serializers.ListField( child=serializers.CharField(), help_text=_("A list of resources required by that task."), read_only=True, ) class Meta: model = models.Task fields = ModelSerializer.Meta.fields + ( "state", "name", "logging_cid", "started_at", "finished_at", "error", "worker", "parent_task", "child_tasks", "task_group", "progress_reports", "created_resources", "reserved_resources_record", )
class DistributionSerializer(ModelSerializer): _href = IdentityField( view_name='distributions-detail' ) name = serializers.CharField( help_text=_('A unique distribution name. Ex, `rawhide` and `stable`.'), validators=[validators.MaxLengthValidator( models.Distribution._meta.get_field('name').max_length, message=_('Distribution name length must be less than {} characters').format( models.Distribution._meta.get_field('name').max_length )), UniqueValidator(queryset=models.Distribution.objects.all())] ) base_path = serializers.CharField( help_text=_('The base (relative) path component of the published url. Avoid paths that \ overlap with other distribution base paths (e.g. "foo" and "foo/bar")'), validators=[validators.MaxLengthValidator( models.Distribution._meta.get_field('base_path').max_length, message=_('Distribution base_path length must be less than {} characters').format( models.Distribution._meta.get_field('base_path').max_length )), UniqueValidator(queryset=models.Distribution.objects.all()), ] ) publisher = DetailRelatedField( required=False, help_text=_('Publications created by this publisher and repository are automatically' 'served as defined by this distribution'), queryset=models.Publisher.objects.all(), allow_null=True ) content_guard = DetailRelatedField( required=False, help_text=_('An optional content-guard.'), queryset=models.ContentGuard.objects.all(), allow_null=True ) publication = RelatedField( required=False, help_text=_('The publication being served as defined by this distribution'), queryset=models.Publication.objects.exclude(complete=False), view_name='publications-detail', allow_null=True ) repository = serializers.HyperlinkedRelatedField( required=False, help_text=_('Publications created by this repository and publisher are automatically' 'served as defined by this distribution'), queryset=models.Repository.objects.all(), view_name='repositories-detail', allow_null=True ) base_url = BaseURLField( source='base_path', read_only=True, help_text=_('The URL for accessing the publication as defined by this distribution.') ) class Meta: model = models.Distribution fields = ModelSerializer.Meta.fields + ( 'name', 'base_path', 'publisher', 'publication', 'base_url', 'repository', 'content_guard', ) def _validate_path_overlap(self, path): # look for any base paths nested in path search = path.split("/")[0] q = Q(base_path=search) for subdir in path.split("/")[1:]: search = "/".join((search, subdir)) q |= Q(base_path=search) # look for any base paths that nest path q |= Q(base_path__startswith='{}/'.format(path)) qs = models.Distribution.objects.filter(q) if self.instance is not None: qs = qs.exclude(pk=self.instance.pk) match = qs.first() if match: raise serializers.ValidationError(detail=_("Overlaps with existing distribution '" "{}'").format(match.name)) return path def validate_base_path(self, path): self._validate_relative_path(path) return self._validate_path_overlap(path) def validate(self, data): super().validate(data) if 'publisher' in data: publisher = data['publisher'] elif self.instance: publisher = self.instance.publisher else: publisher = None if 'repository' in data: repository = data['repository'] elif self.instance: repository = self.instance.repository else: repository = None if publisher and not repository: raise serializers.ValidationError({'repository': _("Repository must be set if " "publisher is set.")}) if repository and not publisher: raise serializers.ValidationError({'publisher': _("Publisher must be set if " "repository is set.")}) return data
class BaseDistributionSerializer(ModelSerializer): _href = IdentityField(view_name='distributions-detail') name = serializers.CharField( help_text=_('A unique distribution name. Ex, `rawhide` and `stable`.'), validators=[ validators.MaxLengthValidator( models.Distribution._meta.get_field('name').max_length, message=_( 'Distribution name length must be less than {} characters' ).format( models.Distribution._meta.get_field('name').max_length)), UniqueValidator(queryset=models.Distribution.objects.all()) ]) base_path = serializers.CharField( help_text= _('The base (relative) path component of the published url. Avoid paths that \ overlap with other distribution base paths (e.g. "foo" and "foo/bar")' ), validators=[ validators.MaxLengthValidator( models.Distribution._meta.get_field('base_path').max_length, message= _('Distribution base_path length must be less than {} characters' ).format( models.Distribution._meta.get_field( 'base_path').max_length)), UniqueValidator(queryset=models.Distribution.objects.all()), ]) publisher = DetailRelatedField( required=False, help_text=_( 'Publications created by this publisher and repository are automatically' 'served as defined by this distribution'), queryset=models.Publisher.objects.all(), allow_null=True) content_guard = DetailRelatedField( required=False, help_text=_('An optional content-guard.'), queryset=models.ContentGuard.objects.all(), allow_null=True) publication = RelatedField( required=False, help_text=_( 'The publication being served as defined by this distribution'), queryset=models.Publication.objects.exclude(complete=False), view_name='publications-detail', allow_null=True) repository = serializers.HyperlinkedRelatedField( required=False, help_text=_( 'Publications created by this repository and publisher are automatically' 'served as defined by this distribution'), queryset=models.Repository.objects.all(), view_name='repositories-detail', allow_null=True) remote = DetailRelatedField( required=False, help_text= _('Remote that can be used to fetch content when using pull-through caching.' ), queryset=models.Remote.objects.all(), allow_null=True) class Meta: fields = ModelSerializer.Meta.fields + ( 'name', 'publisher', 'publication', 'repository', 'content_guard', 'remote', ) def validate(self, data): super().validate(data) if 'publisher' in data: publisher = data['publisher'] elif self.instance: publisher = self.instance.publisher else: publisher = None if 'repository' in data: repository = data['repository'] elif self.instance: repository = self.instance.repository else: repository = None if publisher and not repository: raise serializers.ValidationError({ 'repository': _("Repository must be set if " "publisher is set.") }) if repository and not publisher: raise serializers.ValidationError({ 'publisher': _("Publisher must be set if " "repository is set.") }) return data