Repository: s-knibbs/dataclasses-jsonschema (python)

Issue:
don't allow undeclared object properties
Thanks for the great work on this project, been really useful thus far.

We used `"additionalProperties": false` for objects in our (hand-written) json-schema's, which was helpful to prevent invalid inputs. In this project I see no way of achieving the same.

```python
from dataclasses import dataclass
from dataclasses_jsonschema import JsonSchemaMixin

@dataclass
class Point(JsonSchemaMixin):
    "A 2D point"
    x: float
    y: float

# adding an additional key to the dict results in the key being dropped
print(Point.from_dict({"x": 1.1, "y": 2.2, "z": 3.3}))
Point(x=1.1, y=2.2)
```

This is especially problematic with items that have defaults, if a field name is typod validation will still pass, but the resulting object will have unexpected values.

desired validation can be achieved by updating `dataclass_jsonschema/__init__.py` on line 690:
```python
schema = {
                'type': 'object',
                'required': required,
                'properties': properties,
                'additionalProperties': False
            }
```

```python
Point.from_dict({"x": 1.1, "y": 2.2, "z": 3.3})
Traceback (most recent call last):
  File "/Users/jmmccon/.pyenv/versions/taskcat-v9-3.6.9/lib/python3.6/site-packages/dataclasses_jsonschema/__init__.py", line 421, in _validate
    validate_func(data, cls.json_schema())
  File "/Users/jmmccon/.pyenv/versions/taskcat-v9-3.6.9/lib/python3.6/site-packages/jsonschema/validators.py", line 899, in validate
    raise error
jsonschema.exceptions.ValidationError: Additional properties are not allowed ('z' was unexpected)
```

I'm happy to pr a change, but not sure if I've missed some way to do this that is already catered for, or even if others want this behavior (One thought to make it optional is to provide an additional mixin `StrictJsonSchemaMixin` that adds this)

Pull request:
Add class argument to allow/disallow additional properties
Fixes #102 

Patch:
diff --git a/dataclasses_jsonschema/__init__.py b/dataclasses_jsonschema/__init__.py
index 8d3e789..77b392f 100644
--- a/dataclasses_jsonschema/__init__.py
+++ b/dataclasses_jsonschema/__init__.py
@@ -188,12 +188,13 @@ class JsonSchemaMixin:
     __discriminator_name: Optional[str]
     # True if __discriminator_name is inherited from the base class
     __discriminator_inherited: bool
+    __allow_additional_props: bool
 
     @classmethod
     def _discriminator(cls) -> Optional[str]:
         return cls.__discriminator_name
 
-    def __init_subclass__(cls, discriminator: Optional[Union[str, bool]] = None):
+    def __init_subclass__(cls, discriminator: Optional[Union[str, bool]] = None, allow_additional_props: bool = True):
         # Initialise caches
         cls.__schema = {}
         cls.__compiled_schema = {}
@@ -219,6 +220,7 @@ class JsonSchemaMixin:
                     cls.__discriminator_inherited = True
             else:
                 cls.__discriminator_name = None
+        cls.__allow_additional_props = allow_additional_props
 
     @classmethod
     def field_mapping(cls) -> Dict[str, str]:
@@ -693,6 +695,9 @@ class JsonSchemaMixin:
                 'properties': properties
             }
 
+            if not cls.__allow_additional_props:
+                schema["additionalProperties"] = False
+
             if cls.__discriminator_name is not None and \
                     schema_type == SchemaType.OPENAPI_3 and \
                     not cls.__discriminator_inherited:


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
index 45b3532..b956692 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -766,3 +766,23 @@ def test_any_type_schema():
         "required": ["id", "data"]
     })
     assert GraphNode.json_schema() == expected_schema
+
+
+def test_additional_properties_allowed():
+    @dataclass
+    class Scorpion(JsonSchemaMixin, allow_additional_props=False):
+        """A scorpion"""
+        species: str
+        venom_rating: int
+
+    expected_schema = compose_schema({
+        "type": "object",
+        "description": "A scorpion",
+        "properties": {
+            "species": {"type": "string"},
+            "venom_rating": {"type": "integer"},
+        },
+        "required": ["species", "venom_rating"],
+        "additionalProperties": False,
+    })
+    assert Scorpion.json_schema() == expected_schema