def raw_data(self):
        """Validate and return parsed JSON payload."""
        if not has_request_context():
            # `raw_data` doesn't make sense if we don't have a request
            raise AttributeError

        if not hasattr(self, '_raw_data'):
            if request.method in ('PUT', 'POST') or request.data:
                if request.mimetype and 'json' not in request.mimetype:
                    raise ValidationError({
                        'error':
                        "Please send valid JSON with a 'Content-Type: application/json' header."
                    })
                if request.headers.get('Transfer-Encoding') == 'chunked':
                    raise ValidationError({
                        'error':
                        "Chunked Transfer-Encoding is not supported."
                    })

                try:
                    self._raw_data = json.loads(
                        request.data.decode('utf-8'),
                        parse_constant=self._enforce_strict_json)
                except ValueError:
                    raise ValidationError(
                        {'error': 'The request contains invalid JSON.'})
                if not isinstance(self._raw_data, dict):
                    raise ValidationError(
                        {'error': 'JSON data must be a dict.'})
            else:
                self._raw_data = {}

        return self._raw_data
Exemple #2
0
def validate_data(doc, sender=None, project=None):
    d = fdict(doc, delimiter=delimiter)

    for key in list(d.keys()):
        key = key.strip()
        nodes = key.split(delimiter)
        is_quantity_key = int(nodes[-1] in quantity_keys)

        if len(nodes) > max_depth + is_quantity_key:
            raise ValidationError(
                {"error": f"max nesting ({max_depth}) exceeded for {key}"})

        if is_quantity_key:
            continue

        for node in nodes:
            for char in node:
                if char in invalidChars:
                    raise ValidationError({
                        "error":
                        f"invalid character '{char}' in {node} ({key})"
                    })

        value = str(d[key])
        words = value.split()
        try_quantity = bool(len(words) == 2 and is_float(words[0]))
        if try_quantity or isinstance(d[key], (int, float)):
            try:
                q = Q_(value).to_compact()
                if not q.check(0):
                    q.ito_reduced_units()
                if sender:
                    _key = key.replace(".", "__")
                    query = {"project": project, f"data__{_key}__exists": True}
                    sample = (sender.objects.only(f"data.{key}.unit").filter(
                        **query).first())
                    if sample:
                        sample_fdct = fdict(sample["data"],
                                            delimiter=delimiter)
                        q.ito(sample_fdct[f"{key}.unit"])
                v = Decimal(str(q.magnitude))
                vt = v.as_tuple()
                if vt.exponent < 0:
                    dgts = len(vt.digits)
                    dgts = max_dgts if dgts > max_dgts else dgts
                    v = f"{v:.{dgts}g}"
                    if try_quantity:
                        q = Q_(f"{v} {q.units}")
            except Exception as ex:
                raise ValidationError({"error": str(ex)})
            d[key] = {
                "display": str(q),
                "value": q.magnitude,
                "unit": str(q.units)
            }

    return d.to_dict_nested()
 def handle_validation_error(self, e):
     if isinstance(e, ValidationError):
         raise
     elif isinstance(e, mongoengine.ValidationError):
         raise ValidationError(serialize_mongoengine_validation_error(e))
     else:
         raise
Exemple #4
0
 def pre_save_post_validation(cls, sender, document, **kwargs):
     document.other = validate_data(document.other)
     if len(document.urls) > 5:
         raise ValidationError(
             {"error": f"too many URL references (max. 5)"})
     for label in document.urls.keys():
         len_label = len(label)
         if len_label < 3 or len_label > 8:
             raise ValidationError({
                 "error":
                 f"length of URL label {label} should be 3-8 characters"
             })
         for char in label:
             if char in invalidChars:
                 raise ValidationError(
                     {"error": f"invalid character '{char}' in {label}"})
Exemple #5
0
def date_prep(field, value, op):
    try:
        value = isoparse(value)
    except ValueError:
        raise ValidationError("Invalid date format - use ISO 8601")

    return {f"{field}__{op}": value}
