├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── doc └── images │ └── twilio_user_account_dashboard.png ├── example ├── cli │ ├── calls │ │ └── list_calls.dart │ └── messages │ │ ├── list_text.dart │ │ ├── read_text.dart │ │ └── send_text.dart └── twilio_dart_web │ ├── pubspec.yaml │ └── web │ ├── index.html │ ├── twilio_dart_web.css │ └── twilio_dart_web.dart ├── lib ├── resources │ ├── accounts.dart │ ├── calls.dart │ └── messages.dart ├── src │ └── validator.dart ├── twilio.dart ├── twilio_browser.dart └── utils │ ├── http_headers.dart │ └── utils.dart ├── pubspec.yaml └── test ├── fixtures ├── expected_messages.dart └── status_messages.dart ├── suite.dart ├── twilio_call_test.dart ├── twilio_message_test.dart ├── twilio_test.dart └── utils └── utils_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | packages 2 | .project 3 | *.iml 4 | *.ipr 5 | *.iws 6 | .idea/ 7 | *.dart.js 8 | *.js_ 9 | *.js.deps 10 | *.js.map 11 | .buildlog 12 | web/out/* 13 | .DS_Store 14 | *.lock 15 | /build/* 16 | .packages 17 | .dart_tool -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: dart 2 | dart_task: 3 | 4 | # As long as you don't want any other configuration, you can just use the name 5 | # of a task instead of "name: true". 6 | - test 7 | 8 | # Warnings are fatal, but we only analyze the lib/ directory. 9 | - dartanalyzer: --fatal-warnings lib 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.6 2 | * Add call capabilities skeleton 3 | * Add call log list 4 | 5 | ## 0.0.5 6 | * Release version to public 7 | * Complete documentation 8 | 9 | ## 0.0.4+1 10 | * Separate unit tests 11 | * Add more unit test coverage 12 | * Finish off messages 13 | 14 | ## 0.0.4 15 | * Add readme 16 | * Refactor library and imports 17 | * Add browser capabilities 18 | 19 | ## 0.0.1 20 | 21 | * First version -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Marcos Placona 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/mplacona/twilio-dart.svg?branch=master)](https://travis-ci.com/mplacona/twilio-dart) 2 | 3 | # Twilio Wrapper for Dart 4 | --- 5 | A Future-based wrapper for the [Twilio API](https://www.twilio.com/docs/quickstart/). With this wrapper you can get yourself on the wonderful world of communication via [SMS](https://www.twilio.com/sms/api) and [Voice](https://www.twilio.com/voice/api) (WIP). 6 | 7 | ## Dependencies 8 | * [http](https://pub.dartlang.org/packages/http) 9 | 10 | ## [Getting Started](id:getting_started) 11 | * Add a dependency to `twilio_dart` to your pubspec.yaml 12 | * Run `pub get` 13 | * Obtain a key and an authentication code on the [dashboard](https://www.twilio.com/user/account) of the Twilio website. The account is limited but free. 14 | 15 | ![Twilio user account dashboard](docs/images/twilio_user_account_dashboard.png "Twilio user account dashboard") 16 | 17 | * You can now create a new Twilio object with your account details as such: 18 | 19 | ```dart 20 | import 'package:twilio_dart/twilio.dart'; 21 | 22 | var key = "your_twilio_key"; 23 | var authToken = "your_auth_token"; 24 | var version = "2010-04-01"; 25 | //create a new twilio object 26 | Twilio twilio = new Twilio(key, authToken, version); 27 | ``` 28 | 29 | > And this will allow you access to any of the currently available methods on this wrapper (more on this later) 30 | 31 | ### [Sending an SMS message](id:sending) 32 | * Create a new Twilio object as described in [Getting Started](#getting_started). 33 | * Replace the sample values with the ones obtained when you created your Twilio account (also described above in [Getting Started](#getting_started)). 34 | * Define a `from`, a `to` and a `body`. __Remember you can only send SMS's from the numbers obtained from your Twilio account__. 35 | 36 | ```dart 37 | var from = "your_twilio_phone"; 38 | var to = "your_mobile_number"; 39 | var body = "Look ma! Dart can now send SMS's in under 15 lines"; 40 | ``` 41 | * Send the message away! 42 | ```dart 43 | twilio.sendSMS(from, to, body).then((response) => print("Your message has been sent!")).catchError(print); 44 | ``` 45 | > You can also turn your SMS message into an [MMS](http://en.wikipedia.org/wiki/Multimedia_Messaging_Service) message by including an attachment with it. The API accepts a 4th argument in the form of a URL pointing to the image you would like to send. 46 | 47 | ### [Reading a message](id:reading) 48 | Any messages sent via Twilio are stored, so they can be later retrieved. But most importantly, your account can also receive messages, and you need a way to retrieve it, don't you? 49 | 50 | The same Twilio object you created in Getting Started](#getting_started) can now be used to retrieve any messages by its Twilio ID. 51 | 52 | ```dart 53 | twilio.readSMS(_messageSid); 54 | ``` 55 | 56 | ### [Listing all messages](id:listing) 57 | You're gonna wanna show a list of the messages you've received, so you can drill down to each individual message. To do so... you guessed right, you will again utilise the Twilio object you created earlier as such: 58 | 59 | ```dart 60 | twilio.readSMSList() 61 | ``` 62 | 63 | This time, unlike with [Reading a message](#reading), you won't be passing any message ID's, as we want to list every single message in the account. 64 | 65 | #### To be implemented 66 | Optional parameters for: 67 | From 68 | To 69 | DateSent 70 | 71 | ## But what about the browser? 72 | The browser implementation is still highly experimental, and although tests have proven it works, I'm still not 100% happy with the way it's been implemented. 73 | 74 | It can be used via the `twilio_browser` option. Just import 75 | 76 | ```dart 77 | import 'package:twilio_dart/twilio_browser.dart'; 78 | ``` 79 | 80 | Instead of your normal 81 | 82 | ```dart 83 | import 'package:twilio_dart/twilio.dart'; 84 | ``` 85 | 86 | __Yes, this needs work :-D __ 87 | 88 | ## Bugs? 89 | Create a new issue tagged `bug` 90 | 91 | ## Collaborate 92 | Collaboration is the key to every successful library, and this one is no different. I would love to see my code improved, and new features added to the library. 93 | 94 | Just __fork__ this repository, and send me a __pull request__ when you're done. I'm using [git flow](https://github.com/nvie/gitflow) to manage new features and bugs, and would highly recommend you used the same thing. I will then be happy to turn your pull request into a release :-). 95 | 96 | The process is as follows after you've created your fork: 97 | 98 | ``` 99 | git clone git@github.com:/twilio-dart.git 100 | cd twilio-dart 101 | git branch master origin/master 102 | git flow init -d 103 | git flow feature start 104 | ``` 105 | 106 | Do your magic and commit as often as you like. Once done run: 107 | 108 | ``` 109 | git flow feature publish 110 | ``` 111 | 112 | When that's completed, open a pull request to your feature branch on GitHub. 113 | 114 | [This cheatsheet](http://danielkummer.github.io/git-flow-cheatsheet/) will be useful if you want to do some other cool things. 115 | 116 | ## Test 117 | I always try to add as much cover as I can, and am especially careful to make sure I always play nice with third party API's. If code is written correctly, you can easily mock functionality without having to actually hit the API's directly. 118 | 119 | This obviously saves not only on the number of requests you will make every time you run the test suite, but in any limitations that might arise from running unit tests over and over again hitting the API on a trial account. 120 | 121 | It also decouples me from the web, which means I can code on the move (I commute to work), and still run my entire suite without internet connectivity. I have used the following libraries for my unit tests: 122 | 123 | * [mock](https://pub.dartlang.org/packages/mock) 124 | * [unittest](https://pub.dartlang.org/packages/unittest) 125 | 126 | ## Roadmap 127 | Like I said before, Twilio's API is very big, and the ability to send texts is just the tip of the iceberg. The following are the next things I would like to implement in the future when time permits: 128 | 129 | * Calls 130 | * Make a call 131 | * View call 132 | * View call list 133 | * Modify a Live Call 134 | * View Call Recording List 135 | * View Call Notification List 136 | * Recordings 137 | * View Recording 138 | * View Recording List 139 | * View Transcription List for Recording 140 | * Delete Recording 141 | * Usage Records 142 | * View Usage Record List 143 | * View Usage Record Subresource 144 | * Conferences 145 | * View Conference 146 | * View Conference List 147 | * View Participant for Conference 148 | * View Participant List for Conference 149 | * Mute Participant 150 | * Delete Participant 151 | 152 | Again, not a full list, as there is much more that can be done, however, the list above are my personal preferences ;-) 153 | 154 | ## Disclaimer 155 | I am in no way affiliated to Twilio, other than having a trial account with them, like you probably also do by now. 156 | 157 | This library is also not endorsed or officially supported by Twilio, though I'm pretty sure they will be happy to help if you have questions about the API itself. 158 | 159 | -------------------------------------------------------------------------------- /doc/images/twilio_user_account_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mplacona/twilio-dart/7e45575fb5cc4103d0d9d41d206223ae6c861bf9/doc/images/twilio_user_account_dashboard.png -------------------------------------------------------------------------------- /example/cli/calls/list_calls.dart: -------------------------------------------------------------------------------- 1 | import 'package:twilio_dart/twilio.dart'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | main() { 5 | var key = Platform.environment['TWILIO_KEY']; 6 | var authToken = Platform.environment['TWILIO_TOKEN']; 7 | var version = "2010-04-01"; 8 | 9 | //create a new twilio object 10 | Twilio twilio = new Twilio(key, authToken, version); 11 | 12 | // Read SMS list 13 | twilio.readCallsList().then((response) { 14 | print(response.toString()); 15 | jsonDecode(response)['calls'].forEach((e) => print(e)); 16 | }).catchError((error) => print(error.toString())); 17 | } 18 | -------------------------------------------------------------------------------- /example/cli/messages/list_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:twilio_dart/twilio.dart'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | main() { 6 | var key = Platform.environment['TWILIO_KEY']; 7 | var authToken = Platform.environment['TWILIO_TOKEN']; 8 | var version = "2010-04-01"; 9 | 10 | //create a new twilio object 11 | Twilio twilio = new Twilio(key, authToken, version); 12 | 13 | // Read SMS list 14 | twilio.readSMSList().then((response) { 15 | print(response.toString()); 16 | jsonDecode(response)['messages'].forEach((e) => print(e)); 17 | }).catchError((error) => print(error.toString())); 18 | } 19 | -------------------------------------------------------------------------------- /example/cli/messages/read_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:twilio_dart/twilio.dart'; 2 | import 'dart:io'; 3 | main() { 4 | var key = Platform.environment['TWILIO_KEY']; 5 | var authToken = Platform.environment['TWILIO_TOKEN']; 6 | var version = "2010-04-01"; 7 | var sid = "your_twilio_sid"; 8 | 9 | //create a new twilio object 10 | Twilio twilio = new Twilio(key, authToken, version); 11 | 12 | // Read single SMS message 13 | twilio.readSMS(sid).then((response) => print(response.toString())).catchError((error) => print(error.toString())); 14 | } 15 | -------------------------------------------------------------------------------- /example/cli/messages/send_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:twilio_dart/twilio.dart'; 2 | import 'dart:io'; 3 | main() { 4 | var key = Platform.environment['TWILIO_KEY']; 5 | var authToken = Platform.environment['TWILIO_TOKEN']; 6 | var version = "2010-04-01"; 7 | var from = "your_twilio_phone"; 8 | var to = "your_phone_number"; 9 | var body = "Look ma! Dart can now send SMS's in under 15 lines"; 10 | 11 | //create a new twilio object 12 | Twilio twilio = new Twilio(key, authToken, version); 13 | 14 | // Send SMS for LOLZ 15 | var future = twilio.sendSMS(from, to, body); 16 | future.then((res) => print(res)).catchError((error) => print(error)); 17 | } 18 | -------------------------------------------------------------------------------- /example/twilio_dart_web/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: twilio_dart_web 2 | description: A sample web application 3 | dependencies: 4 | twilio_dart: 5 | path: ../../ 6 | dev_dependencies: 7 | build_web_compilers: ^0.4.0 8 | build_runner: ^1.0.0 9 | build_test: ^0.10.2 10 | -------------------------------------------------------------------------------- /example/twilio_dart_web/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Twilio dart web 7 | 8 | 9 | 10 | 11 |

Twilio dart web

12 | 13 |

Message someone!

14 |

15 | 16 | 17 |

18 | 19 | 20 | -------------------------------------------------------------------------------- /example/twilio_dart_web/web/twilio_dart_web.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | background-color: #F8F8F8; 4 | font-family: 'Open Sans', sans-serif; 5 | font-size: 14px; 6 | font-weight: normal; 7 | line-height: 1.2em; 8 | margin: 15px; 9 | } 10 | 11 | h1, p { 12 | color: #333; 13 | } 14 | 15 | #sample_container_id { 16 | width: 100%; 17 | height: 400px; 18 | position: relative; 19 | border: 1px solid #ccc; 20 | background-color: #fff; 21 | } 22 | 23 | #sample_text_id { 24 | font-size: 24pt; 25 | text-align: center; 26 | margin-top: 140px; 27 | -webkit-user-select: none; 28 | user-select: none; 29 | } 30 | -------------------------------------------------------------------------------- /example/twilio_dart_web/web/twilio_dart_web.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html'; 2 | import 'package:twilio_dart/twilio_browser.dart'; 3 | 4 | void main() { 5 | var key = "your_twilio_key"; 6 | var authToken = "your_auth_token"; 7 | var version = "2010-04-01"; 8 | //create a new twillio object 9 | Twilio twilio = new Twilio(key, authToken, version); 10 | 11 | // Send SMS for LOLZ 12 | var from = "your_twilio_phone"; 13 | var to = "your_mobile_number"; 14 | var body = "Look ma! Dart can now send SMS's in under 15 lines"; 15 | 16 | Element status = querySelector('#status'); 17 | 18 | querySelector('button').onClick.listen((e) { 19 | twilio.sendSMS(from, to, body).then((response) => status.text = "Your message has been sent!").catchError((error) => status.text = error.toString()); 20 | }); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/resources/accounts.dart: -------------------------------------------------------------------------------- 1 | library twilio.Accounts; 2 | 3 | class Accounts{ 4 | final resource = "Accounts"; 5 | } -------------------------------------------------------------------------------- /lib/resources/calls.dart: -------------------------------------------------------------------------------- 1 | library twilio.Voice; 2 | import 'package:twilio_dart/resources/accounts.dart'; 3 | 4 | class Calls extends Accounts { 5 | String _resource; 6 | final String extension = ".json"; 7 | Calls(String accountSid) { 8 | final String account = super.resource; 9 | _resource = "$account/$accountSid/Calls"; 10 | } 11 | 12 | String getGetCallsLogResource() { 13 | return this._resource + "/" + extension; 14 | } 15 | 16 | String getPostMakeCallResource() { 17 | return this._resource + "/" + extension; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/resources/messages.dart: -------------------------------------------------------------------------------- 1 | library twilio.Messages; 2 | import 'package:twilio_dart/resources/accounts.dart'; 3 | 4 | class Messages extends Accounts{ 5 | String _resource; 6 | final String extension = ".json"; 7 | Messages(String accountSid){ 8 | final String account = super.resource; 9 | _resource = "$account/$accountSid/Messages"; 10 | } 11 | 12 | String getPostResource(){ 13 | return this._resource + extension; 14 | } 15 | 16 | String getGetMessageResource(String messageSid){ 17 | return this._resource + "/" + messageSid + extension; 18 | } 19 | 20 | String getGetMessageListResource(){ 21 | return this._resource + extension; 22 | } 23 | } -------------------------------------------------------------------------------- /lib/src/validator.dart: -------------------------------------------------------------------------------- 1 | library twilio.src.validator; 2 | import 'dart:async'; 3 | 4 | abstract class IValidator { 5 | validateResponse(T response); 6 | } 7 | 8 | class Validator implements IValidator { 9 | @override 10 | validateResponse(response) { 11 | if (response.toString().contains("20003")) { 12 | return new Future.error(throw new ArgumentError("twilio-dart requires a valid Account SID and Auth Token")); 13 | } else if (response.toString().contains("21212")) { 14 | return new Future.error(throw new ArgumentError("the provided 'From' number is not a valid phone number or shortcode")); 15 | } else if (response.toString().contains("20404")) { 16 | return new Future.error(throw new ArgumentError("twilio-dart requires a valid Account SID and Auth Token")); 17 | } else if (response.toString().contains("21606")) { 18 | return new Future.error(throw new ArgumentError("this phone number is not owned by your account or is not SMS-capable.")); 19 | } else { 20 | return response; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/twilio.dart: -------------------------------------------------------------------------------- 1 | library twilio_dart_cli; 2 | import 'package:http/http.dart' as http; 3 | import 'dart:async'; 4 | import 'resources/messages.dart'; 5 | import 'resources/calls.dart'; 6 | import 'utils/utils.dart'; 7 | import 'src/validator.dart'; 8 | 9 | class Twilio { 10 | final _baseUri = "api.twilio.com"; 11 | http.Client _httpClient; 12 | Messages _messages; 13 | Calls _calls; 14 | Map _auth = new Map(); 15 | IValidator _validator = new Validator(); 16 | 17 | Twilio(String key, String authToken, String version, [http.Client httpClient = null]) { 18 | _auth['accountSid'] = key; 19 | _auth['authToken'] = authToken; 20 | _auth['apiVersion'] = version; 21 | _auth['baseUri'] = _baseUri; 22 | this._httpClient = (httpClient == null) ? new http.Client() : httpClient; 23 | this._messages = new Messages(key); 24 | this._calls = new Calls(key); 25 | } 26 | 27 | // Messages 28 | Future sendSMS(String from, String to, String body, [String mediaUrl = null]) { 29 | Map _parameters; 30 | _parameters = { 31 | 'From': from, 32 | 'To': to, 33 | 'Body': body 34 | }; 35 | // If we're trying to send an MMS 36 | if (mediaUrl != null) { 37 | _parameters.addAll({ 38 | 'media_url': mediaUrl 39 | }); 40 | } 41 | return apiRequest(_messages.getPostResource(), _httpClient, _auth, verb: 'POST', body: _parameters).then((msg) { 42 | return _validator.validateResponse(msg); 43 | }); 44 | } 45 | 46 | Future readSMS(String messageSid) { 47 | return apiRequest(_messages.getGetMessageResource(messageSid), _httpClient, _auth).then((msg) { 48 | return _validator.validateResponse(msg); 49 | }); 50 | } 51 | 52 | Future readSMSList() { 53 | return apiRequest(_messages.getGetMessageListResource(), _httpClient, _auth).then((msg) { 54 | return _validator.validateResponse(msg); 55 | }); 56 | } 57 | 58 | // Calls 59 | Future readCallsList({String from, String to, String status, String startTime, String parentCallSid}) { 60 | Map _parameters; 61 | // this does not look good, so will need some refactoring 62 | if(from != null) _parameters.addAll( { 'From': from }); 63 | if(to != null) _parameters.addAll( { 'To': to }); 64 | if(status != null) _parameters.addAll( { 'Status': status }); 65 | if(startTime != null) _parameters.addAll( { 'StartTime': startTime }); 66 | if(parentCallSid != null) _parameters.addAll( { 'ParentCallSid': parentCallSid }); 67 | 68 | return apiRequest(_calls.getGetCallsLogResource(), _httpClient, _auth, body: _parameters).then((msg) { 69 | return _validator.validateResponse(msg); 70 | }); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /lib/twilio_browser.dart: -------------------------------------------------------------------------------- 1 | library twilio_dart_browser; 2 | import 'package:http/browser_client.dart' as http; 3 | import 'dart:async'; 4 | import 'resources/messages.dart'; 5 | import 'utils/utils.dart'; 6 | import 'src/validator.dart'; 7 | 8 | class Twilio { 9 | final _baseUri = "api.twilio.com"; 10 | http.BrowserClient _httpClient; 11 | Messages _messages; 12 | Map _auth = new Map(); 13 | IValidator _validator = new Validator(); 14 | 15 | Twilio(String key, String authToken, String version, [http.BrowserClient httpClient = null]) { 16 | _auth['accountSid'] = key; 17 | _auth['authToken'] = authToken; 18 | _auth['apiVersion'] = version; 19 | _auth['baseUri'] = _baseUri; 20 | this._httpClient = (httpClient == null) ? new http.BrowserClient() : httpClient; 21 | this._messages = new Messages(key); 22 | } 23 | 24 | Future sendSMS(String from, String to, String body, [String mediaUrl = null]) { 25 | Map _parameters; 26 | _parameters = { 27 | 'From': from, 28 | 'To': to, 29 | 'Body': body 30 | }; 31 | // If we're trying to send an MMS 32 | if (mediaUrl != null) { 33 | _parameters.addAll({ 34 | 'media_url': mediaUrl 35 | }); 36 | } 37 | return apiRequest(_messages.getPostResource(), _httpClient, _auth, verb: 'POST', body: _parameters).then((msg) { 38 | return _validator.validateResponse(msg); 39 | }); 40 | } 41 | 42 | Future readSMS(String messageSid) { 43 | return apiRequest(_messages.getGetMessageResource(messageSid), _httpClient, _auth).then((msg) { 44 | return _validator.validateResponse(msg); 45 | }); 46 | } 47 | 48 | Future readSMSList() { 49 | return apiRequest(_messages.getGetMessageListResource(), _httpClient, _auth).then((msg) { 50 | return _validator.validateResponse(msg); 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/utils/http_headers.dart: -------------------------------------------------------------------------------- 1 | abstract class HttpHeaders { 2 | static const ACCEPT = "accept"; 3 | static const ACCEPT_CHARSET = "accept-charset"; 4 | static const ACCEPT_ENCODING = "accept-encoding"; 5 | static const ACCEPT_LANGUAGE = "accept-language"; 6 | static const ACCEPT_RANGES = "accept-ranges"; 7 | static const AGE = "age"; 8 | static const ALLOW = "allow"; 9 | static const AUTHORIZATION = "authorization"; 10 | static const CACHE_CONTROL = "cache-control"; 11 | static const CONNECTION = "connection"; 12 | static const CONTENT_ENCODING = "content-encoding"; 13 | static const CONTENT_LANGUAGE = "content-language"; 14 | static const CONTENT_LENGTH = "content-length"; 15 | static const CONTENT_LOCATION = "content-location"; 16 | static const CONTENT_MD5 = "content-md5"; 17 | static const CONTENT_RANGE = "content-range"; 18 | static const CONTENT_TYPE = "content-type"; 19 | static const DATE = "date"; 20 | static const ETAG = "etag"; 21 | static const EXPECT = "expect"; 22 | static const EXPIRES = "expires"; 23 | static const FROM = "from"; 24 | static const HOST = "host"; 25 | static const IF_MATCH = "if-match"; 26 | static const IF_MODIFIED_SINCE = "if-modified-since"; 27 | static const IF_NONE_MATCH = "if-none-match"; 28 | static const IF_RANGE = "if-range"; 29 | static const IF_UNMODIFIED_SINCE = "if-unmodified-since"; 30 | static const LAST_MODIFIED = "last-modified"; 31 | static const LOCATION = "location"; 32 | static const MAX_FORWARDS = "max-forwards"; 33 | static const PRAGMA = "pragma"; 34 | static const PROXY_AUTHENTICATE = "proxy-authenticate"; 35 | static const PROXY_AUTHORIZATION = "proxy-authorization"; 36 | static const RANGE = "range"; 37 | static const REFERER = "referer"; 38 | static const RETRY_AFTER = "retry-after"; 39 | static const SERVER = "server"; 40 | static const TE = "te"; 41 | static const TRAILER = "trailer"; 42 | static const TRANSFER_ENCODING = "transfer-encoding"; 43 | static const UPGRADE = "upgrade"; 44 | static const USER_AGENT = "user-agent"; 45 | static const VARY = "vary"; 46 | static const VIA = "via"; 47 | static const WARNING = "warning"; 48 | static const WWW_AUTHENTICATE = "www-authenticate"; 49 | } 50 | -------------------------------------------------------------------------------- /lib/utils/utils.dart: -------------------------------------------------------------------------------- 1 | library twilio.utils.utils; 2 | import 'dart:async'; 3 | import 'package:http/http.dart' as http; 4 | import 'http_headers.dart'; 5 | 6 | Future apiRequest(String resource, http.Client httpClient, Map auth, {String verb : 'GET', Map body : null}) { 7 | var url = buildAuthUrl(resource, auth).toString(); 8 | var request = new http.Request(verb, Uri.parse(url)); 9 | request.headers[HttpHeaders.USER_AGENT] = 'twilio-dart'; 10 | if (body != null && body.isNotEmpty) { 11 | request.bodyFields = body; 12 | } 13 | print(url); 14 | return httpClient.send(request).then((response) => response.stream.bytesToString().then((value) => value.toString())); 15 | } 16 | 17 | String buildAuthUrl(String resource, Map auth) { 18 | return 'https://' + auth['accountSid'] + ':' + auth['authToken'] + '@' + auth['baseUri'] + '/' + auth['apiVersion'] + '/' + resource; 19 | } 20 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: twilio_dart 2 | version: 0.0.6 3 | author: Marcos Placona 4 | description: An implementation of the Twilio API for Dart. 5 | homepage: http://mplacona.github.io/twilio-dart 6 | environment: 7 | sdk: '>=2.0.0-dev.68.0 <3.0.0' 8 | dependencies: 9 | http: ^0.12.0+1 10 | dev_dependencies: 11 | mockito: any 12 | test: any 13 | -------------------------------------------------------------------------------- /test/fixtures/expected_messages.dart: -------------------------------------------------------------------------------- 1 | library twilio.test.fixtures.ExpectedMessages; 2 | import 'dart:convert'; 3 | 4 | var smsWrongAccount = jsonEncode({ 5 | "code": 20003, 6 | "detail": "Your AccountSid or AuthToken was incorrect.", 7 | "message": "Authenticate", 8 | "more_info": "https://www.twilio.com/docs/errors/20003", 9 | "status": 401 10 | }); 11 | 12 | var smsWrongFrom = jsonEncode({ 13 | "code": 21212, 14 | "message": "The 'From' number +112345678 is not a valid phone number or shortcode.", 15 | "more_info": "https://www.twilio.com/docs/errors/21212", 16 | "status": 400 17 | }); 18 | 19 | var smsReadWrongAccount = jsonEncode({ 20 | "code": 20404, 21 | "message": "The requested resource /2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3be/Messages/SMd9390361a08a9138b256a73804f233e9.json was not found", 22 | "more_info": "https://www.twilio.com/docs/errors/20404", 23 | "status": 404 24 | }); 25 | 26 | var smsReadResponse = jsonEncode({ 27 | 'sid': 'SMb938df8bb21a4b7faf240e5c99e6efbd', 28 | 'date_created': 'Sat, 14 Jun 2014 22:56:51 +0000', 29 | 'date_updated': 'Sat, 14 Jun 2014 22:56:57 +0000', 30 | 'date_sent': 'Sat, 14 Jun 2014 22:56:52 +0000', 31 | 'account_sid': 'AC9d3e7fbe4b0d27fa1b5c60146fcb3bea', 32 | "to": "+44123456789", 33 | "from": "+112345678", 34 | 'body': 'this is a test', 35 | 'status': 'delivered', 36 | 'num_segments': '1', 37 | 'num_media': '0', 38 | 'direction': 'outbound-api', 39 | 'api_version': '2010-04-01', 40 | 'price': '-0.04000', 41 | 'price_unit': 'USD', 42 | 'error_code': null, 43 | 'error_message': null, 44 | 'uri': '/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages/SMb938df8bb21a4b7faf240e5c99e6efbd.json', 45 | 'subresource_uris': { 46 | 'media': '/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages/SMb938df8bb21a4b7faf240e5c99e6efbd/Media.json' 47 | } 48 | }); 49 | 50 | var smsReadListResponse = jsonEncode({ 51 | 'first_page_uri': '/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages.json?PageSize=50&Page=0', 52 | 'last_page_uri': '/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages.json?PageSize=50&Page=0', 53 | 'previous_page_uri': null, 54 | 'end': 1, 55 | 'messages': [{ 56 | 'sid': 'SMb938df8bb21a4b7faf240e5c99e6efbd', 57 | 'date_created': 'Sat, 14 Jun 2014 22:56:51 +0000', 58 | 'date_updated': 'Sat, 14 Jun 2014 22:56:57 +0000', 59 | 'date_sent': 'Sat, 14 Jun 2014 22:56:52 +0000', 60 | 'account_sid': 'AC9d3e7fbe4b0d27fa1b5c60146fcb3bea', 61 | "to": "+44123456789", 62 | "from": "+112345678", 63 | 'body': 'this is a test', 64 | 'status': 'delivered', 65 | 'num_segments': '1', 66 | 'num_media': '0', 67 | 'direction': 'outbound-api', 68 | 'api_version': '2010-04-01', 69 | 'price': '-0.04000', 70 | 'price_unit': 'USD', 71 | 'error_code': null, 72 | 'error_message': null, 73 | 'uri': '/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages/SMb938df8bb21a4b7faf240e5c99e6efbd.json', 74 | 'subresource_uris': { 75 | 'media': '/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages/SMb938df8bb21a4b7faf240e5c99e6efbd/Media.json' 76 | } 77 | }, { 78 | 'sid': 'SMa3a5b04a6bac47be8c972ead9dfe3235', 79 | 'date_created': 'Thu, 12 Jun 2014 15:42:13 +0000', 80 | 'date_updated': 'Thu, 12 Jun 2014 15:42:16 +0000', 81 | 'date_sent': 'Thu, 12 Jun 2014 15:42:13 +0000', 82 | 'account_sid': 'AC9d3e7fbe4b0d27fa1b5c60146fcb3bea', 83 | "to": "+44123456789", 84 | "from": "+112345678", 85 | 'body': 'this is a test', 86 | 'status': 'delivered', 87 | 'num_segments': '1', 88 | 'num_media': '0', 89 | 'direction': 'outbound-api', 90 | 'api_version': '2010-04-01', 91 | 'price': '-0.04000', 92 | 'price_unit': 'USD', 93 | 'error_code': null, 94 | 'error_message': null, 95 | 'uri': '/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages/SMa3a5b04a6bac47be8c972ead9dfe3235.json', 96 | 'subresource_uris': { 97 | 'media': '/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages/SMa3a5b04a6bac47be8c972ead9dfe3235/Media.json' 98 | } 99 | }], 100 | 'uri': '/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages.json?PageSize=50&Page=0', 101 | 'page_size': 50, 102 | 'start': 0, 103 | 'next_page_uri': null, 104 | 'num_pages': 1, 105 | 'total': 2, 106 | 'page': 0 107 | }); 108 | 109 | var smsWriteResponse = jsonEncode({ 110 | "sid": "SMb938df8bb21a4b7faf240e5c99e6efbd", 111 | "date_created": "Sat, 14 Jun 2014 22:56:51 +0000", 112 | "date_updated": "Sat, 14 Jun 2014 22:56:51 +0000", 113 | "date_sent": null, 114 | "account_sid": "AC9d3e7fbe4b0d27fa1b5c60146fcb3bea", 115 | "to": "+44123456789", 116 | "from": "+112345678", 117 | "body": "this is a test", 118 | "status": "queued", 119 | "num_segments": "1", 120 | "num_media": "0", 121 | "direction": "outbound-api", 122 | "api_version": "2010-04-01", 123 | "price": null, 124 | "price_unit": "USD", 125 | "error_code": null, 126 | "error_message": null, 127 | "uri": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages/SMb938df8bb21a4b7faf240e5c99e6efbd.json", 128 | "subresource_uris": { 129 | "media": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages/SMb938df8bb21a4b7faf240e5c99e6efbd/Media.json" 130 | } 131 | }); 132 | 133 | var callListResponse = jsonEncode({ 134 | "first_page_uri": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/.json?PageSize=50&Page=0", 135 | "last_page_uri": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/.json?PageSize=50&Page=0", 136 | "calls": [{ 137 | "sid": "CAcb3d7a3ad6c4195aaaeb882e98eaea31", 138 | "date_created": "Sun, 08 Jun 2014 21:15:06 +0000", 139 | "date_updated": "Sun, 08 Jun 2014 21:15:30 +0000", 140 | "parent_call_sid": null, 141 | "account_sid": "AC9d3e7fbe4b0d27fa1b5c60146fcb3bea", 142 | "to": "+447590566866", 143 | "to_formatted": "+447590566866", 144 | "from": "+441276300056", 145 | "from_formatted": "+441276300056", 146 | "phone_number_sid": "", 147 | "status": "completed", 148 | "start_time": "Sun, 08 Jun 2014 21:15:13 +0000", 149 | "end_time": "Sun, 08 Jun 2014 21:15:30 +0000", 150 | "duration": "17", 151 | "price": "-0.05000", 152 | "price_unit": "USD", 153 | "direction": "outbound-api", 154 | "answered_by": null, 155 | "annotation": null, 156 | "api_version": "2010-04-01", 157 | "forwarded_from": null, 158 | "group_sid": null, 159 | "caller_name": "", 160 | "uri": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/CAcb3d7a3ad6c4195aaaeb882e98eaea31.json", 161 | "subresource_uris": { 162 | "notifications": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/CAcb3d7a3ad6c4195aaaeb882e98eaea31/Notifications.json", 163 | "recordings": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/CAcb3d7a3ad6c4195aaaeb882e98eaea31/Recordings.json" 164 | } 165 | }, { 166 | "sid": "CAe4d9a20aeac207007624ecaca226637c", 167 | "date_created": "Fri, 09 May 2014 17:05:55 +0000", 168 | "date_updated": "Fri, 09 May 2014 17:06:59 +0000", 169 | "parent_call_sid": null, 170 | "account_sid": "AC9d3e7fbe4b0d27fa1b5c60146fcb3bea", 171 | "to": "+447590566866", 172 | "to_formatted": "+447590566866", 173 | "from": "+14157234000", 174 | "from_formatted": "(415) 723-4000", 175 | "phone_number_sid": "", 176 | "status": "completed", 177 | "start_time": "Fri, 09 May 2014 17:06:06 +0000", 178 | "end_time": "Fri, 09 May 2014 17:06:59 +0000", 179 | "duration": "53", 180 | "price": "-0.05000", 181 | "price_unit": "USD", 182 | "direction": "outbound-api", 183 | "answered_by": null, 184 | "annotation": null, 185 | "api_version": "2010-04-01", 186 | "forwarded_from": null, 187 | "group_sid": null, 188 | "caller_name": "", 189 | "uri": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/CAe4d9a20aeac207007624ecaca226637c.json", 190 | "subresource_uris": { 191 | "notifications": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/CAe4d9a20aeac207007624ecaca226637c/Notifications.json", 192 | "recordings": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/CAe4d9a20aeac207007624ecaca226637c/Recordings.json" 193 | } 194 | }, { 195 | "sid": "CAe12f956aefb19b87887d192307986d7d", 196 | "date_created": "Fri, 09 May 2014 17:03:07 +0000", 197 | "date_updated": "Fri, 09 May 2014 17:04:08 +0000", 198 | "parent_call_sid": null, 199 | "account_sid": "AC9d3e7fbe4b0d27fa1b5c60146fcb3bea", 200 | "to": "+447590566866", 201 | "to_formatted": "+447590566866", 202 | "from": "+14157234000", 203 | "from_formatted": "(415) 723-4000", 204 | "phone_number_sid": "", 205 | "status": "completed", 206 | "start_time": "Fri, 09 May 2014 17:03:19 +0000", 207 | "end_time": "Fri, 09 May 2014 17:04:08 +0000", 208 | "duration": "49", 209 | "price": "-0.05000", 210 | "price_unit": "USD", 211 | "direction": "outbound-api", 212 | "answered_by": null, 213 | "annotation": null, 214 | "api_version": "2010-04-01", 215 | "forwarded_from": null, 216 | "group_sid": null, 217 | "caller_name": "", 218 | "uri": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/CAe12f956aefb19b87887d192307986d7d.json", 219 | "subresource_uris": { 220 | "notifications": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/CAe12f956aefb19b87887d192307986d7d/Notifications.json", 221 | "recordings": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/CAe12f956aefb19b87887d192307986d7d/Recordings.json" 222 | } 223 | }], 224 | "previous_page_uri": null, 225 | "end": 2, 226 | "uri": "/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Calls/.json?PageSize=50&Page=0", 227 | "page_size": 50, 228 | "start": 0, 229 | "next_page_uri": null, 230 | "num_pages": 1, 231 | "total": 3, 232 | "page": 0 233 | }); 234 | -------------------------------------------------------------------------------- /test/fixtures/status_messages.dart: -------------------------------------------------------------------------------- 1 | library twilio.test.fixtures.statusMessages; 2 | import 'dart:convert'; 3 | 4 | var message401 = jsonEncode({ 5 | "code": 20003, 6 | "detail": "Your AccountSid or AuthToken was incorrect.", 7 | "message": "Authenticate", 8 | "more_info": "https://www.twilio.com/docs/errors/20003", 9 | "status": 401 10 | }); -------------------------------------------------------------------------------- /test/suite.dart: -------------------------------------------------------------------------------- 1 | library twilio_dart.tests; 2 | import 'twilio_test.dart' as twilioTest; 3 | import 'twilio_message_test.dart' as twilioMessageTest; 4 | import 'twilio_call_test.dart' as twilioCallTest; 5 | import 'utils/utils_test.dart' as utilsTest; 6 | 7 | 8 | void main(){ 9 | print("Twilio API Tests!"); 10 | twilioTest.main(); 11 | twilioMessageTest.main(); 12 | twilioCallTest.main(); 13 | utilsTest.main(); 14 | } -------------------------------------------------------------------------------- /test/twilio_call_test.dart: -------------------------------------------------------------------------------- 1 | library twilio_dart.test.twilio_call_test; 2 | 3 | import 'package:test/test.dart'; 4 | import 'package:twilio_dart/twilio.dart'; 5 | import 'dart:async'; 6 | import 'package:http/http.dart' as http; 7 | import 'fixtures/status_messages.dart'; 8 | import 'fixtures/expected_messages.dart'; 9 | import 'package:http/testing.dart'; 10 | 11 | void main() { 12 | group('Methods :: ', () { 13 | var _accountSid = "AC9d3e7fbe4b0d27fa1b5c60146fcb3bea", 14 | _authToken = "12345", 15 | _apiVersion = "2010-04-01", 16 | _baseUri = "api.twilio.com"; 17 | 18 | group('Calls :: ', () { 19 | group('List Calls :: ', () { 20 | test("Return success", () { 21 | var mockHttpClient = new MockClient((request) { 22 | return new Future.value( 23 | new http.Response(callListResponse, 200, headers: { 24 | 'content-type': 'application/json' 25 | }) 26 | ); 27 | }); 28 | 29 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 30 | Future future; 31 | future = twilio.readCallsList(); 32 | expect(future.then((value) => value.toString()), completion(contains('CAcb3d7a3ad6c4195aaaeb882e98eaea31'))); 33 | }); 34 | 35 | test("Errors with wrong account", () { 36 | var _authToken = "something_that_doesnt_exist"; 37 | var mockHttpClient = new MockClient((request) { 38 | return new Future.value( 39 | new http.Response(message401, 401, headers: { 40 | 'content-type': 'application/json' 41 | }) 42 | ); 43 | }); 44 | 45 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 46 | Future future; 47 | 48 | future = twilio.readCallsList(); 49 | expect(future, throwsArgumentError); 50 | }); 51 | }); 52 | group('Make Call ::', (){ 53 | test("Return success", (){ 54 | 55 | }); 56 | }); 57 | }); 58 | 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /test/twilio_message_test.dart: -------------------------------------------------------------------------------- 1 | library twilio_dart.test.twilio_message_test; 2 | 3 | import 'package:test/test.dart'; 4 | import 'package:twilio_dart/twilio.dart'; 5 | import 'dart:convert'; 6 | import 'dart:async'; 7 | import 'package:http/http.dart' as http; 8 | import 'fixtures/status_messages.dart'; 9 | import 'fixtures/expected_messages.dart'; 10 | import 'package:http/testing.dart'; 11 | 12 | void main() { 13 | group('Methods :: ', () { 14 | var _accountSid = "AC9d3e7fbe4b0d27fa1b5c60146fcb3bea", 15 | _authToken = "12345", 16 | _apiVersion = "2010-04-01", 17 | _baseUri = "api.twilio.com"; 18 | 19 | group('Message :: ', () { 20 | group('Send SMS :: ', () { 21 | test("Send SMS errors with wrong account", () { 22 | 23 | var mockHttpClient = new MockClient((request) { 24 | return new Future.value( 25 | new http.Response(smsWrongAccount, 400, headers: { 26 | 'content-type': 'application/json' 27 | }) 28 | ); 29 | }); 30 | 31 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 32 | Future future; 33 | 34 | future = twilio.sendSMS("+112345678", "+44123456789", "this is a test"); 35 | expect(future, throwsArgumentError); 36 | 37 | }); 38 | 39 | test("Send SMS errors with wrong 'From' number", () { 40 | var mockHttpClient = new MockClient((request) { 41 | return new Future.value( 42 | new http.Response(smsWrongFrom, 400, headers: { 43 | 'content-type': 'application/json' 44 | }) 45 | ); 46 | }); 47 | 48 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 49 | Future future; 50 | 51 | future = twilio.sendSMS("+112345678", "+44123456789", "this is a test"); 52 | expect(future, throwsArgumentError); 53 | 54 | }); 55 | 56 | test("Send SMS not owned by the account", () { 57 | var mockHttpClient = new MockClient((request) { 58 | return new Future.value( 59 | new http.Response(smsWrongFrom, 400, headers: { 60 | 'content-type': 'application/json' 61 | }) 62 | ); 63 | }); 64 | 65 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 66 | Future future; 67 | 68 | future = twilio.sendSMS("+15005550007", "+44123456789", "this is a test"); 69 | expect(future, throwsArgumentError); 70 | }); 71 | 72 | test("Send SMS successfuly sent", () { 73 | var mockHttpClient = new MockClient((request) { 74 | return new Future.value( 75 | new http.Response(smsWriteResponse, 200, headers: { 76 | 'content-type': 'application/json' 77 | }) 78 | ); 79 | }); 80 | 81 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 82 | Future future; 83 | 84 | future = twilio.sendSMS("+441234567890", "+44123456789", "this is a test"); 85 | expect(future.then((value) => jsonDecode(value.toString())), completion(containsValue("SMb938df8bb21a4b7faf240e5c99e6efbd"))); 86 | }); 87 | 88 | test("Send MMS successfuly sent", () { 89 | var mockHttpClient = new MockClient((request) { 90 | return new Future.value( 91 | new http.Response(smsWriteResponse, 200, headers: { 92 | 'content-type': 'application/json' 93 | }) 94 | ); 95 | }); 96 | 97 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 98 | Future future; 99 | 100 | future = twilio.sendSMS("+441234567890", "+44123456789", "this is a test", "http://www.example.com/sample.png"); 101 | expect(future.then((value) => jsonDecode(value.toString())), completion(containsValue("SMb938df8bb21a4b7faf240e5c99e6efbd"))); 102 | }); 103 | }); 104 | 105 | group('Read SMS :: ', () { 106 | var _messageSid = "something_that_doesnt_exist"; 107 | test("Read SMS errors with wrong account", () { 108 | 109 | var mockHttpClient = new MockClient((request) { 110 | return new Future.value( 111 | new http.Response(smsReadWrongAccount, 401, headers: { 112 | 'content-type': 'application/json' 113 | }) 114 | ); 115 | }); 116 | 117 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 118 | Future future; 119 | 120 | future = twilio.readSMS(_messageSid); 121 | expect(future, throwsArgumentError); 122 | }); 123 | 124 | test("Read SMS", () { 125 | var mockHttpClient = new MockClient((request) { 126 | return new Future.value( 127 | new http.Response(smsReadResponse, 200, headers: { 128 | 'content-type': 'application/json' 129 | }) 130 | ); 131 | }); 132 | 133 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 134 | Future future; 135 | 136 | future = twilio.readSMS(_messageSid); 137 | expect(future.then((value) => jsonDecode(value.toString())), completion(containsValue("this is a test"))); 138 | }); 139 | }); 140 | 141 | group('Read SMS List :: ', () { 142 | test("Read SMS errors with wrong account", () { 143 | var mockHttpClient = new MockClient((request) { 144 | return new Future.value( 145 | new http.Response(message401, 401, headers: { 146 | 'content-type': 'application/json' 147 | }) 148 | ); 149 | }); 150 | 151 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 152 | Future future; 153 | 154 | future = twilio.readSMSList(); 155 | expect(future, throwsArgumentError); 156 | }); 157 | 158 | test("SMS List", () { 159 | var mockHttpClient = new MockClient((request) { 160 | return new Future.value( 161 | new http.Response(smsReadListResponse, 200, headers: { 162 | 'content-type': 'application/json' 163 | }) 164 | ); 165 | }); 166 | 167 | var twilio = new Twilio(_accountSid, _authToken, _apiVersion, mockHttpClient); 168 | Future future; 169 | 170 | future = twilio.readSMSList(); 171 | expect(future.then((value) => jsonDecode(value.toString()).toString()), completion(matches("this is a test"))); 172 | }); 173 | }); 174 | 175 | }); 176 | 177 | }); 178 | } 179 | -------------------------------------------------------------------------------- /test/twilio_test.dart: -------------------------------------------------------------------------------- 1 | library twilio_dart.test.twilio_test; 2 | 3 | import 'package:test/test.dart'; 4 | import 'package:twilio_dart/twilio.dart'; 5 | 6 | void main() { 7 | group('Object Setup :: ', () { 8 | var key = "12345"; 9 | var authToken = "12345"; 10 | var version = "2010-04-01"; 11 | 12 | test("New instance of Twilio is created", () { 13 | var twilio = new Twilio(key, authToken, version); 14 | expect(twilio is Twilio, isTrue); 15 | }); 16 | test("New instance of Twilio is created with format specified", () { 17 | var twilio = new Twilio(key, authToken, version); 18 | expect(twilio is Twilio, isTrue); 19 | }); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /test/utils/utils_test.dart: -------------------------------------------------------------------------------- 1 | library twilio_dart.test.utils.utils_test; 2 | 3 | import 'package:test/test.dart'; 4 | import 'package:twilio_dart/utils/utils.dart'; 5 | import 'package:twilio_dart/resources/accounts.dart'; 6 | import 'package:twilio_dart/resources/messages.dart'; 7 | import 'package:http/testing.dart'; 8 | import 'package:http/http.dart' as http; 9 | import 'dart:async'; 10 | 11 | 12 | void main() { 13 | group('URL & HTTP :: ', () { 14 | var _accountSid = "AC9d3e7fbe4b0d27fa1b5c60146fcb3bea", 15 | _authToken = "12345", 16 | _apiVersion = "2010-04-01", 17 | _baseUri = "api.twilio.com"; 18 | Map _auth = new Map(); 19 | _auth['accountSid'] = _accountSid; 20 | _auth['authToken'] = _authToken; 21 | _auth['apiVersion'] = _apiVersion; 22 | _auth['baseUri'] = _baseUri; 23 | 24 | // This is foobar and needs fixing 25 | 26 | group('Http Request :: ', () { 27 | test("Mocked always returns 200", () { 28 | var mockHttpClient = new MockClient((request) { 29 | return new Future.value( 30 | new http.Response("something will always return", 200, headers: { 31 | 'content-type': 'application/json' 32 | }) 33 | ); 34 | }); 35 | var resource = "http://iama200.com"; 36 | Future future = apiRequest(resource, mockHttpClient, _auth); 37 | future.then((value) { 38 | expect(value, equals("something will always return")); 39 | }); 40 | }); 41 | 42 | }); 43 | 44 | 45 | group('Send SMS URL :: ', () { 46 | test("New SMS URL is created correctly", () { 47 | var accounts = new Accounts().resource; 48 | var messages = new Messages(_accountSid); 49 | var url = "https://AC9d3e7fbe4b0d27fa1b5c60146fcb3bea:12345@api.twilio.com/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages.json"; 50 | expect(buildAuthUrl(messages.getPostResource(), _auth), equals(url)); 51 | }); 52 | 53 | }); 54 | 55 | group('Read SMS URL :: ', () { 56 | var _messageSid = "SMb938df8bb21a4b7faf240e5c99e6efbd"; 57 | test("Read SMS URL is created correctly", () { 58 | var messages = new Messages(_accountSid); 59 | var url = "https://AC9d3e7fbe4b0d27fa1b5c60146fcb3bea:12345@api.twilio.com/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages/SMb938df8bb21a4b7faf240e5c99e6efbd.json"; 60 | expect(buildAuthUrl(messages.getGetMessageResource(_messageSid), _auth), equals(url)); 61 | }); 62 | }); 63 | 64 | group('Read SMS List URL :: ', () { 65 | test("Read SMS List URL is created correctly", () { 66 | var messages = new Messages(_accountSid); 67 | var url = "https://AC9d3e7fbe4b0d27fa1b5c60146fcb3bea:12345@api.twilio.com/2010-04-01/Accounts/AC9d3e7fbe4b0d27fa1b5c60146fcb3bea/Messages.json"; 68 | expect(buildAuthUrl(messages.getGetMessageListResource(), _auth), equals(url)); 69 | }); 70 | }); 71 | }); 72 | } 73 | --------------------------------------------------------------------------------