Skip to content

Commit

Permalink
Handling content types (#109)
Browse files Browse the repository at this point in the history
Co-authored-by: Carapacik <[email protected]>
Co-authored-by: Elena Ferro <[email protected]>
  • Loading branch information
3 people authored Oct 26, 2023
1 parent 1cfa2a1 commit bf00eb6
Show file tree
Hide file tree
Showing 16 changed files with 128 additions and 79 deletions.
1 change: 1 addition & 0 deletions swagger_parser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 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))
- Add new config parameter `default_content_type`
- Support String values with spaces for enums ([#127](https://github.com/Carapacik/swagger_parser/issues/127))

## 1.10.6
Expand Down
5 changes: 4 additions & 1 deletion swagger_parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ swagger_parser:
# Optional (dart only). Set root client name
root_client_name: RestClient
# Optional. Set API name for folder and export file (coming soon).
# Optional. Set default content-type for all requests
default_content_type: "application/json"
# Optional. Set API name for folder and export file
# If not specified, the file name is used.
name: null
Expand Down
6 changes: 3 additions & 3 deletions swagger_parser/example/swagger_parser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ swagger_parser:
# Optional (dart only). Set root client name
root_client_name: RestClient

# Optional. Set default content-type for all requests
default_content_type: "application/json"

# Optional. Set API name for folder and export file (coming soon).
# If not specified, the file name is used.
name: null
Expand Down Expand Up @@ -58,9 +61,6 @@ 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 @@ -39,6 +39,7 @@ final class YamlConfig {
this.unknownEnumValue,
this.markFilesAsGenerated,
this.originalHttpResponse,
this.defaultContentType,
this.replacementRules = const [],
});

Expand Down Expand Up @@ -218,6 +219,13 @@ final class YamlConfig {
);
}

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

