Repository: getmoto/moto
Pull request: Fix: DynamoDB:GetItem throws wrong error when table doesn't exist
Closes #3695 

Problem statement:
Mocked DynamoDB throws wrong error when table doesn't exist
## Impact

I am unable to use moto to accurately test code that detects when a DynamoDB table is not replicated in the current region

## Reproduce

```python
from moto import mock_dynamodb2
import boto3

@mock_dynamodb2
def repro():
  client = boto3.client('dynamodb', 'us-east-1')
  result = client.get_item(TableName='foo', Key={'site-id': {'S': 'foo'}})
```

## Expected result

`ResourceNotFound` raised. Here's the stack trace from boto3 for a live API request:

```
  File "test.py", line 3, in <module>
    client.get_item(TableName='foo', Key={'site-id': {'S': 'foo'}})
  File "/usr/local/lib/python3.9/site-packages/botocore/client.py", line 357, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/usr/local/lib/python3.9/site-packages/botocore/client.py", line 676, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.ResourceNotFoundException: An error occurred (ResourceNotFoundException) when calling the GetItem operation: Requested resource not found
```

## Actual result

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "venv/lib/python3.9/site-packages/moto/core/models.py", line 100, in wrapper
    result = func(*args, **kwargs)
  File "<stdin>", line 4, in test_my_model_save
  File "venv/lib/python3.9/site-packages/botocore/client.py", line 337, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "venv/lib/python3.9/site-packages/botocore/client.py", line 656, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the GetItem operation: Validation Exception
```

## Versions
- Python 3.9.0
- Moto 1.3.16
- botocore 1.18.0

Hints from issue discussion:
Thanks for raising this @max-reify. Doesn't look like we're explicitly checking for this scenario yet, so marking it as an enhancement

Commit message: Fix: DynamoDB:GetItem throws wrong error when table doesn't exist

Patch to review:
diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py
index fe1a463d63c6..cc86a9d7578b 100644
--- a/moto/dynamodb2/responses.py
+++ b/moto/dynamodb2/responses.py
@@ -359,9 +359,14 @@ def get_item(self):
 
         try:
             item = self.dynamodb_backend.get_item(name, key, projection_expression)
-        except ValueError:
-            er = "com.amazon.coral.validate#ValidationException"
-            return self.error(er, "Validation Exception")
+        except ValueError as e:
+            if str(e) == "No table found":
+                er = "com.amazonaws.dynamodb.v20120810#ResourceNotFoundException"
+                msg = "Requested resource not found"
+            else:
+                er = "com.amazon.coral.validate#ValidationException"
+                msg = "Validation Exception"
+            return self.error(er, msg)
         if item:
             item_dict = item.describe_attrs(attributes=None)
             item_dict["ConsumedCapacity"] = {"TableName": name, "CapacityUnits": 0.5}
diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py
index 2ad45ddc7060..446d0238096d 100644
--- a/tests/test_dynamodb2/test_dynamodb.py
+++ b/tests/test_dynamodb2/test_dynamodb.py
@@ -5698,3 +5698,12 @@ def test_update_item_add_to_list_using_legacy_attribute_updates():
 
     resp = table.get_item(Key={"id": "list_add"})
     resp["Item"]["attr"].should.equal(["a", "b", "c", "d", "e"])
+
+
+@mock_dynamodb2
+def test_get_item_for_non_existent_table_raises_error():
+    client = boto3.client("dynamodb", "us-east-1")
+    with pytest.raises(ClientError) as ex:
+        client.get_item(TableName="non-existent", Key={"site-id": {"S": "foo"}})
+    ex.value.response["Error"]["Code"].should.equal("ResourceNotFoundException")
+    ex.value.response["Error"]["Message"].should.equal("Requested resource not found")


Code review comments:
diff_hunk: @@ -359,9 +359,14 @@ def get_item(self):
 
         try:
             item = self.dynamodb_backend.get_item(name, key, projection_expression)
-        except ValueError:
-            er = "com.amazon.coral.validate#ValidationException"
-            return self.error(er, "Validation Exception")
+        except ValueError as e:
+            if str(e) == "No table found":
path: moto/dynamodb2/responses.py
text: @user1:
Is it possible to throw a custom exception when the table does not exist? I feel like this would be a cleaner approach:
```
try:
...
except TableNotFound:
...
except ValueError:
...
```

@author:
That's what I did initially, but unfortunately it breaks other tests.  I opted for the simplest solution, though I agree with you that's it's not the most elegant.  Fixing this properly requires fairly major surgery, which I'm pretty hesitant to undertake.

@author:
...looked at this again.  Decided to just fix the reported issue, without touching anything else.  Let me know what you think.

@user1:
Yeah, I think this makes sense, to fail as early as possible. Thanks!