Skip to content

Commit

Permalink
Add @FormUrlEncoded and filter data which have null value
Browse files Browse the repository at this point in the history
  • Loading branch information
lwj1994 committed Feb 29, 2024
1 parent 4a1de44 commit af17af0
Show file tree
Hide file tree
Showing 6 changed files with 410 additions and 1 deletion.
24 changes: 24 additions & 0 deletions chopper/lib/src/annotations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,30 @@ final class PartFileMap {
const PartFileMap();
}

/// {@template FormUrlEncoded}
///
///
// Denotes that the request body will use form URL encoding. Fields should be declared as parameters
// and annotated with [Field]/[FieldMap].
//
// Requests made with this annotation will have application/x-www-form-urlencoded MIME
// type. Field names and values will be UTF-8 encoded before being URI-encoded in accordance to <a
// href="https://datatracker.ietf.org/doc/html/rfc3986">RFC-3986</a>.
//
///
/// ```dart
/// @Post(path: '/something')
/// @FormUrlEncoded
/// Future<Response> fetch(@Field("param") String? param);
/// ```
/// {@endtemplate}
@immutable
@Target({TargetKind.method})
final class FormUrlEncoded {
/// {@macro FormUrlEncoded}
const FormUrlEncoded();
}

/// {@macro ChopperApi}
const chopperApi = ChopperApi();

Expand Down
34 changes: 33 additions & 1 deletion chopper_generator/lib/src/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ final class ChopperGenerator
) {
final ConstantReader? method = _getMethodAnnotation(m);
final bool multipart = _hasAnnotation(m, chopper.Multipart);
final bool formUrlEncoded = _hasAnnotation(m, chopper.FormUrlEncoded);
final ConstantReader? factoryConverter = _getFactoryConverterAnnotation(m);

final Map<String, ConstantReader> body = _getAnnotation(m, chopper.Body);
Expand All @@ -172,7 +173,7 @@ final class ChopperGenerator
final Map<String, ConstantReader> fileFieldMap =
_getAnnotation(m, chopper.PartFileMap);

final Code? headers = _generateHeaders(m, method!);
final Code? headers = _generateHeaders(m, method!, formUrlEncoded);
final Expression url = _generateUrl(
method,
paths,
Expand Down Expand Up @@ -288,6 +289,10 @@ final class ChopperGenerator

final bool hasQuery = hasQueryMap || queries.isNotEmpty;

if (hasQuery) {
_filterNullValue(blocks, Vars.parameters.toString());
}

if (headers != null) {
blocks.add(headers);
}
Expand Down Expand Up @@ -331,6 +336,11 @@ final class ChopperGenerator

hasBody = hasBody || hasFieldMap;

if (hasBody) {
_filterNullValue(blocks, Vars.body.toString(),
valueToString: formUrlEncoded);
}

bool hasParts = multipart && (parts.isNotEmpty || fileFields.isNotEmpty);
if (hasParts) {
blocks.add(
Expand Down Expand Up @@ -466,6 +476,22 @@ final class ChopperGenerator
});
}

static void _filterNullValue(List<Code> blocks, String refName,
{bool valueToString = false}) {
final filterName = '${refName}filter';
blocks.add(Code('if ($refName is Map) {'));
blocks.add(Code('final Map<String, dynamic> $filterName = {};'));
blocks.add(Code('($refName as Map).forEach((key, value) {'));
blocks.add(Code('if (value != null) {'));
blocks.add(Code(
"$filterName[key] = ${valueToString ? "value.toString()" : "value"};"));
blocks.add(Code('}'));
blocks.add(Code('});'));
blocks.add(Code('($refName as Map).clear();'));
blocks.add(Code('($refName as Map).addAll($filterName);'));
blocks.add(Code('}'));
}

static String _factoryForFunction(FunctionTypedElement function) =>
// ignore: deprecated_member_use
function.enclosingElement is ClassElement
Expand Down Expand Up @@ -718,6 +744,7 @@ final class ChopperGenerator
static Code? _generateHeaders(
MethodElement methodElement,
ConstantReader method,
bool formUrlEncoded,
) {
final StringBuffer codeBuffer = StringBuffer('')..writeln('{');

Expand Down Expand Up @@ -756,6 +783,11 @@ final class ChopperGenerator
}
});

if (formUrlEncoded) {
codeBuffer
.writeln("'content-type': 'application/x-www-form-urlencoded',");
}

codeBuffer.writeln('}');
final String code = codeBuffer.toString();

Expand Down
145 changes: 145 additions & 0 deletions chopper_generator/test/test_service.chopper.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions chopper_generator/test/test_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ abstract class HttpTestService extends ChopperService {
@Part('2') Map b,
);

@Post(path: 'formUrlEncoded')
@FormUrlEncoded()
Future<Response> postFormUrlEncode(
@Field('a') String a,
@Field('a1') String a2,
@FieldMap() Map<String, dynamic> b,
);

@Post(path: 'file')
@multipart
Future<Response> postFile(
Expand Down
Loading

0 comments on commit af17af0

Please sign in to comment.