diff --git a/client/src/App.tsx b/client/src/App.tsx index ea6b3ca..3ce1ad5 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -9,13 +9,14 @@ import "ace-builds/src-noconflict/theme-monokai"; const apiUrl = "https://ufgjji253b.execute-api.us-east-1.amazonaws.com/prod"; const defaultJsonObject = '{\n\t"foo": 5, \n\t"barBaz": "hello"\n}'; -const defaultOptions = { forceOptional: false, snakeCased: false }; +const defaultOptions = { forceOptional: false, snakeCased: false, includeExamples: false }; const loadingMessage = "# loading..."; const invalidJsonMessage = "# invalid json"; type RequestOptions = { forceOptional: boolean; snakeCased: boolean; + includeExamples: boolean; }; type RequestBody = { @@ -30,7 +31,7 @@ function App() { useEffect(() => { if (validJson(jsonObject)) { - fetchConversion(jsonObject, options.forceOptional, options.snakeCased); + fetchConversion(jsonObject, options.forceOptional, options.snakeCased, options.includeExamples); } else { setPydanticModel(invalidJsonMessage); } @@ -52,11 +53,12 @@ function App() { function fetchConversion( newValue: string, forceOptional: boolean, - snakeCased: boolean + snakeCased: boolean, + includeExamples: boolean ) { console.log("fetching"); setPydanticModel(loadingMessage); - const requestOptions: RequestOptions = { forceOptional, snakeCased }; + const requestOptions: RequestOptions = { forceOptional, snakeCased, includeExamples }; const requestBody: RequestBody = { data: newValue, options: requestOptions, @@ -124,8 +126,8 @@ function App() {

-
-

+

+

+
+

+ +

+


diff --git a/server/app/main.py b/server/app/main.py index 1a26be9..b0a6c3c 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -23,6 +23,7 @@ class Options(BaseModel): force_optional: bool = Field(False, alias="forceOptional") snake_cased: bool = Field(False, alias="snakeCased") + include_examples: bool = Field(False, alias="includeExamples") class BasicRequest(BaseModel): @@ -39,6 +40,7 @@ async def convert(basic_request: BasicRequest): basic_request.data, options.force_optional, options.snake_cased, + options.include_examples, ) } diff --git a/server/app/scripts/generator.py b/server/app/scripts/generator.py index dabfd48..52e3164 100644 --- a/server/app/scripts/generator.py +++ b/server/app/scripts/generator.py @@ -3,12 +3,44 @@ from pydantic import Json from datamodel_code_generator.parser.jsonschema import JsonSchemaParser from genson import SchemaBuilder +from genson.schema.strategies import String, Number + + +class StringWithExample(String): + + def add_object(self, obj): + super().add_object(obj) + if not hasattr(self, "example"): + self.example = obj + + def to_schema(self): + schema = super().to_schema() + if hasattr(self, "example"): + schema['example'] = self.example + return schema + + +class NumberWithExample(Number): + def add_object(self, obj): + super().add_object(obj) + if not hasattr(self, "example"): + self.example = obj + + def to_schema(self): + schema = super().to_schema() + if hasattr(self, "example"): + schema['example'] = self.example + return schema + + +class ExampleSchemaBuilder(SchemaBuilder): + EXTRA_STRATEGIES = (StringWithExample, NumberWithExample) def translate( - input_text: Union[Json, Dict[str, Any]], all_optional: bool, snake_case_field: bool + input_text: Union[Json, Dict[str, Any]], all_optional: bool, snake_case_field: bool, include_examples: bool = False ) -> str: - builder = SchemaBuilder() + builder = ExampleSchemaBuilder() if include_examples else SchemaBuilder() builder.add_object(input_text) schema = builder.to_schema() if all_optional: @@ -18,6 +50,7 @@ def translate( source=json.dumps(schema), base_class="pydantic.BaseModel", snake_case_field=snake_case_field, + field_extra_keys={"example"} if include_examples else {}, ) return parser.parse()