Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add validation params to generated freezed classes as static consts #264

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
14 changes: 14 additions & 0 deletions swagger_parser/lib/src/config/swp_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class SWPConfig {
this.mergeClients = false,
this.enumsParentPrefix = true,
this.skippedParameters = const <String>[],
this.generateValidator = false,
});

/// Internal constructor of [SWPConfig]
Expand Down Expand Up @@ -60,6 +61,7 @@ class SWPConfig {
required this.mergeClients,
required this.enumsParentPrefix,
required this.skippedParameters,
required this.generateValidator,
});

/// Creates a [SWPConfig] from [YamlMap].
Expand Down Expand Up @@ -283,6 +285,13 @@ class SWPConfig {
}
}

final generateValidator = yamlMap['generate_validator'];
if (generateValidator is! bool?) {
throw const ConfigException(
"Config parameter 'generate_validator' must be bool.",
);
}

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

Expand Down Expand Up @@ -312,6 +321,7 @@ class SWPConfig {
markFilesAsGenerated: markFilesAsGenerated ?? dc.markFilesAsGenerated,
originalHttpResponse: originalHttpResponse ?? dc.originalHttpResponse,
replacementRules: replacementRules ?? dc.replacementRules,
generateValidator: generateValidator ?? dc.generateValidator,
);
}

