コード例 #1
0
 def validate_asset_types(self, key, value):
     """Validate if values for asset_types are correct."""
     max_types = 1
     if len(value) > max_types:
         raise ValidationError(message='Invalid number of type of assets',
                               name=key)
     members = AssetTypes.__members__
     for item in value:
         if item not in members:
             raise ValidationError(message='Invalid type of asset',
                                   name=key)
     return value
コード例 #2
0
ファイル: order.py プロジェクト: BriefyHQ/briefy.leica
    def validate_requirement_items(self, key: str,
                                   values: t.Sequence) -> t.Sequence:
        """Validate if requirement_items payload is in the correct format.

        :param key: Attribute name.
        :param values: Requirement items payload.
        :return: Validated payload.
        """
        request = self.request
        user_id = str(request.user.id) if request else None
        current_value = list(
            self.requirement_items) if self.requirement_items else []
        if values:
            for item in values:
                if not item.get('created_by') and user_id:
                    item['created_by'] = user_id
                if not item.get('created_at'):
                    item['created_at'] = datetime_utcnow().isoformat()

        if values or current_value:
            requirements_schema = RequirementItems()
            try:
                values = requirements_schema.deserialize(values)
            except colander.Invalid as exc:
                raise ValidationError(
                    message='Invalid payload for requirement_items', name=key)

        return values
コード例 #3
0
 def validate_add_order_roles(self, key, value):
     """Validate if values for add_order_roles are correct."""
     all_groups = [item.value for item in Groups]
     for item in value:
         if item not in all_groups:
             raise ValidationError(message='Invalid role', name=key)
     return value
コード例 #4
0
def create_sorting_from_query_params(
        query_params: dict,
        allowed_fields: t.Sequence[str],
        default: str = '',
        default_direction: int = 1) -> t.Sequence[Sort]:
    """Process a query parameters dictionary and return a list of Sort objects.

    :param query_params: Dictionary containing query_params for a request.
    :param allowed_fields: List of fields that support sorting.
    :param default: Default field for sorting.
    :param default_direction: Default direction for sorting.
    :return: list of Sort objects.
    """
    specified = query_params.get('_sort', '').split(',')
    sorting = []
    for field in specified:
        field = field.strip()
        m = re.match(r'^([\-+]?)([\.\w]+)$', field)
        if m:
            order, field = m.groups()
            if field not in allowed_fields:
                raise ValidationError(
                    message=f'Unknown sort field \'{field}\'',
                    location='querystring',
                    name=field)
            direction = -1 if order == '-' else 1
            sorting.append(Sort(field, direction))
    if not sorting and (default and default_direction):
        sorting.append(Sort(default, default_direction))
    return sorting
コード例 #5
0
ファイル: order.py プロジェクト: BriefyHQ/briefy.leica
    def availability(self, value: list):
        """Set availabilities for an Order."""
        project = self.project
        timezone = self.timezone
        if isinstance(timezone, str):
            timezone = pytz.timezone(timezone)
        user = self.workflow.context
        not_pm = 'g:briefy_pm' not in user.groups if user else True
        validated_value = []

        if value and len(value) != len(set(value)):
            msg = 'Availability dates should be different.'
            raise ValidationError(message=msg, name='availability')

        if value and timezone and project:
            if not_pm:
                availability_window = project.availability_window
            else:
                # allow less than 24hs for PMs but not in the past
                availability_window = 0
            now = datetime.now(tz=timezone)
            for availability in value:
                if isinstance(availability, str):
                    availability = parse(availability)
                tz_availability = availability.astimezone(timezone)
                date_diff = tz_availability - now
                if date_diff.days < availability_window:
                    msg = 'Both availability dates must be at least {window} days from now.'
                    msg = msg.format(window=availability_window)
                    raise ValidationError(message=msg, name='availability')

                validated_value.append(availability.isoformat())
        elif value:
            logger.warn(
                'Could not check availability dates. Order {id}'.format(
                    id=self.id))

        self._availability = validated_value if validated_value else value
