Example #1
0
    def __init__(self, schema):
        self.schema = schema

        required_attrs = ('name', 'hash_key', 'read', 'write')
        optional_attrs = ('range_key', )

        for attr in required_attrs:
            if not hasattr(self, attr):
                raise MissingTableAttribute(
                    "Missing required Table attribute: {0}".format(attr))

        for attr in optional_attrs:
            if not hasattr(self, attr):
                setattr(self, attr, None)

        if self.hash_key not in self.schema.dynamorm_fields():
            raise InvalidSchemaField(
                "The hash key '{0}' does not exist in the schema".format(
                    self.hash_key))

        if self.range_key and self.range_key not in self.schema.dynamorm_fields(
        ):
            raise InvalidSchemaField(
                "The range key '{0}' does not exist in the schema".format(
                    self.range_key))
Example #2
0
    def __init__(self):
        for attr in self.REQUIRED_ATTRS:
            if getattr(self, attr) is None:
                raise MissingTableAttribute("Missing required Table attribute: {0}".format(attr))

        if self.hash_key not in self.schema.dynamorm_fields():
            raise InvalidSchemaField("The hash key '{0}' does not exist in the schema".format(self.hash_key))

        if self.range_key and self.range_key not in self.schema.dynamorm_fields():
            raise InvalidSchemaField("The range key '{0}' does not exist in the schema".format(self.range_key))
Example #3
0
    def get_batch(self,
                  keys,
                  consistent=False,
                  attrs=None,
                  batch_get_kwargs=None):
        batch_get_kwargs = batch_get_kwargs or {}

        batch_get_kwargs['Keys'] = []
        for kwargs in keys:
            for k, v in six.iteritems(kwargs):
                if k not in self.schema.dynamorm_fields():
                    raise InvalidSchemaField(
                        "{0} does not exist in the schema fields".format(k))

            batch_get_kwargs['Keys'].append(kwargs)

        if consistent:
            batch_get_kwargs['ConsistentRead'] = True

        if attrs:
            batch_get_kwargs['ProjectionExpression'] = attrs

        while True:
            response = self.resource.batch_get_item(
                RequestItems={self.name: batch_get_kwargs})

            for item in response['Responses'][self.name]:
                yield item

            try:
                batch_get_kwargs = response['UnprocessedKeys'][self.name]
            except KeyError:
                # once our table is no longer listed in UnprocessedKeys we're done our while True loop
                break
Example #4
0
    def query(self, query_kwargs=None, **kwargs):
        assert len(kwargs) in (1, 2), "Query only takes 1 or 2 keyword arguments"

        if query_kwargs is None:
            query_kwargs = {}

        while len(kwargs):
            key, value = kwargs.popitem()

            try:
                key, op = key.split('__')
            except ValueError:
                op = 'eq'

            if key not in (self.hash_key, self.range_key):
                raise InvalidSchemaField("{0} is not our hash or range key".format(key))

            key = Key(key)
            op = getattr(key, op)

            if 'KeyConditionExpression' in query_kwargs:
                query_kwargs['KeyConditionExpression'] = query_kwargs['KeyConditionExpression'] & op(value)
            else:
                query_kwargs['KeyConditionExpression'] = op(value)

        log.debug("Query: %s", query_kwargs)
        return self.table.query(**query_kwargs)