Exemple #6
0
    def post(self, **kwargs):
        if kwargs.pop("pk"):
            raise NotFound("Did you mean to use PUT?")

        # Set the view_method on a resource instance
        raw_data = self._resource.raw_data
        if isinstance(raw_data, dict):
            # create single object
            self._resource.view_method = methods.Create
            return self.create_object()
        elif isinstance(raw_data, list):
            limit = self._resource.bulk_update_limit
            if len(raw_data) > limit:
                raise ValidationError(f"Can only create {limit} documents at once")
            raw_data_deque = deque(raw_data)
            self._resource.view_method = methods.BulkCreate
            count = 0
            tic = time.perf_counter()
            while len(raw_data_deque):
                self._resource._raw_data = raw_data_deque.popleft()
                skip = bool(raw_data_deque)
                kwargs = {"skip_post_save": skip}
                if not skip:
                    kwargs["remaining_time"] = TIMEOUT - (time.perf_counter() - tic)

                self.create_object(**kwargs)
                count += 1
                dt = time.perf_counter() - tic
                avg = dt / count
                if dt + avg > TIMEOUT:
                    break

            msg = f"Created {count} objects in {dt:0.1f}s ({avg:0.1f}s per object)."
            print(msg)
            ret = {"count": count}
            if raw_data_deque:
                remain = len(raw_data_deque)
                msg += f" Remaining {remain} objects skipped to avoid Server Timeout."
                ret["warning"] = msg
            return ret, "201 Created"
        else:
            raise ValidationError("wrong payload type")
    def get_skip_and_limit(self, params=None):
        """
        Perform validation and return sanitized values for _skip and _limit
        params of the request that's currently being processed.
        """
        max_limit = self.get_max_limit()
        if params is None:
            params = self.params
        if self.paginate:
            # _limit and _skip validation
            if not isint(params.get('_limit', 1)):
                raise ValidationError({
                    'error':
                    '_limit must be an integer (got "%s" instead).' %
                    params['_limit']
                })
            if not isint(params.get('_skip', 1)):
                raise ValidationError({
                    'error':
                    '_skip must be an integer (got "%s" instead).' %
                    params['_skip']
                })
            if params.get('_limit') and int(params['_limit']) > max_limit:
                raise ValidationError({
                    'error':
                    "The limit you set is larger than the maximum limit for this resource (max_limit = %d)."
                    % max_limit
                })
            if params.get('_skip') and int(params['_skip']) < 0:
                raise ValidationError({
                    'error':
                    '_skip must be a non-negative integer (got "%s" instead).'
                    % params['_skip']
                })

            limit = min(int(params.get('_limit', self.default_limit)),
                        max_limit)
            # Fetch one more so we know if there are more results.
            return int(params.get('_skip', 0)), limit
        else:
            return 0, max_limit
    def validate_request(self, obj=None):
        """
        Validate the request that's currently being processed and fill in
        the self.data dict that'll later be used to save/update an object.

        `obj` points to the object that's being updated, or is empty if a new
        object is being created.
        """
        # When creating or updating a single object, delegate the validation
        # to a more specific subresource, if it exists
        if (request.method == 'PUT' and obj) or request.method == 'POST':
            subresource = self._subresource(obj)
            if subresource:
                subresource._raw_data = self._raw_data
                subresource.validate_request(obj=obj)
                self.data = subresource.data
                return

        # Don't work on original raw data, we may reuse the resource for bulk
        # updates.
        self.data = self.raw_data.copy()

        # Do renaming in two passes to prevent potential multiple renames
        # depending on dict traversal order.
        # E.g. if a -> b, b -> c, then a should never be renamed to c.
        fields_to_delete = []
        fields_to_update = {}
        for k, v in self._rename_fields.items():
            if v in self.data:
                fields_to_update[k] = self.data[v]
                fields_to_delete.append(v)
        for k in fields_to_delete:
            del self.data[k]
        for k, v in fields_to_update.items():
            self.data[k] = v

        # If CleanCat schema exists on this resource, use it to perform the
        # validation
        if self.schema:
            if request.method == 'PUT' and obj is not None:
                obj_data = dict([(key, getattr(obj, key))
                                 for key in obj._fields.keys()])
            else:
                obj_data = None

            schema = self.schema(self.data, obj_data)
            try:
                self.data = schema.full_clean()
            except SchemaValidationError:
                raise ValidationError({
                    'field-errors': schema.field_errors,
                    'errors': schema.errors
                })
    def get_objects(self, qs=None, qfilter=None):
        """
        Return objects fetched from the database based on all the parameters
        of the request that's currently being processed.

        Params:
        - Custom queryset can be passed via `qs`. Otherwise `self.get_queryset`
          is used.
        - Pass `qfilter` function to modify the queryset.
        """
        params = self.params

        custom_qs = True
        if qs is None:
            custom_qs = False
            qs = self.get_queryset()

        # If a queryset filter was provided, pass our current queryset in and
        # get a new one out
        if qfilter:
            qs = qfilter(qs)

        # Apply filters and ordering, based on the params supplied by the
        # request
        qs = self.apply_filters(qs, params)
        qs = self.apply_ordering(qs, params)

        # Apply limit and skip to the queryset
        limit = None
        if self.view_method == methods.BulkUpdate:
            # limit the number of objects that can be bulk-updated at a time
            qs = qs.limit(self.bulk_update_limit)
        elif not custom_qs:
            # no need to skip/limit if a custom `qs` was provided
            skip, limit = self.get_skip_and_limit(params)
            qs = qs.skip(skip).limit(limit+1)

        # Needs to be at the end as it returns a list, not a queryset
        if self.select_related:
            qs = qs.select_related()

        # Evaluate the queryset
        objs = list(qs)

        # Raise a validation error if bulk update would result in more than
        # bulk_update_limit updates
        if self.view_method == methods.BulkUpdate and len(objs) >= self.bulk_update_limit:
            raise ValidationError({
                'errors': ["It's not allowed to update more than %d objects at once" % self.bulk_update_limit]
            })

        # Determine the value of has_more
        if self.view_method != methods.BulkUpdate and self.paginate:
            has_more = len(objs) > limit
            if has_more:
                objs = objs[:-1]
        else:
            has_more = None

        # bulk-fetch related resources for moar speed
        if self.related_resources_hints:
            self.fetch_related_resources(objs, self.get_requested_fields(params=params))

        return objs, has_more
Exemple #10
0
def send_email(to, subject, template):
    try:
        sns_client.publish(TopicArn=to, Message=template, Subject=subject)
    except Exception as ex:
        raise ValidationError({"error": str(ex)})