Browse Source

dynamite: Support per operation security

pull/232/head
jld3103 2 years ago
parent
commit
2fa0758e3e
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 4
      packages/dynamite/lib/src/models/operation.dart
  2. 9
      packages/dynamite/lib/src/models/operation.g.dart
  3. 263
      packages/dynamite/lib/src/openapi_builder.dart

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

@ -2,6 +2,7 @@ import 'package:dynamite/src/models/parameter.dart';
import 'package:dynamite/src/models/request_body.dart';
import 'package:dynamite/src/models/response.dart';
import 'package:dynamite/src/models/responses.dart';
import 'package:dynamite/src/models/security_requirement.dart';
import 'package:json_annotation/json_annotation.dart';
part 'operation.g.dart';
@ -17,6 +18,7 @@ class Operation {
this.parameters,
this.requestBody,
this.responses,
this.security,
});
factory Operation.fromJson(final Map<String, dynamic> json) => _$OperationFromJson(json);
@ -37,4 +39,6 @@ class Operation {
final RequestBody? requestBody;
final Responses? responses;
final List<SecurityRequirement>? security;
}

9
packages/dynamite/lib/src/models/operation.g.dart

@ -17,7 +17,8 @@ Operation _$OperationFromJson(Map<String, dynamic> json) {
'tags',
'parameters',
'requestBody',
'responses'
'responses',
'security'
],
);
return Operation(
@ -32,6 +33,11 @@ Operation _$OperationFromJson(Map<String, dynamic> json) {
responses: (json['responses'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, Response.fromJson(e as Map<String, dynamic>)),
),
security: (json['security'] as List<dynamic>?)
?.map((e) => (e as Map<String, dynamic>).map(
(k, e) => MapEntry(k, (e as List<dynamic>).map((e) => e as String).toList()),
))
.toList(),
);
}
@ -52,5 +58,6 @@ Map<String, dynamic> _$OperationToJson(Operation instance) {
writeNotNull('parameters', instance.parameters?.map((e) => e.toJson()).toList());
writeNotNull('requestBody', instance.requestBody?.toJson());
writeNotNull('responses', instance.responses?.map((k, e) => MapEntry(k, e.toJson())));
writeNotNull('security', instance.security);
return val;
}

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

