Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions swagger_parser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 1.41.0
- Add `field_parsers` option for setting field parsers for JSON serializable

## 1.40.0
- Add `include_paths` option for filtering endpoints by paths

Expand Down
16 changes: 16 additions & 0 deletions swagger_parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,22 @@ swagger_parser:
- "/some/wildcard/*/path"
- "/another/wildcard/**"

# Optional (dart & json_serializable only). Set field parsers for JSON serializable.
#
# Field parsers are used to parse specific fields of a DTO.
#
# Parser must implements JsonConverter<T, Object?> interface from json_annotation package.
field_parsers:
- apply_to_type: "int"
parser_name: "CustomIntParser"
parser_absolute_path: "package:your_package/lib/utils/parsers/custom_int_parser.dart"
- apply_to_type: "int?"
parser_name: "CustomNullableIntParser"
parser_absolute_path: "package:your_package/lib/utils/parsers/custom_nullable_int_parser.dart"
- apply_to_type: "bool"
parser_name: "CustomBoolParser"
parser_absolute_path: "package:your_package/lib/utils/parsers/custom_bool_parser.dart"

# Optional. Skip parameters with names.
skipped_parameters:
- "X-Some-Token"
Expand Down
39 changes: 39 additions & 0 deletions swagger_parser/lib/src/config/swp_config.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:args/args.dart';
import 'package:swagger_parser/src/config/config_exception.dart';
import 'package:swagger_parser/src/generator/config/generator_config.dart';
import 'package:swagger_parser/src/generator/model/field_parser.dart';
import 'package:swagger_parser/src/generator/model/json_serializer.dart';
import 'package:swagger_parser/src/generator/model/programming_language.dart';
import 'package:swagger_parser/src/parser/swagger_parser_core.dart';
Expand Down Expand Up @@ -50,6 +51,7 @@ class SWPConfig {
this.inferRequiredFromNullable = false,
this.useFlutterCompute = false,
this.generateUrlsConstants = false,
this.fieldParsers = const [],
});

/// Internal constructor of [SWPConfig]
Expand Down Expand Up @@ -93,6 +95,7 @@ class SWPConfig {
required this.inferRequiredFromNullable,
required this.useFlutterCompute,
required this.generateUrlsConstants,
required this.fieldParsers,
this.fallbackUnion,
});

