Example #1
0
    def parse_key_schema(cls, key_def_list_json):
        hash_key_attr_name = None
        range_key_attr_name = None

        for key_def in key_def_list_json:
            dynamodb_key_attr_name = key_def.get(Props.ATTRIBUTE_NAME, None)
            dynamodb_key_type = key_def.get(Props.KEY_TYPE, None)

            if dynamodb_key_type == Values.KEY_TYPE_HASH:
                if hash_key_attr_name is not None:
                    raise exception.AWSValidationException(
                        "Only one 'HASH' key is allowed"
                    )
                hash_key_attr_name = dynamodb_key_attr_name
            elif dynamodb_key_type == Values.KEY_TYPE_RANGE:
                if range_key_attr_name is not None:
                    raise exception.AWSValidationException(
                        "Only one 'RANGE' key is allowed"
                    )
                range_key_attr_name = dynamodb_key_attr_name
            else:
                raise exception.AWSValidationException(
                    "Only 'RANGE' or 'HASH' key types are allowed, but '{}' "
                    "is found".format(dynamodb_key_type))
        if hash_key_attr_name is None:
            raise exception.AWSValidationException("HASH key is missing")
        if range_key_attr_name:
            return (hash_key_attr_name, range_key_attr_name)
        return (hash_key_attr_name,)
Example #2
0
    def process_action(self, context, service_name, api_version, action_name,
                       action_params):
        service_capabilities = self.capabilities.get(service_name, None)

        if service_capabilities is None:
            raise exception.AWSBadRequestException(
                "Service '%s' isn't supported" % service_name)

        target_capabilities = service_capabilities.get(api_version, None)

        if target_capabilities is None:
            raise (
                exception.AWSBadRequestException(
                    "Service '%s' doesn't support API version '%s'" %
                    (service_name, api_version))
            )

        action = target_capabilities.get(action_name, None)

        if action is None:
            raise (
                exception.AWSValidationException(
                    "Service '%s', API version '%s' "
                    "doesn't support action '%s'" %
                    (service_name, api_version, action_name))
            )

        context.request_type = action_name

        return action.perform(context, action_params)
Example #3
0
    def parse_local_secondary_index(cls, local_secondary_index_json):
        key_attrs_for_projection = cls.parse_key_schema(
            local_secondary_index_json.get(Props.KEY_SCHEMA, {})
        )
        hash_key = key_attrs_for_projection[0]

        try:
            range_key = key_attrs_for_projection[1]
        except IndexError:
            raise exception.AWSValidationException(
                "Range key in index wasn't specified"
            )

        index_name = local_secondary_index_json[Props.INDEX_NAME]

        projection_type = local_secondary_index_json.get(
            Props.PROJECTION_TYPE, Values.PROJECTION_TYPE_INCLUDE
        )

        if projection_type == Values.PROJECTION_TYPE_ALL:
            projected_attrs = None
        elif projection_type == Values.PROJECTION_TYPE_KEYS_ONLY:
            projected_attrs = frozenset()
        else:
            projected_attrs = local_secondary_index_json.get(
                Props.NON_KEY_ATTRIBUTES, None
            )

        return index_name, models.IndexDefinition(hash_key,
                                                  range_key,
                                                  projected_attrs)
