Skip to content

Commit 57b2bbd

Browse files
chrisbobbegnprice
authored andcommitted
api: Add contentType param to ApiConnection.postFileFromStream
1 parent 67ce17c commit 57b2bbd

File tree

3 files changed

+31
-5
lines changed

3 files changed

+31
-5
lines changed

lib/api/core.dart

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:io';
33

44
import 'package:flutter/foundation.dart';
55
import 'package:http/http.dart' as http;
6+
import 'package:http_parser/http_parser.dart';
67

78
import '../log.dart';
89
import '../model/binding.dart';
@@ -192,10 +193,20 @@ class ApiConnection {
192193
}
193194

194195
Future<T> postFileFromStream<T>(String routeName, T Function(Map<String, dynamic>) fromJson,
195-
String path, Stream<List<int>> content, int length, {String? filename}) async {
196+
String path, Stream<List<int>> content, int length,
197+
{String? filename, String? contentType}) async {
196198
final url = realmUrl.replace(path: "/api/v1/$path");
199+
MediaType? parsedContentType;
200+
if (contentType != null) {
201+
try {
202+
parsedContentType = MediaType.parse(contentType);
203+
} on FormatException {
204+
// TODO log
205+
}
206+
}
197207
final request = http.MultipartRequest('POST', url)
198-
..files.add(http.MultipartFile('file', content, length, filename: filename));
208+
..files.add(http.MultipartFile('file', content, length,
209+
filename: filename, contentType: parsedContentType));
199210
return send(routeName, fromJson, request);
200211
}
201212

test/api/core_test.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,17 @@ void main() {
8181
});
8282

8383
test('ApiConnection.postFileFromStream', () async {
84-
Future<void> checkRequest(List<List<int>> content, int length, {String? filename}) {
84+
Future<void> checkRequest(List<List<int>> content, int length,
85+
{String? filename, String? contentType, bool isContentTypeInvalid = false}) {
8586
return FakeApiConnection.with_(account: eg.selfAccount, (connection) async {
8687
connection.prepare(json: {});
8788
await connection.postFileFromStream(
8889
kExampleRouteName, (json) => json, 'example/route',
89-
Stream.fromIterable(content), length, filename: filename);
90+
Stream.fromIterable(content), length,
91+
filename: filename, contentType: contentType);
92+
final expectedContentType = (contentType != null && !isContentTypeInvalid)
93+
? contentType
94+
: 'application/octet-stream';
9095
check(connection.lastRequest!).isA<http.MultipartRequest>()
9196
..method.equals('POST')
9297
..url.asString.equals('${eg.realmUrl.origin}/api/v1/example/route')
@@ -99,6 +104,7 @@ void main() {
99104
..field.equals('file')
100105
..length.equals(length)
101106
..filename.equals(filename)
107+
..contentType.asString.equals(expectedContentType)
102108
..has<Future<List<int>>>((f) => f.finalize().toBytes(), 'contents')
103109
.completes((it) => it.deepEquals(content.expand((l) => l)))
104110
);
@@ -110,6 +116,10 @@ void main() {
110116
checkRequest(['asd'.codeUnits, 'f'.codeUnits], 4, filename: null);
111117

112118
checkRequest(['asdf'.codeUnits], 4, filename: 'info.txt');
119+
checkRequest(['asdf'.codeUnits], 4,
120+
filename: 'image.jpg', contentType: 'image/jpeg');
121+
checkRequest(['asdf'.codeUnits], 4,
122+
filename: 'image.jpg', contentType: 'asdfjkl;', isContentTypeInvalid: true);
113123

114124
checkRequest(['asdf'.codeUnits], 1, filename: null); // nothing on client side catches a wrong length
115125
checkRequest(['asdf'.codeUnits], 100, filename: null);

test/stdlib_checks.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'dart:convert';
99

1010
import 'package:checks/checks.dart';
1111
import 'package:http/http.dart' as http;
12+
import 'package:http_parser/http_parser.dart';
1213

1314
extension ListChecks<T> on Subject<List<T>> {
1415
Subject<T> operator [](int index) => has((l) => l[index], '[$index]');
@@ -136,6 +137,10 @@ extension HttpMultipartFileChecks on Subject<http.MultipartFile> {
136137
Subject<String> get field => has((f) => f.field, 'field');
137138
Subject<int> get length => has((f) => f.length, 'length');
138139
Subject<String?> get filename => has((f) => f.filename, 'filename');
139-
// TODO Subject<MediaType> get contentType => has((f) => f.contentType, 'contentType');
140+
Subject<MediaType> get contentType => has((f) => f.contentType, 'contentType');
140141
Subject<bool> get isFinalized => has((f) => f.isFinalized, 'isFinalized');
141142
}
143+
144+
extension MediaTypeChecks on Subject<MediaType> {
145+
Subject<String> get asString => has((x) => x.toString(), 'toString'); // TODO(checks): what's a good convention for this?
146+
}

0 commit comments

Comments
 (0)