Repository: localstack/localstack

Issue:
bug: Creating a DynamoDb table with billingMode == PAY_PER_REQUEST and provisionedThroughput set should throw 400 error.
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

When creating a DynamoDb table with billingMode == PAY_PER_REQUEST and provisionedThroughput set, the table is successfully created.

sample output
```
{
    "TableDescription": {
        "AttributeDefinitions": [
            {
                "AttributeName": "id",
                "AttributeType": "S"
            }
        ],
        "TableName": "test-create-table",
        "KeySchema": [
            {
                "AttributeName": "id",
                "KeyType": "HASH"
            }
        ],
        "TableStatus": "ACTIVE",
        "CreationDateTime": "2021-11-22T09:31:40.829000-05:00",
        "ProvisionedThroughput": {
            "ReadCapacityUnits": 10,
            "WriteCapacityUnits": 10
        },
        "TableSizeBytes": 0,
        "ItemCount": 0,
        "TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/test-create-table",
        "TableId": "be6e6143-5031-4709-9432-3a7aa740a84d",
        "BillingModeSummary": {
            "BillingMode": "PAY_PER_REQUEST",
            "LastUpdateToPayPerRequestDateTime": "2021-11-22T09:31:40.829000-05:00"
        }
    }
}
```


### Expected Behavior

An attempt to create a DynamoDb table with billingMode == PAY_PER_REQUEST and provisionedThroughput set should result in a 400 error as it does when calling the AWS API.

The AWS API returns:
```
An error occurred (ValidationException) when calling the CreateTable operation: One or more parameter values were invalid: Neither ReadCapacityUnits nor WriteCapacityUnits can be specified when BillingMode is PAY_PER_REQUEST
```

### How are you starting LocalStack?

With a `docker run` command

### Steps To Reproduce

#### How are you starting localstack (e.g., `bin/localstack` command, arguments, or `docker-compose.yml`)

    docker run -p 4566:4566  -it localstack/localstack:latest

#### Client commands (e.g., AWS SDK code snippet, or sequence of "awslocal" commands)

```
aws --endpoint-url=http://localhost:4566 dynamodb create-table --table-name test-create-table --attribute-definitions '[{"AttributeName":"id", "AttributeType":"S"}]' --key-schema '[{"AttributeName":"id", "KeyType":"HASH"}]' --billing-mode PAY_PER_REQUEST --provisioned-throughput '{ "ReadCapacityUnits": 10, "WriteCapacityUnits": 10 }'
```


### Environment

```markdown
- OS: macOS Catalina (10.15.7)
- LocalStack: latest
```


### Anything else?

_No response_


Discussion:

Resolving patch:
diff --git a/localstack/services/cloudformation/models/dynamodb.py b/localstack/services/cloudformation/models/dynamodb.py
--- a/localstack/services/cloudformation/models/dynamodb.py
+++ b/localstack/services/cloudformation/models/dynamodb.py
@@ -9,6 +9,9 @@ def get_ddb_provisioned_throughput(params, **kwargs):
     args = params.get("ProvisionedThroughput")
     if args == PLACEHOLDER_AWS_NO_VALUE:
         return {}
+    is_ondemand = params.get("BillingMode") == "PAY_PER_REQUEST"
+    if is_ondemand and args is None:
+        return
     if args:
         if isinstance(args["ReadCapacityUnits"], str):
             args["ReadCapacityUnits"] = int(args["ReadCapacityUnits"])
@@ -64,6 +67,15 @@ def fetch_state(self, stack_name, resources):
         table_name = self.resolve_refs_recursively(stack_name, table_name, resources)
         return aws_stack.connect_to_service("dynamodb").describe_table(TableName=table_name)
 
+    @staticmethod
+    def add_defaults(resource, stack_name: str):
+        is_pay_per_request = resource.get("Properties", {}).get("BillingMode") == "PAY_PER_REQUEST"
+        if not is_pay_per_request:
+            resource["Properties"]["ProvisionedThroughput"] = {
+                "ReadCapacityUnits": 5,
+                "WriteCapacityUnits": 5,
+            }
+
     @classmethod
     def get_deploy_templates(cls):
         def _pre_create(resource_id, resources, resource_type, func, stack_name):
@@ -96,12 +108,6 @@ def _generate_res_name():  # TODO: generalize
                             )
                         ),
                     },
-                    "defaults": {
-                        "ProvisionedThroughput": {
-                            "ReadCapacityUnits": 5,
-                            "WriteCapacityUnits": 5,
-                        }
-                    },
                 },
                 {
                     "function": "enable_kinesis_streaming_destination",
diff --git a/localstack/services/dynamodb/provider.py b/localstack/services/dynamodb/provider.py
--- a/localstack/services/dynamodb/provider.py
+++ b/localstack/services/dynamodb/provider.py
@@ -23,6 +23,7 @@
     BatchGetRequestMap,
     BatchWriteItemInput,
     BatchWriteItemOutput,
+    BillingMode,
     CreateGlobalTableOutput,
     CreateTableInput,
     CreateTableOutput,
@@ -386,6 +387,13 @@ def create_table(
         table_name = create_table_input["TableName"]
         if self.table_exists(table_name):
             raise ResourceInUseException("Cannot create preexisting table")
+        billing_mode = create_table_input.get("BillingMode")
+        provisioned_throughput = create_table_input.get("ProvisionedThroughput")
+        if billing_mode == BillingMode.PAY_PER_REQUEST and provisioned_throughput is not None:
+            raise ValidationException(
+                "One or more parameter values were invalid: Neither ReadCapacityUnits nor WriteCapacityUnits can be "
+                "specified when BillingMode is PAY_PER_REQUEST"
+            )
 
         # forward request to backend
         result = self.forward_request(context)


Test patch:
diff --git a/tests/integration/test_dynamodb.py b/tests/integration/test_dynamodb.py
--- a/tests/integration/test_dynamodb.py
+++ b/tests/integration/test_dynamodb.py
@@ -978,6 +978,20 @@ def test_dynamodb_batch_write_item(self):
 
         assert result.get("UnprocessedItems") == {}
 
+    def test_dynamodb_pay_per_request(self):
+        dynamodb = aws_stack.create_external_boto_client("dynamodb")
+        table_name = "ddb-table-%s" % short_uid()
+
+        with pytest.raises(Exception) as e:
+            dynamodb.create_table(
+                TableName=table_name,
+                KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
+                AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}],
+                ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
+                BillingMode="PAY_PER_REQUEST",
+            )
+        assert e.match("ValidationException")
+
     def test_dynamodb_create_table_with_sse_specification(self):
         dynamodb = aws_stack.create_external_boto_client("dynamodb")
         table_name = "ddb-table-%s" % short_uid()