Repository: podhmo/swagger-marshmallow-codegen (language: python)

Issue:
Does not support minItems on array (generate ItemsRange validator instead of Length)
Hello podhmo,

First I would like to thank you for upgrading your library to support OpenAPI 3.0.0, it's working very nicely and seems to work quite nicely.

However I need to report a minor issue: minItems property on array is handled like minimum on number which is incorrect and render the generated marshmallow model unusable.

The following standalone script reproduces the issue.

```python
"""
Generated Marshmallow models crash with TypeError: '<' not supported between instances of 'list' and 'int'
When OpenAPI uses minItems on an array definition
"""


import tempfile
import subprocess


OPENAPI_SPEC = """
openapi: 3.0.0
components:
  schemas:
    ExampleArray:
      description: Example array requiring at least one element
      type: array
      items:
        type: string
      minItems: 1
    ExampleSchema:
      type: object
      properties:
        array:
          $ref: "#/components/schemas/ExampleArray"
      required:
        - array
      additionalProperties: false
"""


def main() -> None:
    """
    Generate Marshmallow models from OpenAPI spec and attempt to use it
    """

    with tempfile.NamedTemporaryFile("w", encoding="utf-8") as temp_fh:
        temp_fh.write(OPENAPI_SPEC.strip())
        temp_fh.seek(0)
        output = subprocess.check_output(["swagger-marshmallow-codegen", temp_fh.name])

    output_str = str(output, "utf-8")

    print()
    print("Generated marshmallow model:")
    print()
    print(output_str)
    print()
    print("Please note validate=[ItemsRange(min=1 is probably incorrect")
    print("It should be using Length validator:")
    print("https://marshmallow.readthedocs.io/en/stable/marshmallow.validate.html?highlight=length#marshmallow.validate.Length")
    print()

    with open("generated_marshmallow_models.py", "w", encoding="utf-8") as path_fh:
        path_fh.write(output_str)

    print("Using the generated model with proper payload is now going to crash")
    print("with TypeError: '<' not supported between instances of 'list' and 'int'")
    print()
    print('Payload is {"array": ["string1", "string2"]}')
    print()

    from generated_marshmallow_models import ExampleSchema

    schema = ExampleSchema()
    schema.load({"array": ["string1", "string2"]})


if __name__ == "__main__":

    main()
```

Thanks in advance for your help,

Best regards, Adam.

PS: Add a Paypal link somewhere or send it to me, I would like to make a small donation


Discussion / hints:
Oh, ok I'l fix it.
> PS: Add a Paypal link somewhere or send it to me, I would like to make a small donation

Wow, if you want to pay for my code, that's grateful.
But this is hobby work, so, instead, I suggest donating to PSF.


Fix patch:
diff --git a/swagger_marshmallow_codegen/validate.py b/swagger_marshmallow_codegen/validate.py
index 82f7d6a..d73bbeb 100644
--- a/swagger_marshmallow_codegen/validate.py
+++ b/swagger_marshmallow_codegen/validate.py
@@ -1,3 +1,4 @@
+import typing as t
 from marshmallow import validate as v
 from marshmallow.validate import Length, Regexp, OneOf  # NOQA
 
@@ -36,6 +37,9 @@ class ItemsRange(v.Range):
     message_lte = "less than or equal to"
     message_lt = "less than"
 
+    def __call__(self, value: t.Any) -> t.Any:
+        return super().__call__(len(value))
+
 
 class Unique(v.Validator):
     message = "{input} is Not unique"


Test patch:
diff --git a/swagger_marshmallow_codegen/tests/test_validate.py b/swagger_marshmallow_codegen/tests/test_validate.py
index e1aade5..9b61be1 100644
--- a/swagger_marshmallow_codegen/tests/test_validate.py
+++ b/swagger_marshmallow_codegen/tests/test_validate.py
@@ -59,6 +59,34 @@ def test_minimum(self):
                     self.assertFalse(c.ok)
 
 
+class ItemsRangeTests(unittest.TestCase):
+    def _makeOne(self, *args, **kwargs):
+        from swagger_marshmallow_codegen.validate import ItemsRange
+
+        return ItemsRange(*args, **kwargs)
+
+    def test_it(self):
+        from marshmallow.validate import ValidationError
+
+        C = namedtuple("C", "kwargs, value, ok")
+
+        # minItems: 1
+        min1_kwargs = dict(min=1, max=None, min_inclusive=True, max_inclusive=True)
+
+        candidates = [
+            C(kwargs=min1_kwargs, value=[], ok=False),
+            C(kwargs=min1_kwargs, value=[1], ok=True),
+        ]
+        for c in candidates:
+            with self.subTest(kwargs=c.kwargs, value=c.value):
+                target = self._makeOne(**c.kwargs)
+                try:
+                    target(c.value)
+                    self.assertTrue(c.ok)
+                except ValidationError:
+                    self.assertFalse(c.ok)
+
+
 class MultipleOfTests(unittest.TestCase):
     def _makeOne(self, *args, **kwargs):
         from swagger_marshmallow_codegen.validate import MultipleOf