Example #4
0
    def __call__(self):
        try:
            table_name = self.action_params.get(parser.Props.TABLE_NAME, None)

            # get attributes_to_get
            attributes_to_get = self.action_params.get(
                parser.Props.ATTRIBUTES_TO_GET, None)

            select_type = (models.SelectType.all()
                           if attributes_to_get is None else
                           models.AttributeToGet.specified(attributes_to_get))

            # parse key_attributes
            key_attributes = parser.Parser.parse_item_attributes(
                self.action_params[parser.Props.KEY])

            # TODO(dukhlov):
            # it would be nice to validate given table_name, key_attributes and
            # attributes_to_get  to schema expectation

            consistent_read = self.action_params.get(
                parser.Props.CONSISTENT_READ, False)

            return_consumed_capacity = self.action_params.get(
                parser.Props.RETURN_CONSUMED_CAPACITY,
                parser.Values.RETURN_CONSUMED_CAPACITY_NONE)

        except Exception:
            raise exception.AWSValidationException()

        try:
            # get item
            result = storage.get_item(self.context,
                                      table_name,
                                      key_attributes,
                                      select_type=select_type,
                                      consistent=consistent_read)

            # format response
            if result.count == 0:
                return {}

            assert result.count == 1

            response = {
                parser.Props.ITEM:
                parser.Parser.format_item_attributes(result.items[0])
            }

            if (return_consumed_capacity !=
                    parser.Values.RETURN_CONSUMED_CAPACITY_NONE):
                response[parser.Props.CONSUMED_CAPACITY] = (
                    parser.Parser.format_consumed_capacity(
                        return_consumed_capacity, None))

            return response
        except exception.AWSErrorResponseException as e:
            raise e
        except Exception:
            raise exception.AWSErrorResponseException()
Example #5
0
    def parse_attribute_condition(cls, condition_type, condition_args,
                                  condition_class=models.IndexedCondition):

        actual_args_count = (
            len(condition_args) if condition_args is not None else 0
        )
        if condition_type == Values.BETWEEN:
            if actual_args_count != 2:
                raise exception.AWSValidationException(
                    "{} condition type requires exactly 2 arguments, "
                    "but {} given".format(condition_type, actual_args_count),
                )
            if condition_args[0].attr_type != condition_args[1].attr_type:
                raise exception.AWSValidationException(
                    "{} condition type requires arguments of the "
                    "same type, but different types given".format(
                        condition_type
                    ),
                )

            return [
                condition_class.ge(condition_args[0]),
                condition_class.le(condition_args[1])
            ]

        if condition_type == Values.BEGINS_WITH:
            first = condition_class(
                condition_class.CONDITION_TYPE_GREATER_OR_EQUAL,
                condition_args
            )
            condition_arg = first.arg
            second = condition_class.le(
                models.AttributeValue(
                    condition_arg.attr_type, decoded_value=(
                        condition_arg.decoded_value[:-1] +
                        chr(ord(condition_arg.decoded_value[-1]) + 1)
                    )
                )
            )

            return [first, second]

        return [condition_class(condition_type, condition_args)]
Example #6
0
    def parse_typed_attr_value(cls, typed_attr_value_json):
        if len(typed_attr_value_json) != 1:
            raise exception.AWSValidationException(
                "Can't recognize attribute format ['{}']".format(
                    json.dumps(typed_attr_value_json)
                )
            )
        (attr_type_json, attr_value_json) = typed_attr_value_json.items()[0]

        return models.AttributeValue(attr_type_json, attr_value_json)
Example #7
0
    def validate_params(cls, params):
        assert isinstance(params, dict)

        validator = jsonschema.Draft4Validator(cls.schema)
        if not validator.is_valid(params):
            errors = sorted(validator.iter_errors(params),
                            key=lambda e: e.path)
            error_msg = cls.format_validation_msg(errors)
            LOG.info(error_msg)
            raise exception.AWSValidationException(error_msg)
Example #8
0
    def process_request(self, req, body):
        target = req.environ['HTTP_X_AMZ_TARGET']

        if not target:
            raise (
                exception.AWSValidationException(
                    "'x-amz-target' header wasn't found")
            )

        matcher = re.match("(\w+)_(\w+)\.(\w+)", target)

        if not matcher:
            raise (
                exception.AWSValidationException(
                    "'x-amz-target' header wasn't recognized (actual: %s, "
                    "expected format: <<serviceName>>_<<API version>>."
                    "<<operationName>>")
            )
        service_name = matcher.group(1)
        api_version = matcher.group(2)
        action_name = matcher.group(3)

        return self.process_action(req.context, service_name, api_version,
                                   action_name, body)
