generate method
Library
generate()
Implementation
Library generate() {
lb.body.add(Directive.import('package:http_parser/http_parser.dart'));
lb.body.add(Directive.import('dart:convert'));
lb.body.add(Directive.part(partFileName));
lb.body.add(Directive.part(freezedPartFileName));
// create class for each schema..
for (final schemaEntry in api.components!.schemas!.entries) {
_schemaReference(schemaEntry.key, schemaEntry.value!);
}
final fields = [
MapEntry('dio', _dio),
MapEntry('baseUri', refer('Uri')),
];
final grouped = api.paths!.entries.groupListsBy((element) =>
element.value!.operations.values.map((e) => e!.tags ?? []).expand((element) => element).toSet().join());
final classes = grouped.entries.map((value) => Class((cb) {
cb
..name = '${value.key.titleCase.replaceAll(' ', '')}Api'
..constructors.add(Constructor(
(cb) => cb
..requiredParameters.addAll(fields.map((f) => Parameter((pb) => pb
..name = f.key
..toThis = true))),
))
..fields.addAll(fields.map((f) => Field((fb) => fb
..name = f.key
..type = f.value
..modifier = FieldModifier.final$)));
//cb.name = baseName;
cb.abstract = false;
for (final path in value.value) {
for (final operation in path.value!.operations.entries) {
final pathName = path.key
.replaceAll(
// language=RegExp
RegExp(r'[{}]'),
'')
.camelCase;
final operationName = operation.value!.id == null
? '$pathName'
'${operation.key.pascalCase}'
: operation.value!.id!.camelCase;
final responseClass = ClassBuilder();
responseClass
..name = '${operationName.pascalCase}Response'
..abstract = true
..constructors.add(Constructor());
Reference? successResponseBodyType;
for (final response in operation.value!.responses!.entries) {
final clientResponseParseParams = <Expression>[];
final content = (response.value!.content ?? {})[mediaTypeJson.contentType];
// OpenApiContentType responseContentType;
Reference? bodyType;
if (content != null) {
if (content.schema!.type == APIType.array) {
bodyType = _referType('List', generics: [
_schemaReference(content.schema!.items!.referenceURI!.pathSegments.last, content.schema!)
]);
} else {
bodyType = _schemaReference(responseClass.name!, content.schema!);
}
clientResponseParseParams.add(bodyType
.newInstanceNamed('fromJson', [refer('response').property('responseBodyJson')([]).awaited]));
} else {
if (response.value!.content?.length == 1) {
final responseContent = response.value!.content!.entries.first;
bodyType = _toDartType('${refer(responseClass.name!)}Content', responseContent.value!.schema!);
if (_typeString == bodyType) {
clientResponseParseParams.add(refer('response').property('responseBodyString')([]).awaited);
} else if (_dioResponseBody == bodyType) {
clientResponseParseParams.add(refer('response').property('responseBodyBytes')([]).awaited);
} else {
throw StateError('Unsupported bodyType $bodyType for responses.');
}
}
}
if (response.key.startsWith('2') || (response.key == 'default' && successResponseBodyType == null)) {
successResponseBodyType = bodyType;
}
}
final generics = [_dioResponse.addGenerics(successResponseBodyType ?? _void)];
//lb.body.add(responseClass.build());
final clientMethod = MethodBuilder()
..name = operationName
..addDartDoc(operation.value!.summary)
..addDartDoc(operation.value!.description)
..docs.add('/// ${operation.key}: ${path.key}')
..docs.add('///')
..returns = _referType('Future', generics: generics)
..modifier = MethodModifier.async;
final clientCode = <Code>[];
Method((mb) {
mb
..name = operationName
..addDartDoc(operation.value!.summary)
..addDartDoc(operation.value!.description)
..docs.add('/// ${operation.key}: ${path.key}')
..returns = _referType('Future', generics: generics);
final routerParams = <Expression>[];
final routerParamsNamed = <String, Expression>{};
if (apiMethodsWithRequest) {
mb.requiredParameters.add(Parameter((pb) => pb..name = 'request'));
routerParams.add(refer('request'));
}
// ignore: avoid_function_literals_in_foreach_calls
final allParameters = [...?path.value!.parameters, ...?operation.value!.parameters];
for (final param in allParameters) {
final paramType = _toDartType(operationName, param!.schema!);
final paramNameCamelCase = param.name!.camelCase;
if (param.description != null) {
clientMethod.docs.add('/// * [$paramNameCamelCase]: ${param.description}');
}
final p = Parameter((pb) => pb
..name = paramNameCamelCase
..type = paramType.asNullable(!param.isRequired)
..asRequired(this, param.isRequired)
..named = true);
mb.optionalParameters.add(p);
clientMethod.optionalParameters.add(p);
final decodeParameterFrom = (APIParameter param, Expression expression) {
final schemaType = ArgumentError.checkNotNull(param.schema?.type, 'param.schema.type');
switch (schemaType) {
case APIType.string:
final Expression? asString = refer('paramToString')([expression]);
if (param.schema!.format == 'uuid') {
assert(paramType == _apiUuid);
return _apiUuid.newInstanceNamed('parse', [asString!]);
} else if (paramType != _typeString) {
//throw StateError('Unsupported paramType for string $paramType');
return _apiUuid.newInstanceNamed('parse', [asString!]);
}
return asString;
case APIType.number:
break;
case APIType.integer:
return refer('paramToInt')([expression]);
case APIType.boolean:
return refer('paramToBool')([expression]);
case APIType.array:
//checkState(param.schema!.items!.type == APIType.string);
if (param.schema!.items!.enumerated != null && param.schema!.items!.enumerated!.isNotEmpty) {
final paramEnumType = (paramType as TypeReference).types.first;
return expression
.property('map')([
Method(
(mb) => mb
..lambda = true
..requiredParameters.add(Parameter((pb) => pb..name = 'e'))
..body =
refer(paramEnumType.symbol! + 'Ext').property('fromName')([refer('e')]).code,
).closure
])
.property('toList')([]);
} else {
final paramEnumType = (paramType as TypeReference).types.first;
return refer('paramToInt')([expression]);
return expression
.property('map')([
Method(
(mb) => mb
..lambda = true
..requiredParameters.add(Parameter((pb) => pb..name = 'e'))
..body = refer('e').code,
).closure
])
.property('toList')([]);
}
return expression;
case APIType.object:
return expression;
default:
throw StateError('Invalid schema type $schemaType');
}
};
final decodeParameter = (Expression? expression) {
return refer(param.isRequired ? 'paramRequired' : 'paramOpt')([], {
'name': literalString(param.name!),
'value': expression!,
'decode': Method((mb) {
final paramFrom = decodeParameterFrom(param, refer('value'));
mb
..lambda = true
..requiredParameters.add(Parameter((pb) => pb..name = 'value'))
..body = paramFrom?.code;
}).closure,
});
};
final paramLocation = ArgumentError.checkNotNull(param.location);
final paramName = ArgumentError.checkNotNull(param.name);
routerParamsNamed[paramNameCamelCase] = decodeParameter(_readFromRequest(paramLocation, paramName));
}
final body = operation.value!.requestBody;
if (body != null && body.content!.isNotEmpty) {
final entry = body.content!.entries.first;
if (body.content!.length > 1) {
_logger.warning('Right now we only support one request body, '
'but found: ${body.content!.keys}, only using $entry');
}
Map.fromEntries([entry]).forEach((key, reqBody) {
final contentType = OpenApiContentType.parse(key);
// final ct = OpenApiContentType.allKnown
// .firstWhere((element) => element.matches(contentType));
_createRequestBody(
contentType,
reqBody!,
body.isRequired,
operationName,
mb,
routerParams,
clientMethod,
clientCode,
);
});
}
});
clientCode.add(Code('final queryParams = <String, dynamic>{};'));
final allParameters = [
...?operation.value?.parameters?.where((element) => element?.location == APIParameterLocation.query),
...?path.value?.parameters?.where((element) => element?.location == APIParameterLocation.query),
];
for (final element in allParameters) {
if (element?.schema?.type == APIType.array) {
clientCode.add(Code(
'''if (${element!.name!.camelCase} != null) queryParams['${element.name}'] = ${element.name!.camelCase}.join(',');'''));
} else if (element?.schema?.enumerated != null) {
clientCode.add(Code(
'''if (${element!.name!.camelCase} != null) queryParams['${element.name}'] = ${element.name!.camelCase}.name;'''));
} else {
clientCode.add(Code(
'''if (${element!.name!.camelCase} != null) queryParams['${element.name}'] = ${element.name!.camelCase}.toString();'''));
}
}
clientCode.add(Code('''final uri = baseUri.replace(
queryParameters: queryParams, path: baseUri.path + '${path.key.replaceAll('{', '\${')}');'''));
if (successResponseBodyType == _dioResponseBody) {
clientCode.add(
Code(
'final response = await dio.${operation.key}Uri<${successResponseBodyType!.symbol}>(uri${operation.value?.requestBody != null ? ', data: body' : ''}, options: Options(responseType: ResponseType.stream));'),
);
} else if (successResponseBodyType?.symbol == 'List') {
clientCode.add(
Code(
'final response = await dio.${operation.key}Uri<${successResponseBodyType != null ? 'List<dynamic>' : 'void'}>(uri${operation.value?.requestBody != null ? ', data: body' : ''});'),
);
} else if (operation.value!.requestBody?.content?.keys.firstOrNull == 'multipart/form-data') {
final body = '''FormData.fromMap(<String, dynamic>{
${operation.value!.requestBody!.content!.values.first!.schema!.properties!.entries.map((element) => '\'${element.key}\': ${element.value!.type == APIType.object ? 'MultipartFile.fromString(jsonEncode(' + element.key + '.toJson()), filename: \'' + element.key + '.json\', contentType: MediaType(\'application\', \'json\'))' : element.key}').join(',')}
})''';
clientCode.add(
Code(
'final response = await dio.${operation.key}Uri<${successResponseBodyType != null ? 'Map<String, dynamic>' : 'void'}>(uri${operation.value?.requestBody != null ? ', data: $body' : ''});'),
);
} else {
// TODO list all primitives?
if (successResponseBodyType == _typeString) {
clientCode.add(
Code(
'final response = await dio.${operation.key}Uri<${successResponseBodyType!.symbol}>(uri${operation.value?.requestBody != null ? ', data: body' : ''});'),
);
} else {
clientCode.add(
Code(
'final response = await dio.${operation.key}Uri<${successResponseBodyType != null ? 'Map<String, dynamic>' : 'void'}>(uri${operation.value?.requestBody != null ? ', data: body' : ''});'),
);
}
}
if (successResponseBodyType == null || successResponseBodyType == _dioResponseBody) {
clientCode.add(Code('return response;'));
} else if (successResponseBodyType.symbol == 'List') {
final listType = (successResponseBodyType as TypeReference).types.last;
clientCode.add(
Code(
'''final parsed = response.data!.map((dynamic e) => ${listType.symbol}.fromJson(e as Map<String, dynamic>)).toList();
return Response<${successResponseBodyType.symbol}<${listType.symbol}>>(
data: parsed,
headers: response.headers,
requestOptions: response.requestOptions,
isRedirect: response.isRedirect,
statusCode: response.statusCode,
redirects: response.redirects,
extra: response.extra,
);
'''),
);
} else {
clientCode.add(
Code(
'''final parsed = ${successResponseBodyType == _typeString ? 'response.data!' : '${successResponseBodyType.symbol}.fromJson(response.data!)'};
return Response<${successResponseBodyType.symbol}>(
data: parsed,
headers: response.headers,
requestOptions: response.requestOptions,
isRedirect: response.isRedirect,
statusCode: response.statusCode,
redirects: response.redirects,
extra: response.extra,
);
'''),
);
}
clientMethod.body = Block.of(clientCode);
cb.methods.add(clientMethod.build());
}
}
}));
for (final element in classes) {
lb.body.add(element);
}
return lb.build();
}