withings-api 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. data/.gitignore +9 -0
  2. data/.simplecov +5 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +7 -0
  5. data/README.rdoc +84 -0
  6. data/Rakefile +14 -0
  7. data/cucumber.yml +2 -0
  8. data/examples/callback_landing.txt +1 -0
  9. data/examples/create_access_token.rb +62 -0
  10. data/features/get_access_token.feature +70 -0
  11. data/features/get_request_token.feature +68 -0
  12. data/features/measure_getmeas_api.feature +16 -0
  13. data/features/step_definitions/api.rb +113 -0
  14. data/features/support/method_mocker.rb +36 -0
  15. data/features/support/world.rb +34 -0
  16. data/lib/withings-api.rb +19 -0
  17. data/lib/withings-api/api_actions.rb +27 -0
  18. data/lib/withings-api/api_response.rb +23 -0
  19. data/lib/withings-api/consts.rb +12 -0
  20. data/lib/withings-api/errors.rb +23 -0
  21. data/lib/withings-api/oauth_actions.rb +94 -0
  22. data/lib/withings-api/oauth_base.rb +121 -0
  23. data/lib/withings-api/query_string.rb +16 -0
  24. data/lib/withings-api/results/measure_getmeas_results.rb +73 -0
  25. data/lib/withings-api/tokens.rb +35 -0
  26. data/lib/withings-api/types.rb +108 -0
  27. data/lib/withings-api/utils.rb +14 -0
  28. data/lib/withings-api/version.rb +5 -0
  29. data/spec/api_actions/measure_getmeas_spec.rb +22 -0
  30. data/spec/measure_getmeas_results_spec.rb +124 -0
  31. data/spec/method_aliaser_spec.rb +96 -0
  32. data/spec/query_string_spec.rb +20 -0
  33. data/spec/spec_helper.rb +30 -0
  34. data/spec/tokens_spec.rb +38 -0
  35. data/spec/types/atttribution_type_spec.rb +15 -0
  36. data/spec/types/category_type_spec.rb +15 -0
  37. data/spec/types/device_type_spec.rb +15 -0
  38. data/spec/types/measurement_type_spec.rb +15 -0
  39. data/spec/withings_api_spec.rb +67 -0
  40. data/test/README +1 -0
  41. data/test/helpers/http_stubber.rb +32 -0
  42. data/test/helpers/method_aliaser.rb +48 -0
  43. data/test/helpers/stubbed_withings_api.rb +41 -0
  44. data/test/http_stub_responses/access_token/invalid.txt +10 -0
  45. data/test/http_stub_responses/access_token/success.txt +11 -0
  46. data/test/http_stub_responses/access_token/unauthorized_token.txt +11 -0
  47. data/test/http_stub_responses/authorization_callback/success.txt +9 -0
  48. data/test/http_stub_responses/measure_getmeas/forbidden.txt +12 -0
  49. data/test/http_stub_responses/measure_getmeas/oauth_error.txt +9 -0
  50. data/test/http_stub_responses/measure_getmeas/success.txt +9 -0
  51. data/test/http_stub_responses/request_token/invalid_consumer_credentials.txt +10 -0
  52. data/test/http_stub_responses/request_token/success.txt +11 -0
  53. data/test/sample_json/measure_getmeas.json +49 -0
  54. data/test/sample_json/notify_get.json +7 -0
  55. data/test/sample_json/notify_list.json +15 -0
  56. data/test/sample_json/notify_revoke.json +3 -0
  57. data/test/sample_json/notify_subscribe.json +3 -0
  58. data/test/sample_json/once_probe.json +1 -0
  59. data/test/sample_json/user_getbyuserid.json +16 -0
  60. data/withings-api.gemspec +32 -0
  61. metadata +220 -0
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "Token" do
4
+ context "Constructor" do
5
+ it "Default" do
6
+ subject.class.new
7
+ end
8
+
9
+ it "Key + Secret" do
10
+ subject.class.new("", "")
11
+ end
12
+ end
13
+
14
+ context "Methods" do
15
+ it "#key getter" do
16
+ subject.should respond_to :key
17
+ end
18
+
19
+ it "#secret getter" do
20
+ subject.should respond_to :secret
21
+ end
22
+ end
23
+ end
24
+
25
+ describe Withings::Api::ConsumerToken do
26
+ it_behaves_like "Token"
27
+
28
+ end
29
+
30
+ describe Withings::Api::RequestToken do
31
+ it_behaves_like "Token"
32
+
33
+ end
34
+
35
+ describe Withings::Api::AccessToken do
36
+ it_behaves_like "Token"
37
+
38
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Withings::Api::AttributionType do
4
+ context "It Should Have Static Fields" do
5
+ subject { Withings::Api::AttributionType }
6
+
7
+ # ensure expected type constants are defined
8
+ [:DEVICE, :DEVICE_AMBIGUOUS, :MANUAL, :MANUAL_AT_CREATION].each do |name|
9
+ it name do
10
+ subject.const_get(name).should be
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Withings::Api::CategoryType do
4
+ context "It Should Have Static Fields" do
5
+ subject { Withings::Api::CategoryType }
6
+
7
+ # ensure expected type constants are defined
8
+ [:MEASURE, :TARGET].each do |name|
9
+ it name do
10
+ subject.const_get(name).should be
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Withings::Api::DeviceType do
4
+ context "It Should Have Static Fields" do
5
+ subject { Withings::Api::DeviceType }
6
+
7
+ # ensure expected type constants are defined
8
+ [:USER, :BODY_SCALE, :BLOOD_PRESSURE_MONITOR].each do |name|
9
+ it name do
10
+ subject.const_get(name).should be
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Withings::Api::MeasurementType do
4
+ context "It Should Have Static Fields" do
5
+ subject { Withings::Api::MeasurementType }
6
+
7
+ # ensure expected type constants are defined
8
+ [:WEIGHT, :HEIGHT, :FAT_MASS, :FAT_FREE_MASS, :FAT_RATIO].each do |name|
9
+ it name do
10
+ subject.const_get(name).should be
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,67 @@
1
+ require "spec_helper"
2
+
3
+ module Withings::Api
4
+ describe Withings::Api do
5
+ subject { Withings::Api }
6
+
7
+ def stub_request_token_success
8
+ stub_http_with_canned("request_token/success")
9
+ end
10
+
11
+ context "create_request_token" do
12
+ before(:each) { stub_request_token_success }
13
+
14
+ it "Takes Three Parameters [consumer key, consumer secret, callback URL]" do
15
+ subject.create_request_token("key", "secret", "callback")
16
+ end
17
+
18
+ it "Takes Two Parameters [ConsumerToken, callback URL]" do
19
+ subject.create_request_token(ConsumerToken.new("key", "secret"), "callback")
20
+ end
21
+
22
+ end
23
+
24
+ # create an example RequestTokenResponse for subsequent tests
25
+
26
+ def example_consumer_token
27
+ ConsumerToken.new("key", "secret")
28
+ end
29
+
30
+ def example_request_token
31
+ RequestToken.new("key", "secret")
32
+ end
33
+
34
+ def create_request_token_response
35
+ stub_request_token_success
36
+ subject.create_request_token(example_consumer_token, "callback")
37
+ end
38
+
39
+ def stub_access_token_success
40
+ stub_http_with_canned("access_token/success")
41
+ end
42
+
43
+ context "create_access_token" do
44
+ before(:each) { stub_access_token_success }
45
+
46
+ it "Takes Parameters - [RequestTokenResponse, user id]" do
47
+ request_token_response = create_request_token_response
48
+ subject.create_access_token(request_token_response, "666")
49
+ end
50
+
51
+ it "Takes Parameters - [RequestToken, ConsumerToken, user id]" do
52
+ subject.create_access_token(example_request_token, example_consumer_token, "666")
53
+ end
54
+ end
55
+
56
+ #it "Can access_token With Deserialized Consumer + Request Tokens" do
57
+ # pending
58
+ #
59
+ # api = Withings::Api
60
+ #
61
+ # request_token = {:key => "", :secret => "", :consumer => {:key => "", :secret => ""}}
62
+ # access_token = {:key => "", :secret => "", :consumer => {:key => "", :secret => ""}}
63
+ #
64
+ # api.create_access_token(request_token, "")
65
+ #end
66
+ end
67
+ end
@@ -0,0 +1 @@
1
+ Test resources common to rspec and cucumber.
@@ -0,0 +1,32 @@
1
+ require_relative "method_aliaser"
2
+
3
+ # stubs Net::HTTP with the provided response text
4
+ # @note The response parameter should be of the form of a HTTP response,
5
+ # including status, headers, and body lines.
6
+ def stub_http_with_string(response_string)
7
+ wrapped = MethodAliaser.alias_it(Net::HTTP, :transport_request) do |aliased, *arguments|
8
+ wrapped.unalias_it
9
+
10
+ http_request = arguments.first
11
+ socket_double = Net::BufferedIO.new(StringIO.new(response_string))
12
+
13
+ # construct the Net::HTTPResponse
14
+ http_response = Net::HTTPResponse.read_new(socket_double)
15
+ http_response.reading_body(socket_double, http_request.response_body_permitted?) {
16
+ yield http_response if block_given?
17
+ }
18
+
19
+ http_response
20
+ end
21
+ end
22
+
23
+ HTTP_STUB_RESPONSES_DIR = File.expand_path("../http_stub_responses", File.dirname(__FILE__))
24
+
25
+ # Stubs Net::HTTP with the specified canned response text file.
26
+ # Canned responses are stored on the file system in *.txt files
27
+ #
28
+ # To stub the HTTP response with an arbitrary string, see {#stub_http_with_string}
29
+ def stub_http_with_canned(canned_response)
30
+ file = File.new(File.join(HTTP_STUB_RESPONSES_DIR, canned_response + ".txt")) # will raise if no file
31
+ stub_http_with_string(file.read)
32
+ end
@@ -0,0 +1,48 @@
1
+ # Tools for aliasing methods of a given target object
2
+ #
3
+ # This tool is currently being used for mocking of classes for
4
+ # RSpec / Cucumber purposes
5
+ module MethodAliaser
6
+ def alias_it(target, source_name, alias_name = nil, &block)
7
+ if !alias_name
8
+ begin
9
+ alias_name = "__#{source_name}_#{rand(99999)}"
10
+ end while target.respond_to?(alias_name)
11
+ end
12
+
13
+ raise(ArgumentError, :existing_method) if target.respond_to?(alias_name)
14
+
15
+ target.instance_exec do
16
+ alias_method alias_name, source_name
17
+
18
+ define_method(source_name) do |*define_arguments|
19
+ block.call( lambda { |*lambda_args| self.send(alias_name, *lambda_args) }, *define_arguments)
20
+ end
21
+ end
22
+
23
+ AliasedMethod.new(target, source_name, alias_name)
24
+ end
25
+
26
+ def unalias_it(target, source_name, alias_name)
27
+ target.instance_exec do
28
+ alias_method source_name, alias_name
29
+ remove_method alias_name
30
+ end
31
+ end
32
+
33
+ extend self
34
+
35
+ class AliasedMethod
36
+ attr_reader :target, :source_name, :alias_name
37
+
38
+ def initialize(target, source_name, alias_name)
39
+ @target = target
40
+ @source_name = source_name
41
+ @alias_name = alias_name
42
+ end
43
+
44
+ def unalias_it
45
+ MethodAliaser.unalias_it(target, source_name, alias_name)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,41 @@
1
+ require "withings-api"
2
+ require_relative "http_stubber"
3
+
4
+ module Withings
5
+ # A method-for-method copy of {Withings::Api} that allows
6
+ # us to stub the HTTP responses for testing purposes (for things we
7
+ # cannot, or don't want to hit the live Withings API for)
8
+ module StubbeddApi
9
+ include Withings::Api
10
+
11
+ extend OAuthActions
12
+ extend ApiActions
13
+
14
+ extend self
15
+
16
+ def stub_http(canned_response)
17
+ stub_http_with_canned(canned_response)
18
+ end
19
+
20
+ end
21
+ end
22
+
23
+ CONSUMER_CREDENTIALS = {
24
+ :valid => Withings::Api::ConsumerToken.new("08943c64c3ccfe86d5edb40c8db6ddbbc43b6f58779c77d2b02454284db7ec", "85949316f07fc41346be145fe8fbc18b73f954f280be958033571720510"),
25
+ :invalid_secret => Withings::Api::ConsumerToken.new("08943c64c3ccfe86d5edb40c8db6ddbbc43b6f58779c77d2b02454284db7ec", "85949316f07fc41346be145fe8fbc18b73f954f280be958033571720511"),
26
+ :blank => Withings::Api::ConsumerToken.new("", ""),
27
+ :random => Withings::Api::ConsumerToken.new("b4d2b4445d7cbd6c344f666ccd08cd00bbfec9957e83b8887de7c7425dc23b", "0f82b1506f781059f14e034503ebef3534c37c9f75118961884b9b5f274"),
28
+ }
29
+
30
+ REQUEST_TOKENS = {
31
+ :valid => Withings::Api::RequestToken.new("d1416462226683d9e5b77f1f4382575c0b86f257d14633d378ef26c90932", "3fef451804934cb0b93f0e1e4f7c4f074230090783899972dc640f9c1216"),
32
+ :random => Withings::Api::RequestToken.new("92d97461313352285c5e64d6947636d6d47260beff022c112787f838bf53", "035891292e709930840cf842d437c43f1feb64c0f9f494100bf677e13c09"),
33
+ }
34
+
35
+ ACCOUNT_CREDENTIALS = {
36
+ :test_user_1 => {:username => "withings_api_user_1@keptmetrics.com", :password => "8X4wDDanxwJ8", :user_id => 766103}
37
+ }
38
+
39
+ ACCESS_TOKENS = {
40
+ :valid => Withings::Api::AccessToken.new("f57fc46b3b2f2dd3d88246e34c4048b0dd8d28cc3cfcfd765bff080960", "83f5563765081cc098b9cf82dd18356b915592147e3df53d33c48cbc295d45"),
41
+ }
@@ -0,0 +1,10 @@
1
+ HTTP/1.0 500 Internal Server Error
2
+ date: Tue, 13 Mar 2012 19:57:47 GMT
3
+ server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.19 with Suhosin-Patch mod_ssl/2.2.8 OpenSSL/0.9.8g
4
+ x-powered-by: PHP/5.2.4-2ubuntu5.19
5
+ set-cookie: lang_select_language=en; expires=Thu, 13-Mar-2014 19:57:47 GMT; path=/
6
+ vary: Accept-Encoding
7
+ content-length: 0
8
+ connection: close
9
+ content-type: text/html; charset=UTF-8
10
+
@@ -0,0 +1,11 @@
1
+ HTTP/1.1 200 OK
2
+ date: Thu, 15 Mar 2012 01:24:47 GMT
3
+ server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.19 with Suhosin-Patch mod_ssl/2.2.8 OpenSSL/0.9.8g
4
+ x-powered-by: PHP/5.2.4-2ubuntu5.19
5
+ set-cookie: lang_select_language=en; expires=Sat, 15-Mar-2014 01:24:47 GMT; path=/
6
+ vary: Accept-Encoding
7
+ content-length: 163
8
+ connection: close
9
+ content-type: text/html; charset=UTF-8
10
+
11
+ oauth_token=9985e279033973577fbc2fe4ef08f96d39409361b4e3f042cf662c28013&oauth_token_secret=2cf15854be933b4f6f750f365bf430998652681619613c877eb0950ee9&userid=766103
@@ -0,0 +1,11 @@
1
+ HTTP/1.1 200 OK
2
+ date: Thu, 15 Mar 2012 01:31:59 GMT
3
+ server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.19 with Suhosin-Patch mod_ssl/2.2.8 OpenSSL/0.9.8g
4
+ x-powered-by: PHP/5.2.4-2ubuntu5.19
5
+ set-cookie: lang_select_language=en; expires=Sat, 15-Mar-2014 01:31:59 GMT; path=/
6
+ vary: Accept-Encoding
7
+ content-length: 19
8
+ connection: close
9
+ content-type: text/html; charset=UTF-8
10
+
11
+ unauthorized token
@@ -0,0 +1,9 @@
1
+ GET /?userid=36686&oauth_token=10b94617e9e052047cf6ec5cff05e6bd7972035e7b8e01256670764bbff7f&oauth_verifier=OBtxR5W7uyPc2 HTTP/1.1
2
+ Host: localhost:9000
3
+ User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
4
+ Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5
+ Accept-Language: en-us,en;q=0.5
6
+ Accept-Encoding: gzip, deflate
7
+ Connection: keep-alive
8
+ Referer: http://oauth.withings.com/account/authorize?acceptDelegation=true&oauth_consumer_key=08943c64c3ccfe86d5edb40c8db6ddbbc43b6f58779c77d2b02454284db7ec&oauth_nonce=d593ada53e0ee1719317ca82d2201383&oauth_signature=GffXCc6y3Dvn2sMdRIBW2wZYW0E%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1331669032&oauth_token=10b94617e9e052047cf6ec5cff05e6bd7972035e7b8e01256670764bbff7f&oauth_version=1.0&userid=36686
9
+
@@ -0,0 +1,12 @@
1
+ HTTP/1.1 403 Forbidden
2
+ Connection: Keep-Alive
3
+ Content-Encoding: gzip
4
+ Content-Length: 38
5
+ Content-Type: text/html; charset=iso-8859-1
6
+ Date: Thu, 29 Mar 2012 01:28:07 GMT
7
+ Keep-Alive: timeout=15, max=100
8
+ Server: Apache/2.2.14 (Ubuntu)
9
+ Vary: Accept-Encoding
10
+ Via: 1.1 proxyapi.withings.net
11
+
12
+ { "status": 2556 }
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 200 OK
2
+ date: Thu, 29 Mar 2012 02:00:22 GMT
3
+ server: Apache/2.2.14 (Ubuntu)
4
+ vary: Accept-Encoding
5
+ content-type: text/plain
6
+ via: 1.1 proxyapi.withings.net
7
+ connection: close
8
+
9
+ {"status":342}
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 200 OK
2
+ date: Thu, 29 Mar 2012 02:01:41 GMT
3
+ server: Apache/2.2.14 (Ubuntu)
4
+ vary: Accept-Encoding
5
+ content-type: text/plain
6
+ via: 1.1 proxyapi.withings.net
7
+ connection: close
8
+
9
+ {"status":0,"body":{"updatetime":1332986501,"measuregrps":[{"grpid":54510546,"attrib":0,"date":1331671174,"category":1,"measures":[{"value":63500,"type":1,"unit":-3}]},{"grpid":54510441,"attrib":2,"date":1331671094,"category":1,"measures":[{"value":680,"type":1,"unit":-1},{"value":183,"type":4,"unit":-2}]},{"grpid":54510987,"attrib":2,"date":1264691627,"category":1,"measures":[{"value":6862,"type":1,"unit":-2},{"value":5733,"type":5,"unit":-2},{"value":16453,"type":6,"unit":-3},{"value":1129,"type":8,"unit":-2}]},{"grpid":54510986,"attrib":2,"date":1264606773,"category":1,"measures":[{"value":6840,"type":1,"unit":-2},{"value":5634,"type":5,"unit":-2},{"value":17632,"type":6,"unit":-3},{"value":1206,"type":8,"unit":-2}]}]}}
@@ -0,0 +1,10 @@
1
+ HTTP/1.0 500 Internal Server Error
2
+ date: Tue, 13 Mar 2012 19:57:47 GMT
3
+ server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.19 with Suhosin-Patch mod_ssl/2.2.8 OpenSSL/0.9.8g
4
+ x-powered-by: PHP/5.2.4-2ubuntu5.19
5
+ set-cookie: lang_select_language=en; expires=Thu, 13-Mar-2014 19:57:47 GMT; path=/
6
+ vary: Accept-Encoding
7
+ content-length: 0
8
+ connection: close
9
+ content-type: text/html; charset=UTF-8
10
+
@@ -0,0 +1,11 @@
1
+ HTTP/1.1 200 OK
2
+ date: Tue, 13 Mar 2012 19:58:24 GMT
3
+ server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.19 with Suhosin-Patch mod_ssl/2.2.8 OpenSSL/0.9.8g
4
+ x-powered-by: PHP/5.2.4-2ubuntu5.19
5
+ set-cookie: lang_select_language=en; expires=Thu, 13-Mar-2014 19:58:24 GMT; path=/
6
+ vary: Accept-Encoding
7
+ content-length: 151
8
+ connection: close
9
+ content-type: text/html; charset=UTF-8
10
+
11
+ oauth_token=d677dcdefbfe1d00d86fcdc033d9046c76e92e611b6392893923c121b&oauth_token_secret=379667e6b9c1899f678677ef846f498184c577569b664b6181e4422ee77d4d
@@ -0,0 +1,49 @@
1
+ {
2
+ "status":0,
3
+ "body":{
4
+ "updatetime":1249409679,
5
+ "measuregrps":[
6
+ {
7
+ "grpid":2909,
8
+ "attrib":0,
9
+ "date":1222930968,
10
+ "category":1,
11
+ "measures":[
12
+ {
13
+ "value":79300,
14
+ "type":1,
15
+ "unit":-3
16
+ },
17
+ {
18
+ "value":652,
19
+ "type":5,
20
+ "unit":-1
21
+ },
22
+ {
23
+ "value":178,
24
+ "type":6,
25
+ "unit":-1
26
+ },
27
+ {
28
+ "value":14125,
29
+ "type":8,
30
+ "unit":-3
31
+ }
32
+ ]
33
+ },
34
+ {
35
+ "grpid":2908,
36
+ "attrib":0,
37
+ "date":1222930968,
38
+ "category":1,
39
+ "measures":[
40
+ {
41
+ "value":173,
42
+ "type":4,
43
+ "unit":-2
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ }
49
+ }