From 75ab167d7d84eef45debd452f3564fa3729a93d7 Mon Sep 17 00:00:00 2001 From: Dmytro Shevchenko <162686810+DmytroShevchenkoR@users.noreply.github.com> Date: Mon, 28 Oct 2024 20:30:45 +0100 Subject: [PATCH] Ability to generate custom annotations for any model properties --- example/build.yaml | 3 + example/input_folder/pet_service_json.json | 3 +- .../pet_service_json.swagger.dart | 1 + .../swagger_models_generator.dart | 85 +++++++++++++++++-- lib/src/models/generator_options.dart | 23 ++++- lib/src/models/generator_options.g.dart | 16 ++++ .../responses/swagger_schema.dart | 5 ++ .../responses/swagger_schema.g.dart | 2 + 8 files changed, 131 insertions(+), 7 deletions(-) diff --git a/example/build.yaml b/example/build.yaml index d2858c56..356a53d4 100644 --- a/example/build.yaml +++ b/example/build.yaml @@ -19,6 +19,9 @@ targets: - url: "https://raw.githubusercontent.com/epam-cross-platform-lab/swagger-dart-code-generator/master/example/input_folder/pet_service_json.json" file_name: "some_file_name.json" - url: "https://raw.githubusercontent.com/epam-cross-platform-lab/swagger-dart-code-generator/master/example/input_folder/pet_service_yaml.yaml" + custom_annotations: + - type_name: 'SomeCustomAnnotations' + swagger_key: 'some_custom_annotations' with_base_url: true with_converter: true use_path_for_request_names: true diff --git a/example/input_folder/pet_service_json.json b/example/input_folder/pet_service_json.json index 1c5db9ab..be14f1af 100644 --- a/example/input_folder/pet_service_json.json +++ b/example/input_folder/pet_service_json.json @@ -939,7 +939,8 @@ "userStatus": { "type": "integer", "format": "int32", - "description": "User Status" + "description": "User Status", + "some_custom_annotations": "ski-ba-bop-ba-dop-bop" } }, "xml": { diff --git a/example/lib/swagger_generated_code/pet_service_json.swagger.dart b/example/lib/swagger_generated_code/pet_service_json.swagger.dart index 1366b1db..d30934bd 100644 --- a/example/lib/swagger_generated_code/pet_service_json.swagger.dart +++ b/example/lib/swagger_generated_code/pet_service_json.swagger.dart @@ -553,6 +553,7 @@ class User { @JsonKey(name: 'phone', includeIfNull: false, defaultValue: '') final String? phone; @JsonKey(name: 'userStatus', includeIfNull: false) + @SomeCustomAnnotations('ski-ba-bop-ba-dop-bop') final int? userStatus; static const fromJsonFactory = _$UserFromJson; diff --git a/lib/src/code_generators/swagger_models_generator.dart b/lib/src/code_generators/swagger_models_generator.dart index 5620913d..9c5c7161 100644 --- a/lib/src/code_generators/swagger_models_generator.dart +++ b/lib/src/code_generators/swagger_models_generator.dart @@ -526,11 +526,30 @@ class $className implements json.JsonConverter<${value.type}, String> { typeName = typeName.makeNullable(); } + var jsonCustomAnnotationContent = ''; + var rawJson = prop.rawJson; + if (null != rawJson) { + for (var customAnnotation in options.customAnnotations) { + if (rawJson.containsKey(customAnnotation.swaggerKey)) { + var jsonPropKeyValue = rawJson[customAnnotation.swaggerKey]; + if (null != jsonPropKeyValue) { + jsonCustomAnnotationContent += "\t@${customAnnotation.typeName}("; + if (jsonPropKeyValue is String) { + jsonCustomAnnotationContent += "'$jsonPropKeyValue'"; + } else { + jsonCustomAnnotationContent += "$jsonPropKeyValue"; + } + jsonCustomAnnotationContent += ")\n"; + } + } + } + } + final jsonKeyContent = "@JsonKey(name: '$propertyKey'$includeIfNullString$dateToJsonValue${unknownEnumValue.jsonKey})\n"; final deprecatedContent = isDeprecated ? '@deprecated\n' : ''; - return '\t$jsonKeyContent$deprecatedContent\tfinal $typeName ${generateFieldName(propertyName)};${unknownEnumValue.fromJson}'; + return '\t$jsonKeyContent$deprecatedContent$jsonCustomAnnotationContent\tfinal $typeName ${generateFieldName(propertyName)};${unknownEnumValue.fromJson}'; } JsonEnumValue generateEnumValue({ @@ -875,7 +894,26 @@ static $returnType $fromJsonFunction($valueType? value) => $enumNameCamelCase$fr typeName += '?'; } - return '\t$jsonKeyContent$deprecatedContent\tfinal $typeName $propertyName;${unknownEnumValue.fromJson}'; + var jsonCustomAnnotationContent = ''; + var rawJson = prop.rawJson; + if (null != rawJson) { + for (var customAnnotation in options.customAnnotations) { + if (rawJson.containsKey(customAnnotation.swaggerKey)) { + var jsonPropKeyValue = rawJson[customAnnotation.swaggerKey]; + if (null != jsonPropKeyValue) { + jsonCustomAnnotationContent += "\t@${customAnnotation.typeName}("; + if (jsonPropKeyValue is String) { + jsonCustomAnnotationContent += "'$jsonPropKeyValue'"; + } else { + jsonCustomAnnotationContent += "$jsonPropKeyValue"; + } + jsonCustomAnnotationContent += ")\n"; + } + } + } + } + + return '\t$jsonKeyContent$deprecatedContent$jsonCustomAnnotationContent\tfinal $typeName $propertyName;${unknownEnumValue.fromJson}'; } String generateEnumPropertyContent({ @@ -1066,8 +1104,26 @@ static $returnType $fromJsonFunction($valueType? value) => $enumNameCamelCase$fr !requiredProperties.contains(propertyKey)) { listPropertyName = listPropertyName.makeNullable(); } - - return '$jsonConverterAnnotation$jsonKeyContent$deprecatedContent final $listPropertyName ${generateFieldName(propertyName)};${unknownEnumValue.fromJson}'; + + var jsonCustomAnnotationContent = ''; + var rawJson = prop.rawJson; + if (null != rawJson) { + for (var customAnnotation in options.customAnnotations) { + if (rawJson.containsKey(customAnnotation.swaggerKey)) { + var jsonPropKeyValue = rawJson[customAnnotation.swaggerKey]; + if (null != jsonPropKeyValue) { + jsonCustomAnnotationContent += "\t@${customAnnotation.typeName}("; + if (jsonPropKeyValue is String) { + jsonCustomAnnotationContent += "'$jsonPropKeyValue'"; + } else { + jsonCustomAnnotationContent += "$jsonPropKeyValue"; + } + jsonCustomAnnotationContent += ")\n"; + } + } + } + } + return '$jsonConverterAnnotation$jsonKeyContent$deprecatedContent$jsonCustomAnnotationContent final $listPropertyName ${generateFieldName(propertyName)};${unknownEnumValue.fromJson}'; } String generateGeneralPropertyContent({ @@ -1144,7 +1200,26 @@ static $returnType $fromJsonFunction($valueType? value) => $enumNameCamelCase$fr typeName = typeName.makeNullable(); } - return '\t$jsonConverterAnnotation$jsonKeyContent$isDeprecatedContent final $typeName $propertyName;${unknownEnumValue.fromJson}'; + var jsonCustomAnnotationContent = ''; + var rawJson = prop.rawJson; + if (null != rawJson) { + for (var customAnnotation in options.customAnnotations) { + if (rawJson.containsKey(customAnnotation.swaggerKey)) { + var jsonPropKeyValue = rawJson[customAnnotation.swaggerKey]; + if (null != jsonPropKeyValue) { + jsonCustomAnnotationContent += "\t@${customAnnotation.typeName}("; + if (jsonPropKeyValue is String) { + jsonCustomAnnotationContent += "'$jsonPropKeyValue'"; + } else { + jsonCustomAnnotationContent += "$jsonPropKeyValue"; + } + jsonCustomAnnotationContent += ")\n"; + } + } + } + } + + return '\t$jsonConverterAnnotation$jsonKeyContent$isDeprecatedContent$jsonCustomAnnotationContent final $typeName $propertyName;${unknownEnumValue.fromJson}'; } String generatePropertyContentByType( diff --git a/lib/src/models/generator_options.dart b/lib/src/models/generator_options.dart index 3092ac40..ab600a2f 100644 --- a/lib/src/models/generator_options.dart +++ b/lib/src/models/generator_options.dart @@ -40,6 +40,7 @@ class GeneratorOptions { this.multipartFileType = 'List', this.urlencodedFileType = 'Map', this.generateFirstSucceedResponse = true, + this.customAnnotations = const[], }); /// Build options from a JSON map. @@ -81,6 +82,8 @@ class GeneratorOptions { final String customReturnType; final List excludePaths; + @JsonKey(defaultValue: []) + final List customAnnotations; /// Convert this options instance to JSON. Map toJson() => _$GeneratorOptionsToJson(this); } @@ -200,4 +203,22 @@ class CustomScalar { }); Map toJson() => _$CustomScalarToJson(this); -} \ No newline at end of file +} + +@JsonSerializable(fieldRename: FieldRename.snake) +class CustomAnnotationMap { + CustomAnnotationMap({required this.typeName, required this.swaggerKey}); + + /// Build a default value map from a JSON map. + factory CustomAnnotationMap.fromJson(Map json) => + _$CustomAnnotationMapFromJson(json); + + @JsonKey(defaultValue: '') + final String typeName; + + @JsonKey(defaultValue: '') + final String swaggerKey; + + /// Convert this default value map instance to JSON. + Map toJson() => _$CustomAnnotationMapToJson(this); +} diff --git a/lib/src/models/generator_options.g.dart b/lib/src/models/generator_options.g.dart index 3ac43703..1889510b 100644 --- a/lib/src/models/generator_options.g.dart +++ b/lib/src/models/generator_options.g.dart @@ -95,6 +95,11 @@ GeneratorOptions _$GeneratorOptionsFromJson(Map json) => GeneratorOptions( json['urlencoded_file_type'] as String? ?? 'Map', generateFirstSucceedResponse: json['generate_first_succeed_response'] as bool? ?? true, + customAnnotations: (json['custom_annotations'] as List?) + ?.map((e) => CustomAnnotationMap.fromJson( + Map.from(e as Map))) + .toList() ?? + [], ); Map _$GeneratorOptionsToJson(GeneratorOptions instance) => @@ -134,6 +139,7 @@ Map _$GeneratorOptionsToJson(GeneratorOptions instance) => 'import_paths': instance.importPaths, 'custom_return_type': instance.customReturnType, 'exclude_paths': instance.excludePaths, + 'custom_annotations': instance.customAnnotations, }; DefaultValueMap _$DefaultValueMapFromJson(Map json) => @@ -217,3 +223,13 @@ Map _$CustomScalarToJson(CustomScalar instance) => 'deserialize': instance.deserialize, 'serialize': instance.serialize, }; + +CustomAnnotationMap _$CustomAnnotationMapFromJson(Map json) => CustomAnnotationMap( + typeName: json['type_name'] as String? ?? '', + swaggerKey: json['swagger_key'] as String? ?? '', + ); + +Map _$CustomAnnotationMapToJson(CustomAnnotationMap instance) => { + 'type_name': instance.typeName, + 'swagger_key': instance.swaggerKey, + }; diff --git a/lib/src/swagger_models/responses/swagger_schema.dart b/lib/src/swagger_models/responses/swagger_schema.dart index 5321b8c5..e3bc5f40 100644 --- a/lib/src/swagger_models/responses/swagger_schema.dart +++ b/lib/src/swagger_models/responses/swagger_schema.dart @@ -28,6 +28,7 @@ class SwaggerSchema { this.readOnly = false, this.writeOnly = false, this.deprecated = false, + this.rawJson, }) : _type = type; @JsonKey(name: 'readOnly') @@ -36,6 +37,9 @@ class SwaggerSchema { @JsonKey(name: 'writeOnly') bool writeOnly; + @JsonKey(name: 'rawJson') + Map? rawJson; + @JsonKey(name: 'type') dynamic _type; String get type { @@ -132,6 +136,7 @@ class SwaggerSchema { factory SwaggerSchema.fromJson(Map json) => _$SwaggerSchemaFromJson(json) + ..rawJson = json ..enumNames = ((json[kEnumNames] ?? json[kEnumVarnames]) as List?) ?.map((e) => e as String) .toList() diff --git a/lib/src/swagger_models/responses/swagger_schema.g.dart b/lib/src/swagger_models/responses/swagger_schema.g.dart index 44366f25..8b678b3c 100644 --- a/lib/src/swagger_models/responses/swagger_schema.g.dart +++ b/lib/src/swagger_models/responses/swagger_schema.g.dart @@ -55,12 +55,14 @@ SwaggerSchema _$SwaggerSchemaFromJson(Map json) => readOnly: json['readOnly'] as bool? ?? false, writeOnly: json['writeOnly'] as bool? ?? false, deprecated: json['deprecated'] as bool? ?? false, + rawJson: json['rawJson'] as Map?, ); Map _$SwaggerSchemaToJson(SwaggerSchema instance) => { 'readOnly': instance.readOnly, 'writeOnly': instance.writeOnly, + 'rawJson': instance.rawJson, 'type': instance.type, 'deprecated': instance.deprecated, 'title': instance.title,