def extract_pk(uri): """ Resolve a resource URI to a simple PK value. Provides a means to resolve an href passed in a POST body to a primary key. Doesn't assume anything about whether the resource corresponding to the URI passed in actually exists. Note: Cannot be used with nested URIs where the PK of the final resource is not present. e.g. RepositoryVersion URI consists of repository PK and version number - no RepositoryVersion PK is present within the URI. Args: uri (str): A resource URI. Returns: primary_key (uuid.uuid4): The primary key of the resource extracted from the URI. Raises: rest_framework.exceptions.ValidationError: on invalid URI. """ try: match = resolve(urlparse(uri).path) except Resolver404: raise DRFValidationError(detail=_("URI not valid: {u}").format( u=uri)) try: return match.kwargs["pk"] except KeyError: raise DRFValidationError( "URI does not contain an unqualified resource PK")
def get_resource(uri, model=None): """ Resolve a resource URI to an instance of the resource. Provides a means to resolve an href passed in a POST body to an instance of the resource. Args: uri (str): A resource URI. model (django.models.Model): A model class. If not provided, the method automatically determines the used model from the resource URI. Returns: django.models.Model: The resource fetched from the DB. Raises: rest_framework.exceptions.ValidationError: on invalid URI or resource not found. """ try: match = resolve(urlparse(uri).path) except Resolver404: raise DRFValidationError(detail=_("URI not valid: {u}").format(u=uri)) if "pk" in match.kwargs: kwargs = {"pk": match.kwargs["pk"]} else: kwargs = {} for key, value in match.kwargs.items(): if key.endswith("_pk"): kwargs["{}__pk".format(key[:-3])] = value else: kwargs[key] = value if model is None: model = match.func.cls.queryset.model try: return model.objects.get(**kwargs) except model.MultipleObjectsReturned: raise DRFValidationError( detail=_("URI {u} matches more than one {m}.").format( u=uri, m=model._meta.model_name ) ) except model.DoesNotExist: raise DRFValidationError( detail=_("URI {u} not found for {m}.").format(u=uri, m=model._meta.model_name) ) except ValidationError: raise DRFValidationError(detail=_("ID invalid: {u}").format(u=kwargs["pk"])) except FieldError: raise DRFValidationError( detail=_("URI {u} is not a valid {m}.").format(u=uri, m=model._meta.model_name) )
def sync(self, request, pk): """ Dispatches a sync task. """ repository = self.get_object() serializer = RpmRepositorySyncURLSerializer(data=request.data, context={ "request": request, "repository_pk": pk }) serializer.is_valid(raise_exception=True) remote = serializer.validated_data.get("remote", repository.remote) mirror = serializer.validated_data.get("mirror") sync_policy = serializer.validated_data.get("sync_policy") skip_types = serializer.validated_data.get("skip_types") optimize = serializer.validated_data.get("optimize") if not sync_policy: sync_policy = SYNC_POLICIES.ADDITIVE if not mirror else SYNC_POLICIES.MIRROR_COMPLETE # validate some invariants that involve repository-wide settings. if sync_policy in (SYNC_POLICIES.MIRROR_COMPLETE, SYNC_POLICIES.MIRROR_CONTENT_ONLY): err_msg = ( "Cannot use '{}' in combination with a 'mirror_complete' or " "'mirror_content_only' sync policy.") if repository.retain_package_versions > 0: raise DRFValidationError( err_msg.format("retain_package_versions")) if sync_policy == SYNC_POLICIES.MIRROR_COMPLETE: err_msg = "Cannot use '{}' in combination with a 'mirror_complete' sync policy." if repository.autopublish: raise DRFValidationError(err_msg.format("autopublish")) if skip_types: raise DRFValidationError(err_msg.format("skip_types")) result = dispatch( tasks.synchronize, shared_resources=[remote], exclusive_resources=[repository], kwargs={ "sync_policy": sync_policy, "remote_pk": str(remote.pk), "repository_pk": str(repository.pk), "skip_types": skip_types, "optimize": optimize, }, ) return OperationPostponedResponse(result, request)
def sync(self, request, pk): """ Dispatches a sync task. """ repository = self.get_object() serializer = RpmRepositorySyncURLSerializer( data=request.data, context={"request": request, "repository_pk": pk} ) serializer.is_valid(raise_exception=True) remote = serializer.validated_data.get("remote", repository.remote) mirror = serializer.validated_data.get("mirror") skip_types = serializer.validated_data.get("skip_types") optimize = serializer.validated_data.get("optimize") if repository.retain_package_versions > 0 and mirror: raise DRFValidationError("Cannot use 'retain_package_versions' with mirror-mode sync") result = enqueue_with_reservation( tasks.synchronize, [repository, remote], kwargs={ "mirror": mirror, "remote_pk": remote.pk, "repository_pk": repository.pk, "skip_types": skip_types, "optimize": optimize, }, ) return OperationPostponedResponse(result, request)
def get_resource(uri, model): """ Resolve a resource URI to an instance of the resource. Provides a means to resolve an href passed in a POST body to an instance of the resource. Args: uri (str): A resource URI. model (django.models.Model): A model class. Returns: django.models.Model: The resource fetched from the DB. Raises: rest_framework.exceptions.ValidationError: on invalid URI or resource not found. """ try: match = resolve(urlparse(uri).path) except Resolver404: raise DRFValidationError(detail=_('URI not valid: {u}').format( u=uri)) if 'pk' in match.kwargs: kwargs = {'pk': match.kwargs['pk']} else: kwargs = {} for key, value in match.kwargs.items(): if key.endswith('_pk'): kwargs["{}__pk".format(key[:-3])] = value else: kwargs[key] = value try: return model.objects.get(**kwargs) except model.MultipleObjectsReturned: raise DRFValidationError( detail=_('URI {u} matches more than one {m}.').format( u=uri, m=model._meta.model_name)) except model.DoesNotExist: raise DRFValidationError(detail=_('URI {u} not found for {m}.'). format(u=uri, m=model._meta.model_name)) except ValidationError: raise DRFValidationError(detail=_('ID invalid: {u}').format( u=kwargs['pk'])) except FieldError: raise DRFValidationError(detail=_('URI {u} is not a valid {m}.'). format(u=uri, m=model._meta.model_name))
def filter(self, qs, value): """ Args: qs (django.db.models.query.QuerySet): The Model queryset value (string): label search querry Returns: Queryset of the Models filtered by label(s) Raises: rest_framework.exceptions.ValidationError: on invalid search string """ if value is None: # user didn't supply a value return qs for term in value.split(","): match = re.match(r"(!?[\w\s]+)(=|!=|~)?(.*)?", term) if not match: raise DRFValidationError( _("Invalid search term: '{}'.").format(term)) key, op, val = match.groups() if key.startswith("!") and op: raise DRFValidationError( _("Cannot use an operator with '{}'.").format(key)) if op == "=": labels = Label.objects.filter(key=key, value=val) qs = qs.filter(pulp_labels__in=labels) elif op == "!=": labels = Label.objects.filter(key=key).exclude(value=val) qs = qs.filter(pulp_labels__in=labels) elif op == "~": labels = Label.objects.filter(key=key, value__icontains=val) qs = qs.filter(pulp_labels__in=labels) else: # 'foo', '!foo' if key.startswith("!"): labels = Label.objects.filter(key=key[1:]) qs = qs.exclude(pulp_labels__in=labels) else: labels = Label.objects.filter(key=key) qs = qs.filter(pulp_labels__in=labels) return qs
def _check_author_link_permissions(self, serializer_row): """ Checks if legitimate to link to a resource We consider that legitimacy comes from having right to edit the resource we link to. """ if (not self.model.has_author_direct_link() and not self.model.is_author_class()): referent_obj = self.model.get_directly_linked_obj(serializer_row) perm = self._get_owner_permission() if not perm.can_user_do(self.request.user, referent_obj, 'change'): raise DRFValidationError( 'You tried to link to a forbidden resource : {}'.format( referent_obj))
def _process_config(self, config): """ Change the hrefs into pks within config. This method also implicitly validates that the hrefs map to objects and it returns a list of repos so that the task can lock on them. """ result = [] # exclusive use of the destination repos is needed since new repository versions are being # created, but source repos can be accessed in a read-only fashion in parallel, so long # as there are no simultaneous modifications. shared_repos = [] exclusive_repos = [] for entry in config: r = dict() source_version = NamedModelViewSet().get_resource( entry["source_repo_version"], RepositoryVersion) dest_repo = NamedModelViewSet().get_resource( entry["dest_repo"], RpmRepository) r["source_repo_version"] = source_version.pk r["dest_repo"] = dest_repo.pk shared_repos.append(source_version.repository) exclusive_repos.append(dest_repo) if "dest_base_version" in entry: try: r["dest_base_version"] = dest_repo.versions.get( number=entry["dest_base_version"]).pk except RepositoryVersion.DoesNotExist: message = _( "Version {version} does not exist for repository " "'{repo}'.").format(version=entry["dest_base_version"], repo=dest_repo.name) raise DRFValidationError(detail=message) if entry.get("content") is not None: r["content"] = [] for c in entry["content"]: r["content"].append(NamedModelViewSet().extract_pk(c)) result.append(r) return result, shared_repos, exclusive_repos
def _process_config(self, config): """ Change the hrefs into pks within config. This method also implicitly validates that the hrefs map to objects and it returns a list of repos so that the task can lock on them. """ result = [] exclusive_resources = [] shared_resources = [] for entry in config: r = dict() source_version = NamedModelViewSet().get_resource( entry["source_repo_version"], RepositoryVersion ) dest_repo = NamedModelViewSet().get_resource(entry["dest_repo"], AnsibleRepository) r["source_repo_version"] = source_version.pk r["dest_repo"] = dest_repo.pk exclusive_resources.append(dest_repo) shared_resources.append(source_version.repository) if "dest_base_version" in entry: try: r["dest_base_version"] = dest_repo.versions.get( number=entry["dest_base_version"] ).pk except RepositoryVersion.DoesNotExist: message = _( "Version {version} does not exist for repository " "'{repo}'." ).format(version=entry["dest_base_version"], repo=dest_repo.name) raise DRFValidationError(detail=message) if entry.get("content") is not None: r["content"] = [] for c in entry["content"]: r["content"].append(NamedModelViewSet().extract_pk(c)) result.append(r) return result, exclusive_resources, shared_resources
def update(self, instance, validated_data, *args, **kwargs): try: return super().update(instance, validated_data, *args, **kwargs) except DjangoValidationError as e: raise DRFValidationError(e.error_list)
def create(self, validated_data, *args, **kwargs): try: return super().create(validated_data, *args, **kwargs) except DjangoValidationError as e: raise DRFValidationError(e.error_list)