Example #9
0
    def __call__(self):

        table_name = self.action_params.get(parser.Props.TABLE_NAME, None)

        if not table_name:
            raise ddb_exception.AWSValidationException(
                message='Table name is not defined')

        try:
            table_meta = storage.describe_table(self.context, table_name)

            result = {
                parser.Props.TABLE: {
                    parser.Props.ATTRIBUTE_DEFINITIONS:
                    (parser.Parser.format_attribute_definitions(
                        table_meta.schema.attribute_type_map)),
                    parser.Props.CREATION_DATE_TIME:
                    0,
                    parser.Props.ITEM_COUNT:
                    0,
                    parser.Props.KEY_SCHEMA: (parser.Parser.format_key_schema(
                        table_meta.schema.key_attributes)),
                    parser.Props.PROVISIONED_THROUGHPUT:
                    (parser.Values.PROVISIONED_THROUGHPUT_DUMMY),
                    parser.Props.TABLE_NAME:
                    table_name,
                    parser.Props.TABLE_STATUS:
                    (parser.Parser.format_table_status(table_meta.status)),
                    parser.Props.TABLE_SIZE_BYTES:
                    0
                }
            }

            if table_meta.schema.index_def_map:
                table_def = result[parser.Props.TABLE]
                table_def[parser.Props.LOCAL_SECONDARY_INDEXES] = (
                    parser.Parser.format_local_secondary_indexes(
                        table_meta.schema.key_attributes[0],
                        table_meta.schema.index_def_map))
            return result

        except exception.TableNotExistsException:
            raise ddb_exception.AWSResourceNotFoundException()
        except ddb_exception.AWSErrorResponseException as e:
            raise e
        except Exception:
            raise ddb_exception.AWSErrorResponseException()
Example #10
0
    def __call__(self):
        try:
            table_name = self.action_params.get(parser.Props.TABLE_NAME, None)

            # parse expected item conditions
            expected_item_conditions = (
                parser.Parser.parse_expected_attribute_conditions(
                    self.action_params.get(parser.Props.EXPECTED, {})
                )
            )

            # parse item
            item_attributes = parser.Parser.parse_item_attributes(
                self.action_params[parser.Props.ITEM]
            )

            # parse return_values param
            return_values_json = self.action_params.get(
                parser.Props.RETURN_VALUES, parser.Values.RETURN_VALUES_NONE
            )

            return_values = models.InsertReturnValuesType(return_values_json)

            # parse return_item_collection_metrics
            return_item_collection_metrics = self.action_params.get(
                parser.Props.RETURN_ITEM_COLLECTION_METRICS,
                parser.Values.RETURN_ITEM_COLLECTION_METRICS_NONE
            )

            return_consumed_capacity = self.action_params.get(
                parser.Props.RETURN_CONSUMED_CAPACITY,
                parser.Values.RETURN_CONSUMED_CAPACITY_NONE
            )
        except Exception:
            raise exception.AWSValidationException()

        try:
            # put item
            result, old_item = storage.put_item(
                self.context,
                table_name,
                item_attributes,
                return_values,
                if_not_exist=False,
                expected_condition_map=expected_item_conditions
            )

            if not result:
                raise exception.AWSErrorResponseException()

            # format response
            response = {}

            if old_item:
                response[parser.Props.ATTRIBUTES] = (
                    parser.Parser.format_item_attributes(item_attributes)
                )

            if (return_item_collection_metrics !=
                    parser.Values.RETURN_ITEM_COLLECTION_METRICS_NONE):
                response[parser.Props.ITEM_COLLECTION_METRICS] = {
                    parser.Props.ITEM_COLLECTION_KEY: {
                        parser.Parser.format_item_attributes(
                            models.AttributeValue("S", "key")
                        )
                    },
                    parser.Props.SIZE_ESTIMATED_RANGE_GB: [0]
                }

            if (return_consumed_capacity !=
                    parser.Values.RETURN_CONSUMED_CAPACITY_NONE):
                response[parser.Props.CONSUMED_CAPACITY] = (
                    parser.Parser.format_consumed_capacity(
                        return_consumed_capacity, None
                    )
                )

            return response
        except exception.AWSErrorResponseException as e:
            raise e
        except Exception:
            raise exception.AWSErrorResponseException()