コード例 #6
0
def order_charges_update(current_value: t.Sequence, new_value: t.Sequence) -> t.Sequence:
    """Function to handle OrderCharges modification.

    :param current_value: Current OrderCharges.
    :param new_value: New OrderCharges.
    :return: OrderCharges to be persisted.
    """
    processed = []
    current_dict = {line['id']: line for line in current_value}
    to_update = {line['id']: line for line in new_value if line.get('id') in current_dict}
    to_delete = {k: v for k, v in current_dict.items() if k not in to_update}

    # Validate if we can delete
    for key in to_delete:
        line = to_delete[key]
        invoice_number = line.get('invoice_number')
        invoice_date = line.get('invoice_date')
        if invoice_date and invoice_number:
            raise ValidationError(
                'Not possible to delete an already invoiced item.', name='additional_charges'
            )

    for line in new_value:
        line_id = line.get('id')
        invoice_date = line.get('invoice_date')
        if isinstance(invoice_date, date):
            line['invoice_date'] = invoice_date.isoformat()
        if line_id not in to_update:
            line['created_at'] = utc_now_serialized()
            if not line_id:
                line['id'] = str(uuid4())
            current_item = line
        else:
            current_item = current_dict[line_id]
            for field in _FIELDS:
                current_item[field] = line[field]
        processed.append(current_item)

    return processed
コード例 #7
0
ファイル: order.py プロジェクト: BriefyHQ/briefy.leica
    def validate_additional_charges(self, key: str,
                                    value: t.Sequence) -> t.Sequence:
        """Validate if additional_charges payload is in the correct format.

        :param key: Attribute name.
        :param value: Additional charges payload.
        :return: Validated payload
        """
        current_value = list(
            self.additional_charges) if self.additional_charges else []
        if value or current_value:
            charges_schema = OrderCharges()
            try:
                new_value = charges_schema.deserialize(value)
            except colander.Invalid as e:
                raise ValidationError(
                    message='Invalid payload for additional_charges', name=key)

            value = order_charges_update(current_value, new_value)

            # Force total_order_price recalculation
            flag_modified(self, 'actual_order_price')
            flag_modified(self, 'additional_charges')
        return value
コード例 #8
0
def raise_validation_exception(message: str, key: str):
    """Raise a ValidationException."""
    raise ValidationError(message, key)
