From f41f367808a21f58b094f2e6657deb681946f577 Mon Sep 17 00:00:00 2001 From: regiostech Date: Thu, 3 Mar 2016 22:47:20 -0500 Subject: [PATCH] Tests failing + incomplete --- .gitignore | 75 +++- Test Results - Run_All_Tests.html | 636 ++++++++++++++++++++++++++++++ lib/body_parser.dart | 67 ++++ pubspec.yaml | 9 + test/all_tests.dart | 60 +++ 5 files changed, 830 insertions(+), 17 deletions(-) create mode 100644 Test Results - Run_All_Tests.html create mode 100644 lib/body_parser.dart create mode 100644 pubspec.yaml create mode 100644 test/all_tests.dart diff --git a/.gitignore b/.gitignore index 7c280441..b306bf88 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,68 @@ -# See https://www.dartlang.org/tools/private-files.html +### VisualStudioCode template +.settings -# Files and directories created by pub +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +.idea/ +/Test Results - Run_All_Tests.html +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +### Dart template +# Don’t commit the following directories created by pub. .buildlog -.packages -.project .pub/ build/ -**/packages/ +packages +.packages -# Files created by dart2js -# (Most Dart developers will use pub build to compile Dart, use/modify these -# rules if you intend to use dart2js directly -# Convention is to use extension '.dart.js' for Dart compiled to Javascript to -# differentiate from explicit Javascript files) +# Or the files created by dart2js. *.dart.js -*.part.js +*.js_ *.js.deps *.js.map -*.info.json -# Directory created by dartdoc -doc/api/ - -# Don't commit pubspec lock file -# (Library packages only! Remove pattern if developing an application package) +# Include when developing application packages. pubspec.lock + diff --git a/Test Results - Run_All_Tests.html b/Test Results - Run_All_Tests.html new file mode 100644 index 00000000..5eb5a1aa --- /dev/null +++ b/Test Results - Run_All_Tests.html @@ -0,0 +1,636 @@ + + + + +Test Results — Run All Tests + + + + + + + + + +
+ +
+
    +
  • + +
    922 ms
    +
    Test server support
    +
      +
    • + +
      922 ms
      +
      JSON
      +
        +
      • + +
        528 ms
        +
        passedPost Simple JSON
        +
          +
        • +Test server listening on http://localhost:60667
          +
        • +
        +
      • +
      • + +
        394 ms
        +
        failedPost Complex JSON
        +
          +
        • +Test server listening on http://localhost:60669
          +
        • +
        • +package:test expect
          test\all_tests.dart 56:9 main.<fn>.<fn>.<fn>.<async>
          ===== asynchronous gap ===========================
          dart:async _Completer.completeError
          test\all_tests.dart 57:8 main.<fn>.<fn>.<fn>.<async>
          ===== asynchronous gap ===========================
          dart:async _asyncThenWrapperHelper
          test\all_tests.dart main.<fn>.<fn>.<fn>
          +
        • +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + diff --git a/lib/body_parser.dart b/lib/body_parser.dart new file mode 100644 index 00000000..cdb02558 --- /dev/null +++ b/lib/body_parser.dart @@ -0,0 +1,67 @@ +// A library for parsing HTTP request bodies and queries. + +library body_parser; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'package:json_god/json_god.dart'; + +/// A representation of data from an incoming request. +class BodyParseResult { + /// The parsed body. + Map body = {}; + + /// The parsed query string. + Map query = {}; +} + +/// Grabs data from an incoming request. +/// +/// Supports urlencoded and JSON. +Future parseBody(HttpRequest request) async { + BodyParseResult result = new BodyParseResult(); + ContentType contentType = request.headers.contentType; + + // Parse body + if (contentType != null) { + if (contentType.mimeType == 'application/json') + result.body = JSON.decode(await request.transform(UTF8.decoder).join()); + else if (contentType.mimeType == 'application/x-www-form-urlencoded') { + String body = await request.transform(UTF8.decoder).join(); + buildMapFromUri(result.body, body); + } + } + + // Parse query + RegExp queryRgx = new RegExp(r'\?(.+)$'); + String uriString = request.requestedUri.toString(); + if (queryRgx.hasMatch(uriString)) { + Match queryMatch = queryRgx.firstMatch(uriString); + buildMapFromUri(result.query, queryMatch.group(1)); + } + + return result; +} + +/// Parses a URI-encoded string into real data! **Wow!** +buildMapFromUri(Map map, String body) { + God god = new God(); + for (String keyValuePair in body.split('&')) { + if (keyValuePair.contains('=')) { + List split = keyValuePair.split('='); + String key = Uri.decodeQueryComponent(split[0]); + String value = Uri.decodeQueryComponent(split[1]); + num numValue = num.parse(value, (_) => double.NAN); + if (!numValue.isNaN) + map[key] = numValue; + else if (value.startsWith('[') && value.endsWith(']')) + map[key] = god.deserialize(value); + else if (value.startsWith('{') && value.endsWith('}')) + map[key] = god.deserialize(value); + else if (value.trim().toLowerCase() == 'null') + map[key] = null; + else map[key] = value; + } else map[Uri.decodeQueryComponent(keyValuePair)] = true; + } +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 00000000..1bdc9254 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,9 @@ +name: body_parser +author: Tobe O +version: 1.0.0-dev +description: Parse request bodies and query strings in Dart. +dependencies: + json_god: any +dev_dependencies: + http: any + test: any \ No newline at end of file diff --git a/test/all_tests.dart b/test/all_tests.dart new file mode 100644 index 00000000..de173cd8 --- /dev/null +++ b/test/all_tests.dart @@ -0,0 +1,60 @@ +import 'dart:io'; + +import 'package:body_parser/body_parser.dart'; +import 'package:http/http.dart' as http; +import 'package:json_god/json_god.dart'; +import 'package:test/test.dart'; + +main() { + group('Test server support', () { + HttpServer server; + String url; + http.Client client; + God god; + + setUp(() async { + server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 0); + server.listen((HttpRequest request) async { + //Server will simply return a JSON representation of the parsed body + request.response.write(god.serialize(await parseBody(request))); + await request.response.close(); + }); + url = 'http://localhost:${server.port}'; + print('Test server listening on $url'); + client = new http.Client(); + god = new God(); + }); + tearDown(() async { + await server.close(force: true); + client.close(); + server = null; + url = null; + client = null; + god = null; + }); + + group('JSON', () { + test('Post Simple JSON', () async { + var response = await client.post(url, body: { + 'hello': 'world' + }); + expect(response.body, equals('{"body":{"hello":"world"},"query":{}}')); + }); + + test('Post Complex JSON', () async { + var postData = god.serialize({ + 'hello': 'world', + 'nums': [1, 2.0, 3 - 1], + 'map': { + 'foo': { + 'bar': 'baz' + } + } + }); + var response = await client.post(url, body: postData); + var body = god.deserialize(response.body)['body']; + expect(body['hello'], equals('world')); + }); + }); + }); +} \ No newline at end of file