Example #11
0
    def __call__(self):
        try:
            table_name = self.action_params.get(parser.Props.TABLE_NAME, None)

            # parse select_type
            attributes_to_get = self.action_params.get(
                parser.Props.ATTRIBUTES_TO_GET, None)
            if attributes_to_get is not None:
                attributes_to_get = frozenset(attributes_to_get)

            select = self.action_params.get(parser.Props.SELECT, None)

            index_name = self.action_params.get(parser.Props.INDEX_NAME, None)

            select_type = parser.Parser.parse_select_type(
                select, attributes_to_get, index_name)

            # parse exclusive_start_key_attributes
            exclusive_start_key_attributes = self.action_params.get(
                parser.Props.EXCLUSIVE_START_KEY, None)
            if exclusive_start_key_attributes is not None:
                exclusive_start_key_attributes = (
                    parser.Parser.parse_item_attributes(
                        exclusive_start_key_attributes))

            # parse indexed_condition_map
            indexed_condition_map = parser.Parser.parse_attribute_conditions(
                self.action_params.get(parser.Props.KEY_CONDITIONS, None))

            # TODO(dukhlov):
            # it would be nice to validate given table_name, key_attributes and
            # attributes_to_get to schema expectation

            consistent_read = self.action_params.get(
                parser.Props.CONSISTENT_READ, False)

            limit = self.action_params.get(parser.Props.LIMIT, None)

            return_consumed_capacity = self.action_params.get(
                parser.Props.RETURN_CONSUMED_CAPACITY,
                parser.Values.RETURN_CONSUMED_CAPACITY_NONE)

            order_asc = self.action_params.get(parser.Props.SCAN_INDEX_FORWARD,
                                               None)

            order_type = (None if order_asc is None else models.ORDER_TYPE_ASC
                          if order_asc else models.ORDER_TYPE_DESC)
        except Exception:
            raise exception.AWSValidationException()

        try:
            # select item
            result = storage.query(
                self.context,
                table_name,
                indexed_condition_map,
                select_type=select_type,
                index_name=index_name,
                limit=limit,
                consistent=consistent_read,
                order_type=order_type,
                exclusive_start_key=exclusive_start_key_attributes)

            # format response
            if select_type.type == models.SelectType.SELECT_TYPE_COUNT:
                response = {parser.Props.COUNT: result.count}
            else:
                response = {
                    parser.Props.COUNT:
                    result.count,
                    parser.Props.ITEMS: [
                        parser.Parser.format_item_attributes(row)
                        for row in result.items
                    ]
                }

            if (return_consumed_capacity !=
                    parser.Values.RETURN_CONSUMED_CAPACITY_NONE):
                response[parser.Props.CONSUMED_CAPACITY] = (
                    parser.Parser.format_consumed_capacity(
                        return_consumed_capacity, None))

            if limit == result.count:
                response[parser.Props.LAST_EVALUATED_KEY] = (
                    parser.Parser.format_item_attributes(
                        result.last_evaluated_key))
            return response
        except exception.AWSErrorResponseException as e:
            raise e
        except Exception:
            raise exception.AWSErrorResponseException()