final originalHttpResponse = yamlConfig['original_http_response'];
if (originalHttpResponse is! bool?) {
throw const ConfigException(
Expand Down Expand Up @@ -277,6 +285,7 @@ final class YamlConfig {
unknownEnumValue: unknownEnumValue ?? rootConfig?.unknownEnumValue,
markFilesAsGenerated:
markFilesAsGenerated ?? rootConfig?.markFilesAsGenerated,
defaultContentType: defaultContentType ?? rootConfig?.defaultContentType,
replacementRules: replacementRules ?? rootConfig?.replacementRules ?? [],
);
}
Expand Down Expand Up @@ -372,6 +381,7 @@ final class YamlConfig {
final bool? enumsPrefix;
final bool? unknownEnumValue;
final bool? markFilesAsGenerated;
final String? defaultContentType;
final bool? originalHttpResponse;
final List<ReplacementRule> replacementRules;
}
6 changes: 5 additions & 1 deletion swagger_parser/lib/src/generator/fill_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ final class FillController {
bool enumsToJson = false,
bool unknownEnumValue = true,
bool markFilesAsGenerated = false,
String defaultContentType = 'application/json',
}) : _openApiInfo = openApiInfo,
_clientPostfix = clientPostfix,
_programmingLanguage = programmingLanguage,
Expand All @@ -26,7 +27,8 @@ final class FillController {
_freezed = freezed,
_enumsToJson = enumsToJson,
_unknownEnumValue = unknownEnumValue,
_markFilesAsGenerated = markFilesAsGenerated;
_markFilesAsGenerated = markFilesAsGenerated,
_defaultContentType = defaultContentType;

final OpenApiInfo _openApiInfo;
final ProgrammingLanguage _programmingLanguage;
Expand All @@ -37,6 +39,7 @@ final class FillController {
final bool _enumsToJson;
final bool _unknownEnumValue;
final bool _markFilesAsGenerated;
final String _defaultContentType;

/// Return [GeneratedFile] generated from given [UniversalDataClass]
GeneratedFile fillDtoContent(UniversalDataClass dataClass) => GeneratedFile(
Expand Down Expand Up @@ -66,6 +69,7 @@ final class FillController {
restClient,
restClient.name.toPascal + _clientPostfix.toPascal,
markFilesAsGenerated: _markFilesAsGenerated,
defaultContentType: _defaultContentType,
),
);
}
Expand Down
13 changes: 10 additions & 3 deletions swagger_parser/lib/src/generator/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ final class Generator {
bool? enumsToJson,
bool? enumsPrefix,
bool? unknownEnumValue,
String? defaultContentType,
bool? markFilesAsGenerated,
List<ReplacementRule>? replacementRules,
}) : _schemaPath = schemaPath,
Expand All @@ -68,8 +69,9 @@ final class Generator {
_putInFolder = putInFolder ?? false,
_enumsToJson = enumsToJson ?? false,
_enumsPrefix = enumsPrefix ?? false,
unknownEnumValue = unknownEnumValue ?? true,
_unknownEnumValue = unknownEnumValue ?? true,
_markFilesAsGenerated = markFilesAsGenerated ?? true,
_defaultContentType = defaultContentType ?? 'application/json',
_replacementRules = replacementRules ?? const [];

/// Applies parameters set from yaml config file
Expand Down Expand Up @@ -160,11 +162,14 @@ final class Generator {
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;
final bool _unknownEnumValue;

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

/// Content type for all requests, default 'application/json'
final String _defaultContentType;

/// List of rules used to replace patterns in generated class names
final List<ReplacementRule> _replacementRules;

Expand Down Expand Up @@ -270,6 +275,7 @@ final class Generator {
squashClients: _squashClients,
replacementRules: _replacementRules,
originalHttpResponse: _originalHttpResponse,
defaultContentType: _defaultContentType,
);
_openApiInfo = parser.parseOpenApiInfo();
_restClients = parser.parseRestClients();
Expand Down Expand Up @@ -299,8 +305,9 @@ final class Generator {
freezed: _freezed,
putClientsInFolder: _putClientsInFolder,
enumsToJson: _enumsToJson,
unknownEnumValue: unknownEnumValue,
unknownEnumValue: _unknownEnumValue,
markFilesAsGenerated: _markFilesAsGenerated,
defaultContentType: _defaultContentType,
);
final files = <GeneratedFile>[];
for (final client in _restClients) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,14 @@ enum ProgrammingLanguage {
UniversalRestClient restClient,
String name, {
required bool markFilesAsGenerated,
required String defaultContentType,
}) =>
switch (this) {
dart => dartRetrofitClientTemplate(
restClient: restClient,
name: name,
markFileAsGenerated: markFilesAsGenerated,
defaultContentType: defaultContentType,
),
kotlin => kotlinRetrofitClientTemplate(
restClient: restClient,
Expand Down
13 changes: 8 additions & 5 deletions swagger_parser/lib/src/generator/models/universal_request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ final class UniversalRequest {
required this.route,
required this.returnType,
required this.parameters,
this.contentType = 'application/json',
this.description,
this.isMultiPart = false,
this.isFormUrlEncoded = false,
this.isDeprecated = false,
this.isOriginalHttpResponse = false,
});
Expand All @@ -37,11 +36,15 @@ final class UniversalRequest {
/// Request parameters
final List<UniversalRequestType> parameters;

/// Request type 'multipart/form-data'
final bool isMultiPart;
/// Request content-type
final String contentType;

/// Request has Content-Type 'multipart/form-data'
bool get isMultiPart => contentType == 'multipart/form-data';

/// Request type 'application/x-www-form-urlencoded'
final bool isFormUrlEncoded;
bool get isFormUrlEncoded =>
contentType == 'application/x-www-form-urlencoded';

/// Value indicating whether this request is deprecated
final bool isDeprecated;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ String dartRetrofitClientTemplate({
required UniversalRestClient restClient,
required String name,
required bool markFileAsGenerated,
required String defaultContentType,
}) {
final sb = StringBuffer(
'''
${generatedFileComment(markFileAsGenerated: markFileAsGenerated)}${_fileImport(restClient)}import 'package:dio/dio.dart';
${generatedFileComment(markFileAsGenerated: markFileAsGenerated)}${_fileImport(restClient)}import 'package:dio/dio.dart' hide Headers;
import 'package:retrofit/retrofit.dart';
${dartImports(imports: restClient.imports, pathPrefix: '../models/')}
part '${name.toSnake}.g.dart';
Expand All @@ -28,20 +29,20 @@ abstract class $name {
''',
);
for (final request in restClient.requests) {
sb.write(_toClientRequest(request));
sb.write(_toClientRequest(request, defaultContentType));
}
sb.write('}\n');
return sb.toString();
}

String _toClientRequest(UniversalRequest request) {
String _toClientRequest(UniversalRequest request, String defaultContentType) {
final responseType = request.returnType == null
? 'void'
: request.returnType!.toSuitableType(ProgrammingLanguage.dart);
final sb = StringBuffer(
'''
${descriptionComment(request.description, tabForFirstLine: false, tab: ' ', end: ' ')}${request.isDeprecated ? "@Deprecated('This method is marked as deprecated')\n " : ''}${request.isMultiPart ? '@MultiPart()\n ' : ''}${request.isFormUrlEncoded ? '@FormUrlEncoded()\n ' : ''}@${request.requestType.name.toUpperCase()}('${request.route}')
${descriptionComment(request.description, tabForFirstLine: false, tab: ' ', end: ' ')}${request.isDeprecated ? "@Deprecated('This method is marked as deprecated')\n " : ''}${_contentTypeHeader(request, defaultContentType)}@${request.requestType.name.toUpperCase()}('${request.route}')
Future<${request.isOriginalHttpResponse ? 'HttpResponse<$responseType>' : responseType}> ${request.name}(''',
);
if (request.parameters.isNotEmpty) {
Expand Down Expand Up @@ -76,6 +77,22 @@ String _toParameter(UniversalRequestType parameter) =>
'${parameter.type.toSuitableType(ProgrammingLanguage.dart)} '
'${parameter.type.name!.toCamel}${_defaultValue(parameter.type)},';

String _contentTypeHeader(
UniversalRequest request,
String defaultContentType,
) {
if (request.isMultiPart) {
return '@MultiPart()\n ';
}
if (request.isFormUrlEncoded) {
return '@FormUrlEncoded()\n ';
}
if (request.contentType != defaultContentType) {
return "@Headers(<String, String>{'Content-Type': '${request.contentType}'})\n ";
}
return '';
}

/// return required if isRequired
String _required(UniversalType t) =>
t.isRequired && t.defaultValue == null ? 'required ' : '';
Expand Down
Loading

0 comments on commit bf00eb6

Please sign in to comment.