@ -55,7 +55,7 @@ class OpenAPIBuilder implements Builder {
: a.compareTo(b),
);
final hasAnySecurity = spec.security?.isNotEmpty ?? false;
final hasAnySecurity = spec.components?.securitySchemes?.isNotEmpty ?? false;
final state = State(prefix);
final output = <String>[
@ -255,128 +255,152 @@ class OpenAPIBuilder implements Builder {
(final b) => b
..name = '${prefix}Authentication'
..abstract = true
..methods.add(
..methods.addAll([
Method(
(final b) => b
..name = 'id'
..type = MethodType.getter
..returns = refer('String'),
),
Method(
(final b) => b
..name = 'headers'
..type = MethodType.getter
..returns = refer('Map<String, String>'),
),
),
]),
).accept(emitter).toString(),
],
];
if (spec.security != null) {
for (final securityRequirement in spec.security!) {
for (final name in securityRequirement.keys) {
final securityScheme = spec.components!.securitySchemes![name]!;
switch (securityScheme.type) {
case 'http':
switch (securityScheme.scheme) {
case 'basic':
output.add(
Class(
(final b) {
final fields = ['username', 'password'];
b
..name = '${prefix}HttpBasicAuthentication'
..extend = refer('${prefix}Authentication')
..constructors.add(
Constructor(
(final b) => b
..optionalParameters.addAll(
fields.map(
(final name) => Parameter(
(final b) => b
..name = name
..toThis = true
..named = true
..required = true,
),
),
),
),
)
..fields.addAll(
fields.map(
(final name) => Field(
(final b) => b
..name = name
..type = refer('String')
..modifier = FieldModifier.final$,
),
),
)
..methods.add(
Method(
(final b) => b
..name = 'headers'
..type = MethodType.getter
..returns = refer('Map<String, String>')
..lambda = true
..body = const Code(r'''
{
'Authorization': 'Basic ${base64.encode(utf8.encode('$username:$password'))}',
}
'''),
),
);
},
).accept(emitter).toString(),
);
continue;
case 'bearer':
output.add(
Class(
(final b) {
b
..name = '${prefix}HttpBearerAuthentication'
..extend = refer('${prefix}Authentication')
..constructors.add(
Constructor(
(final b) => b
..optionalParameters.add(
Parameter(
if (spec.components?.securitySchemes != null) {
for (final name in spec.components!.securitySchemes!.keys) {
final securityScheme = spec.components!.securitySchemes![name]!;
switch (securityScheme.type) {
case 'http':
switch (securityScheme.scheme) {
case 'basic':
output.add(
Class(
(final b) {
final fields = ['username', 'password'];
b
..name = '${prefix}HttpBasicAuthentication'
..extend = refer('${prefix}Authentication')
..constructors.add(
Constructor(
(final b) => b
..optionalParameters.addAll(
fields.map(
(final name) => Parameter(
(final b) => b
..name = 'token'
..name = name
..toThis = true
..named = true
..required = true,
),
),
),
)
..fields.add(
Field(
),
),
)
..fields.addAll(
fields.map(
(final name) => Field(
(final b) => b
..name = 'token'
..name = name
..type = refer('String')
..modifier = FieldModifier.final$,
),
)
..methods.add(
Method(
(final b) => b
..name = 'headers'
..type = MethodType.getter
..returns = refer('Map<String, String>')
..lambda = true
..body = const Code(r'''
),
)
..methods.addAll([
Method(
(final b) => b
..name = 'id'
..annotations.add(refer('override'))
..type = MethodType.getter
..lambda = true
..returns = refer('String')
..body = Code("'$name'"),
),
Method(
(final b) => b
..name = 'headers'
..annotations.add(refer('override'))
..type = MethodType.getter
..returns = refer('Map<String, String>')
..lambda = true
..body = const Code(r'''
{
'Authorization': 'Basic ${base64.encode(utf8.encode('$username:$password'))}',
}
'''),
),
]);
},
).accept(emitter).toString(),
);
continue;
case 'bearer':
output.add(
Class(
(final b) {
b
..name = '${prefix}HttpBearerAuthentication'
..extend = refer('${prefix}Authentication')
..constructors.add(
Constructor(
(final b) => b
..optionalParameters.add(
Parameter(
(final b) => b
..name = 'token'
..toThis = true
..named = true
..required = true,
),
),
),
)
..fields.addAll([
Field(
(final b) => b
..name = 'token'
..type = refer('String')
..modifier = FieldModifier.final$,
),
])
..methods.addAll([
Method(
(final b) => b
..name = 'id'
..annotations.add(refer('override'))
..type = MethodType.getter
..lambda = true
..returns = refer('String')
..body = Code("'$name'"),
),
Method(
(final b) => b
..name = 'headers'
..annotations.add(refer('override'))
..type = MethodType.getter
..returns = refer('Map<String, String>')
..lambda = true
..body = const Code(r'''
{
'Authorization': 'Bearer $token',
}
'''),
),
);
},
).accept(emitter).toString(),
);
continue;
}
}
throw Exception('Can not work with security scheme ${securityScheme.toJson()}');
),
]);
},
).accept(emitter).toString(),
);
continue;
}
}
throw Exception('Can not work with security scheme ${securityScheme.toJson()}');
}
}
@ -438,8 +462,8 @@ class OpenAPIBuilder implements Builder {
if (hasAnySecurity) ...[
Field(
(final b) => b
..name = 'authentication'
..type = refer('${prefix}Authentication?')
..name = 'authentications'
..type = refer('List<${prefix}Authentication>')
..modifier = FieldModifier.final$,
),
],
@ -482,22 +506,18 @@ class OpenAPIBuilder implements Builder {
if (hasAnySecurity) ...[
Parameter(
(final b) => b
..name = 'authentication'
..name = 'authentications'
..toThis = true
..named = true,
..named = true
..defaultTo = const Code('const []'),
),
],
])
..body = Code('''
..body = const Code('''
this.baseHeaders = {
if (baseHeaders != null) ...{
...baseHeaders,
},
${hasAnySecurity ? '''
if (authentication != null) ...{
...authentication!.headers,
},
''' : ''}
};
this.httpClient = (httpClient ?? HttpClient())..userAgent = userAgent;
'''),
@ -651,6 +671,33 @@ class OpenAPIBuilder implements Builder {
Uint8List? body;
''');
final securityRequirements =
(operation.security ?? spec.security) ?? <Map<String, List<String>>>[];
final isOptionalSecurity =
securityRequirements.where((final requirement) => requirement.keys.isEmpty).isNotEmpty;
if (isOptionalSecurity) {
code.write('''
if (${isRootClient ? '' : 'rootClient.'}authentications.isNotEmpty) {
headers.addAll(${isRootClient ? '' : 'rootClient.'}authentications.first.headers);
}
''');
} else {
for (final requirement in securityRequirements) {
code.write('''
if (${isRootClient ? '' : 'rootClient.'}authentications.map((final a) => a.id).contains('${requirement.keys.single}')) {
headers.addAll(${isRootClient ? '' : 'rootClient.'}authentications.singleWhere((final a) => a.id == '${requirement.keys.single}').headers);
} else
''');
}
if (securityRequirements.isNotEmpty) {
code.write('''
{
throw Exception('Missing authentication for ${securityRequirements.map((final r) => r.keys.single).join(' or ')}');
}
''');
}
}
for (final parameter in parameters) {
final nullable = _isParameterNullable(
parameter.required,

Loading…
Cancel
Save