コード例 #9
0
    def filter_query(self,
                     query: Query,
                     query_params: t.Optional[dict] = None) -> Query:
        """Apply request filters to a query."""
        raw_filters = filter.create_filter_from_query_params(
            query_params, self.filter_allowed_fields)

        for raw_filter in raw_filters:
            with_transformation = False
            mapper = None
            key = raw_filter.field
            value = raw_filter.value
            op = raw_filter.operator.value
            query, column, sub_key = self.get_column_from_key(query, key)

            if not column:
                raise ValidationError(
                    message=f'Unknown filter field \'{key}\'',
                    location='querystring',
                    name=key)

            if value == 'null':
                value = None

            possible_names = [
                name.format(op=op) for name in ['{op}', '{op}_', '__{op}__']
            ]
            if isinstance(column, AssociationProxy) and '.' in key:
                mapper = getattr(column.remote_attr.prop, 'mapper', None)
                if mapper:
                    remote_class = mapper.class_
                    dest_column = getattr(remote_class, sub_key)
                    attrs = [
                        getattr(dest_column, name) for name in possible_names
                        if hasattr(dest_column, name)
                    ]
                else:
                    attrs = [
                        getattr(column, name) for name in possible_names
                        if hasattr(column, name)
                    ]
            elif isinstance(column, InstrumentedAttribute) and '.' in key:
                mapper = getattr(column.property, 'mapper', None)
                dest_column = getattr(mapper.c, sub_key, None)
                # try to get the original field starting with underscore
                if dest_column is None:
                    dest_column = getattr(mapper.c, f'_{sub_key}', None)
                attrs = [
                    getattr(dest_column, name) for name in possible_names
                    if hasattr(dest_column, name)
                ]
            else:

                if isinstance(column, AssociationProxy):
                    remote_attr = column.remote_attr
                    if isinstance(column.remote_attr.comparator,
                                  BaseComparator):
                        with_transformation = True
                        attrs = [
                            getattr(remote_attr, name)
                            for name in possible_names
                            if hasattr(remote_attr, name)
                        ]
                    else:
                        attrs = [
                            getattr(column, name) for name in possible_names
                            if hasattr(column, name)
                        ]
                        if not attrs:
                            attrs = [
                                getattr(remote_attr, name)
                                for name in possible_names
                                if hasattr(remote_attr, name)
                            ]
                else:
                    if isinstance(column.comparator, BaseComparator):
                        with_transformation = True
                    attrs = [
                        getattr(column, name) for name in possible_names
                        if hasattr(column, name)
                    ]

            # validate before try to create the filter
            if not attrs:
                error_details = {
                    'location': 'querystring',
                    'description': f'Invalid filter operator: \'{op}\'',
                    'name': key,
                    'value': value
                }
                return self.raise_invalid(**error_details)

            expression = attrs[0](value)
            is_proxy = isinstance(column, AssociationProxy)
            is_instrumented = isinstance(column, InstrumentedAttribute)

            if is_proxy or is_instrumented and '.' in key:
                if is_proxy and not column.scalar:
                    filt = column.any(expression)
                elif is_instrumented and column.property.uselist:
                    filt = column.any(expression)
                elif is_instrumented or mapper:
                    filt = column.has(expression)
                else:
                    filt = expression
            else:
                filt = expression

            if with_transformation:
                query = query.with_transformation(filt)
            else:
                query = query.filter(filt)

        return query
コード例 #10
0
def create_filter_from_query_params(
        query_params: dict,
        allowed_fields: t.Sequence[str]) -> t.Sequence[Filter]:
    """Process a query parameters dictionary and return a list of Filter objects.

    :param query_params: Dictionary containing query_params for a request.
    :param allowed_fields: List of fields that support sorting.
    :return: list of Filter objects.
    """
    filters = []
    for param, param_value in query_params.items():
        param = param.strip()

        # Ignore specific fields
        if param.startswith('_') and param not in ('_since', '_to', '_before'):
            continue

        # Handle the _since specific filter.
        if param in ('_since', '_to', '_before'):
            try:
                value = int(param_value)
            except ValueError as exc:
                raise ValueError(
                    f'Parameter "{param}" is not a valid integer.')

            operators = {
                '_since': COMPARISON.GT,
                '_to': COMPARISON.MAX,
                '_before': COMPARISON.LT,
            }

            operator = operators[param]

            filters.append(Filter(UPDATED_AT, value, operator))
            continue

        m = re.match(r'^(min|max|not|lt|gt|in|exclude|like|ilike)_([\.\w]+)$',
                     param)
        if m:
            keyword, field = m.groups()
            operator = getattr(COMPARISON, keyword.upper())
        else:
            operator, field = COMPARISON.EQ, param

        if field not in allowed_fields:
            raise ValidationError(message=f'Unknown filter field \'{field}\'',
                                  location='querystring',
                                  name=field)

        value = data.native_value(param_value, field)
        if operator in (COMPARISON.IN, COMPARISON.EXCLUDE):
            value = set(
                [data.native_value(v, field) for v in param_value.split(',')])
        elif operator in (
                COMPARISON.LIKE,
                COMPARISON.ILIKE,
        ):
            value = value.replace('%', '')
            value = f'%{value}%'
        filters.append(Filter(field, value, operator))
    return filters