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

Unknown enum value #126

Merged
merged 10 commits into from
Oct 26, 2023
Merged
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
7 changes: 6 additions & 1 deletion swagger_parser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.11.0
- Added unknown value to all enums to maintain backwards compatibility when adding new values on the backend
- Add new config parameter `unknown_enum_value` (dart only) ([#106](https://github.com/Carapacik/swagger_parser/issues/106))
- Support String values with spaces for enums ([#127](https://github.com/Carapacik/swagger_parser/issues/127))

## 1.10.6
- Fixed map objects parsing as separate entities ([#124](https://github.com/Carapacik/swagger_parser/issues/124))

Expand All @@ -8,7 +13,7 @@
- Fixed error with `additionalProperties` ([#114](https://github.com/Carapacik/swagger_parser/issues/114))

## 1.10.3
- Add new config parameter `original_http_response`(only for dart) ([#115](https://github.com/Carapacik/swagger_parser/issues/115))
- Add new config parameter `original_http_response` (dart only) ([#115](https://github.com/Carapacik/swagger_parser/issues/115))

## 1.10.2
- Fix error in `body` with name in dart template
Expand Down
5 changes: 4 additions & 1 deletion swagger_parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ swagger_parser:

# Sets the url of the OpenApi schema
schema_url: https://petstore.swagger.io/v2/swagger.json

# Required. Sets output directory for generated files (Clients and DTOs).
output_directory: lib/api

Expand Down Expand Up @@ -107,6 +107,9 @@ swagger_parser:
# Optional. Set 'true' to set enum prefix from parent component.
enums_prefix: false

# Optional (dart only). Set 'true' to maintain backwards compatibility when adding new values on the backend.
unknown_enum_value: true

# Optional. Set 'false' to not put a comment at the beginning of the generated files.
mark_files_as_generated: true

Expand Down
3 changes: 3 additions & 0 deletions swagger_parser/example/swagger_parser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ swagger_parser:
# Optional. Set 'true' to set enum prefix from parent component.
enums_prefix: false

# Optional (dart only). Set 'true' to maintain backwards compatibility when adding new values on the backend.
unknown_enum_value: true

# Optional. Set 'false' to not put a comment at the beginning of the generated files.
mark_files_as_generated: true

Expand Down
10 changes: 10 additions & 0 deletions swagger_parser/lib/src/config/yaml_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ final class YamlConfig {
this.putInFolder,
this.enumsToJson,
this.enumsPrefix,
this.unknownEnumValue,
this.markFilesAsGenerated,
this.originalHttpResponse,
this.replacementRules = const [],
Expand Down Expand Up @@ -203,6 +204,13 @@ final class YamlConfig {
);
}

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

final markFilesAsGenerated = yamlConfig['mark_files_as_generated'];
if (markFilesAsGenerated is! bool?) {
throw const ConfigException(
Expand Down Expand Up @@ -266,6 +274,7 @@ final class YamlConfig {
originalHttpResponse ?? rootConfig?.originalHttpResponse,
enumsToJson: enumsToJson ?? rootConfig?.enumsToJson,
enumsPrefix: enumsPrefix ?? rootConfig?.enumsPrefix,
unknownEnumValue: unknownEnumValue ?? rootConfig?.unknownEnumValue,
markFilesAsGenerated:
markFilesAsGenerated ?? rootConfig?.markFilesAsGenerated,
replacementRules: replacementRules ?? rootConfig?.replacementRules ?? [],
Expand Down Expand Up @@ -361,6 +370,7 @@ final class YamlConfig {
final bool? putInFolder;
final bool? enumsToJson;
final bool? enumsPrefix;
final bool? unknownEnumValue;
final bool? markFilesAsGenerated;
final bool? originalHttpResponse;
final List<ReplacementRule> replacementRules;
Expand Down
4 changes: 4 additions & 0 deletions swagger_parser/lib/src/generator/fill_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ final class FillController {
bool putClientsInFolder = false,
bool freezed = false,
bool enumsToJson = false,
bool unknownEnumValue = true,
bool markFilesAsGenerated = false,
}) : _openApiInfo = openApiInfo,
_clientPostfix = clientPostfix,
Expand All @@ -24,6 +25,7 @@ final class FillController {
_putClientsInFolder = putClientsInFolder,
_freezed = freezed,
_enumsToJson = enumsToJson,
_unknownEnumValue = unknownEnumValue,
_markFilesAsGenerated = markFilesAsGenerated;

final OpenApiInfo _openApiInfo;
Expand All @@ -33,6 +35,7 @@ final class FillController {
final bool _freezed;
final bool _putClientsInFolder;
final bool _enumsToJson;
final bool _unknownEnumValue;
final bool _markFilesAsGenerated;

/// Return [GeneratedFile] generated from given [UniversalDataClass]
Expand All @@ -44,6 +47,7 @@ final class FillController {
dataClass,
freezed: _freezed,
enumsToJson: _enumsToJson,
unknownEnumValue: _unknownEnumValue,
markFilesAsGenerated: _markFilesAsGenerated,
),
);
Expand Down
7 changes: 7 additions & 0 deletions swagger_parser/lib/src/generator/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ final class Generator {
bool? putInFolder,
bool? enumsToJson,
bool? enumsPrefix,
bool? unknownEnumValue,
bool? markFilesAsGenerated,
List<ReplacementRule>? replacementRules,
}) : _schemaPath = schemaPath,
Expand All @@ -67,6 +68,7 @@ final class Generator {
_putInFolder = putInFolder ?? false,
_enumsToJson = enumsToJson ?? false,
_enumsPrefix = enumsPrefix ?? false,
unknownEnumValue = unknownEnumValue ?? true,
_markFilesAsGenerated = markFilesAsGenerated ?? true,
_replacementRules = replacementRules ?? const [];

Expand All @@ -91,6 +93,7 @@ final class Generator {
putInFolder: yamlConfig.putInFolder,
enumsToJson: yamlConfig.enumsToJson,
enumsPrefix: yamlConfig.enumsPrefix,
unknownEnumValue: yamlConfig.unknownEnumValue,
markFilesAsGenerated: yamlConfig.markFilesAsGenerated,
replacementRules: yamlConfig.replacementRules,
);
Expand Down Expand Up @@ -156,6 +159,9 @@ final class Generator {
/// If true, generated enums will have parent component name in its class name
final bool _enumsPrefix;

/// If true, adds an unknown value for all enums to maintain backward compatibility when adding new values on the backend.
final bool unknownEnumValue;

/// If true, generated files will be marked as generated
final bool _markFilesAsGenerated;

Expand Down Expand Up @@ -293,6 +299,7 @@ final class Generator {
freezed: _freezed,
putClientsInFolder: _putClientsInFolder,
enumsToJson: _enumsToJson,
unknownEnumValue: unknownEnumValue,
markFilesAsGenerated: _markFilesAsGenerated,
);
final files = <GeneratedFile>[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ enum ProgrammingLanguage {
UniversalDataClass dataClass, {
required bool freezed,
required bool enumsToJson,
required bool unknownEnumValue,
required bool markFilesAsGenerated,
}) {
switch (this) {
Expand All @@ -47,6 +48,7 @@ enum ProgrammingLanguage {
dataClass,
freezed: freezed,
enumsToJson: enumsToJson,
unknownEnumValue: unknownEnumValue,
markFileAsGenerated: markFilesAsGenerated,
);
} else if (dataClass is UniversalComponentClass) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,60 @@ String dartEnumDtoTemplate(
UniversalEnumClass enumClass, {
required bool freezed,
required bool enumsToJson,
required bool unknownEnumValue,
required bool markFileAsGenerated,
}) {
final className = enumClass.name.toPascal;
final jsonParam = unknownEnumValue || enumsToJson;

final values =
'${enumClass.items.mapIndexed((i, e) => _enumValue(i, enumClass.type, e, jsonParam: jsonParam)).join(',\n')}${unknownEnumValue ? ',' : ';'}';
final unkownEnumValueStr = unknownEnumValue ? _unkownEnumValue() : '';
final constructorStr = jsonParam ? _constructor(className) : '';
final fromJsonStr = unknownEnumValue ? _fromJson(className, enumClass) : '';
final jsonFieldStr = jsonParam ? _jsonField(enumClass) : '';
final toJsonStr = enumsToJson ? _toJson(enumClass, className) : '';

return '''
${generatedFileComment(
markFileAsGenerated: markFileAsGenerated,
)}import '${freezed ? 'package:freezed_annotation/freezed_annotation.dart' : 'package:json_annotation/json_annotation.dart'}';

${descriptionComment(enumClass.description)}@JsonEnum()
enum $className {
${enumClass.items.mapIndexed((i, e) => _jsonValue(i, enumClass.type, e)).join(',\n')};
${enumsToJson ? _toJson(enumClass, className) : '}'}
$values$unkownEnumValueStr$constructorStr$fromJsonStr$jsonFieldStr$toJsonStr
}
''';
}

String _jsonValue(
String _constructor(String className) => '\n\n const $className(this.json);\n';

String _jsonField(UniversalEnumClass enumClass) =>
'\n final ${enumClass.type.toDartType()}? json;';

String _unkownEnumValue() => r'''


/// Default value for all unparsed values, allows backward compatibility when adding new values on the backend.
$unknown(null);''';

String _fromJson(String className, UniversalEnumClass enumClass) => '''

factory $className.fromJson(${enumClass.type.toDartType()} json) => values.firstWhere(
(e) => e.json == json,
orElse: () => \$unknown,
);
''';

String _enumValue(
int index,
String type,
UniversalEnumItem item,
) =>
UniversalEnumItem item, {
required bool jsonParam,
}) =>
'''
${index != 0 && item.description != null ? '\n' : ''}${descriptionComment(item.description, tab: ' ')} @JsonValue(${type == 'string' ? "'${item.jsonKey}'" : item.jsonKey})
${item.name.toCamel}''';

String _toJson(UniversalEnumClass enumClass, String className) => '''

${enumClass.type.toDartType()} toJson() => _\$${className}EnumMap[this]!;
}
${index != 0 ? '\n' : ''}${descriptionComment(item.description, tab: ' ')} @JsonValue(${type == 'string' ? "'${item.jsonKey}'" : item.jsonKey})
${item.name.toCamel}${jsonParam ? '(${type == 'string' ? "'${item.jsonKey}'" : item.jsonKey})' : ''}''';

const _\$${className}EnumMap = {
${enumClass.items.map(
(e) => '$className.${e.name.toCamel}: '
'${enumClass.type == 'string' ? "'" : ''}${e.jsonKey}${enumClass.type == 'string' ? "'" : ''}',
).join(',\n ')},
};''';
String _toJson(UniversalEnumClass enumClass, String className) =>
'\n\n ${enumClass.type.toDartType()}? toJson() => json;';
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,5 @@ String _required(UniversalType t) =>
String _defaultValue(UniversalType t) => t.defaultValue != null
? ' = '
'${t.arrayDepth > 0 ? 'const ' : ''}'
'${t.enumType != null ? '${t.type}.${protectDefaultEnum(t.defaultValue)?.toCamel}' : protectDefaultValue(t.defaultValue, type: t.type)}'
'${t.enumType != null ? '${t.type}.${protectDefaultEnum(t.defaultValue?.toCamel)?.toCamel}' : protectDefaultValue(t.defaultValue, type: t.type)}'
: '';
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,5 @@ String _toQueryParameter(UniversalRequestType parameter) =>
/// return defaultValue if have
String _defaultValue(UniversalType t) => t.defaultValue != null
? ' = '
'${t.enumType != null ? '${t.type}.${protectDefaultEnum(t.defaultValue)?.toScreamingSnake}' : protectDefaultValue(t.defaultValue, type: t.type, dart: false)}'
'${t.enumType != null ? '${t.type}.${protectDefaultEnum(t.defaultValue?.toScreamingSnake)?.toScreamingSnake}' : protectDefaultValue(t.defaultValue, type: t.type, dart: false)}'
: '';
2 changes: 1 addition & 1 deletion swagger_parser/lib/src/utils/type_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ String uniqueName({bool isEnum = false}) {
return name;
}

final _enumNameRegExp = RegExp(r'^[a-zA-Z\d_-]*$');
final _enumNameRegExp = RegExp(r'^[a-zA-Z\d_-\s]*$');
final _startWithNumberRegExp = RegExp(r'^-?\d+');

/// Protect default enum value from incorrect symbols, keywords, etc.
Expand Down
Loading