Skip to content

Commit 6cfebaa

Browse files
jirikuncarci.datadog-api-spectherve
authored
Generate examples using pre-commit hook (#956)
* Generate examples using pre-commit hook * pre-commit fixes * pre-commit fixes * pre-commit fixes Co-authored-by: ci.datadog-api-spec <[email protected]> Co-authored-by: Thomas Hervé <[email protected]>
1 parent 466f670 commit 6cfebaa

File tree

345 files changed

+3561
-772
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

345 files changed

+3561
-772
lines changed

.generator/conftest.py

Lines changed: 571 additions & 0 deletions
Large diffs are not rendered by default.

.generator/poetry.lock

Lines changed: 331 additions & 87 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.generator/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ click = "8.0.1"
1111
PyYAML = "6.0"
1212
jsonref = "0.2"
1313
jinja2 = "3.0.3"
14+
pytest = "^7.1.1"
15+
pytest-bdd = "^5.0.0"
16+
dateutils = "^0.6.12"
1417

1518
[tool.poetry.dev-dependencies]
1619

.generator/src/generator/openapi.py

Lines changed: 163 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import hashlib
12
import json
23
import pathlib
4+
import random
5+
import uuid
36
import yaml
7+
48
from jsonref import JsonRef
59
from yaml import CSafeLoader
610

@@ -58,7 +62,12 @@ def type_to_python(schema, alternative_name=None):
5862
return (
5963
alternative_name
6064
if alternative_name
61-
and ("properties" in schema or "oneOf" in schema or "anyOf" in schema or "allOf" in schema)
65+
and (
66+
"properties" in schema
67+
or "oneOf" in schema
68+
or "anyOf" in schema
69+
or "allOf" in schema
70+
)
6271
else "dict"
6372
)
6473
elif type_ == "null":
@@ -70,7 +79,9 @@ def type_to_python(schema, alternative_name=None):
7079
def get_type_for_attribute(schema, attribute, current_name=None):
7180
"""Return Python type name for the attribute."""
7281
child_schema = schema.get("properties", {}).get(attribute)
73-
alternative_name = current_name + formatter.camel_case(attribute) if current_name else None
82+
alternative_name = (
83+
current_name + formatter.camel_case(attribute) if current_name else None
84+
)
7485
return type_to_python(child_schema, alternative_name=alternative_name)
7586

7687

@@ -155,7 +166,9 @@ def child_models(schema, alternative_name=None, seen=None):
155166
yield name, schema
156167

157168
for key, child in schema.get("properties", {}).items():
158-
yield from child_models(child, alternative_name=name + formatter.camel_case(key), seen=seen)
169+
yield from child_models(
170+
child, alternative_name=name + formatter.camel_case(key), seen=seen
171+
)
159172

160173
if current_name and schema.get("type") == "array":
161174
if name in seen:
@@ -211,7 +224,11 @@ def get_references_for_model(model, model_name):
211224
result = []
212225
top_name = formatter.get_name(model) or model_name
213226
for key, definition in model.get("properties", {}).items():
214-
if definition.get("type") == "object" or definition.get("enum") or definition.get("oneOf"):
227+
if (
228+
definition.get("type") == "object"
229+
or definition.get("enum")
230+
or definition.get("oneOf")
231+
):
215232
name = formatter.get_name(definition)
216233
if name:
217234
result.append(name)
@@ -322,7 +339,8 @@ def get_api_models(operations):
322339
break
323340
for content in operation.get("parameters", []):
324341
if "schema" in content and (
325-
content["schema"].get("type") in ("object", "array") or content["schema"].get("enum")
342+
content["schema"].get("type") in ("object", "array")
343+
or content["schema"].get("enum")
326344
):
327345
name = formatter.get_name(content["schema"])
328346
if name and name not in seen:
@@ -349,7 +367,9 @@ def parameters(operation):
349367

350368
if "requestBody" in operation:
351369
if "multipart/form-data" in operation["requestBody"]["content"]:
352-
parent = operation["requestBody"]["content"]["multipart/form-data"]["schema"]
370+
parent = operation["requestBody"]["content"]["multipart/form-data"][
371+
"schema"
372+
]
353373
for name, schema in parent["properties"].items():
354374
yield name, {
355375
"in": "form",
@@ -412,3 +432,140 @@ def collection_format(parameter):
412432
style = parameter.get("style", in_to_style[in_])
413433
explode = parameter.get("explode", True if style == "form" else False)
414434
return matrix.get((style, explode), "multi")
435+
436+
437+
def generate_value(schema, use_random=False, prefix=None):
438+
spec = schema.spec
439+
if not use_random:
440+
if "example" in spec:
441+
return spec["example"]
442+
if "default" in spec:
443+
return spec["default"]
444+
445+
if spec["type"] == "string":
446+
if use_random:
447+
return str(
448+
uuid.UUID(
449+
bytes=hashlib.sha256(
450+
str(prefix or schema.keys).encode("utf-8"),
451+
).digest()[:16]
452+
)
453+
)
454+
return "string"
455+
elif spec["type"] == "integer":
456+
return (
457+
random.randint(0, 32000) if use_random else len(str(prefix or schema.keys))
458+
)
459+
elif spec["type"] == "number":
460+
return random.random() if use_random else 1.0 / len(str(prefix or schema.keys))
461+
elif spec["type"] == "boolean":
462+
return True
463+
elif spec["type"] == "array":
464+
return [generate_value(schema[0], use_random=use_random)]
465+
elif spec["type"] == "object":
466+
return {
467+
key: generate_value(schema[key], use_random=use_random)
468+
for key in spec["properties"]
469+
}
470+
else:
471+
raise TypeError(f"Unknown type: {spec['type']}")
472+
473+
474+
class Schema:
475+
def __init__(self, spec, value=None, keys=None):
476+
self.spec = spec
477+
self.value = value if value is not None else generate_value
478+
self.keys = keys or tuple()
479+
480+
def __getattr__(self, key):
481+
return self[key]
482+
483+
def __getitem__(self, key):
484+
type_ = self.spec.get("type", "object")
485+
if type_ == "object":
486+
try:
487+
return self.__class__(
488+
self.spec["properties"][key],
489+
value=self.value,
490+
keys=self.keys + (key,),
491+
)
492+
except KeyError:
493+
if "oneOf" in self.spec:
494+
for schema in self.spec["oneOf"]:
495+
if schema.get("type", "object") == "object":
496+
try:
497+
return self.__class__(
498+
schema["properties"][key],
499+
value=self.value,
500+
keys=self.keys + (key,),
501+
)
502+
except KeyError:
503+
pass
504+
raise KeyError(
505+
f"{key} not found in {self.spec.get('properties', {}).keys()}: {self.spec}"
506+
)
507+
if type_ == "array":
508+
return self.__class__(
509+
self.spec["items"], value=self.value, keys=self.keys + (key,)
510+
)
511+
512+
raise KeyError(f"{key} not found in {self.spec}")
513+
514+
def __repr__(self):
515+
value = self.value(self)
516+
if isinstance(value, (dict, list)):
517+
return json.dumps(value, indent=2)
518+
return str(value)
519+
520+
521+
class Operation:
522+
def __init__(self, name, spec, method, path):
523+
self.name = name
524+
self.spec = spec
525+
self.method = method
526+
self.path = path
527+
528+
def server_url_and_method(self, spec, server_index=0, server_variables=None):
529+
def format_server(server, path):
530+
url = server["url"] + path
531+
# replace potential path variables
532+
for variable, value in server_variables.items():
533+
url = url.replace("{" + variable + "}", value)
534+
# replace server variables if they were not replace before
535+
for variable in server["variables"]:
536+
if variable in server_variables:
537+
continue
538+
url = url.replace(
539+
"{" + variable + "}", server["variables"][variable]["default"]
540+
)
541+
return url
542+
543+
server_variables = server_variables or {}
544+
if "servers" in self.spec:
545+
server = self.spec["servers"][server_index]
546+
else:
547+
server = spec["servers"][server_index]
548+
return format_server(server, self.path), self.method
549+
550+
def response_code_and_accept_type(self):
551+
for response in self.spec["responses"]:
552+
return int(response), next(
553+
iter(self.spec["responses"][response].get("content", {None: None}))
554+
)
555+
return None, None
556+
557+
def request_content_type(self):
558+
return next(iter(self.spec.get("requestBody", {}).get("content", {None: None})))
559+
560+
def response(self):
561+
for response in self.spec["responses"]:
562+
return Schema(
563+
next(iter((self.spec["responses"][response]["content"].values())))[
564+
"schema"
565+
]
566+
)
567+
568+
def request(self):
569+
return Schema(
570+
next(iter(self.spec["requestBody"]["content"].values()))["schema"]
571+
)

0 commit comments

Comments
 (0)