Skip to content

Commit

Permalink
patch(v0.4.1): implement and test full formdata spec
Browse files Browse the repository at this point in the history
  • Loading branch information
iyifr committed Dec 12, 2024
1 parent 26fe57c commit 7986902
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 28 deletions.
69 changes: 69 additions & 0 deletions lib/utils/formdata.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
class FormData {
final Map<String, List<FormDataEntry>> _data = {};

void append(String name, dynamic value,
{String? filename, String? contentType}) {
_data.putIfAbsent(name, () => []).add(FormDataEntry(
value: value, filename: filename, contentType: contentType));
}

void set(String name, dynamic value,
{String? filename, String? contentType}) {
_data[name] = [
FormDataEntry(value: value, filename: filename, contentType: contentType)
];
}

bool has(String name) {
return _data.containsKey(name);
}

void delete(String name) {
_data.remove(name);
}

List<String> keys() {
return _data.keys.toList();
}

List<List<FormDataEntry>> values() {
return _data.values.toList();
}

List<MapEntry<String, List<FormDataEntry>>> entries() {
return _data.entries.toList();
}

dynamic get(String name) {
final values = _data[name];
if (values?.isNotEmpty == true) {
return values!.first.value;
}
return null;
}

List<dynamic>? getAll(String name) {
return _data[name]?.map((entry) => entry.value).toList();
}

@override
String toString() {
return _data.toString();
}
}

class FormDataEntry {
final dynamic value;
final String? filename;
final String? contentType;

FormDataEntry({required this.value, this.filename, this.contentType});

@override
String toString() {
if (filename != null) {
return 'File: $filename ($contentType)';
}
return value.toString();
}
}
30 changes: 2 additions & 28 deletions lib/utils/req_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import 'dart:typed_data';

import 'package:h4/create.dart';
import 'package:h4/src/logger.dart';
import 'package:h4/utils/formdata.dart';
import 'package:mime/mime.dart';
import 'package:path/path.dart' as path;

export 'package:h4/utils/req_utils.dart' hide handleMultipartFormdata, FormData;
export 'package:h4/utils/req_utils.dart' hide handleMultipartFormdata;

String? getRequestIp(H4Event event) {
var ip = event.node["value"]?.headers
Expand Down Expand Up @@ -56,33 +57,6 @@ void handleCors(H4Event event, {String origin = "*", String methods = "*"}) {
.set(HttpHeaders.accessControlAllowMethodsHeader, methods);
}

class FormData {
final Map<String, List<String>> _data = {};

void append(String name, dynamic value) {
_data.putIfAbsent(name, () => []).add(value);
}

log() {
print(_data);
}

@override
String toString() {
return _data.toString();
}

dynamic get(String name) {
final values = _data[name];
var result = values?.isNotEmpty == true ? values!.first : null;
return result;
}

List<dynamic>? getAll(String name) {
return _data[name];
}
}

Future<FormData> readFormData(dynamic event) async {
final HttpRequest request = event.node["value"];
final contentType = request.headers.contentType;
Expand Down
58 changes: 58 additions & 0 deletions test/h4_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:h4/src/router.dart';
import 'package:h4/utils/request_utils.dart';
import 'package:test/test.dart';
import 'package:h4/src/h4.dart';
import 'package:h4/utils/formdata.dart' as h4_formdata;

void main() {
H4? app;
Expand Down Expand Up @@ -254,4 +255,61 @@ void main() {
expect(data['address']['street'], equals('123 Main St'));
expect(data['address']['city'], equals('New York'));
});

test('FormData implementation follows spec', () {
var formData = h4_formdata.FormData();

// Test append and get
formData.append('name', 'John Doe');
expect(formData.get('name'), equals('John Doe'));
expect(formData.has('name'), isTrue);

// Test multiple values via append
formData.append('hobbies', 'reading');
formData.append('hobbies', 'gaming');
formData.append('hobbies', 'coding');
var hobbies = formData
.entries()
.firstWhere((e) => e.key == 'hobbies')
.value
.map((e) => e.value)
.toList();
expect(hobbies, containsAll(['reading', 'gaming', 'coding']));

// Test set (should replace existing values)
formData.set('hobbies', 'swimming');
expect(formData.get('hobbies'), equals('swimming'));
hobbies = formData
.entries()
.firstWhere((e) => e.key == 'hobbies')
.value
.map((e) => e.value)
.toList();
expect(hobbies.length, equals(1));

// Test delete
formData.delete('name');
expect(formData.has('name'), isFalse);
expect(formData.get('name'), isNull);

// Test keys and values
formData.set('age', '25');
formData.set('city', 'New York');
expect(formData.keys(), containsAll(['hobbies', 'age', 'city']));
expect(formData.values().length, equals(3));

// Test entries
var entries = formData.entries();
expect(entries.length, equals(3));
var ageEntry = entries.firstWhere((e) => e.key == 'age');
expect(ageEntry.value.first.value, equals('25'));

// Test with file metadata
formData.append('file', 'file-content',
filename: 'test.txt', contentType: 'text/plain');
var fileEntry =
formData.entries().firstWhere((e) => e.key == 'file').value.first;
expect(fileEntry.filename, equals('test.txt'));
expect(fileEntry.contentType, equals('text/plain'));
});
}

0 comments on commit 7986902

Please sign in to comment.