Nikolas Rimikis
1 year ago
committed by
GitHub
38 changed files with 2156 additions and 1892 deletions
@ -1,16 +1,4 @@ |
|||||||
|
// ignore: unnecessary_library_directive |
||||||
library dynamite; |
library dynamite; |
||||||
|
|
||||||
import 'dart:convert'; |
export 'src/openapi_builder.dart'; |
||||||
|
|
||||||
import 'package:build/build.dart'; |
|
||||||
import 'package:code_builder/code_builder.dart'; |
|
||||||
import 'package:collection/collection.dart'; |
|
||||||
import 'package:dart_style/dart_style.dart'; |
|
||||||
import 'package:dynamite/src/models/open_api.dart'; |
|
||||||
import 'package:dynamite/src/models/parameter.dart' as spec_parameter; |
|
||||||
import 'package:dynamite/src/models/path_item.dart'; |
|
||||||
import 'package:dynamite/src/models/schema.dart'; |
|
||||||
import 'package:dynamite/src/type_result/type_result.dart'; |
|
||||||
import 'package:path/path.dart' as p; |
|
||||||
|
|
||||||
part 'src/openapi_builder.dart'; |
|
||||||
|
@ -0,0 +1,653 @@ |
|||||||
|
import 'package:code_builder/code_builder.dart'; |
||||||
|
import 'package:collection/collection.dart'; |
||||||
|
import 'package:dynamite/src/builder/resolve_object.dart'; |
||||||
|
import 'package:dynamite/src/builder/resolve_type.dart'; |
||||||
|
import 'package:dynamite/src/builder/state.dart'; |
||||||
|
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||||
|
import 'package:dynamite/src/helpers/dynamite.dart'; |
||||||
|
import 'package:dynamite/src/helpers/type_result.dart'; |
||||||
|
import 'package:dynamite/src/models/open_api.dart'; |
||||||
|
import 'package:dynamite/src/models/path_item.dart'; |
||||||
|
import 'package:dynamite/src/models/schema.dart'; |
||||||
|
import 'package:dynamite/src/type_result/type_result.dart'; |
||||||
|
|
||||||
|
List<Class> generateDynamiteOverrides(final State state) => [ |
||||||
|
Class( |
||||||
|
(final b) => b |
||||||
|
..name = '${state.classPrefix}Response' |
||||||
|
..types.addAll([ |
||||||
|
refer('T'), |
||||||
|
refer('U'), |
||||||
|
]) |
||||||
|
..extend = refer('DynamiteResponse<T, U>') |
||||||
|
..constructors.add( |
||||||
|
Constructor( |
||||||
|
(final b) => b |
||||||
|
..requiredParameters.addAll( |
||||||
|
['data', 'headers'].map( |
||||||
|
(final name) => Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = name |
||||||
|
..toSuper = true, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
) |
||||||
|
..methods.add( |
||||||
|
Method( |
||||||
|
(final b) => b |
||||||
|
..name = 'toString' |
||||||
|
..returns = refer('String') |
||||||
|
..annotations.add(refer('override')) |
||||||
|
..lambda = true |
||||||
|
..body = Code( |
||||||
|
"'${state.classPrefix}Response(data: \$data, headers: \$headers)'", |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
Class( |
||||||
|
(final b) => b |
||||||
|
..name = '${state.classPrefix}ApiException' |
||||||
|
..extend = refer('DynamiteApiException') |
||||||
|
..constructors.add( |
||||||
|
Constructor( |
||||||
|
(final b) => b |
||||||
|
..requiredParameters.addAll( |
||||||
|
['statusCode', 'headers', 'body'].map( |
||||||
|
(final name) => Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = name |
||||||
|
..toSuper = true, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
) |
||||||
|
..methods.addAll([ |
||||||
|
Method( |
||||||
|
(final b) => b |
||||||
|
..name = 'fromResponse' |
||||||
|
..returns = refer('Future<${state.classPrefix}ApiException>') |
||||||
|
..static = true |
||||||
|
..modifier = MethodModifier.async |
||||||
|
..requiredParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'response' |
||||||
|
..type = refer('HttpClientResponse'), |
||||||
|
), |
||||||
|
) |
||||||
|
..body = Code(''' |
||||||
|
String body; |
||||||
|
try { |
||||||
|
body = await response.body; |
||||||
|
} on FormatException { |
||||||
|
body = 'binary'; |
||||||
|
} |
||||||
|
|
||||||
|
return ${state.classPrefix}ApiException( |
||||||
|
response.statusCode, |
||||||
|
response.responseHeaders, |
||||||
|
body, |
||||||
|
); |
||||||
|
'''), |
||||||
|
), |
||||||
|
Method( |
||||||
|
(final b) => b |
||||||
|
..name = 'toString' |
||||||
|
..returns = refer('String') |
||||||
|
..annotations.add(refer('override')) |
||||||
|
..lambda = true |
||||||
|
..body = Code( |
||||||
|
"'${state.classPrefix}ApiException(statusCode: \$statusCode, headers: \$headers, body: \$body)'", |
||||||
|
), |
||||||
|
), |
||||||
|
]), |
||||||
|
), |
||||||
|
]; |
||||||
|
|
||||||
|
Iterable<Class> generateClients( |
||||||
|
final OpenAPI spec, |
||||||
|
final State state, |
||||||
|
) sync* { |
||||||
|
yield* generateDynamiteOverrides(state); |
||||||
|
|
||||||
|
final tags = generateTags(spec); |
||||||
|
yield buildRootClient(spec, state, tags); |
||||||
|
|
||||||
|
for (final tag in tags) { |
||||||
|
yield buildClient(spec, state, tags, tag); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Class buildRootClient( |
||||||
|
final OpenAPI spec, |
||||||
|
final State state, |
||||||
|
final List<String> tags, |
||||||
|
) => |
||||||
|
Class( |
||||||
|
(final b) { |
||||||
|
b |
||||||
|
..extend = refer('DynamiteClient') |
||||||
|
..name = '${state.classPrefix}Client' |
||||||
|
..docs.addAll(spec.formattedTagsFor(null)) |
||||||
|
..constructors.addAll([ |
||||||
|
Constructor( |
||||||
|
(final b) => b |
||||||
|
..requiredParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'baseURL' |
||||||
|
..toSuper = true, |
||||||
|
), |
||||||
|
) |
||||||
|
..optionalParameters.addAll([ |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'baseHeaders' |
||||||
|
..toSuper = true |
||||||
|
..named = true, |
||||||
|
), |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'userAgent' |
||||||
|
..toSuper = true |
||||||
|
..named = true, |
||||||
|
), |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'httpClient' |
||||||
|
..toSuper = true |
||||||
|
..named = true, |
||||||
|
), |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'cookieJar' |
||||||
|
..toSuper = true |
||||||
|
..named = true, |
||||||
|
), |
||||||
|
if (spec.hasAnySecurity) ...[ |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'authentications' |
||||||
|
..toSuper = true |
||||||
|
..named = true, |
||||||
|
), |
||||||
|
], |
||||||
|
]), |
||||||
|
), |
||||||
|
Constructor( |
||||||
|
(final b) => b |
||||||
|
..name = 'fromClient' |
||||||
|
..requiredParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'client' |
||||||
|
..type = refer('DynamiteClient'), |
||||||
|
), |
||||||
|
) |
||||||
|
..initializers.add( |
||||||
|
const Code(''' |
||||||
|
super( |
||||||
|
client.baseURL, |
||||||
|
baseHeaders: client.baseHeaders, |
||||||
|
httpClient: client.httpClient, |
||||||
|
cookieJar: client.cookieJar, |
||||||
|
authentications: client.authentications, |
||||||
|
) |
||||||
|
'''), |
||||||
|
), |
||||||
|
), |
||||||
|
]); |
||||||
|
|
||||||
|
for (final tag in tags.where((final t) => !t.contains('/'))) { |
||||||
|
final client = '${state.classPrefix}${clientName(tag)}'; |
||||||
|
|
||||||
|
b.methods.add( |
||||||
|
Method( |
||||||
|
(final b) => b |
||||||
|
..name = toDartName(tag) |
||||||
|
..lambda = true |
||||||
|
..type = MethodType.getter |
||||||
|
..returns = refer(client) |
||||||
|
..body = Code('$client(this)'), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
b.methods.addAll(buildTags(spec, state, tags, null)); |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
Class buildClient( |
||||||
|
final OpenAPI spec, |
||||||
|
final State state, |
||||||
|
final List<String> tags, |
||||||
|
final String tag, |
||||||
|
) => |
||||||
|
Class( |
||||||
|
(final b) { |
||||||
|
b |
||||||
|
..name = '${state.classPrefix}${clientName(tag)}' |
||||||
|
..docs.addAll(spec.formattedTagsFor(tag)) |
||||||
|
..constructors.add( |
||||||
|
Constructor( |
||||||
|
(final b) => b.requiredParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = '_rootClient' |
||||||
|
..toThis = true, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
) |
||||||
|
..fields.add( |
||||||
|
Field( |
||||||
|
(final b) => b |
||||||
|
..name = '_rootClient' |
||||||
|
..type = refer('${state.classPrefix}Client') |
||||||
|
..modifier = FieldModifier.final$, |
||||||
|
), |
||||||
|
); |
||||||
|
|
||||||
|
for (final t in tags.where((final t) => t.startsWith('$tag/'))) { |
||||||
|
b.methods.add( |
||||||
|
Method( |
||||||
|
(final b) => b |
||||||
|
..name = toDartName(t.substring('$tag/'.length)) |
||||||
|
..lambda = true |
||||||
|
..type = MethodType.getter |
||||||
|
..returns = refer('${state.classPrefix}${clientName(t)}') |
||||||
|
..body = Code('${state.classPrefix}${clientName(t)}(_rootClient)'), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
b.methods.addAll(buildTags(spec, state, tags, tag)); |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
Iterable<Method> buildTags( |
||||||
|
final OpenAPI spec, |
||||||
|
final State state, |
||||||
|
final List<String> tags, |
||||||
|
final String? tag, |
||||||
|
) sync* { |
||||||
|
final isRootClient = tag == null; |
||||||
|
final paths = generatePaths(spec, tag); |
||||||
|
|
||||||
|
for (final pathEntry in paths.entries) { |
||||||
|
for (final operationEntry in pathEntry.value.operations.entries) { |
||||||
|
yield Method( |
||||||
|
(final b) { |
||||||
|
final httpMethod = operationEntry.key; |
||||||
|
final operation = operationEntry.value; |
||||||
|
final operationId = operation.operationId ?? toDartName('$httpMethod-${pathEntry.key}'); |
||||||
|
final parameters = [ |
||||||
|
...?pathEntry.value.parameters, |
||||||
|
...?operation.parameters, |
||||||
|
]..sort(sortRequiredParameters); |
||||||
|
b |
||||||
|
..name = toDartName(filterMethodName(operationId, tag ?? '')) |
||||||
|
..modifier = MethodModifier.async |
||||||
|
..docs.addAll(operation.formattedDescription); |
||||||
|
if (operation.deprecated ?? false) { |
||||||
|
b.annotations.add(refer('Deprecated').call([refer("''")])); |
||||||
|
} |
||||||
|
|
||||||
|
final acceptHeader = operation.responses?.values |
||||||
|
.map((final response) => response.content?.keys) |
||||||
|
.whereNotNull() |
||||||
|
.expand((final element) => element) |
||||||
|
.toSet() |
||||||
|
.join(',') ?? |
||||||
|
''; |
||||||
|
final code = StringBuffer(''' |
||||||
|
var _path = '${pathEntry.key}'; |
||||||
|
final _queryParameters = <String, dynamic>{}; |
||||||
|
final _headers = <String, String>{${acceptHeader.isNotEmpty ? "'Accept': '$acceptHeader'," : ''}}; |
||||||
|
Uint8List? _body; |
||||||
|
'''); |
||||||
|
|
||||||
|
final security = operation.security ?? spec.security ?? []; |
||||||
|
final securityRequirements = security.where((final requirement) => requirement.isNotEmpty); |
||||||
|
final isOptionalSecurity = securityRequirements.length != security.length; |
||||||
|
code.write(' // coverage:ignore-start\n'); |
||||||
|
for (final requirement in securityRequirements) { |
||||||
|
final securityScheme = spec.components!.securitySchemes![requirement.keys.single]!; |
||||||
|
code.write(''' |
||||||
|
if (${isRootClient ? 'this' : '_rootClient'}.authentications.where((final a) => a.type == '${securityScheme.type}' && a.scheme == '${securityScheme.scheme}').isNotEmpty) { |
||||||
|
_headers.addAll(${isRootClient ? 'this' : '_rootClient'}.authentications.singleWhere((final a) => a.type == '${securityScheme.type}' && a.scheme == '${securityScheme.scheme}').headers); |
||||||
|
} |
||||||
|
'''); |
||||||
|
if (securityRequirements.last != requirement) { |
||||||
|
code.write('else '); |
||||||
|
} |
||||||
|
} |
||||||
|
if (securityRequirements.isNotEmpty && !isOptionalSecurity) { |
||||||
|
code.write(''' |
||||||
|
else { |
||||||
|
throw Exception('Missing authentication for ${securityRequirements.map((final r) => r.keys.single).join(' or ')}'); |
||||||
|
} |
||||||
|
'''); |
||||||
|
} |
||||||
|
code.write(' // coverage:ignore-end\n'); |
||||||
|
|
||||||
|
for (final parameter in parameters) { |
||||||
|
final dartParameterNullable = isDartParameterNullable( |
||||||
|
parameter.required, |
||||||
|
parameter.schema, |
||||||
|
); |
||||||
|
|
||||||
|
final result = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
toDartName( |
||||||
|
'$operationId-${parameter.name}', |
||||||
|
uppercaseFirstCharacter: true, |
||||||
|
), |
||||||
|
parameter.schema!, |
||||||
|
nullable: dartParameterNullable, |
||||||
|
).dartType; |
||||||
|
|
||||||
|
if (result.name == 'String') { |
||||||
|
if (parameter.schema?.pattern != null) { |
||||||
|
code.write(''' |
||||||
|
if (!RegExp(r'${parameter.schema!.pattern!}').hasMatch(${toDartName(parameter.name)})) { |
||||||
|
throw Exception('Invalid value "\$${toDartName(parameter.name)}" for parameter "${toDartName(parameter.name)}" with pattern "\${r'${parameter.schema!.pattern!}'}"'); // coverage:ignore-line |
||||||
|
} |
||||||
|
'''); |
||||||
|
} |
||||||
|
if (parameter.schema?.minLength != null) { |
||||||
|
code.write(''' |
||||||
|
if (${toDartName(parameter.name)}.length < ${parameter.schema!.minLength!}) { |
||||||
|
throw Exception('Parameter "${toDartName(parameter.name)}" has to be at least ${parameter.schema!.minLength!} characters long'); // coverage:ignore-line |
||||||
|
} |
||||||
|
'''); |
||||||
|
} |
||||||
|
if (parameter.schema?.maxLength != null) { |
||||||
|
code.write(''' |
||||||
|
if (${toDartName(parameter.name)}.length > ${parameter.schema!.maxLength!}) { |
||||||
|
throw Exception('Parameter "${toDartName(parameter.name)}" has to be at most ${parameter.schema!.maxLength!} characters long'); // coverage:ignore-line |
||||||
|
} |
||||||
|
'''); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final defaultValueCode = parameter.schema?.default_ != null |
||||||
|
? valueToEscapedValue(result, parameter.schema!.default_.toString()) |
||||||
|
: null; |
||||||
|
|
||||||
|
b.optionalParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) { |
||||||
|
b |
||||||
|
..named = true |
||||||
|
..name = toDartName(parameter.name) |
||||||
|
..required = parameter.isDartRequired; |
||||||
|
if (parameter.schema != null) { |
||||||
|
b.type = refer(result.nullableName); |
||||||
|
} |
||||||
|
if (defaultValueCode != null) { |
||||||
|
b.defaultTo = Code(defaultValueCode); |
||||||
|
} |
||||||
|
}, |
||||||
|
), |
||||||
|
); |
||||||
|
|
||||||
|
if (dartParameterNullable) { |
||||||
|
code.write('if (${toDartName(parameter.name)} != null) {'); |
||||||
|
} |
||||||
|
final value = result.encode( |
||||||
|
toDartName(parameter.name), |
||||||
|
onlyChildren: result is TypeResultList && parameter.in_ == 'query', |
||||||
|
); |
||||||
|
if (defaultValueCode != null && parameter.in_ == 'query') { |
||||||
|
code.write('if (${toDartName(parameter.name)} != $defaultValueCode) {'); |
||||||
|
} |
||||||
|
switch (parameter.in_) { |
||||||
|
case 'path': |
||||||
|
code.write( |
||||||
|
"_path = _path.replaceAll('{${parameter.name}}', Uri.encodeQueryComponent($value));", |
||||||
|
); |
||||||
|
case 'query': |
||||||
|
code.write( |
||||||
|
"_queryParameters['${parameter.name}'] = $value;", |
||||||
|
); |
||||||
|
case 'header': |
||||||
|
code.write( |
||||||
|
"_headers['${parameter.name}'] = $value;", |
||||||
|
); |
||||||
|
default: |
||||||
|
throw Exception('Can not work with parameter in "${parameter.in_}"'); |
||||||
|
} |
||||||
|
if (defaultValueCode != null && parameter.in_ == 'query') { |
||||||
|
code.write('}'); |
||||||
|
} |
||||||
|
if (dartParameterNullable) { |
||||||
|
code.write('}'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (operation.requestBody != null) { |
||||||
|
if (operation.requestBody!.content!.length > 1) { |
||||||
|
throw Exception('Can not work with multiple mime types right now'); |
||||||
|
} |
||||||
|
for (final content in operation.requestBody!.content!.entries) { |
||||||
|
final mimeType = content.key; |
||||||
|
final mediaType = content.value; |
||||||
|
|
||||||
|
code.write("_headers['Content-Type'] = '$mimeType';"); |
||||||
|
|
||||||
|
final dartParameterNullable = isDartParameterNullable( |
||||||
|
operation.requestBody!.required, |
||||||
|
mediaType.schema, |
||||||
|
); |
||||||
|
|
||||||
|
final result = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
toDartName('$operationId-request-$mimeType', uppercaseFirstCharacter: true), |
||||||
|
mediaType.schema!, |
||||||
|
nullable: dartParameterNullable, |
||||||
|
); |
||||||
|
final parameterName = toDartName(result.name.replaceFirst(state.classPrefix, '')); |
||||||
|
switch (mimeType) { |
||||||
|
case 'application/json': |
||||||
|
case 'application/x-www-form-urlencoded': |
||||||
|
final dartParameterRequired = isRequired( |
||||||
|
operation.requestBody!.required, |
||||||
|
mediaType.schema?.default_, |
||||||
|
); |
||||||
|
b.optionalParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = parameterName |
||||||
|
..type = refer(result.nullableName) |
||||||
|
..named = true |
||||||
|
..required = dartParameterRequired, |
||||||
|
), |
||||||
|
); |
||||||
|
|
||||||
|
if (dartParameterNullable) { |
||||||
|
code.write('if ($parameterName != null) {'); |
||||||
|
} |
||||||
|
code.write( |
||||||
|
'_body = utf8.encode(${result.encode(parameterName, mimeType: mimeType)}) as Uint8List;', |
||||||
|
); |
||||||
|
if (dartParameterNullable) { |
||||||
|
code.write('}'); |
||||||
|
} |
||||||
|
default: |
||||||
|
throw Exception('Can not parse mime type "$mimeType"'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
code.write( |
||||||
|
''' |
||||||
|
final _response = await ${isRootClient ? 'this' : '_rootClient'}.doRequest( |
||||||
|
'$httpMethod', |
||||||
|
Uri(path: _path, queryParameters: _queryParameters.isNotEmpty ? _queryParameters : null), |
||||||
|
_headers, |
||||||
|
_body, |
||||||
|
); |
||||||
|
''', |
||||||
|
); |
||||||
|
|
||||||
|
if (operation.responses != null) { |
||||||
|
if (operation.responses!.length > 1) { |
||||||
|
throw Exception('Can not work with multiple status codes right now'); |
||||||
|
} |
||||||
|
for (final responseEntry in operation.responses!.entries) { |
||||||
|
final statusCode = responseEntry.key; |
||||||
|
final response = responseEntry.value; |
||||||
|
code.write('if (_response.statusCode == $statusCode) {'); |
||||||
|
|
||||||
|
String? headersType; |
||||||
|
String? headersValue; |
||||||
|
if (response.headers != null) { |
||||||
|
final identifier = |
||||||
|
'${tag != null ? toDartName(tag, uppercaseFirstCharacter: true) : null}${toDartName(operationId, uppercaseFirstCharacter: true)}Headers'; |
||||||
|
final result = resolveObject( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
identifier, |
||||||
|
Schema( |
||||||
|
properties: response.headers!.map( |
||||||
|
(final headerName, final value) => MapEntry( |
||||||
|
headerName.toLowerCase(), |
||||||
|
value.schema!, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
isHeader: true, |
||||||
|
); |
||||||
|
headersType = result.name; |
||||||
|
headersValue = result.deserialize('_response.responseHeaders'); |
||||||
|
} |
||||||
|
|
||||||
|
String? dataType; |
||||||
|
String? dataValue; |
||||||
|
bool? dataNeedsAwait; |
||||||
|
if (response.content != null) { |
||||||
|
if (response.content!.length > 1) { |
||||||
|
throw Exception('Can not work with multiple mime types right now'); |
||||||
|
} |
||||||
|
for (final content in response.content!.entries) { |
||||||
|
final mimeType = content.key; |
||||||
|
final mediaType = content.value; |
||||||
|
|
||||||
|
final result = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
toDartName( |
||||||
|
'$operationId-response-$statusCode-$mimeType', |
||||||
|
uppercaseFirstCharacter: true, |
||||||
|
), |
||||||
|
mediaType.schema!, |
||||||
|
); |
||||||
|
|
||||||
|
if (mimeType == '*/*' || mimeType == 'application/octet-stream' || mimeType.startsWith('image/')) { |
||||||
|
dataType = 'Uint8List'; |
||||||
|
dataValue = '_response.bodyBytes'; |
||||||
|
dataNeedsAwait = true; |
||||||
|
} else if (mimeType.startsWith('text/') || mimeType == 'application/javascript') { |
||||||
|
dataType = 'String'; |
||||||
|
dataValue = '_response.body'; |
||||||
|
dataNeedsAwait = true; |
||||||
|
} else if (mimeType == 'application/json') { |
||||||
|
dataType = result.name; |
||||||
|
if (result.name == 'dynamic') { |
||||||
|
dataValue = ''; |
||||||
|
} else if (result.name == 'String') { |
||||||
|
dataValue = '_response.body'; |
||||||
|
dataNeedsAwait = true; |
||||||
|
} else if (result is TypeResultEnum || result is TypeResultBase) { |
||||||
|
dataValue = result.deserialize(result.decode('await _response.body')); |
||||||
|
dataNeedsAwait = false; |
||||||
|
} else { |
||||||
|
dataValue = result.deserialize('await _response.jsonBody'); |
||||||
|
dataNeedsAwait = false; |
||||||
|
} |
||||||
|
} else { |
||||||
|
throw Exception('Can not parse mime type "$mimeType"'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (headersType != null && dataType != null) { |
||||||
|
b.returns = refer('Future<${state.classPrefix}Response<$dataType, $headersType>>'); |
||||||
|
code.write( |
||||||
|
'return ${state.classPrefix}Response<$dataType, $headersType>(${dataNeedsAwait ?? false ? 'await ' : ''}$dataValue, $headersValue,);', |
||||||
|
); |
||||||
|
} else if (headersType != null) { |
||||||
|
b.returns = refer('Future<$headersType>'); |
||||||
|
code.write('return $headersValue;'); |
||||||
|
} else if (dataType != null) { |
||||||
|
b.returns = refer('Future<$dataType>'); |
||||||
|
code.write('return $dataValue;'); |
||||||
|
} else { |
||||||
|
b.returns = refer('Future'); |
||||||
|
code.write('return;'); |
||||||
|
} |
||||||
|
|
||||||
|
code.write('}'); |
||||||
|
} |
||||||
|
code.write( |
||||||
|
'throw await ${state.classPrefix}ApiException.fromResponse(_response); // coverage:ignore-line\n', |
||||||
|
); |
||||||
|
} else { |
||||||
|
b.returns = refer('Future'); |
||||||
|
} |
||||||
|
b.body = Code(code.toString()); |
||||||
|
}, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Map<String, PathItem> generatePaths(final OpenAPI spec, final String? tag) { |
||||||
|
final paths = <String, PathItem>{}; |
||||||
|
|
||||||
|
if (spec.paths != null) { |
||||||
|
for (final path in spec.paths!.entries) { |
||||||
|
for (final operationEntry in path.value.operations.entries) { |
||||||
|
final operation = operationEntry.value; |
||||||
|
if ((operation.tags != null && operation.tags!.contains(tag)) || |
||||||
|
(tag == null && (operation.tags == null || operation.tags!.isEmpty))) { |
||||||
|
paths[path.key] ??= PathItem( |
||||||
|
description: path.value.description, |
||||||
|
parameters: path.value.parameters, |
||||||
|
); |
||||||
|
paths[path.key] = paths[path.key]!.copyWithOperations({operationEntry.key: operation}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return paths; |
||||||
|
} |
||||||
|
|
||||||
|
List<String> generateTags(final OpenAPI spec) { |
||||||
|
final tags = <String>[]; |
||||||
|
|
||||||
|
if (spec.paths != null) { |
||||||
|
for (final pathItem in spec.paths!.values) { |
||||||
|
for (final operation in pathItem.operations.values) { |
||||||
|
if (operation.tags != null) { |
||||||
|
for (final tag in operation.tags!) { |
||||||
|
final tagPart = tag.split('/').first; |
||||||
|
if (!tags.contains(tagPart)) { |
||||||
|
tags.add(tagPart); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return tags..sort((final a, final b) => a.compareTo(b)); |
||||||
|
} |
@ -0,0 +1,135 @@ |
|||||||
|
import 'package:code_builder/code_builder.dart'; |
||||||
|
import 'package:dynamite/src/builder/resolve_type.dart'; |
||||||
|
import 'package:dynamite/src/builder/state.dart'; |
||||||
|
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||||
|
import 'package:dynamite/src/helpers/dynamite.dart'; |
||||||
|
import 'package:dynamite/src/models/open_api.dart'; |
||||||
|
import 'package:dynamite/src/models/schema.dart'; |
||||||
|
import 'package:dynamite/src/type_result/type_result.dart'; |
||||||
|
|
||||||
|
Spec buildHeaderSerializer(final State state, final String identifier, final OpenAPI spec, final Schema schema) => |
||||||
|
Class( |
||||||
|
(final b) => b |
||||||
|
..name = '_\$${state.classPrefix}${identifier}Serializer' |
||||||
|
..implements.add(refer('StructuredSerializer<${state.classPrefix}$identifier>')) |
||||||
|
..fields.addAll([ |
||||||
|
Field( |
||||||
|
(final b) => b |
||||||
|
..name = 'types' |
||||||
|
..modifier = FieldModifier.final$ |
||||||
|
..type = refer('Iterable<Type>') |
||||||
|
..annotations.add(refer('override')) |
||||||
|
..assignment = Code('const [${state.classPrefix}$identifier, _\$${state.classPrefix}$identifier]'), |
||||||
|
), |
||||||
|
Field( |
||||||
|
(final b) => b |
||||||
|
..name = 'wireName' |
||||||
|
..modifier = FieldModifier.final$ |
||||||
|
..type = refer('String') |
||||||
|
..annotations.add(refer('override')) |
||||||
|
..assignment = Code("r'${state.classPrefix}$identifier'"), |
||||||
|
), |
||||||
|
]) |
||||||
|
..methods.addAll([ |
||||||
|
Method((final b) { |
||||||
|
b |
||||||
|
..name = 'serialize' |
||||||
|
..returns = refer('Iterable<Object?>') |
||||||
|
..annotations.add(refer('override')) |
||||||
|
..requiredParameters.addAll([ |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'serializers' |
||||||
|
..type = refer('Serializers'), |
||||||
|
), |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'object' |
||||||
|
..type = refer('${state.classPrefix}$identifier'), |
||||||
|
), |
||||||
|
]) |
||||||
|
..optionalParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'specifiedType' |
||||||
|
..type = refer('FullType') |
||||||
|
..named = true |
||||||
|
..defaultTo = const Code('FullType.unspecified'), |
||||||
|
), |
||||||
|
) |
||||||
|
..body = const Code('throw UnimplementedError();'); |
||||||
|
}), |
||||||
|
Method((final b) { |
||||||
|
b |
||||||
|
..name = 'deserialize' |
||||||
|
..returns = refer('${state.classPrefix}$identifier') |
||||||
|
..annotations.add(refer('override')) |
||||||
|
..requiredParameters.addAll([ |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'serializers' |
||||||
|
..type = refer('Serializers'), |
||||||
|
), |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'serialized' |
||||||
|
..type = refer('Iterable<Object?>'), |
||||||
|
), |
||||||
|
]) |
||||||
|
..optionalParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'specifiedType' |
||||||
|
..type = refer('FullType') |
||||||
|
..named = true |
||||||
|
..defaultTo = const Code('FullType.unspecified'), |
||||||
|
), |
||||||
|
) |
||||||
|
..body = Code(''' |
||||||
|
final result = new ${state.classPrefix}${identifier}Builder(); |
||||||
|
|
||||||
|
final iterator = serialized.iterator; |
||||||
|
while (iterator.moveNext()) { |
||||||
|
final key = iterator.current! as String; |
||||||
|
iterator.moveNext(); |
||||||
|
final value = iterator.current! as String; |
||||||
|
switch (key) { |
||||||
|
${deserializeProperty(state, identifier, spec, schema).join('\n')} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return result.build(); |
||||||
|
'''); |
||||||
|
}), |
||||||
|
]), |
||||||
|
); |
||||||
|
|
||||||
|
Iterable<String> deserializeProperty( |
||||||
|
final State state, |
||||||
|
final String identifier, |
||||||
|
final OpenAPI spec, |
||||||
|
final Schema schema, |
||||||
|
) sync* { |
||||||
|
for (final property in schema.properties!.entries) { |
||||||
|
final propertyName = property.key; |
||||||
|
final propertySchema = property.value; |
||||||
|
final result = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
'${identifier}_${toDartName(propertyName, uppercaseFirstCharacter: true)}', |
||||||
|
propertySchema, |
||||||
|
nullable: isDartParameterNullable(schema.required?.contains(propertyName), propertySchema), |
||||||
|
); |
||||||
|
|
||||||
|
yield "case '$propertyName':"; |
||||||
|
if (result.className != 'String') { |
||||||
|
if (result is TypeResultBase || result is TypeResultEnum) { |
||||||
|
yield 'result.${toDartName(propertyName)} = ${result.deserialize(result.decode('value!'))};'; |
||||||
|
} else { |
||||||
|
yield 'result.${toDartName(propertyName)}.replace(${result.deserialize(result.decode('value!'))});'; |
||||||
|
} |
||||||
|
} else { |
||||||
|
yield 'result.${toDartName(propertyName)} = value!;'; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
import 'package:build/build.dart'; |
||||||
|
import 'package:code_builder/code_builder.dart'; |
||||||
|
import 'package:path/path.dart' as p; |
||||||
|
|
||||||
|
List<Spec> generateImports(final AssetId outputId) => [ |
||||||
|
const Code('// ignore_for_file: camel_case_types'), |
||||||
|
const Code('// ignore_for_file: public_member_api_docs'), |
||||||
|
Directive.import('dart:convert'), |
||||||
|
Directive.import('dart:typed_data'), |
||||||
|
const Code(''), |
||||||
|
Directive.import('package:built_collection/built_collection.dart'), |
||||||
|
Directive.import('package:built_value/built_value.dart'), |
||||||
|
Directive.import('package:built_value/json_object.dart'), |
||||||
|
Directive.import('package:built_value/serializer.dart'), |
||||||
|
Directive.import('package:built_value/standard_json_plugin.dart'), |
||||||
|
Directive.import('package:dynamite_runtime/content_string.dart'), |
||||||
|
Directive.import('package:dynamite_runtime/http_client.dart'), |
||||||
|
Directive.import('package:universal_io/io.dart'), |
||||||
|
const Code(''), |
||||||
|
Directive.export('package:dynamite_runtime/http_client.dart'), |
||||||
|
const Code(''), |
||||||
|
Directive.part(p.basename(outputId.changeExtension('.g.dart').path)), |
||||||
|
const Code(''), |
||||||
|
]; |
@ -0,0 +1,229 @@ |
|||||||
|
import 'package:built_collection/built_collection.dart'; |
||||||
|
import 'package:code_builder/code_builder.dart'; |
||||||
|
import 'package:dynamite/src/builder/resolve_type.dart'; |
||||||
|
import 'package:dynamite/src/builder/state.dart'; |
||||||
|
import 'package:dynamite/src/helpers/built_value.dart'; |
||||||
|
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||||
|
import 'package:dynamite/src/models/open_api.dart'; |
||||||
|
import 'package:dynamite/src/models/schema.dart'; |
||||||
|
import 'package:dynamite/src/type_result/type_result.dart'; |
||||||
|
|
||||||
|
TypeResult resolveOfs( |
||||||
|
final OpenAPI spec, |
||||||
|
final State state, |
||||||
|
final String identifier, |
||||||
|
final Schema schema, { |
||||||
|
final bool nullable = false, |
||||||
|
}) { |
||||||
|
final result = TypeResultObject( |
||||||
|
'${state.classPrefix}$identifier', |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
|
||||||
|
if (state.resolvedTypes.add(result)) { |
||||||
|
final results = schema.ofs! |
||||||
|
.map( |
||||||
|
(final s) => resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
'$identifier${schema.ofs!.indexOf(s)}', |
||||||
|
s, |
||||||
|
nullable: !(schema.allOf?.contains(s) ?? false), |
||||||
|
), |
||||||
|
) |
||||||
|
.toList(); |
||||||
|
|
||||||
|
final fields = <String, String>{}; |
||||||
|
for (final result in results) { |
||||||
|
final dartName = toDartName(result.name.replaceFirst(state.classPrefix, '')); |
||||||
|
fields[result.name] = toFieldName(dartName, result.name.replaceFirst(state.classPrefix, '')); |
||||||
|
} |
||||||
|
|
||||||
|
state.output.addAll([ |
||||||
|
buildBuiltClass( |
||||||
|
'${state.classPrefix}$identifier', |
||||||
|
BuiltList.build((final b) { |
||||||
|
b.add( |
||||||
|
Method( |
||||||
|
(final b) { |
||||||
|
b |
||||||
|
..name = 'data' |
||||||
|
..returns = refer('JsonObject') |
||||||
|
..type = MethodType.getter; |
||||||
|
}, |
||||||
|
), |
||||||
|
); |
||||||
|
|
||||||
|
for (final result in results) { |
||||||
|
b.add( |
||||||
|
Method( |
||||||
|
(final b) { |
||||||
|
final s = schema.ofs![results.indexOf(result)]; |
||||||
|
b |
||||||
|
..name = fields[result.name] |
||||||
|
..returns = refer(result.nullableName) |
||||||
|
..type = MethodType.getter |
||||||
|
..docs.addAll(s.formattedDescription); |
||||||
|
}, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
}), |
||||||
|
customSerializer: true, |
||||||
|
), |
||||||
|
Class( |
||||||
|
(final b) => b |
||||||
|
..name = '_\$${state.classPrefix}${identifier}Serializer' |
||||||
|
..implements.add(refer('PrimitiveSerializer<${state.classPrefix}$identifier>')) |
||||||
|
..fields.addAll([ |
||||||
|
Field( |
||||||
|
(final b) => b |
||||||
|
..name = 'types' |
||||||
|
..modifier = FieldModifier.final$ |
||||||
|
..type = refer('Iterable<Type>') |
||||||
|
..annotations.add(refer('override')) |
||||||
|
..assignment = Code('const [${state.classPrefix}$identifier, _\$${state.classPrefix}$identifier]'), |
||||||
|
), |
||||||
|
Field( |
||||||
|
(final b) => b |
||||||
|
..name = 'wireName' |
||||||
|
..modifier = FieldModifier.final$ |
||||||
|
..type = refer('String') |
||||||
|
..annotations.add(refer('override')) |
||||||
|
..assignment = Code("r'${state.classPrefix}$identifier'"), |
||||||
|
), |
||||||
|
]) |
||||||
|
..methods.addAll([ |
||||||
|
Method((final b) { |
||||||
|
b |
||||||
|
..name = 'serialize' |
||||||
|
..returns = refer('Object') |
||||||
|
..annotations.add(refer('override')) |
||||||
|
..requiredParameters.addAll([ |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'serializers' |
||||||
|
..type = refer('Serializers'), |
||||||
|
), |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'object' |
||||||
|
..type = refer('${state.classPrefix}$identifier'), |
||||||
|
), |
||||||
|
]) |
||||||
|
..optionalParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'specifiedType' |
||||||
|
..type = refer('FullType') |
||||||
|
..named = true |
||||||
|
..defaultTo = const Code('FullType.unspecified'), |
||||||
|
), |
||||||
|
) |
||||||
|
..body = const Code('return object.data.value;'); |
||||||
|
}), |
||||||
|
Method((final b) { |
||||||
|
b |
||||||
|
..name = 'deserialize' |
||||||
|
..returns = refer('${state.classPrefix}$identifier') |
||||||
|
..annotations.add(refer('override')) |
||||||
|
..requiredParameters.addAll([ |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'serializers' |
||||||
|
..type = refer('Serializers'), |
||||||
|
), |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'data' |
||||||
|
..type = refer('Object'), |
||||||
|
), |
||||||
|
]) |
||||||
|
..optionalParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'specifiedType' |
||||||
|
..type = refer('FullType') |
||||||
|
..named = true |
||||||
|
..defaultTo = const Code('FullType.unspecified'), |
||||||
|
), |
||||||
|
) |
||||||
|
..body = Code( |
||||||
|
<String>[ |
||||||
|
'final result = new ${state.classPrefix}${identifier}Builder()', |
||||||
|
'..data = JsonObject(data);', |
||||||
|
if (schema.allOf != null) ...[ |
||||||
|
for (final result in results) ...[ |
||||||
|
if (result is TypeResultBase || result is TypeResultEnum) ...[ |
||||||
|
'result.${fields[result.name]!} = ${result.deserialize('data')};', |
||||||
|
] else ...[ |
||||||
|
'result.${fields[result.name]!}.replace(${result.deserialize('data')});', |
||||||
|
], |
||||||
|
], |
||||||
|
] else ...[ |
||||||
|
if (schema.discriminator != null) ...[ |
||||||
|
'if (data is! Iterable) {', |
||||||
|
r"throw StateError('Expected an Iterable but got ${data.runtimeType}');", |
||||||
|
'}', |
||||||
|
'', |
||||||
|
'String? discriminator;', |
||||||
|
'', |
||||||
|
'final iterator = data.iterator;', |
||||||
|
'while (iterator.moveNext()) {', |
||||||
|
'final key = iterator.current! as String;', |
||||||
|
'iterator.moveNext();', |
||||||
|
'final Object? value = iterator.current;', |
||||||
|
"if (key == '${schema.discriminator!.propertyName}') {", |
||||||
|
'discriminator = value! as String;', |
||||||
|
'break;', |
||||||
|
'}', |
||||||
|
'}', |
||||||
|
], |
||||||
|
for (final result in results) ...[ |
||||||
|
if (schema.discriminator != null) ...[ |
||||||
|
"if (discriminator == '${result.name.replaceFirst(state.classPrefix, '')}'", |
||||||
|
if (schema.discriminator!.mapping != null && schema.discriminator!.mapping!.isNotEmpty) ...[ |
||||||
|
for (final key in schema.discriminator!.mapping!.entries |
||||||
|
.where( |
||||||
|
(final entry) => |
||||||
|
entry.value.endsWith('/${result.name.replaceFirst(state.classPrefix, '')}'), |
||||||
|
) |
||||||
|
.map((final entry) => entry.key)) ...[ |
||||||
|
" || discriminator == '$key'", |
||||||
|
], |
||||||
|
') {', |
||||||
|
], |
||||||
|
], |
||||||
|
'try {', |
||||||
|
if (result is TypeResultBase || result is TypeResultEnum) ...[ |
||||||
|
'result._${fields[result.name]!} = ${result.deserialize('data')};', |
||||||
|
] else ...[ |
||||||
|
'result._${fields[result.name]!} = ${result.deserialize('data')}.toBuilder();', |
||||||
|
], |
||||||
|
'} catch (_) {', |
||||||
|
if (schema.discriminator != null) ...[ |
||||||
|
'rethrow;', |
||||||
|
], |
||||||
|
'}', |
||||||
|
if (schema.discriminator != null) ...[ |
||||||
|
'}', |
||||||
|
], |
||||||
|
], |
||||||
|
if (schema.oneOf != null) ...[ |
||||||
|
"assert([${fields.values.map((final e) => 'result._$e').join(',')}].where((final x) => x != null).length >= 1, 'Need oneOf for \${result._data}');", |
||||||
|
], |
||||||
|
if (schema.anyOf != null) ...[ |
||||||
|
"assert([${fields.values.map((final e) => 'result._$e').join(',')}].where((final x) => x != null).length >= 1, 'Need anyOf for \${result._data}');", |
||||||
|
], |
||||||
|
], |
||||||
|
'return result.build();', |
||||||
|
].join(), |
||||||
|
); |
||||||
|
}), |
||||||
|
]), |
||||||
|
), |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
import 'package:code_builder/code_builder.dart'; |
||||||
|
import 'package:dynamite/src/builder/resolve_type.dart'; |
||||||
|
import 'package:dynamite/src/builder/state.dart'; |
||||||
|
import 'package:dynamite/src/helpers/built_value.dart'; |
||||||
|
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||||
|
import 'package:dynamite/src/helpers/type_result.dart'; |
||||||
|
import 'package:dynamite/src/models/open_api.dart'; |
||||||
|
import 'package:dynamite/src/models/schema.dart'; |
||||||
|
import 'package:dynamite/src/type_result/type_result.dart'; |
||||||
|
|
||||||
|
TypeResult resolveEnum( |
||||||
|
final OpenAPI spec, |
||||||
|
final State state, |
||||||
|
final String identifier, |
||||||
|
final Schema schema, |
||||||
|
final TypeResult subResult, { |
||||||
|
final bool nullable = false, |
||||||
|
}) { |
||||||
|
if (state.resolvedTypes.add(TypeResultEnum('${state.classPrefix}$identifier', subResult))) { |
||||||
|
state.output.add( |
||||||
|
Class( |
||||||
|
(final b) => b |
||||||
|
..name = '${state.classPrefix}$identifier' |
||||||
|
..extend = refer('EnumClass') |
||||||
|
..constructors.add( |
||||||
|
Constructor( |
||||||
|
(final b) => b |
||||||
|
..name = '_' |
||||||
|
..constant = true |
||||||
|
..requiredParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'name' |
||||||
|
..toSuper = true, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
) |
||||||
|
..fields.addAll( |
||||||
|
schema.enum_!.map( |
||||||
|
(final value) => Field( |
||||||
|
(final b) { |
||||||
|
final result = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
'$identifier${toDartName(value.toString(), uppercaseFirstCharacter: true)}', |
||||||
|
schema, |
||||||
|
ignoreEnum: true, |
||||||
|
); |
||||||
|
b |
||||||
|
..name = toDartName(value.toString()) |
||||||
|
..static = true |
||||||
|
..modifier = FieldModifier.constant |
||||||
|
..type = refer('${state.classPrefix}$identifier') |
||||||
|
..assignment = Code( |
||||||
|
'_\$${toCamelCase('${state.classPrefix}$identifier')}${toDartName(value.toString(), uppercaseFirstCharacter: true)}', |
||||||
|
); |
||||||
|
|
||||||
|
if (toDartName(value.toString()) != value.toString()) { |
||||||
|
if (result.name != 'String' && result.name != 'int') { |
||||||
|
throw Exception( |
||||||
|
'Sorry enum values are a bit broken. ' |
||||||
|
'See https://github.com/google/json_serializable.dart/issues/616. ' |
||||||
|
'Please remove the enum values on ${state.classPrefix}$identifier.', |
||||||
|
); |
||||||
|
} |
||||||
|
b.annotations.add( |
||||||
|
refer('BuiltValueEnumConst').call([], { |
||||||
|
'wireName': refer(valueToEscapedValue(result, value.toString())), |
||||||
|
}), |
||||||
|
); |
||||||
|
} |
||||||
|
}, |
||||||
|
), |
||||||
|
), |
||||||
|
) |
||||||
|
..methods.addAll([ |
||||||
|
Method( |
||||||
|
(final b) => b |
||||||
|
..name = 'values' |
||||||
|
..returns = refer('BuiltSet<${state.classPrefix}$identifier>') |
||||||
|
..lambda = true |
||||||
|
..static = true |
||||||
|
..body = Code('_\$${toCamelCase('${state.classPrefix}$identifier')}Values') |
||||||
|
..type = MethodType.getter, |
||||||
|
), |
||||||
|
Method( |
||||||
|
(final b) => b |
||||||
|
..name = 'valueOf' |
||||||
|
..returns = refer('${state.classPrefix}$identifier') |
||||||
|
..lambda = true |
||||||
|
..static = true |
||||||
|
..requiredParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'name' |
||||||
|
..type = refer(subResult.name), |
||||||
|
), |
||||||
|
) |
||||||
|
..body = Code('_\$valueOf${state.classPrefix}$identifier(name)'), |
||||||
|
), |
||||||
|
buildSerializer('${state.classPrefix}$identifier'), |
||||||
|
]), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
return TypeResultEnum( |
||||||
|
'${state.classPrefix}$identifier', |
||||||
|
subResult, |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
import 'package:built_collection/built_collection.dart'; |
||||||
|
import 'package:code_builder/code_builder.dart'; |
||||||
|
import 'package:dynamite/src/builder/header_serializer.dart'; |
||||||
|
import 'package:dynamite/src/builder/resolve_type.dart'; |
||||||
|
import 'package:dynamite/src/builder/state.dart'; |
||||||
|
import 'package:dynamite/src/helpers/built_value.dart'; |
||||||
|
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||||
|
import 'package:dynamite/src/helpers/dynamite.dart'; |
||||||
|
import 'package:dynamite/src/helpers/type_result.dart'; |
||||||
|
import 'package:dynamite/src/models/open_api.dart'; |
||||||
|
import 'package:dynamite/src/models/schema.dart'; |
||||||
|
import 'package:dynamite/src/type_result/type_result.dart'; |
||||||
|
|
||||||
|
TypeResult resolveObject( |
||||||
|
final OpenAPI spec, |
||||||
|
final State state, |
||||||
|
final String identifier, |
||||||
|
final Schema schema, { |
||||||
|
final bool nullable = false, |
||||||
|
final bool isHeader = false, |
||||||
|
}) { |
||||||
|
final result = TypeResultObject( |
||||||
|
'${state.classPrefix}$identifier', |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
if (state.resolvedTypes.add(result)) { |
||||||
|
final defaults = <String>[]; |
||||||
|
for (final property in schema.properties!.entries) { |
||||||
|
final propertySchema = property.value; |
||||||
|
if (propertySchema.default_ != null) { |
||||||
|
final value = propertySchema.default_!.toString(); |
||||||
|
final result = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
propertySchema.type!, |
||||||
|
propertySchema, |
||||||
|
); |
||||||
|
defaults.add('..${toDartName(property.key)} = ${valueToEscapedValue(result, value)}'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
state.output.add( |
||||||
|
buildBuiltClass( |
||||||
|
'${state.classPrefix}$identifier', |
||||||
|
BuiltList.build((final b) { |
||||||
|
for (final property in schema.properties!.entries) { |
||||||
|
b.add( |
||||||
|
Method( |
||||||
|
(final b) { |
||||||
|
final propertyName = property.key; |
||||||
|
final propertySchema = property.value; |
||||||
|
final result = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
'${identifier}_${toDartName(propertyName, uppercaseFirstCharacter: true)}', |
||||||
|
propertySchema, |
||||||
|
nullable: isDartParameterNullable( |
||||||
|
schema.required?.contains(propertyName), |
||||||
|
propertySchema, |
||||||
|
), |
||||||
|
); |
||||||
|
|
||||||
|
b |
||||||
|
..name = toDartName(propertyName) |
||||||
|
..returns = refer(result.nullableName) |
||||||
|
..type = MethodType.getter |
||||||
|
..docs.addAll(propertySchema.formattedDescription); |
||||||
|
|
||||||
|
if (toDartName(propertyName) != propertyName) { |
||||||
|
b.annotations.add( |
||||||
|
refer('BuiltValueField').call([], { |
||||||
|
'wireName': literalString(propertyName), |
||||||
|
}), |
||||||
|
); |
||||||
|
} |
||||||
|
}, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
}), |
||||||
|
defaults: defaults, |
||||||
|
customSerializer: isHeader, |
||||||
|
), |
||||||
|
); |
||||||
|
if (isHeader) { |
||||||
|
state.output.add(buildHeaderSerializer(state, identifier, spec, schema)); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
@ -0,0 +1,165 @@ |
|||||||
|
import 'package:dynamite/src/builder/ofs_builder.dart'; |
||||||
|
import 'package:dynamite/src/builder/resolve_enum.dart'; |
||||||
|
import 'package:dynamite/src/builder/resolve_object.dart'; |
||||||
|
import 'package:dynamite/src/builder/state.dart'; |
||||||
|
import 'package:dynamite/src/models/open_api.dart'; |
||||||
|
import 'package:dynamite/src/models/schema.dart'; |
||||||
|
import 'package:dynamite/src/type_result/type_result.dart'; |
||||||
|
|
||||||
|
TypeResult resolveType( |
||||||
|
final OpenAPI spec, |
||||||
|
final State state, |
||||||
|
final String identifier, |
||||||
|
final Schema schema, { |
||||||
|
final bool ignoreEnum = false, |
||||||
|
final bool nullable = false, |
||||||
|
}) { |
||||||
|
TypeResult? result; |
||||||
|
if (schema.ref == null && schema.ofs == null && schema.type == null) { |
||||||
|
return TypeResultBase( |
||||||
|
'JsonObject', |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} |
||||||
|
if (schema.ref != null) { |
||||||
|
final name = schema.ref!.split('/').last; |
||||||
|
result = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
name, |
||||||
|
spec.components!.schemas![name]!, |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} else if (schema.ofs != null) { |
||||||
|
result = resolveOfs( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
identifier, |
||||||
|
schema, |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} else if (schema.isContentString) { |
||||||
|
final subResult = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
identifier, |
||||||
|
schema.contentSchema!, |
||||||
|
); |
||||||
|
|
||||||
|
result = TypeResultObject( |
||||||
|
'ContentString', |
||||||
|
generics: [subResult], |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} else { |
||||||
|
switch (schema.type) { |
||||||
|
case 'boolean': |
||||||
|
result = TypeResultBase( |
||||||
|
'bool', |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
case 'integer': |
||||||
|
result = TypeResultBase( |
||||||
|
'int', |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
case 'number': |
||||||
|
result = TypeResultBase( |
||||||
|
'num', |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
case 'string': |
||||||
|
switch (schema.format) { |
||||||
|
case 'binary': |
||||||
|
result = TypeResultBase( |
||||||
|
'Uint8List', |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
result = TypeResultBase( |
||||||
|
'String', |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
case 'array': |
||||||
|
if (schema.items != null) { |
||||||
|
final subResult = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
identifier, |
||||||
|
schema.items!, |
||||||
|
); |
||||||
|
result = TypeResultList( |
||||||
|
'BuiltList', |
||||||
|
subResult, |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} else { |
||||||
|
result = TypeResultList( |
||||||
|
'BuiltList', |
||||||
|
TypeResultBase('JsonObject'), |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} |
||||||
|
case 'object': |
||||||
|
if (schema.properties == null) { |
||||||
|
if (schema.additionalProperties == null) { |
||||||
|
result = TypeResultBase( |
||||||
|
'JsonObject', |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} else if (schema.additionalProperties is EmptySchema) { |
||||||
|
result = TypeResultMap( |
||||||
|
'BuiltMap', |
||||||
|
TypeResultBase('JsonObject'), |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} else { |
||||||
|
final subResult = resolveType( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
identifier, |
||||||
|
schema.additionalProperties!, |
||||||
|
); |
||||||
|
result = TypeResultMap( |
||||||
|
'BuiltMap', |
||||||
|
subResult, |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} |
||||||
|
} else if (schema.properties!.isEmpty) { |
||||||
|
result = TypeResultMap( |
||||||
|
'BuiltMap', |
||||||
|
TypeResultBase('JsonObject'), |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} else { |
||||||
|
result = resolveObject( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
identifier, |
||||||
|
schema, |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (result != null) { |
||||||
|
if (!ignoreEnum && schema.enum_ != null) { |
||||||
|
result = resolveEnum( |
||||||
|
spec, |
||||||
|
state, |
||||||
|
identifier, |
||||||
|
schema, |
||||||
|
result, |
||||||
|
nullable: nullable, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
state.resolvedTypes.add(result); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
throw Exception('Can not convert OpenAPI type "${schema.toJson()}" to a Dart type'); |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
import 'package:code_builder/code_builder.dart'; |
||||||
|
import 'package:dynamite/src/builder/state.dart'; |
||||||
|
|
||||||
|
List<Spec> buildSerializer(final State state) { |
||||||
|
if (state.resolvedTypes.isNotEmpty) { |
||||||
|
return [ |
||||||
|
const Code('// coverage:ignore-start'), |
||||||
|
const Code('final Serializers _serializers = (Serializers().toBuilder()'), |
||||||
|
...state.resolvedTypes |
||||||
|
.map((final type) => type.serializers) |
||||||
|
.expand((final element) => element) |
||||||
|
.toSet() |
||||||
|
.map(Code.new), |
||||||
|
const Code(').build();'), |
||||||
|
const Code(''), |
||||||
|
Code('Serializers get ${state.variablePrefix}Serializers => _serializers;'), |
||||||
|
const Code(''), |
||||||
|
const Code( |
||||||
|
'final Serializers _jsonSerializers = (_serializers.toBuilder()..addPlugin(StandardJsonPlugin())..addPlugin(const ContentStringPlugin())).build();', |
||||||
|
), |
||||||
|
const Code(''), |
||||||
|
Code( |
||||||
|
'T deserialize${state.classPrefix}<T>(final Object data) => _serializers.deserialize(data, specifiedType: FullType(T))! as T;', |
||||||
|
), |
||||||
|
const Code(''), |
||||||
|
Code( |
||||||
|
'Object? serialize${state.classPrefix}<T>(final T data) => _serializers.serialize(data, specifiedType: FullType(T));', |
||||||
|
), |
||||||
|
const Code('// coverage:ignore-end'), |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
return []; |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
import 'package:code_builder/code_builder.dart'; |
||||||
|
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||||
|
import 'package:dynamite/src/type_result/type_result.dart'; |
||||||
|
|
||||||
|
class State { |
||||||
|
State(final String prefix) |
||||||
|
: classPrefix = toDartName(prefix, uppercaseFirstCharacter: true), |
||||||
|
variablePrefix = toDartName(prefix); |
||||||
|
|
||||||
|
final String classPrefix; |
||||||
|
final String variablePrefix; |
||||||
|
|
||||||
|
final output = <Spec>[]; |
||||||
|
final resolvedTypes = <TypeResult>{}; |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
import 'package:built_collection/built_collection.dart'; |
||||||
|
import 'package:code_builder/code_builder.dart'; |
||||||
|
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||||
|
|
||||||
|
Spec buildBuiltClass( |
||||||
|
final String className, |
||||||
|
final BuiltList<Method> methods, { |
||||||
|
final Iterable<String>? defaults, |
||||||
|
final bool customSerializer = false, |
||||||
|
}) => |
||||||
|
Class( |
||||||
|
(final b) { |
||||||
|
b |
||||||
|
..name = className |
||||||
|
..abstract = true |
||||||
|
..implements.add( |
||||||
|
refer( |
||||||
|
'Built<$className, ${className}Builder>', |
||||||
|
), |
||||||
|
) |
||||||
|
..constructors.addAll([ |
||||||
|
builtValueConstructor(className), |
||||||
|
hiddenConstructor, |
||||||
|
fromJsonConstructor, |
||||||
|
]) |
||||||
|
..methods.addAll([ |
||||||
|
toJsonMethod, |
||||||
|
...methods, |
||||||
|
buildSerializer(className, isCustom: customSerializer), |
||||||
|
]); |
||||||
|
|
||||||
|
if (defaults != null && defaults.isNotEmpty) { |
||||||
|
b.methods.add( |
||||||
|
Method( |
||||||
|
(final b) => b |
||||||
|
..name = '_defaults' |
||||||
|
..returns = refer('void') |
||||||
|
..static = true |
||||||
|
..lambda = true |
||||||
|
..annotations.add( |
||||||
|
refer('BuiltValueHook').call([], { |
||||||
|
'initializeBuilder': literalTrue, |
||||||
|
}), |
||||||
|
) |
||||||
|
..requiredParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'b' |
||||||
|
..type = refer('${className}Builder'), |
||||||
|
), |
||||||
|
) |
||||||
|
..body = Code( |
||||||
|
<String?>[ |
||||||
|
'b', |
||||||
|
...defaults, |
||||||
|
].join(), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
Method get toJsonMethod => Method( |
||||||
|
(final b) => b |
||||||
|
..name = 'toJson' |
||||||
|
..returns = refer('Map<String, dynamic>') |
||||||
|
..lambda = true |
||||||
|
..body = const Code('_jsonSerializers.serializeWith(serializer, this)! as Map<String, dynamic>'), |
||||||
|
); |
||||||
|
|
||||||
|
Method buildSerializer(final String className, {final bool isCustom = false}) => Method((final b) { |
||||||
|
b |
||||||
|
..name = 'serializer' |
||||||
|
..returns = refer('Serializer<$className>') |
||||||
|
..lambda = true |
||||||
|
..static = true |
||||||
|
..body = Code( |
||||||
|
isCustom ? '_\$${className}Serializer()' : '_\$${toCamelCase(className)}Serializer', |
||||||
|
) |
||||||
|
..type = MethodType.getter; |
||||||
|
if (isCustom) { |
||||||
|
b.annotations.add(refer('BuiltValueSerializer').call([], {'custom': literalTrue})); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
Constructor builtValueConstructor(final String className) => Constructor( |
||||||
|
(final b) => b |
||||||
|
..factory = true |
||||||
|
..lambda = true |
||||||
|
..optionalParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'b' |
||||||
|
..type = refer('void Function(${className}Builder)?'), |
||||||
|
), |
||||||
|
) |
||||||
|
..redirect = refer('_\$$className'), |
||||||
|
); |
||||||
|
|
||||||
|
Constructor get hiddenConstructor => Constructor( |
||||||
|
(final b) => b |
||||||
|
..name = '_' |
||||||
|
..constant = true, |
||||||
|
); |
||||||
|
|
||||||
|
Constructor get fromJsonConstructor => Constructor( |
||||||
|
(final b) => b |
||||||
|
..factory = true |
||||||
|
..name = 'fromJson' |
||||||
|
..lambda = true |
||||||
|
..requiredParameters.add( |
||||||
|
Parameter( |
||||||
|
(final b) => b |
||||||
|
..name = 'json' |
||||||
|
..type = refer('Map<String, dynamic>'), |
||||||
|
), |
||||||
|
) |
||||||
|
..body = const Code('_jsonSerializers.deserializeWith(serializer, json)!'), |
||||||
|
); |
@ -0,0 +1,107 @@ |
|||||||
|
String toDartName( |
||||||
|
final String name, { |
||||||
|
final bool uppercaseFirstCharacter = false, |
||||||
|
}) { |
||||||
|
var result = ''; |
||||||
|
var upperCase = uppercaseFirstCharacter; |
||||||
|
var firstCharacter = !uppercaseFirstCharacter; |
||||||
|
for (final char in name.split('')) { |
||||||
|
if (_isNonAlphaNumericString(char)) { |
||||||
|
upperCase = true; |
||||||
|
} else { |
||||||
|
result += firstCharacter ? char.toLowerCase() : (upperCase ? char.toUpperCase() : char); |
||||||
|
upperCase = false; |
||||||
|
firstCharacter = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (_dartKeywords.contains(result) || RegExp(r'^[0-9]+$', multiLine: true).hasMatch(result)) { |
||||||
|
return '\$$result'; |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
final _dartKeywords = [ |
||||||
|
'assert', |
||||||
|
'break', |
||||||
|
'case', |
||||||
|
'catch', |
||||||
|
'class', |
||||||
|
'const', |
||||||
|
'continue', |
||||||
|
'default', |
||||||
|
'do', |
||||||
|
'else', |
||||||
|
'enum', |
||||||
|
'extends', |
||||||
|
'false', |
||||||
|
'final', |
||||||
|
'finally', |
||||||
|
'for', |
||||||
|
'if', |
||||||
|
'in', |
||||||
|
'is', |
||||||
|
'new', |
||||||
|
'null', |
||||||
|
'rethrow', |
||||||
|
'return', |
||||||
|
'super', |
||||||
|
'switch', |
||||||
|
'this', |
||||||
|
'throw', |
||||||
|
'true', |
||||||
|
'try', |
||||||
|
'var', |
||||||
|
'void', |
||||||
|
'while', |
||||||
|
'with', |
||||||
|
'async', |
||||||
|
'hide', |
||||||
|
'on', |
||||||
|
'show', |
||||||
|
'sync', |
||||||
|
'abstract', |
||||||
|
'as', |
||||||
|
'covariant', |
||||||
|
'deferred', |
||||||
|
'dynamic', |
||||||
|
'export', |
||||||
|
'extension', |
||||||
|
'external', |
||||||
|
'factory', |
||||||
|
'function', |
||||||
|
'get', |
||||||
|
'implements', |
||||||
|
'import', |
||||||
|
'interface', |
||||||
|
'library', |
||||||
|
'mixin', |
||||||
|
'operator', |
||||||
|
'part', |
||||||
|
'set', |
||||||
|
'static', |
||||||
|
'typedef', |
||||||
|
]; |
||||||
|
|
||||||
|
bool _isNonAlphaNumericString(final String input) => !RegExp(r'^[a-zA-Z0-9]$').hasMatch(input); |
||||||
|
|
||||||
|
String toFieldName(final String dartName, final String type) => dartName == type ? '\$$dartName' : dartName; |
||||||
|
|
||||||
|
String toCamelCase(final String name) { |
||||||
|
var result = ''; |
||||||
|
var upperCase = false; |
||||||
|
var firstCharacter = true; |
||||||
|
for (final char in name.split('')) { |
||||||
|
if (char == '_') { |
||||||
|
upperCase = true; |
||||||
|
} else if (char == r'$') { |
||||||
|
result += r'$'; |
||||||
|
} else { |
||||||
|
result += firstCharacter ? char.toLowerCase() : (upperCase ? char.toUpperCase() : char); |
||||||
|
upperCase = false; |
||||||
|
firstCharacter = false; |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
const docsSeparator = '///'; |
||||||
|
|
||||||
|
Iterable<String> descriptionToDocs(final String? description) sync* { |
||||||
|
if (description != null && description.isNotEmpty) { |
||||||
|
for (final line in description.split('\n')) { |
||||||
|
yield '$docsSeparator $line'; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
// ignore_for_file: avoid_positional_boolean_parameters |
||||||
|
|
||||||
|
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||||
|
import 'package:dynamite/src/models/parameter.dart'; |
||||||
|
import 'package:dynamite/src/models/schema.dart'; |
||||||
|
|
||||||
|
String filterMethodName(final String operationId, final String tag) { |
||||||
|
final expandedTag = tag.split('/').toList(); |
||||||
|
final parts = operationId.split('-'); |
||||||
|
final output = <String>[]; |
||||||
|
for (var i = 0; i < parts.length; i++) { |
||||||
|
if (expandedTag.length <= i || expandedTag[i] != parts[i]) { |
||||||
|
output.add(parts[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
return output.join('-'); |
||||||
|
} |
||||||
|
|
||||||
|
String clientName(final String tag) => '${toDartName(tag, uppercaseFirstCharacter: true)}Client'; |
||||||
|
|
||||||
|
bool isDartParameterNullable( |
||||||
|
final bool? required, |
||||||
|
final Schema? schema, |
||||||
|
) => |
||||||
|
(!(required ?? false) && schema?.default_ == null) || (schema?.nullable ?? false); |
||||||
|
|
||||||
|
bool isRequired( |
||||||
|
final bool? required, |
||||||
|
final dynamic default_, |
||||||
|
) => |
||||||
|
(required ?? false) && default_ == null; |
||||||
|
|
||||||
|
int sortRequiredParameters(final Parameter a, final Parameter b) { |
||||||
|
if (a.isDartRequired != b.isDartRequired) { |
||||||
|
if (a.isDartRequired && !b.isDartRequired) { |
||||||
|
return -1; |
||||||
|
} else { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||||
|
import 'package:dynamite/src/type_result/type_result.dart'; |
||||||
|
|
||||||
|
String valueToEscapedValue(final TypeResult result, final String value) { |
||||||
|
if (result is TypeResultBase && result.name == 'String') { |
||||||
|
return "'$value'"; |
||||||
|
} |
||||||
|
if (result is TypeResultList) { |
||||||
|
return 'const $value'; |
||||||
|
} |
||||||
|
if (result is TypeResultEnum) { |
||||||
|
return '${result.name}.${toDartName(value)}'; |
||||||
|
} |
||||||
|
return value; |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue