Browse Source

Merge pull request #130 from provokateurin/feature/response-headers

Feature/response headers
pull/131/head
Kate 2 years ago committed by GitHub
parent
commit
33c092368f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/dynamite/lib/src/models/components.dart
  2. 22
      packages/dynamite/lib/src/models/header.dart
  3. 34
      packages/dynamite/lib/src/models/header.g.dart
  4. 6
      packages/dynamite/lib/src/models/info.dart
  5. 6
      packages/dynamite/lib/src/models/info.g.dart
  6. 4
      packages/dynamite/lib/src/models/license.dart
  7. 2
      packages/dynamite/lib/src/models/media_type.dart
  8. 10
      packages/dynamite/lib/src/models/open_api.dart
  9. 10
      packages/dynamite/lib/src/models/operation.dart
  10. 6
      packages/dynamite/lib/src/models/parameter.dart
  11. 20
      packages/dynamite/lib/src/models/path_item.dart
  12. 6
      packages/dynamite/lib/src/models/request_body.dart
  13. 6
      packages/dynamite/lib/src/models/response.dart
  14. 6
      packages/dynamite/lib/src/models/response.g.dart
  15. 26
      packages/dynamite/lib/src/models/schema.dart
  16. 4
      packages/dynamite/lib/src/models/security_scheme.dart
  17. 2
      packages/dynamite/lib/src/models/server.dart
  18. 4
      packages/dynamite/lib/src/models/server_variable.dart
  19. 175
      packages/dynamite/lib/src/openapi_builder.dart

4
packages/dynamite/lib/src/models/components.dart

@ -7,8 +7,8 @@ part 'components.g.dart';
@JsonSerializable()
class Components {
Components({
required this.securitySchemes,
required this.schemas,
this.securitySchemes,
this.schemas,
});
factory Components.fromJson(final Map<String, dynamic> json) => _$ComponentsFromJson(json);

22
packages/dynamite/lib/src/models/header.dart

@ -0,0 +1,22 @@
import 'package:dynamite/src/models/schema.dart';
import 'package:json_annotation/json_annotation.dart';
part 'header.g.dart';
@JsonSerializable()
class Header {
Header({
this.description,
this.required,
this.schema,
});
factory Header.fromJson(final Map<String, dynamic> json) => _$HeaderFromJson(json);
Map<String, dynamic> toJson() => _$HeaderToJson(this);
final String? description;
final bool? required;
final Schema? schema;
}

34
packages/dynamite/lib/src/models/header.g.dart

@ -0,0 +1,34 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'header.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Header _$HeaderFromJson(Map<String, dynamic> json) {
$checkKeys(
json,
allowedKeys: const ['description', 'required', 'schema'],
);
return Header(
description: json['description'] as String?,
required: json['required'] as bool?,
schema: json['schema'] == null ? null : Schema.fromJson(json['schema'] as Map<String, dynamic>),
);
}
Map<String, dynamic> _$HeaderToJson(Header instance) {
final val = <String, dynamic>{};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('description', instance.description);
writeNotNull('required', instance.required);
writeNotNull('schema', instance.schema?.toJson());
return val;
}

6
packages/dynamite/lib/src/models/info.dart