Example #12
0
    def __call__(self):
        try:
            table_name = self.action_params.get(parser.Props.TABLE_NAME, None)

            # parse expected item conditions
            expected_item_conditions = (
                parser.Parser.parse_expected_attribute_conditions(
                    self.action_params.get(parser.Props.EXPECTED, {})))

            # parse item
            key_attributes = parser.Parser.parse_item_attributes(
                self.action_params[parser.Props.KEY])

            # parse return_values param
            return_values = self.action_params.get(
                parser.Props.RETURN_VALUES, parser.Values.RETURN_VALUES_NONE)

            # parse return_item_collection_metrics
            return_item_collection_metrics = self.action_params.get(
                parser.Props.RETURN_ITEM_COLLECTION_METRICS,
                parser.Values.RETURN_ITEM_COLLECTION_METRICS_NONE)

            return_consumed_capacity = self.action_params.get(
                parser.Props.RETURN_CONSUMED_CAPACITY,
                parser.Values.RETURN_CONSUMED_CAPACITY_NONE)
        except Exception:
            raise ddb_exception.AWSValidationException()

        try:
            # put item
            result = storage.delete_item(
                self.context,
                table_name,
                key_attributes,
                expected_condition_map=expected_item_conditions)
        except ddb_exception.AWSErrorResponseException as e:
            raise e
        except Exception:
            raise ddb_exception.AWSErrorResponseException()

        if not result:
            raise ddb_exception.AWSErrorResponseException()

        # format response
        response = {}

        try:
            if return_values != parser.Values.RETURN_VALUES_NONE:
                # TODO(dukhlov):
                # It is needed to return all deleted item attributes
                #
                response[parser.Props.ATTRIBUTES] = (
                    parser.Parser.format_item_attributes(key_attributes))

            if (return_item_collection_metrics !=
                    parser.Values.RETURN_ITEM_COLLECTION_METRICS_NONE):
                response[parser.Props.ITEM_COLLECTION_METRICS] = {
                    parser.Props.ITEM_COLLECTION_KEY: {
                        parser.Parser.format_item_attributes(
                            models.AttributeValue(models.ATTRIBUTE_TYPE_STRING,
                                                  "key"))
                    },
                    parser.Props.SIZE_ESTIMATED_RANGE_GB: [0]
                }

            if (return_consumed_capacity !=
                    parser.Values.RETURN_CONSUMED_CAPACITY_NONE):
                response[parser.Props.CONSUMED_CAPACITY] = (
                    parser.Parser.format_consumed_capacity(
                        return_consumed_capacity, None))

            return response
        except Exception:
            raise exception.ddb_exception.AWSErrorResponseException()
Example #13
0
    def __call__(self):
        try:
            table_name = self.action_params.get(parser.Props.TABLE_NAME, None)

            # parse table attributes
            attribute_definitions = parser.Parser.parse_attribute_definitions(
                self.action_params.get(parser.Props.ATTRIBUTE_DEFINITIONS, {}))

            # parse table key schema
            key_attrs = parser.Parser.parse_key_schema(
                self.action_params.get(parser.Props.KEY_SCHEMA, []))

            # parse table indexed field list
            indexed_def_map = parser.Parser.parse_local_secondary_indexes(
                self.action_params.get(parser.Props.LOCAL_SECONDARY_INDEXES,
                                       []))

            # prepare table_schema structure
            table_schema = models.TableSchema(attribute_definitions, key_attrs,
                                              indexed_def_map)

        except Exception:
            raise ddb_exception.AWSValidationException()

        try:
            # creating table
            table_meta = storage.create_table(self.context, table_name,
                                              table_schema)

            result = {
                parser.Props.TABLE_DESCRIPTION: {
                    parser.Props.ATTRIBUTE_DEFINITIONS:
                    (parser.Parser.format_attribute_definitions(
                        table_meta.schema.attribute_type_map)),
                    parser.Props.CREATION_DATE_TIME:
                    0,
                    parser.Props.ITEM_COUNT:
                    0,
                    parser.Props.KEY_SCHEMA: (parser.Parser.format_key_schema(
                        table_meta.schema.key_attributes)),
                    parser.Props.PROVISIONED_THROUGHPUT:
                    (parser.Values.PROVISIONED_THROUGHPUT_DUMMY),
                    parser.Props.TABLE_NAME:
                    table_name,
                    parser.Props.TABLE_STATUS:
                    (parser.Parser.format_table_status(table_meta.status)),
                    parser.Props.TABLE_SIZE_BYTES:
                    0
                }
            }

            if table_meta.schema.index_def_map:
                table_def = result[parser.Props.TABLE_DESCRIPTION]
                table_def[parser.Props.LOCAL_SECONDARY_INDEXES] = (
                    parser.Parser.format_local_secondary_indexes(
                        table_meta.schema.key_attributes[0],
                        table_meta.schema.index_def_map))

            return result
        except exception.TableAlreadyExistsException:
            raise ddb_exception.AWSDuplicateTableError(table_name)
        except ddb_exception.AWSErrorResponseException as e:
            raise e
        except Exception:
            raise ddb_exception.AWSErrorResponseException()