Skip to content

Commit dce1d91

Browse files
bentskudfangl
andauthored
refresh sqs snapshots and fix exceptions in serializer (localstack#9627)
--------- Co-authored-by: Daniel Fangl <daniel.fangl@localstack.cloud>
1 parent 6d7392a commit dce1d91

File tree

12 files changed

+340
-122
lines changed

12 files changed

+340
-122
lines changed

localstack/aws/protocol/serializer.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,6 +1607,9 @@ class SqsQueryResponseSerializer(QueryResponseSerializer):
16071607

16081608
# Some error code changed between JSON and query, and we need to have a way to map it for legacy reason
16091609
JSON_TO_QUERY_ERROR_CODES = {
1610+
"InvalidParameterValueException": "InvalidParameterValue",
1611+
"MissingRequiredParameterException": "MissingParameter",
1612+
"AccessDeniedException": "AccessDenied",
16101613
"QueueDoesNotExist": "AWS.SimpleQueueService.NonExistentQueue",
16111614
"QueueNameExists": "QueueAlreadyExists",
16121615
}
@@ -1682,6 +1685,9 @@ class SqsResponseSerializer(JSONResponseSerializer):
16821685

16831686
# Some error code changed between JSON and query, and we need to have a way to map it for legacy reason
16841687
JSON_TO_QUERY_ERROR_CODES = {
1688+
"InvalidParameterValueException": "InvalidParameterValue",
1689+
"MissingRequiredParameterException": "MissingParameter",
1690+
"AccessDeniedException": "AccessDenied",
16851691
"QueueDoesNotExist": "AWS.SimpleQueueService.NonExistentQueue",
16861692
"QueueNameExists": "QueueAlreadyExists",
16871693
}

localstack/services/sqs/exceptions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
from localstack.aws.api import CommonServiceException
22

33

4-
class InvalidParameterValue(CommonServiceException):
4+
class InvalidParameterValueException(CommonServiceException):
55
def __init__(self, message):
6-
super().__init__("InvalidParameterValue", message, 400, True)
6+
super().__init__("InvalidParameterValueException", message, 400, True)
77

88

99
class InvalidAttributeValue(CommonServiceException):
1010
def __init__(self, message):
1111
super().__init__("InvalidAttributeValue", message, 400, True)
1212

1313

14-
class MissingParameter(CommonServiceException):
14+
class MissingRequiredParameterException(CommonServiceException):
1515
def __init__(self, message):
16-
super().__init__("MissingParameter", message, 400, True)
16+
super().__init__("MissingRequiredParameterException", message, 400, True)

localstack/services/sqs/models.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
from localstack.services.sqs import constants as sqs_constants
2424
from localstack.services.sqs.exceptions import (
2525
InvalidAttributeValue,
26-
InvalidParameterValue,
27-
MissingParameter,
26+
InvalidParameterValueException,
27+
MissingRequiredParameterException,
2828
)
2929
from localstack.services.sqs.utils import (
3030
decode_receipt_handle,
@@ -354,7 +354,7 @@ def update_visibility_timeout(self, receipt_handle: str, visibility_timeout: int
354354
self.validate_receipt_handle(receipt_handle)
355355

356356
if receipt_handle not in self.receipts:
357-
raise InvalidParameterValue(
357+
raise InvalidParameterValueException(
358358
f"Value {receipt_handle} for parameter ReceiptHandle is invalid. Reason: Message does not exist "
359359
f"or is not available for visibility timeout change."
360360
)
@@ -482,7 +482,7 @@ def enqueue_delayed_messages(self):
482482

483483
def _assert_queue_name(self, name):
484484
if not re.match(r"^[a-zA-Z0-9_-]{1,80}$", name):
485-
raise InvalidParameterValue(
485+
raise InvalidParameterValueException(
486486
"Can only include alphanumeric characters, hyphens, or underscores. 1 to 80 in length"
487487
)
488488

@@ -531,7 +531,7 @@ def add_permission(self, label: str, actions: list[str], account_ids: list[str])
531531
policy.setdefault("Statement", [])
532532
existing_statement_ids = [statement.get("Sid") for statement in policy["Statement"]]
533533
if label in existing_statement_ids:
534-
raise InvalidParameterValue(
534+
raise InvalidParameterValueException(
535535
f"Value {label} for parameter Label is invalid. Reason: Already exists."
536536
)
537537
policy["Statement"].append(statement)
@@ -555,7 +555,7 @@ def remove_permission(self, label: str) -> None:
555555
}
556556
existing_statement_ids = [statement.get("Sid") for statement in policy["Statement"]]
557557
if label not in existing_statement_ids:
558-
raise InvalidParameterValue(
558+
raise InvalidParameterValueException(
559559
f"Value {label} for parameter Label is invalid. Reason: can't find label."
560560
)
561561
policy["Statement"] = [
@@ -593,12 +593,12 @@ def put(
593593
delay_seconds: int = None,
594594
):
595595
if message_deduplication_id:
596-
raise InvalidParameterValue(
596+
raise InvalidParameterValueException(
597597
f"Value {message_deduplication_id} for parameter MessageDeduplicationId is invalid. Reason: The "
598598
f"request includes a parameter that is not valid for this queue type. "
599599
)
600600
if message_group_id:
601-
raise InvalidParameterValue(
601+
raise InvalidParameterValueException(
602602
f"Value {message_group_id} for parameter MessageGroupId is invalid. Reason: The request includes a "
603603
f"parameter that is not valid for this queue type. "
604604
)
@@ -818,21 +818,22 @@ def put(
818818
):
819819
if delay_seconds:
820820
# in fifo queues, delay is only applied on queue level. However, explicitly setting delay_seconds=0 is valid
821-
raise InvalidParameterValue(
821+
raise InvalidParameterValueException(
822822
f"Value {delay_seconds} for parameter DelaySeconds is invalid. Reason: The request include parameter "
823823
f"that is not valid for this queue type."
824824
)
825825

826826
if not message_group_id:
827-
raise MissingParameter("The request must contain the parameter MessageGroupId.")
827+
raise MissingRequiredParameterException(
828+
"The request must contain the parameter MessageGroupId."
829+
)
828830
dedup_id = message_deduplication_id
829831
content_based_deduplication = not is_message_deduplication_id_required(self)
830832
if not dedup_id and content_based_deduplication:
831833
dedup_id = hashlib.sha256(message.get("Body").encode("utf-8")).hexdigest()
832834
if not dedup_id:
833-
raise InvalidParameterValue(
834-
"The Queue should either have ContentBasedDeduplication enabled or MessageDeduplicationId provided "
835-
"explicitly "
835+
raise InvalidParameterValueException(
836+
"The queue should either have ContentBasedDeduplication enabled or MessageDeduplicationId provided explicitly"
836837
)
837838

838839
fifo_message = SqsMessage(
@@ -1017,7 +1018,7 @@ def _on_remove_message(self, message: SqsMessage):
10171018

10181019
def _assert_queue_name(self, name):
10191020
if not name.endswith(".fifo"):
1020-
raise InvalidParameterValue(
1021+
raise InvalidParameterValueException(
10211022
"The name of a FIFO queue can only include alphanumeric characters, hyphens, or underscores, "
10221023
"must end with .fifo suffix and be 1 to 80 in length"
10231024
)

localstack/services/sqs/provider.py

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
from localstack.services.edge import ROUTER
6868
from localstack.services.plugins import ServiceLifecycleHook
6969
from localstack.services.sqs import constants as sqs_constants
70-
from localstack.services.sqs.exceptions import InvalidParameterValue
70+
from localstack.services.sqs.exceptions import InvalidParameterValueException
7171
from localstack.services.sqs.models import (
7272
FifoQueue,
7373
SqsMessage,
@@ -109,15 +109,15 @@ def assert_queue_name(queue_name: str, fifo: bool = False):
109109
if queue_name.endswith(".fifo"):
110110
if not fifo:
111111
# Standard queues with .fifo suffix are not allowed
112-
raise InvalidParameterValue(
112+
raise InvalidParameterValueException(
113113
"Can only include alphanumeric characters, hyphens, or underscores. 1 to 80 in length"
114114
)
115115
# The .fifo suffix counts towards the 80-character queue name quota.
116116
queue_name = queue_name[:-5] + "_fifo"
117117

118118
# slashes are actually not allowed, but we've allowed it explicitly in localstack
119119
if not re.match(r"^[a-zA-Z0-9/_-]{1,80}$", queue_name):
120-
raise InvalidParameterValue(
120+
raise InvalidParameterValueException(
121121
"Can only include alphanumeric characters, hyphens, or underscores. 1 to 80 in length"
122122
)
123123

@@ -133,7 +133,7 @@ def check_message_size(
133133
_message_body_size(message_body) + _message_attributes_size(message_attributes)
134134
> max_message_size
135135
):
136-
raise InvalidParameterValue(error)
136+
raise InvalidParameterValueException(error)
137137

138138

139139
def _message_body_size(body: str):
@@ -386,31 +386,31 @@ def check_attributes(message_attributes: MessageBodyAttributeMap):
386386
return
387387
for attribute_name in message_attributes:
388388
if len(attribute_name) >= 256:
389-
raise InvalidParameterValue(
389+
raise InvalidParameterValueException(
390390
"Message (user) attribute names must be shorter than 256 Bytes"
391391
)
392392
if not re.match(sqs_constants.ATTR_NAME_CHAR_REGEX, attribute_name.lower()):
393-
raise InvalidParameterValue(
393+
raise InvalidParameterValueException(
394394
"Message (user) attributes name can only contain upper and lower score characters, digits, periods, "
395395
"hyphens and underscores. "
396396
)
397397
if not re.match(sqs_constants.ATTR_NAME_PREFIX_SUFFIX_REGEX, attribute_name.lower()):
398-
raise InvalidParameterValue(
398+
raise InvalidParameterValueException(
399399
"You can't use message attribute names beginning with 'AWS.' or 'Amazon.'. "
400400
"These strings are reserved for internal use. Additionally, they cannot start or end with '.'."
401401
)
402402

403403
attribute = message_attributes[attribute_name]
404404
attribute_type = attribute.get("DataType")
405405
if not attribute_type:
406-
raise InvalidParameterValue("Missing required parameter DataType")
406+
raise InvalidParameterValueException("Missing required parameter DataType")
407407
if not re.match(sqs_constants.ATTR_TYPE_REGEX, attribute_type):
408-
raise InvalidParameterValue(
408+
raise InvalidParameterValueException(
409409
f"Type for parameter MessageAttributes.Attribute_name.DataType must be prefixed"
410410
f'with "String", "Binary", or "Number", but was: {attribute_type}'
411411
)
412412
if len(attribute_type) >= 256:
413-
raise InvalidParameterValue(
413+
raise InvalidParameterValueException(
414414
"Message (user) attribute types must be shorter than 256 Bytes"
415415
)
416416

@@ -419,25 +419,25 @@ def check_attributes(message_attributes: MessageBodyAttributeMap):
419419
attribute_value = attribute.get("StringValue")
420420

421421
if not attribute_value:
422-
raise InvalidParameterValue(
422+
raise InvalidParameterValueException(
423423
f"Message (user) attribute '{attribute_name}' must contain a non-empty value of type 'String'."
424424
)
425425

426426
check_message_content(attribute_value)
427427
except InvalidMessageContents as e:
428428
# AWS throws a different exception here
429-
raise InvalidParameterValue(e.args[0])
429+
raise InvalidParameterValueException(e.args[0])
430430

431431

432432
def check_fifo_id(fifo_id):
433433
if not fifo_id:
434434
return
435435
if len(fifo_id) >= 128:
436-
raise InvalidParameterValue(
436+
raise InvalidParameterValueException(
437437
"Message deduplication ID and group ID must be shorter than 128 bytes"
438438
)
439439
if not re.match(sqs_constants.FIFO_MSG_REGEX, fifo_id):
440-
raise InvalidParameterValue(
440+
raise InvalidParameterValueException(
441441
"Invalid characters found. Deduplication ID and group ID can only contain"
442442
"alphanumeric characters as well as TODO"
443443
)
@@ -618,25 +618,37 @@ def on_before_stop(self):
618618
self._stop_cloudwatch_metrics_reporting()
619619

620620
@staticmethod
621-
def _require_queue(account_id: str, region_name: str, name: str) -> SqsQueue:
621+
def _require_queue(
622+
account_id: str, region_name: str, name: str, is_query: bool = False
623+
) -> SqsQueue:
622624
"""
623625
Returns the queue for the given name, or raises QueueDoesNotExist if it does not exist.
624626
625627
:param: context: the request context
626628
:param name: the name to look for
629+
:param is_query: whether the request is using query protocol (error message is different)
627630
:returns: the queue
628631
:raises QueueDoesNotExist: if the queue does not exist
629632
"""
630633
store = SqsProvider.get_store(account_id, region_name)
631634
with _STORE_LOCK:
632635
if name not in store.queues.keys():
633-
raise QueueDoesNotExist("The specified queue does not exist for this wsdl version.")
636+
if is_query:
637+
message = "The specified queue does not exist for this wsdl version."
638+
else:
639+
message = "The specified queue does not exist."
640+
raise QueueDoesNotExist(message)
634641

635642
return store.queues[name]
636643

637644
def _require_queue_by_arn(self, context: RequestContext, queue_arn: str) -> SqsQueue:
638645
arn = parse_arn(queue_arn)
639-
return self._require_queue(arn["account"], arn["region"], arn["resource"])
646+
return self._require_queue(
647+
arn["account"],
648+
arn["region"],
649+
arn["resource"],
650+
is_query=context.service.service_name == "sqs-query",
651+
)
640652

641653
def _resolve_queue(
642654
self,
@@ -655,7 +667,10 @@ def _resolve_queue(
655667
:raises QueueDoesNotExist: if the queue does not exist
656668
"""
657669
account_id, region_name, name = resolve_queue_location(context, queue_name, queue_url)
658-
return self._require_queue(account_id, region_name or context.region, name)
670+
is_query = context.service.service_name == "sqs-query"
671+
return self._require_queue(
672+
account_id, region_name or context.region, name, is_query=is_query
673+
)
659674

660675
def create_queue(
661676
self,
@@ -722,11 +737,12 @@ def create_queue(
722737
def get_queue_url(
723738
self, context: RequestContext, queue_name: String, queue_owner_aws_account_id: String = None
724739
) -> GetQueueUrlResult:
725-
store = self.get_store(queue_owner_aws_account_id or context.account_id, context.region)
726-
if queue_name not in store.queues.keys():
727-
raise QueueDoesNotExist("The specified queue does not exist for this wsdl version.")
728-
729-
queue = store.queues[queue_name]
740+
queue = self._require_queue(
741+
queue_owner_aws_account_id or context.account_id,
742+
context.region,
743+
queue_name,
744+
is_query=context.service.service_name == "sqs-query",
745+
)
730746

731747
return GetQueueUrlResult(QueueUrl=queue.url(context))
732748

@@ -1011,7 +1027,7 @@ def receive_message(
10111027
elif (
10121028
num < 1 or num > MAX_NUMBER_OF_MESSAGES
10131029
) and not SQS_DISABLE_MAX_NUMBER_OF_MESSAGE_LIMIT:
1014-
raise InvalidParameterValue(
1030+
raise InvalidParameterValueException(
10151031
f"Value {num} for parameter MaxNumberOfMessages is invalid. "
10161032
f"Reason: Must be between 1 and 10, if provided."
10171033
)
@@ -1153,18 +1169,20 @@ def set_queue_attributes(
11531169
max_receive_count = _redrive_policy.get("maxReceiveCount")
11541170
# TODO: use the actual AWS responses
11551171
if not dl_target_arn:
1156-
raise InvalidParameterValue(
1172+
raise InvalidParameterValueException(
11571173
"The required parameter 'deadLetterTargetArn' is missing"
11581174
)
11591175
if max_receive_count is None:
1160-
raise InvalidParameterValue("The required parameter 'maxReceiveCount' is missing")
1176+
raise InvalidParameterValueException(
1177+
"The required parameter 'maxReceiveCount' is missing"
1178+
)
11611179
try:
11621180
max_receive_count = int(max_receive_count)
11631181
valid_count = 1 <= max_receive_count <= 1000
11641182
except ValueError:
11651183
valid_count = False
11661184
if not valid_count:
1167-
raise InvalidParameterValue(
1185+
raise InvalidParameterValueException(
11681186
f"Value {redrive_policy} for parameter RedrivePolicy is invalid. Reason: Invalid value for "
11691187
f"maxReceiveCount: {max_receive_count}, valid values are from 1 to 1000 both inclusive."
11701188
)
@@ -1233,7 +1251,7 @@ def _validate_actions(self, actions: ActionNameList):
12331251

12341252
for action in actions:
12351253
if action not in valid:
1236-
raise InvalidParameterValue(
1254+
raise InvalidParameterValueException(
12371255
f"Value SQS:{action} for parameter ActionName is invalid. Reason: Please refer to the appropriate "
12381256
"WSDL for a list of valid actions. "
12391257
)
@@ -1260,12 +1278,12 @@ def _assert_batch(
12601278
"It can be at most 80 letters long."
12611279
)
12621280
if require_message_deduplication_id and not entry.get("MessageDeduplicationId"):
1263-
raise InvalidParameterValue(
1281+
raise InvalidParameterValueException(
12641282
"The queue should either have ContentBasedDeduplication enabled or "
12651283
"MessageDeduplicationId provided explicitly"
12661284
)
12671285
if require_fifo_queue_params and not entry.get("MessageGroupId"):
1268-
raise InvalidParameterValue(
1286+
raise InvalidParameterValueException(
12691287
"The request must contain the parameter MessageGroupId."
12701288
)
12711289
if entry_id in visited:

0 commit comments

Comments
 (0)