Expand Down Expand Up @@ -348,12 +351,38 @@ class SWPConfig {
final generateUrlsConstants = yamlMap['generate_urls_constants'] as bool? ??
rootConfig?.generateUrlsConstants;

final rawFieldParsers = yamlMap['field_parsers'] as YamlList?;
List<FieldParser>? fieldParsers;
if (rawFieldParsers != null) {
fieldParsers = [];
for (final p in rawFieldParsers) {
if (p is! YamlMap ||
p['apply_to_type'] is! String ||
p['parser_name'] is! String ||
p['parser_absolute_path'] is! String) {
throw const ConfigException(
"Config parameter 'field_parsers' values must be List of maps with 'apply_to_type', 'parser_name', and 'parser_absolute_path'.",
);
}
fieldParsers.add(
FieldParser(
applyToType: p['apply_to_type'].toString(),
parserName: p['parser_name'].toString(),
parserAbsolutePath: p['parser_absolute_path'].toString(),
),
);
}
} else if (rootConfig?.fieldParsers != null) {
fieldParsers = List.from(rootConfig!.fieldParsers);
}

// Default config
final dc = SWPConfig(name: name, outputDirectory: outputDirectory);

return SWPConfig._(
schemaPath: schemaPath,
schemaUrl: schemaUrl,
fieldParsers: fieldParsers ?? dc.fieldParsers,
outputDirectory: outputDirectory,
name: name,
pathMethodName: pathMethodName ?? dc.pathMethodName,
Expand Down Expand Up @@ -617,6 +646,15 @@ class SWPConfig {
/// Optional. Set `true` to generate URL constants for all endpoints.
final bool generateUrlsConstants;

/// {@template field_parsers}
/// DART ONLY
/// Optional. Set field parsers.
///
/// Field parsers are used to parse specific fields of a DTO.
///
/// {@endtemplate}
final List<FieldParser> fieldParsers;

/// Convert [SWPConfig] to [GeneratorConfig]
GeneratorConfig toGeneratorConfig() {
return GeneratorConfig(
Expand Down Expand Up @@ -647,6 +685,7 @@ class SWPConfig {
includeIfNull: includeIfNull,
useFlutterCompute: useFlutterCompute,
generateUrlsConstants: generateUrlsConstants,
fieldParsers: fieldParsers,
);
}

Expand Down
5 changes: 5 additions & 0 deletions swagger_parser/lib/src/generator/config/generator_config.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:swagger_parser/src/generator/model/field_parser.dart';
import 'package:swagger_parser/src/generator/model/json_serializer.dart';
import 'package:swagger_parser/src/generator/model/programming_language.dart';
import 'package:swagger_parser/src/parser/model/replacement_rule.dart';
Expand Down Expand Up @@ -33,6 +34,7 @@ class GeneratorConfig {
this.includeIfNull = false,
this.useFlutterCompute = false,
this.generateUrlsConstants = false,
this.fieldParsers = const [],
});

/// Optional. Set API name for folder and export file or merged output file
Expand Down Expand Up @@ -159,4 +161,7 @@ class GeneratorConfig {
/// DART/FLUTTER ONLY
/// Optional. Set `true` to generate URL constants for all endpoints.
final bool generateUrlsConstants;

/// {@macro field_parsers}
final List<FieldParser> fieldParsers;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ final class FillController {
dataClass,
jsonSerializer: config.jsonSerializer,
enumsToJson: config.enumsToJson,
fieldParsers: config.fieldParsers,
unknownEnumValue: config.unknownEnumValue,
markFilesAsGenerated: config.markFilesAsGenerated,
generateValidator: config.generateValidator,
Expand Down
25 changes: 25 additions & 0 deletions swagger_parser/lib/src/generator/model/field_parser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/// {@template field_parser}
/// Configuration for a field parser
/// {@endtemplate}
class FieldParser {
/// {@macro field_parser}
const FieldParser({
required this.applyToType,
required this.parserName,
required this.parserAbsolutePath,
});

/// The type of the field to apply the parser to
final String applyToType;

/// The name of the parser
final String parserName;

/// The absolute path to the parser
final String parserAbsolutePath;

@override
String toString() {
return 'FieldParser(applyToType: $applyToType, parserName: $parserName, parserAbsolutePath: $parserAbsolutePath)';
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:swagger_parser/src/generator/model/field_parser.dart';
import 'package:swagger_parser/src/generator/model/generated_file.dart';
import 'package:swagger_parser/src/generator/model/json_serializer.dart';
import 'package:swagger_parser/src/generator/templates/dart_dart_mappable_dto_template.dart';
Expand Down Expand Up @@ -51,6 +52,7 @@ enum ProgrammingLanguage {
required bool useMultipartFile,
required bool dartMappableConvenientWhen,
required bool includeIfNull,
required List<FieldParser> fieldParsers,
bool useFlutterCompute = false,
String? fallbackUnion,
}) {
Expand Down Expand Up @@ -84,6 +86,7 @@ enum ProgrammingLanguage {
dataClass,
markFileAsGenerated: markFilesAsGenerated,
useMultipartFile: useMultipartFile,
fieldParsers: fieldParsers,
includeIfNull: includeIfNull,
useFlutterCompute: useFlutterCompute,
fallbackUnion: fallbackUnion,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:collection/collection.dart';
import 'package:swagger_parser/src/generator/model/field_parser.dart';
import 'package:swagger_parser/src/generator/model/programming_language.dart';
import 'package:swagger_parser/src/parser/model/normalized_identifier.dart';
import 'package:swagger_parser/src/parser/swagger_parser_core.dart';
Expand All @@ -11,6 +12,7 @@ String dartJsonSerializableDtoTemplate(
required bool markFileAsGenerated,
required bool useMultipartFile,
required bool includeIfNull,
required List<FieldParser> fieldParsers,
bool useFlutterCompute = false,
String? fallbackUnion,
}) {
Expand All @@ -33,17 +35,32 @@ String dartJsonSerializableDtoTemplate(
useFlutterCompute ? _generateFlutterComputeSerializer(className) : '';
final asyncImport = useFlutterCompute ? "import 'dart:async';\n\n" : '';

final actualFieldParsers = fieldParsers
.where(
(e) => dataClass.parameters.any(
(p) =>
_renameUnionTypes(
p.toSuitableType(
ProgrammingLanguage.dart,
useMultipartFile: useMultipartFile,
),
) ==
e.applyToType,
),
)
.toList();

return '''
$asyncImport${ioImport(dataClass.parameters, useMultipartFile: useMultipartFile)}import 'package:json_annotation/json_annotation.dart';
${dartImports(imports: _filterUnionImportsForNonUnion(dataClass))}
${dartImports(imports: _filterUnionImportsForNonUnion(dataClass))}${actualFieldParsers.isEmpty ? '' : '\n${actualFieldParsers.map((e) => "import '${e.parserAbsolutePath}';").toSet().join('\n')}'}
part '$classNameSnake.g.dart';

${descriptionComment(dataClass.description)}@JsonSerializable()
class $className {
const $className(${dataClass.parameters.isNotEmpty ? '{' : ''}${_parametersInConstructor(dataClass.parameters, includeIfNull)}${dataClass.parameters.isNotEmpty ? '\n }' : ''});

factory $className.fromJson(Map<String, Object?> json) => _\$${className}FromJson(json);
${_parametersInClass(dataClass.parameters, useMultipartFile, includeIfNull)}${dataClass.parameters.isNotEmpty ? '\n' : ''}
${_parametersInClass(dataClass.parameters, useMultipartFile, includeIfNull, actualFieldParsers)}${dataClass.parameters.isNotEmpty ? '\n' : ''}
Map<String, Object?> toJson() => _\$${className}ToJson(this);
}
$serializerClass''';
Expand Down Expand Up @@ -395,14 +412,17 @@ String _parametersInClass(
Set<UniversalType> parameters,
bool useMultipartFile,
bool includeIfNull,
List<FieldParser> fieldParsers,
) =>
parameters
.mapIndexed(
(i, e) =>
'\n${i != 0 && (e.description?.isNotEmpty ?? false) ? '\n' : ''}${descriptionComment(e.description, tab: ' ')}'
'${_jsonKey(e, includeIfNull)} final ${_renameUnionTypes(e.toSuitableType(ProgrammingLanguage.dart, useMultipartFile: useMultipartFile))} ${e.name};',
)
.join();
parameters.mapIndexed((i, e) {
final dartType = _renameUnionTypes(e.toSuitableType(
ProgrammingLanguage.dart,
useMultipartFile: useMultipartFile));
final fieldParser =
fieldParsers.firstWhereOrNull((f) => f.applyToType == dartType);
return '\n${i != 0 && (e.description?.isNotEmpty ?? false) ? '\n' : ''}${descriptionComment(e.description, tab: ' ')}'
'${fieldParser != null ? '\t@${fieldParser.parserName}()\n' : ''}${_jsonKey(e, includeIfNull)} final ${_renameUnionTypes(e.toSuitableType(ProgrammingLanguage.dart, useMultipartFile: useMultipartFile))} ${e.name};';
}).join();

String _parametersInConstructor(
Set<UniversalType> parameters, bool includeIfNull) {
Expand Down
1 change: 1 addition & 0 deletions swagger_parser/lib/swagger_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export 'src/generator/exception/generator_exception.dart';
export 'src/generator/generator/fill_controller.dart';
export 'src/generator/generator/generator.dart';
export 'src/generator/generator/generator_processor.dart';
export 'src/generator/model/field_parser.dart';
export 'src/generator/model/generated_file.dart';
export 'src/generator/model/json_serializer.dart';
export 'src/generator/model/programming_language.dart';
Expand Down
Loading