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

Handling content types #109

Merged
merged 9 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
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({'Content-Type': '${request.contentType}'})\n ";
}
return '';
}

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