Expand Down Expand Up @@ -415,6 +425,9 @@ class SWPConfig {
/// List of parameter names that should skip during parsing
final List<String> skippedParameters;

/// Set `true` to generate validator for freezed.
final bool generateValidator;

/// Convert [SWPConfig] to [GeneratorConfig]
GeneratorConfig toGeneratorConfig() {
return GeneratorConfig(
Expand All @@ -435,6 +448,7 @@ class SWPConfig {
markFilesAsGenerated: markFilesAsGenerated,
originalHttpResponse: originalHttpResponse,
replacementRules: replacementRules,
generateValidator: generateValidator,
);
}

Expand Down
4 changes: 4 additions & 0 deletions swagger_parser/lib/src/generator/config/generator_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class GeneratorConfig {
this.markFilesAsGenerated = false,
this.originalHttpResponse = false,
this.replacementRules = const [],
this.generateValidator = false,
});

/// Optional. Set API name for folder and export file
Expand Down Expand Up @@ -105,4 +106,7 @@ class GeneratorConfig {
/// Optional. Set regex replacement rules for the names of the generated classes/enums.
/// All rules are applied in order.
final List<ReplacementRule> replacementRules;

/// Optional. Set `true` to generate validator function and prams for freezed.
final bool generateValidator;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ final class FillController {
enumsToJson: config.enumsToJson,
unknownEnumValue: config.unknownEnumValue,
markFilesAsGenerated: config.markFilesAsGenerated,
generateValidator: config.generateValidator,
),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum ProgrammingLanguage {
required bool enumsToJson,
required bool unknownEnumValue,
required bool markFilesAsGenerated,
required bool generateValidator,
}) {
switch (this) {
case dart:
Expand All @@ -68,6 +69,7 @@ enum ProgrammingLanguage {
return dartFreezedDtoTemplate(
dataClass,
markFileAsGenerated: markFilesAsGenerated,
generateValidator: generateValidator,
);
case JsonSerializer.jsonSerializable:
return dartJsonSerializableDtoTemplate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import '../model/programming_language.dart';
String dartFreezedDtoTemplate(
UniversalComponentClass dataClass, {
required bool markFileAsGenerated,
bool generateValidator = false,
}) {
final className = dataClass.name.toPascal;
return '''
Expand All @@ -26,8 +27,168 @@ class $className with _\$$className {
dataClass.parameters,
)}${dataClass.parameters.isNotEmpty ? '\n }' : ''}) = _$className;
\n factory $className.fromJson(Map<String, Object?> json) => _\$${className}FromJson(json);
${generateValidator ? dataClass.parameters.map(_validationString).nonNulls.join() : ''}}
${generateValidator ? _validateMethod(className, dataClass.parameters) : ''}''';
}
''';

String _validateMethod(String className, List<UniversalType> types) {
final bodyBuffer = StringBuffer();

for (final type in types) {
final staticName = '$className.${type.name}';
final nullCheckCondition = type.nullable ? '${type.name} != null &&' : '';
final typeName = type.nullable ? '${type.name}!' : type.name;

if (type.min != null) {
bodyBuffer
..write('try {\n')
..write(' if ($nullCheckCondition $typeName < ${staticName}Min) {\n')
..write(' return false;\n')
..write(' }\n')
..write('} catch (e) {\n')
..write(' return false;\n')
..write('}\n');
}

if (type.max != null) {
bodyBuffer
..write('try {\n')
..write(' if ($nullCheckCondition $typeName > ${staticName}Max) {\n')
..write(' return false;\n')
..write(' }\n')
..write('} catch (e) {\n')
..write(' return false;\n')
..write('}\n');
}

if (type.minItems != null) {
bodyBuffer
..write('try {\n')
..write(
' if ($nullCheckCondition $typeName.length < ${staticName}MinItems) {\n')
..write(' return false;\n')
..write(' }\n')
..write('} catch (e) {\n')
..write(' return false;\n')
..write('}\n');
}

if (type.maxItems != null) {
bodyBuffer
..write('try {\n')
..write(
' if ($nullCheckCondition $typeName.length > ${staticName}MaxItems) {\n')
..write(' return false;\n')
..write(' }\n')
..write('} catch (e) {\n')
..write(' return false;\n')
..write('}\n');
}

if (type.minLength != null) {
bodyBuffer
..write('try {\n')
..write(
' if ($nullCheckCondition $typeName.length < ${staticName}MinLength) {\n')
..write(' return false;\n')
..write(' }\n')
..write('} catch (e) {\n')
..write(' return false;\n')
..write('}\n');
}

if (type.maxLength != null) {
bodyBuffer
..write('try {\n')
..write(
' if ($nullCheckCondition $typeName.length > ${staticName}MaxLength) {\n')
..write(' return false;\n')
..write(' }\n')
..write('} catch (e) {\n')
..write(' return false;\n')
..write('}\n');
}

if (type.pattern != null) {
bodyBuffer
..write('try {\n')
..write(
' if ($nullCheckCondition !RegExp(${staticName}Pattern).hasMatch($typeName)) {\n')
..write(' return false;\n')
..write(' }\n')
..write('} catch (e) {\n')
..write(' return false;\n')
..write('}\n');
}

if (type.uniqueItems != null) {
bodyBuffer
..write('try {\n')
..write(
' if ($nullCheckCondition ${staticName}UniqueItems && $typeName.toSet().length != $typeName.length) {\n')
..write(' return false;\n')
..write(' }\n')
..write('} catch (e) {\n')
..write(' return false;\n')
..write('}\n');
}
}

if (bodyBuffer.isEmpty) {
return '';
}

final funcBuffer = StringBuffer()
..write('extension ${className}ValidationX on $className {\n')
..write('bool validate() {\n')
..write(bodyBuffer)
..write(' return true;\n}\n')
..write('}\n');

return funcBuffer.toString();
}

String? _validationString(UniversalType type) {
final sb = StringBuffer();
if (type.min != null) {
final numType = type.type == 'integer' ? int : double;
final min = numType == int ? type.min?.toInt() : type.min;
sb.write(' static const $numType ${type.name}Min = $min;\n');
}

if (type.max != null) {
final numType = type.type == 'integer' ? int : double;
final max = numType == int ? type.max?.toInt() : type.max;
sb.write(' static const $numType ${type.name}Max = $max;\n');
}

if (type.minItems != null) {
sb.write(' static const int ${type.name}MinItems = ${type.minItems};\n');
}

if (type.maxItems != null) {
sb.write(' static const int ${type.name}MaxItems = ${type.maxItems};\n');
}

if (type.minLength != null) {
sb.write(' static const int ${type.name}MinLength = ${type.minLength};\n');
}

if (type.maxLength != null) {
sb.write(' static const int ${type.name}MaxLength = ${type.maxLength};\n');
}

if (type.pattern != null) {
sb.write(
' static const String ${type.name}Pattern = r"${type.pattern}";\n');
}

if (type.uniqueItems != null) {
sb.write(
' static const bool ${type.name}UniqueItems = ${type.uniqueItems};\n');
}

return sb.isEmpty ? null : sb.toString();
}

String _parametersToString(List<UniversalType> parameters) {
Expand Down
62 changes: 59 additions & 3 deletions swagger_parser/lib/src/parser/model/universal_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ final class UniversalType {
this.nullable = false,
this.wrappingCollections = const [],
this.enumType,
this.min,
this.max,
this.minItems,
this.maxItems,
this.minLength,
this.maxLength,
this.pattern,
this.uniqueItems,
});

/// Object type
Expand Down Expand Up @@ -56,6 +64,14 @@ final class UniversalType {

/// Whether or not this field is nullable
final bool nullable;
final double? min;
final double? max;
final int? minItems;
final int? maxItems;
final int? minLength;
final int? maxLength;
final String? pattern;
final bool? uniqueItems;

/// Copy of [UniversalType] with new values
UniversalType copyWith({
Expand All @@ -69,6 +85,14 @@ final class UniversalType {
String? enumType,
List<UniversalCollections>? wrappingCollections,
bool? nullable,
double? min,
double? max,
int? minItems,
int? maxItems,
int? minLength,
int? maxLength,
String? pattern,
bool? uniqueItems,
}) {
return UniversalType(
type: type ?? this.type,
Expand All @@ -81,6 +105,14 @@ final class UniversalType {
enumType: enumType ?? this.enumType,
wrappingCollections: wrappingCollections ?? this.wrappingCollections,
nullable: nullable ?? this.nullable,
min: min ?? this.min,
max: max ?? this.max,
minItems: minItems ?? this.minItems,
maxItems: maxItems ?? this.maxItems,
minLength: minLength ?? this.minLength,
maxLength: maxLength ?? this.maxLength,
pattern: pattern ?? this.pattern,
uniqueItems: uniqueItems ?? this.uniqueItems,
);
}

Expand Down Expand Up @@ -109,7 +141,15 @@ final class UniversalType {
enumType == other.enumType &&
const DeepCollectionEquality()
.equals(wrappingCollections, other.wrappingCollections) &&
nullable == other.nullable;
nullable == other.nullable &&
min == other.min &&
max == other.max &&
minItems == other.minItems &&
maxItems == other.maxItems &&
minLength == other.minLength &&
maxLength == other.maxLength &&
pattern == other.pattern &&
uniqueItems == other.uniqueItems;

@override
int get hashCode =>
Expand All @@ -122,7 +162,15 @@ final class UniversalType {
isRequired.hashCode ^
enumType.hashCode ^
wrappingCollections.hashCode ^
nullable.hashCode;
nullable.hashCode ^
min.hashCode ^
max.hashCode ^
minItems.hashCode ^
maxItems.hashCode ^
minLength.hashCode ^
maxLength.hashCode ^
pattern.hashCode ^
uniqueItems.hashCode;

@override
String toString() => 'UniversalType(type: $type, '
Expand All @@ -133,5 +181,13 @@ final class UniversalType {
'isRequired: $isRequired, '
'enumType: $enumType, '
'wrappingCollections: $wrappingCollections, '
'nullable: $nullable)';
'nullable: $nullable, '
'min: $min, '
'max: $max, '
'minItems: $minItems, '
'maxItems: $maxItems, '
'minLength: $minLength, '
'maxLength: $maxLength, '
'pattern: $pattern, '
'uniqueItems: $uniqueItems)';
}
Loading
Loading