3 changed files with 700 additions and 644 deletions
			
			
		@ -0,0 +1,651 @@ | 
				
			|||||||
 | 
					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<Spec> 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 = Block.of([ | 
				
			||||||
 | 
					                  const Code('String body;'), | 
				
			||||||
 | 
					                  const Code('try {'), | 
				
			||||||
 | 
					                  const Code('body = await response.body;'), | 
				
			||||||
 | 
					                  const Code('} on FormatException {'), | 
				
			||||||
 | 
					                  const Code("body = 'binary';"), | 
				
			||||||
 | 
					                  const Code('}'), | 
				
			||||||
 | 
					                  const Code(''), | 
				
			||||||
 | 
					                  Code('return ${state.classPrefix}ApiException('), | 
				
			||||||
 | 
					                  const Code('response.statusCode,'), | 
				
			||||||
 | 
					                  const Code('response.responseHeaders,'), | 
				
			||||||
 | 
					                  const Code('body,'), | 
				
			||||||
 | 
					                  const Code(');'), | 
				
			||||||
 | 
					                ]), | 
				
			||||||
 | 
					            ), | 
				
			||||||
 | 
					            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* { | 
				
			||||||
 | 
					  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)); | 
				
			||||||
 | 
					} | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue