示例#1
0
    def handle_single_key(self, key: str, cf_lock: LockedFieldsJSON) -> None:
        if isinstance(cf_lock, int) and cf_lock <= 0:
            raise InvalidFormat(f"The position of key {key} must be >= 0")

        key_type = get_key_type(key)
        if key_type in (KEY_TYPE.FQID,
                        KEY_TYPE.FQFIELD) and not isinstance(cf_lock, int):
            raise InvalidFormat(
                "CollectionFieldLocks can only be used with collectionfields")

        if key_type == KEY_TYPE.FQID:
            self.locked_fqids[key] = cast(int, cf_lock)
        elif key_type == KEY_TYPE.FQFIELD:
            self.locked_fqfields[key] = cast(int, cf_lock)
        elif key_type == KEY_TYPE.COLLECTIONFIELD:
            if isinstance(cf_lock, int):
                self.locked_collectionfields[key] = cf_lock
            else:
                # build self validating data class
                try:
                    self.locked_collectionfields[key] = from_dict(
                        CollectionFieldLockWithFilter,
                        cf_lock,
                    )
                except (TypeError, MissingValueError, UnionMatchError) as e:
                    raise BadCodingError("Invalid data to initialize class\n" +
                                         str(e))
    def reserve_next_ids(self, collection: str, amount: int) -> List[int]:
        if amount <= 0:
            raise InvalidFormat(f"amount must be >= 1, not {amount}")
        if len(collection) > COLLECTION_MAX_LEN or not collection:
            raise InvalidFormat(
                f"collection length must be between 1 and {COLLECTION_MAX_LEN}"
            )

        query = "select id from id_sequences where collection=%s"
        arguments = [collection]
        low_id = self.connection.query_single_value(query, arguments)
        if low_id is None:
            low_id = 1
        high_id = low_id + amount  # high id not included in the result

        statement = "update id_sequences set id=%s where collection=%s"
        statement = dedent(
            f"""\
            insert into id_sequences (collection, id) values (%s, %s)
            on conflict(collection) do update set id=excluded.id;"""
        )
        arguments = [collection, high_id]
        self.connection.execute(statement, arguments)

        return list(range(low_id, high_id))
def test_union_of_lists_with_fqid_and_fqfield_fail():
    data = MagicMock()

    with patch("shared.util.self_validating_dataclass.assert_is_fqid"
               ) as assert_is_fqid, patch(
                   "shared.util.self_validating_dataclass.assert_is_fqfield"
               ) as assert_is_fqfield:
        assert_is_fqid.side_effect = InvalidFormat("msg")
        assert_is_fqfield.side_effect = InvalidFormat("msg")

        with pytest.raises(InvalidFormat):
            D([data])

        assert_is_fqid.assert_called_with(data)
        assert_is_fqfield.assert_called_with(data)
示例#4
0
    def insert_modified_collectionfields_into_db(
        self, modified_collectionfields, position
    ):
        # insert into db, updating all existing fields with position, returning ids
        value_placeholders = []
        arguments: List[Any] = []
        for collectionfield in modified_collectionfields:
            if len(collectionfield) > COLLECTIONFIELD_MAX_LEN:
                raise InvalidFormat(
                    f"Collection field {collectionfield} is too long "
                    + "(max: {COLLECTIONFIELD_MAX_LEN})"
                )
            arguments.extend(
                (
                    collectionfield,
                    position,
                )
            )
            value_placeholders.append("(%s, %s)")

        values = ",".join(value_placeholders)
        statement = dedent(
            f"""\
            insert into collectionfields (collectionfield, position) values {values}
            on conflict(collectionfield) do update set position=excluded.position
            returning id"""
        )
        return self.connection.query_list_of_single_values(statement, arguments)
 def build_filter_str(self,
                      filter: Filter,
                      arguments: List[str],
                      table_alias="") -> str:
     if isinstance(filter, Not):
         filter_str = self.build_filter_str(filter.not_filter, arguments,
                                            table_alias)
         return f"NOT ({filter_str})"
     elif isinstance(filter, Or):
         return " OR ".join(
             f"({self.build_filter_str(part, arguments, table_alias)})"
             for part in filter.or_filter)
     elif isinstance(filter, And):
         return " AND ".join(
             f"({self.build_filter_str(part, arguments, table_alias)})"
             for part in filter.and_filter)
     elif isinstance(filter, FilterOperator):
         if table_alias:
             table_alias += "."
         if filter.value is None:
             if filter.operator not in ("=", "!="):
                 raise InvalidFormat(
                     "You can only compare to None with = or !=")
             operator = filter.operator[::-1].replace("=", "IS").replace(
                 "!", " NOT")
             condition = f"{table_alias}data->>%s {operator} NULL"
             arguments += [filter.field]
         else:
             condition = f"{table_alias}data->>%s {filter.operator} %s::text"
             arguments += [filter.field, filter.value]
         return condition
     else:
         raise BadCodingError("Invalid filter type")
 def __init__(self, fqid: str, fields: Dict[str, JSON]) -> None:
     super().__init__(fqid)
     if len(fields) <= 0:
         raise InvalidFormat("No fields are given")
     for field in fields.keys():
         assert_is_field(field)
         assert_no_special_field(field)
         self.fields = fields
def test_union_of_lists_with_fqid_fail():
    fqid: Any = MagicMock(name="fqid")

    with patch("shared.util.self_validating_dataclass.assert_is_fqid"
               ) as assert_is_fqid:
        assert_is_fqid.side_effect = InvalidFormat("msg")
        with pytest.raises(InvalidFormat):
            C([fqid])
        assert_is_fqid.assert_called_with(fqid)
示例#8
0
    def reserve_next_ids(self, collection: str, amount: int) -> List[int]:
        if amount <= 0:
            raise InvalidFormat(f"amount must be >= 1, not {amount}")
        if len(collection) > COLLECTION_MAX_LEN or not collection:
            raise InvalidFormat(
                f"collection length must be between 1 and {COLLECTION_MAX_LEN}"
            )

        statement = dedent(
            """\
            insert into id_sequences (collection, id) values (%s, %s)
            on conflict(collection) do update
            set id=id_sequences.id + excluded.id - 1 returning id;"""
        )
        arguments = [collection, amount + 1]
        new_max_id = self.connection.query_single_value(statement, arguments)

        return list(range(new_max_id - amount, new_max_id))
    def handle_single_key(self, key: str, position: int) -> None:
        if position <= 0:
            raise InvalidFormat(f"The position of key {key} must be >= 0")

        key_type = get_key_type(key)
        if key_type == KEY_TYPE.FQID:
            self.locked_fqids[key] = position
        elif key_type == KEY_TYPE.FQFIELD:
            self.locked_fqfields[key] = position
        elif key_type == KEY_TYPE.COLLECTIONFIELD:
            self.locked_collectionfields[key] = position
def test_union_of_lists_with_fqid_and_fqfield_success_2():
    fqfield = MagicMock()

    with patch("shared.util.self_validating_dataclass.assert_is_fqid"
               ) as assert_is_fqid, patch(
                   "shared.util.self_validating_dataclass.assert_is_fqfield"
               ) as assert_is_fqfield:
        assert_is_fqid.side_effect = InvalidFormat("msg")
        D([fqfield])

        assert_is_fqid.assert_called_with(fqfield)
        assert_is_fqfield.assert_called_with(fqfield)
示例#11
0
 def __init__(
     self,
     events: List[BaseRequestEvent],
     information: JSON,
     user_id: int,
     locked_fields: Dict[str, LockedFieldsJSON],
 ) -> None:
     self.events = events
     self.information = information
     self.user_id = user_id
     if len(events) <= 0:
         raise InvalidFormat("No events were given")
     self.parse_locked_fields(locked_fields)
示例#12
0
    def calculate_updated_fields(self, model: Model) -> Tuple[Model, List[str]]:
        all_field_keys = list(self.add.keys()) + list(self.remove.keys())
        for field in all_field_keys:
            db_list = model.get(field, [])
            if not isinstance(db_list, list):
                raise InvalidFormat(
                    f"Field {field} on model {self.fqid} is not a list, but of type"
                    + str(type(db_list))
                )
            for el in db_list:
                if not isinstance(el, (str, int)):
                    raise InvalidFormat(
                        f"Field {field} on model {self.fqid} contains invalid entry "
                        f"for list update (of type {type(el)}): {el}"
                    )

        updated_fields = {}
        deleted_fields = []
        for field, value in self.add.items():
            # Iterate over list and remove all entries from value which are already
            # in the list. If adding multiple entries, this reduces the runtime needed.
            # When a huge amount of data is added, the normal update should be used
            # instead.
            db_list = model.get(field, [])
            for el in db_list:
                if el in value:
                    value.remove(el)
            updated_fields[field] = db_list + value

        for field, value in self.remove.items():
            if field in model:
                db_list = model.get(field)
                updated_list = [el for el in db_list if el not in value]
                updated_fields[field] = updated_list
            else:
                deleted_fields.append(field)

        return updated_fields, deleted_fields
示例#13
0
    def insert_events(
        self, events: List[BaseDbEvent], information: JSON, user_id: int
    ) -> int:
        if not events:
            raise BadCodingError()

        position = self.create_position(information, user_id)
        for event in events:
            if len(event.fqid) > FQID_MAX_LEN:
                raise InvalidFormat(
                    f"fqid {event.fqid} is too long (max: {FQID_MAX_LEN})"
                )
            self.insert_event(event, position)
        return position
示例#14
0
def assert_no_special_field(field: Any) -> None:
    if is_reserved_field(field):
        raise InvalidFormat(f"Field {field} is reserved")