Example #5
0
    def update(self, update_item_kwargs=None, conditions=None, **kwargs):
        update_item_kwargs = update_item_kwargs or {}
        conditions = conditions or {}
        update_key = {}
        update_fields = []
        expr_names = {}
        expr_vals = {}

        UPDATE_FUNCTION_TEMPLATES = {
            'append': '#uk_{0} = list_append(#uk_{0}, :uv_{0})',
            'plus': '#uk_{0} = #uk_{0} + :uv_{0}',
            'minus': '#uk_{0} = #uk_{0} - :uv_{0}',
            'if_not_exists': '#uk_{0} = if_not_exists(#uk_{0}, :uv_{0})',
            None: '#uk_{0} = :uv_{0}'
        }

        for key, value in six.iteritems(kwargs):
            try:
                key, function = key.split('__', 1)
            except ValueError:
                function = None

            # make sure the field (key) exists
            if key not in self.schema.dynamorm_fields():
                raise InvalidSchemaField("{0} does not exist in the schema fields".format(key))

            if key in (self.hash_key, self.range_key):
                update_key[key] = value
            else:
                update_fields.append(UPDATE_FUNCTION_TEMPLATES[function].format(key))
                expr_names['#uk_{0}'.format(key)] = key
                expr_vals[':uv_{0}'.format(key)] = value

        update_item_kwargs['Key'] = update_key
        update_item_kwargs['UpdateExpression'] = 'SET {0}'.format(', '.join(update_fields))
        update_item_kwargs['ExpressionAttributeNames'] = expr_names
        update_item_kwargs['ExpressionAttributeValues'] = expr_vals

        if isinstance(conditions, collections.Mapping):
            condition_expression = Q(**conditions)
        elif isinstance(conditions, collections.Iterable):
            condition_expression = None
            for condition in conditions:
                try:
                    condition_expression = condition_expression & condition
                except TypeError:
                    condition_expression = condition
        else:
            condition_expression = conditions

        if condition_expression:
            update_item_kwargs['ConditionExpression'] = condition_expression

        try:
            return self.table.update_item(**update_item_kwargs)
        except botocore.exceptions.ClientError as exc:
            if exc.response['Error']['Code'] == 'ConditionalCheckFailedException':
                raise ConditionFailed(exc)
            raise
Example #6
0
    def update(self, conditions=None, update_item_kwargs=None, **kwargs):
        update_item_kwargs = update_item_kwargs or {}
        conditions = conditions or {}

        update_key = {}
        update_fields = []
        condition_fields = []
        expr_names = {}
        expr_vals = {}
        for k, v in six.iteritems(kwargs):
            if k not in self.schema.dynamorm_fields():
                raise InvalidSchemaField("{0} does not exist in the schema fields".format(k))

            if k in (self.hash_key, self.range_key):
                update_key[k] = v
            else:
                update_fields.append('#u_{0} = :u_{0}'.format(k))
                expr_names['#u_{0}'.format(k)] = k
                expr_vals[':u_{0}'.format(k)] = v

        for k, v in six.iteritems(conditions):
            if k not in self.schema.dynamorm_fields():
                raise InvalidSchemaField("{0} does not exist in the schema fields".format(k))

            condition_fields.append('#c_{0} = :c_{0}'.format(k))
            expr_names['#c_{0}'.format(k)] = k
            expr_vals[':c_{0}'.format(k)] = v

        update_item_kwargs['Key'] = update_key
        update_item_kwargs['UpdateExpression'] = 'SET {0}'.format(', '.join(update_fields))
        if condition_fields:
            update_item_kwargs['ConditionExpression'] = ' AND '.join(condition_fields)
        update_item_kwargs['ExpressionAttributeNames'] = expr_names
        update_item_kwargs['ExpressionAttributeValues'] = expr_vals

        try:
            return self.table.update_item(**update_item_kwargs)
        except botocore.exceptions.ClientError as exc:
            if exc.response['Error']['Code'] == 'ConditionalCheckFailedException':
                raise ConditionFailed(exc)
            raise
Example #7
0
    def query(self, *args, **kwargs):
        # copy query_kwargs, so that we don't mutate the original later on
        query_kwargs = dict(
            (k, v) for k, v in six.iteritems(kwargs.pop("query_kwargs", {})))
        filter_kwargs = {}

        if "IndexName" in query_kwargs:
            attr_fields = self.index_attribute_fields(
                index_name=query_kwargs["IndexName"])
        else:
            attr_fields = self.table_attribute_fields

        while len(kwargs):
            full_key, value = kwargs.popitem()

            try:
                key, op = full_key.split("__")
            except ValueError:
                key = full_key
                op = "eq"

            if key not in attr_fields:
                filter_kwargs[full_key] = value
                continue

            key = Key(key)
            key_expression = get_expression(key, op, value)

            try:
                query_kwargs["KeyConditionExpression"] = (
                    query_kwargs["KeyConditionExpression"] & key_expression)
            except (KeyError, TypeError):
                query_kwargs["KeyConditionExpression"] = key_expression

        if "KeyConditionExpression" not in query_kwargs:
            raise InvalidSchemaField(
                "Primary key must be specified for queries")

        filter_expression = Q(**filter_kwargs)
        for arg in args:
            try:
                filter_expression = filter_expression & arg
            except TypeError:
                filter_expression = arg

        if filter_expression:
            query_kwargs["FilterExpression"] = filter_expression

        log.debug("Query: %s", query_kwargs)
        return self.table.query(**query_kwargs)