@ -8,8 +8,8 @@ class Info {
Info({
required this.title,
required this.version,
required this.description,
required this.license,
this.description,
});
factory Info.fromJson(final Map<String, dynamic> json) => _$InfoFromJson(json);
@ -19,7 +19,7 @@ class Info {
final String version;
final String? description;
final License license;
final String? description;
}

6
packages/dynamite/lib/src/models/info.g.dart

@ -9,13 +9,13 @@ part of 'info.dart';
Info _$InfoFromJson(Map<String, dynamic> json) {
$checkKeys(
json,
allowedKeys: const ['title', 'version', 'description', 'license'],
allowedKeys: const ['title', 'version', 'license', 'description'],
);
return Info(
title: json['title'] as String,
version: json['version'] as String,
description: json['description'] as String?,
license: License.fromJson(json['license'] as Map<String, dynamic>),
description: json['description'] as String?,
);
}
@ -23,6 +23,7 @@ Map<String, dynamic> _$InfoToJson(Info instance) {
final val = <String, dynamic>{
'title': instance.title,
'version': instance.version,
'license': instance.license.toJson(),
};
void writeNotNull(String key, dynamic value) {
@ -32,6 +33,5 @@ Map<String, dynamic> _$InfoToJson(Info instance) {
}
writeNotNull('description', instance.description);
val['license'] = instance.license.toJson();
return val;
}

4
packages/dynamite/lib/src/models/license.dart

@ -6,8 +6,8 @@ part 'license.g.dart';
class License {
License({
required this.name,
required this.identifier,
required this.url,
this.identifier,
this.url,
});
factory License.fromJson(final Map<String, dynamic> json) => _$LicenseFromJson(json);

2
packages/dynamite/lib/src/models/media_type.dart

@ -6,7 +6,7 @@ part 'media_type.g.dart';
@JsonSerializable()
class MediaType {
MediaType({
required this.schema,
this.schema,
});
factory MediaType.fromJson(final Map<String, dynamic> json) => _$MediaTypeFromJson(json);

10
packages/dynamite/lib/src/models/open_api.dart

@ -14,11 +14,11 @@ class OpenAPI {
OpenAPI({
required this.version,
required this.info,
required this.servers,
required this.security,
required this.tags,
required this.components,
required this.paths,
this.servers,
this.security,
this.tags,
this.components,
this.paths,
});
factory OpenAPI.fromJson(final Map<String, dynamic> json) => _$OpenAPIFromJson(json);

10
packages/dynamite/lib/src/models/operation.dart

@ -9,11 +9,11 @@ part 'operation.g.dart';
@JsonSerializable()
class Operation {
Operation({
required this.operationId,
required this.tags,
required this.parameters,
required this.requestBody,
required this.responses,
this.operationId,
this.tags,
this.parameters,
this.requestBody,
this.responses,
});
factory Operation.fromJson(final Map<String, dynamic> json) => _$OperationFromJson(json);

6
packages/dynamite/lib/src/models/parameter.dart

@ -8,9 +8,9 @@ class Parameter {
Parameter({
required this.name,
required this.in_,
required this.description,
required this.required,
required this.schema,
this.description,
this.required,
this.schema,
});
factory Parameter.fromJson(final Map<String, dynamic> json) => _$ParameterFromJson(json);

20
packages/dynamite/lib/src/models/path_item.dart

@ -7,16 +7,16 @@ part 'path_item.g.dart';
@JsonSerializable()
class PathItem {
PathItem({
required this.description,
required this.parameters,
required this.get,
required this.put,
required this.post,
required this.delete,
required this.options,
required this.head,
required this.patch,
required this.trace,
this.description,
this.parameters,
this.get,
this.put,
this.post,
this.delete,
this.options,
this.head,
this.patch,
this.trace,
});
factory PathItem.fromJson(final Map<String, dynamic> json) => _$PathItemFromJson(json);

6
packages/dynamite/lib/src/models/request_body.dart

@ -6,9 +6,9 @@ part 'request_body.g.dart';
@JsonSerializable()
class RequestBody {
RequestBody({
required this.description,
required this.content,
required this.required,
this.description,
this.content,
this.required,
});
factory RequestBody.fromJson(final Map<String, dynamic> json) => _$RequestBodyFromJson(json);

6
packages/dynamite/lib/src/models/response.dart

@ -1,3 +1,4 @@
import 'package:dynamite/src/models/header.dart';
import 'package:dynamite/src/models/media_type.dart';
import 'package:json_annotation/json_annotation.dart';
@ -7,7 +8,8 @@ part 'response.g.dart';
class Response {
Response({
required this.description,
required this.content,
this.content,
this.headers,
});
factory Response.fromJson(final Map<String, dynamic> json) => _$ResponseFromJson(json);
@ -16,4 +18,6 @@ class Response {
final String description;
final Map<String, MediaType>? content;
final Map<String, Header>? headers;
}

6
packages/dynamite/lib/src/models/response.g.dart

@ -9,13 +9,16 @@ part of 'response.dart';
Response _$ResponseFromJson(Map<String, dynamic> json) {
$checkKeys(
json,
allowedKeys: const ['description', 'content'],
allowedKeys: const ['description', 'content', 'headers'],
);
return Response(
description: json['description'] as String,
content: (json['content'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, MediaType.fromJson(e as Map<String, dynamic>)),
),
headers: (json['headers'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, Header.fromJson(e as Map<String, dynamic>)),
),
);
}
@ -31,5 +34,6 @@ Map<String, dynamic> _$ResponseToJson(Response instance) {
}
writeNotNull('content', instance.content?.map((k, e) => MapEntry(k, e.toJson())));
writeNotNull('headers', instance.headers?.map((k, e) => MapEntry(k, e.toJson())));
return val;
}

26
packages/dynamite/lib/src/models/schema.dart

@ -5,19 +5,19 @@ part 'schema.g.dart';
@JsonSerializable()
class Schema {
Schema({
required this.ref,
required this.oneOf,
required this.anyOf,
required this.allOf,
required this.description,
required this.deprecated,
required this.type,
required this.format,
required this.default_,
required this.enum_,
required this.properties,
required this.required,
required this.items,
this.ref,
this.oneOf,
this.anyOf,
this.allOf,
this.description,
this.deprecated,
this.type,
this.format,
this.default_,
this.enum_,
this.properties,
this.required,
this.items,
});
factory Schema.fromJson(final Map<String, dynamic> json) => _$SchemaFromJson(json);

4
packages/dynamite/lib/src/models/security_scheme.dart

@ -6,8 +6,8 @@ part 'security_scheme.g.dart';
class SecurityScheme {
SecurityScheme({
required this.type,
required this.description,
required this.scheme,
this.description,
this.scheme,
});
factory SecurityScheme.fromJson(final Map<String, dynamic> json) => _$SecuritySchemeFromJson(json);

2
packages/dynamite/lib/src/models/server.dart

@ -7,7 +7,7 @@ part 'server.g.dart';
class Server {
Server({
required this.url,
required this.variables,
this.variables,
});
factory Server.fromJson(final Map<String, dynamic> json) => _$ServerFromJson(json);

4
packages/dynamite/lib/src/models/server_variable.dart

@ -6,8 +6,8 @@ part 'server_variable.g.dart';
class ServerVariable {
ServerVariable({
required this.default_,
required this.enum_,
required this.description,
this.enum_,
this.description,
});
factory ServerVariable.fromJson(final Map<String, dynamic> json) => _$ServerVariableFromJson(json);

175
packages/dynamite/lib/src/openapi_builder.dart

@ -76,6 +76,54 @@ class OpenAPIBuilder implements Builder {
Class(
(final b) => b
..name = 'Response'
..types.addAll([
refer('T'),
refer('U'),
])
..fields.addAll([
Field(
(final b) => b
..name = 'data'
..type = refer('T')
..modifier = FieldModifier.final$,
),
Field(
(final b) => b
..name = 'headers'
..type = refer('U')
..modifier = FieldModifier.final$,
),
])
..constructors.add(
Constructor(
(final b) => b
..requiredParameters.addAll(
['data', 'headers'].map(
(final name) => Parameter(
(final b) => b
..name = name
..toThis = true,
),
),
),
),
)
..methods.add(
Method(
(final b) => b
..name = 'toString'
..returns = refer('String')
..annotations.add(refer('override'))
..lambda = true
..body = const Code(
r"'Response(data: $data, headers: $headers)'",
),
),
),
).accept(emitter).toString(),
Class(
(final b) => b
..name = '_Response'
..fields.addAll([
Field(
(final b) => b
@ -117,15 +165,16 @@ class OpenAPIBuilder implements Builder {
..returns = refer('String')
..annotations.add(refer('override'))
..lambda = true
..body =
const Code(r"'Response(statusCode: $statusCode, headers: $headers, body: ${utf8.decode(body)})'"),
..body = const Code(
r"'_Response(statusCode: $statusCode, headers: $headers, body: ${utf8.decode(body)})'",
),
),
),
).accept(emitter).toString(),
Class(
(final b) => b
..name = 'ApiException'
..extend = refer('Response')
..extend = refer('_Response')
..implements.add(refer('Exception'))
..constructors.addAll(
[
@ -150,7 +199,7 @@ class OpenAPIBuilder implements Builder {
Parameter(
(final b) => b
..name = 'response'
..type = refer('Response'),
..type = refer('_Response'),
),
)
..body = const Code('ApiException(response.statusCode, response.headers, response.body,)'),
@ -258,6 +307,7 @@ class OpenAPIBuilder implements Builder {
final Schema schema, {
final bool ignoreEnum = false,
final Map<String, String>? extraJsonSerializableValues,
final Map<String, Map<String, String>>? extraJsonKeyValues,
}) {
TypeResolveResult? result;
if (schema.ref != null) {
@ -557,6 +607,15 @@ class OpenAPIBuilder implements Builder {
[],
{
'name': refer("'$propertyName'"),
if (extraJsonKeyValues != null) ...{
for (final p in extraJsonKeyValues.keys) ...{
if (p == propertyName) ...{
for (final key in extraJsonKeyValues[p]!.keys) ...{
key: refer(extraJsonKeyValues[p]![key]!),
},
},
},
},
},
),
);
@ -699,14 +758,6 @@ class OpenAPIBuilder implements Builder {
paths[path] = PathItem(
description: pathItem.description,
parameters: pathItem.parameters,
get: null,
put: null,
post: null,
delete: null,
options: null,
head: null,
patch: null,
trace: null,
);
}
paths[path] = paths[path]!.copyWithOperations({method: operation});
@ -829,7 +880,7 @@ class OpenAPIBuilder implements Builder {
Method(
(final b) => b
..name = 'doRequest'
..returns = refer('Future<Response>')
..returns = refer('Future<_Response>')
..modifier = MethodModifier.async
..requiredParameters.addAll([
Parameter(
@ -859,6 +910,7 @@ class OpenAPIBuilder implements Builder {
for (final header in {...baseHeaders, ...headers}.entries) {
request.headers.add(header.key, header.value);
}
<<<<<<< Updated upstream
if (body != null) {
request.add(body.toList());
}
@ -875,6 +927,11 @@ class OpenAPIBuilder implements Builder {
responseHeaders[name] = values.last;
});
return Response(
=======
final response = await http.Response.fromStream(await request.send());
return _Response(
>>>>>>> Stashed changes
response.statusCode,
responseHeaders,
await response.bodyBytes,
@ -913,6 +970,7 @@ class OpenAPIBuilder implements Builder {
Method(
(final b) {
final operation = paths[path]!.operations[httpMethod]!;
final operationId = operation.operationId ?? _toDartName('$httpMethod-$path');
final pathParameters = <spec_parameter.Parameter>[
if (paths[path]!.parameters != null) ...paths[path]!.parameters!,
];
@ -920,7 +978,7 @@ class OpenAPIBuilder implements Builder {
...pathParameters,
if (operation.parameters != null) ...operation.parameters!,
];
final methodName = _toDartName(operation.operationId ?? _toDartName('$httpMethod-$path'));
final methodName = _toDartName(operationId);
b
..name = methodName
..modifier = MethodModifier.async;
@ -986,7 +1044,7 @@ class OpenAPIBuilder implements Builder {
break;
case 'query':
code.write(
"queryParameters['${parameter.name}'] = ${_toDartName(parameter.name)}$enumValueGetter.toString();",
"queryParameters['${parameter.name}${result.isList ? '[]' : ''}'] = ${_toDartName(parameter.name)}$enumValueGetter${result.isList ? '.map((final x) => x.toString()).toList()' : '.toString()'};",
);
break;
case 'header':
@ -1064,6 +1122,51 @@ class OpenAPIBuilder implements Builder {
for (final statusCode in operation.responses!.keys) {
final response = operation.responses![statusCode]!;
code.write('if (response.statusCode == $statusCode) {');
String? headersType;
String? headersValue;
if (response.headers != null) {
final identifier =
'${tag != null ? _toDartName(tag.name, uppercaseFirstCharacter: true) : null}${_toDartName(operationId, uppercaseFirstCharacter: true)}Headers';
final headerParseFunctions = <String, String>{};
for (final headerName in response.headers!.keys) {
final functionIdentifier = '_${_toDartName('${identifier}Parse$headerName')}';
headerParseFunctions[headerName] = functionIdentifier;
final result = resolveType(
identifier,
response.headers![headerName]!.schema!,
);
output.add(
'${result.typeName} $functionIdentifier(final Map data, final String key) => ${_parseFromStringFunctionForType('data[key]', result)};',
);
}
final result = resolveType(
identifier,
Schema(
type: 'object',
properties: {
for (final headerName in response.headers!.keys) ...{
headerName.toLowerCase(): response.headers![headerName]!.schema!,
},
},
),
extraJsonSerializableValues: {
'disallowUnrecognizedKeys': 'false',
},
extraJsonKeyValues: {
for (final headerName in response.headers!.keys) ...{
headerName.toLowerCase(): {
'readValue': headerParseFunctions[headerName]!,
},
},
},
);
headersType = result.typeName;
headersValue = _deserializeFunctionForType('response.headers', result);
}
String? dataType;
String? dataValue;
if (response.content != null) {
if (response.content!.length > 1) {
throw Exception('Can not work with multiple mime types right now');
@ -1077,26 +1180,38 @@ class OpenAPIBuilder implements Builder {
);
switch (mimeType) {
case 'application/json':
b.returns = refer('Future<${result.typeName}>');
code.write('return ${_deserializeFunctionForType(
dataType = result.typeName;
dataValue = _deserializeFunctionForType(
result.isBaseType
? 'utf8.decode(response.body)'
: 'json.decode(utf8.decode(response.body))',
result,
)};');
);
break;
case 'image/png':
b.returns = refer('Future<Uint8List>');
code.write('return response.body;');
dataType = 'Uint8List';
dataValue = 'response.body';
break;
default:
throw Exception('Can not parse mime type "$mimeType"');
}
}
}
if (headersType != null && dataType != null) {
b.returns = refer('Future<Response<$dataType, $headersType>>');
code.write('return Response<$dataType, $headersType>($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 {
code.write('return;');
b.returns = refer('Future');
code.write('return;');
}
code.write('}');
}
code.write('throw ApiException.fromResponse(response); // coverage:ignore-line\n');
@ -1373,6 +1488,24 @@ String _deserializeFunctionForType(final String object, final TypeResolveResult
}
}
String _parseFromStringFunctionForType(final String object, final TypeResolveResult result) {
final o = '$object as String';
if (result.isBaseType) {
switch (result.typeName) {
case 'String':
return o;
case 'int':
return 'int.parse($o)';
default:
throw Exception('Can not parse "${result.typeName}" from String');
}
} else if (result.isEnum) {
return _parseFromStringFunctionForType(o, result.subType!);
} else {
return 'json.decode($o)';
}
}
bool _isParameterNullable(final bool? required, final dynamic default_) => !(required ?? false) && default_ == null;
String _valueToEscapedValue(final String type, final dynamic value) => type == 'String' ? "'$value'" : value.toString();

Loading…
Cancel
Save