Example #8
0
    def query(self, *args, **kwargs):
        query_kwargs = kwargs.pop('query_kwargs', {})
        filter_kwargs = {}

        if 'IndexName' in query_kwargs:
            attr_fields = self.index_attribute_fields(
                index_name=query_kwargs['IndexName'])
        else:
            attr_fields = self.table_attribute_fields

        while len(kwargs):
            full_key, value = kwargs.popitem()

            try:
                key, op = full_key.split('__')
            except ValueError:
                key = full_key
                op = 'eq'

            if key not in attr_fields:
                filter_kwargs[full_key] = value
                continue

            key = Key(key)
            key_expression = get_expression(key, op, value)

            try:
                if not query_kwargs['KeyConditionExpression'] == key_expression:
                    query_kwargs['KeyConditionExpression'] = query_kwargs[
                        'KeyConditionExpression'] & key_expression
            except (KeyError, TypeError):
                query_kwargs['KeyConditionExpression'] = key_expression

        if 'KeyConditionExpression' not in query_kwargs:
            raise InvalidSchemaField(
                "Primary key must be specified for queries")

        filter_expression = Q(**filter_kwargs)
        for arg in args:
            try:
                filter_expression = filter_expression & arg
            except TypeError:
                filter_expression = arg

        if filter_expression:
            query_kwargs['FilterExpression'] = filter_expression

        log.debug("Query: %s", query_kwargs)
        return self.table.query(**query_kwargs)
Example #9
0
    def get(self, consistent=False, get_item_kwargs=None, **kwargs):
        get_item_kwargs = get_item_kwargs or {}

        for k, v in six.iteritems(kwargs):
            if k not in self.schema.dynamorm_fields():
                raise InvalidSchemaField("{0} does not exist in the schema fields".format(k))

        get_item_kwargs['Key'] = kwargs
        if consistent:
            get_item_kwargs['ConsistentRead'] = True

        response = self.table.get_item(**get_item_kwargs)

        if 'Item' in response:
            return response['Item']
Example #10
0
    def get(self, consistent=False, get_item_kwargs=None, **kwargs):
        # copy get_item_kwargs, so that we don't mutate the original later on
        get_item_kwargs = dict(
            (k, v) for k, v in six.iteritems(get_item_kwargs or {}))

        for k, v in six.iteritems(kwargs):
            if k not in self.schema.dynamorm_fields():
                raise InvalidSchemaField(
                    "{0} does not exist in the schema fields".format(k))

        get_item_kwargs["Key"] = kwargs
        if consistent:
            get_item_kwargs["ConsistentRead"] = True

        response = self.table.get_item(**get_item_kwargs)

        if "Item" in response:
            return response["Item"]
Example #11
0
    def get_batch(self,
                  keys,
                  consistent=False,
                  attrs=None,
                  batch_get_kwargs=None):
        # copy batch_get_kwargs, so that we don't mutate the original later on
        batch_get_kwargs = dict(
            (k, v) for k, v in six.iteritems(batch_get_kwargs or {}))

        batch_get_kwargs["Keys"] = []
        for kwargs in keys:
            for k, v in six.iteritems(kwargs):
                if k not in self.schema.dynamorm_fields():
                    raise InvalidSchemaField(
                        "{0} does not exist in the schema fields".format(k))

            batch_get_kwargs["Keys"].append(kwargs)

        if consistent:
            batch_get_kwargs["ConsistentRead"] = True

        if attrs:
            batch_get_kwargs["ProjectionExpression"] = attrs

        while True:
            response = self.resource.batch_get_item(
                RequestItems={self.name: batch_get_kwargs})

            for item in response["Responses"][self.name]:
                yield item

            try:
                batch_get_kwargs = response["UnprocessedKeys"][self.name]
            except KeyError:
                # once our table is no longer listed in UnprocessedKeys we're done our while True loop
                break
Example #12
0
    def update(self, update_item_kwargs=None, conditions=None, **kwargs):
        # copy update_item_kwargs, so that we don't mutate the original later on
        update_item_kwargs = dict(
            (k, v) for k, v in six.iteritems(update_item_kwargs or {}))
        conditions = conditions or {}
        update_fields = []
        expr_names = {}
        expr_vals = {}

        # First, pick out the keys for the update.
        update_key = {
            key: kwargs.pop(key)
            for key in (self.hash_key, self.range_key) if key in kwargs
        }

        # Then, generate the keys and values for the update-expression.
        for i, key in enumerate(kwargs):
            key_parts = key.split("__")
            top_level_key = key_parts[0]

            # Make sure the top-level field (key) exists
            # XXX TODO: Should we validate nested keys as well?
            if top_level_key not in self.schema.dynamorm_fields():
                raise InvalidSchemaField(
                    "{0} does not exist in the schema fields".format(key))

            # Add the actual field expression, keys, and value.
            (
                field_expr,
                field_expr_names,
                field_expr_value,
            ) = self.get_update_expr_for_key(i, key_parts)
            update_fields.append(field_expr)
            expr_names.update(field_expr_names)
            expr_vals[field_expr_value] = kwargs[key]

        update_item_kwargs["Key"] = update_key
        update_item_kwargs["UpdateExpression"] = "SET {0}".format(
            ", ".join(update_fields))
        update_item_kwargs["ExpressionAttributeNames"] = expr_names
        update_item_kwargs["ExpressionAttributeValues"] = expr_vals

        if isinstance(conditions, Mapping):
            condition_expression = Q(**conditions)
        elif isinstance(conditions, Iterable):
            condition_expression = None
            for condition in conditions:
                try:
                    condition_expression = condition_expression & condition
                except TypeError:
                    condition_expression = condition
        else:
            condition_expression = conditions

        if condition_expression:
            update_item_kwargs["ConditionExpression"] = condition_expression

        try:
            return self.table.update_item(**update_item_kwargs)
        except botocore.exceptions.ClientError as exc:
            if exc.response["Error"][
                    "Code"] == "ConditionalCheckFailedException":
                raise ConditionFailed(exc)
            raise
Example #13
0
    def update(self, update_item_kwargs=None, conditions=None, **kwargs):
        # copy update_item_kwargs, so that we don't mutate the original later on
        update_item_kwargs = dict(
            (k, v) for k, v in six.iteritems(update_item_kwargs or {}))
        conditions = conditions or {}
        update_key = {}
        update_fields = []
        expr_names = {}
        expr_vals = {}

        UPDATE_FUNCTION_TEMPLATES = {
            "append": "#uk_{0} = list_append(#uk_{0}, :uv_{0})",
            "plus": "#uk_{0} = #uk_{0} + :uv_{0}",
            "minus": "#uk_{0} = #uk_{0} - :uv_{0}",
            "if_not_exists": "#uk_{0} = if_not_exists(#uk_{0}, :uv_{0})",
            None: "#uk_{0} = :uv_{0}",
        }

        for key, value in six.iteritems(kwargs):
            try:
                key, function = key.split("__", 1)
            except ValueError:
                function = None

            # make sure the field (key) exists
            if key not in self.schema.dynamorm_fields():
                raise InvalidSchemaField(
                    "{0} does not exist in the schema fields".format(key))

            if key in (self.hash_key, self.range_key):
                update_key[key] = value
            else:
                update_fields.append(
                    UPDATE_FUNCTION_TEMPLATES[function].format(key))
                expr_names["#uk_{0}".format(key)] = key
                expr_vals[":uv_{0}".format(key)] = value

        update_item_kwargs["Key"] = update_key
        update_item_kwargs["UpdateExpression"] = "SET {0}".format(
            ", ".join(update_fields))
        update_item_kwargs["ExpressionAttributeNames"] = expr_names
        update_item_kwargs["ExpressionAttributeValues"] = expr_vals

        if isinstance(conditions, collections.Mapping):
            condition_expression = Q(**conditions)
        elif isinstance(conditions, collections.Iterable):
            condition_expression = None
            for condition in conditions:
                try:
                    condition_expression = condition_expression & condition
                except TypeError:
                    condition_expression = condition
        else:
            condition_expression = conditions

        if condition_expression:
            update_item_kwargs["ConditionExpression"] = condition_expression

        try:
            return self.table.update_item(**update_item_kwargs)
        except botocore.exceptions.ClientError as exc:
            if exc.response["Error"][
                    "Code"] == "ConditionalCheckFailedException":
                raise ConditionFailed(exc)
            raise