datacatalog 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ coverage
3
+ setup.rb
4
+ datacatalog*.gem
5
+ sandbox_api.yml
data/CHANGES.md ADDED
@@ -0,0 +1,2 @@
1
+ ### 0.1.0 / 2009-08-15
2
+ * Initial version
data/LICENSE.md ADDED
@@ -0,0 +1,11 @@
1
+ Copyright (c) 2009, Sunlight Foundation.
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
+ * Neither the name of Sunlight Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10
+
11
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Ruby Gem for the National Data Catalog API
2
+
3
+ Under heavy development.
4
+
5
+ ## Installation
6
+
7
+ For now, the gem will not be packaged. Instead, install it manually:
8
+
9
+ $ git clone git clone git://github.com/sunlightlabs/ruby-datacatalog.git
10
+ $ cd ruby-datacatalog
11
+ $ gem build datacatalog.gemspec
12
+ $ gem install datacatalog-0.1.0.gem
13
+
14
+ ## Usage
15
+
16
+ require 'rubygems'
17
+ require 'datacatalog'
18
+
19
+ DataCatalog.api_key = 'c40505247a5e308a24d70a0118f76534b543795b'
20
+
21
+ ## Running Specs
22
+
23
+ We're not mocking out any of the web API calls in the specs. Instead, we expect developers who wish to run the specs to download and run a local sandbox instance of the [Data Catalog API](http://github.com/sunlightlabs/datacatalog-api), a Sinatra app:
24
+
25
+ git clone git://github.com/sunlightlabs/datacatalog-api.git
26
+
27
+ Get the app running like any normal Sinatra app, so you can choose to use thin or Passenger or new hotness like [Unicorn](http://unicorn.bogomips.org/). Some special considerations:
28
+
29
+ 1. We recommend creating a `sandbox` entry in `datacatalog-api`'s `config.yml`.
30
+ 2. Run `RACK_ENV=sandbox rake db:ensure_admin` in the `datacatalog-api` project to create a super admin for the API instance.
31
+ 3. Back here in `ruby-datacatalog`, use the example file in `spec/` to create your own `spec/sandbox_api.yml` with the API key of the admin and your local URI.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "datacatalog"
8
+ gem.summary = %Q{Wrapper for the National Data Catalog API}
9
+ gem.description = %Q{Ruby library that wraps the National Data Catalog API}
10
+ gem.email = "luigi@sunlightfoundation.com"
11
+ gem.homepage = "http://github.com/sunlightlabs/datacatalog"
12
+ gem.authors = ["Luigi Montanez", "David James"]
13
+ gem.add_development_dependency "rspec"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ if File.exist?('VERSION')
40
+ version = File.read('VERSION')
41
+ else
42
+ version = ""
43
+ end
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "datacatalog #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,68 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{datacatalog}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Luigi Montanez", "David James"]
12
+ s.date = %q{2009-10-05}
13
+ s.description = %q{Ruby library that wraps the National Data Catalog API}
14
+ s.email = %q{luigi@sunlightfoundation.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.md",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "CHANGES.md",
22
+ "LICENSE.md",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "datacatalog.gemspec",
27
+ "doc/api_key_spec_for_rest_api.txt",
28
+ "doc/api_key_spec_for_ruby_api.rb",
29
+ "doc/mocking_options.md",
30
+ "lib/datacatalog.rb",
31
+ "lib/datacatalog/api_key.rb",
32
+ "lib/datacatalog/base.rb",
33
+ "lib/datacatalog/source.rb",
34
+ "lib/datacatalog/user.rb",
35
+ "sandbox_api.yml.example",
36
+ "spec/api_key_spec.rb",
37
+ "spec/base_spec.rb",
38
+ "spec/source_spec.rb",
39
+ "spec/spec.opts",
40
+ "spec/spec_helper.rb",
41
+ "spec/user_spec.rb"
42
+ ]
43
+ s.homepage = %q{http://github.com/sunlightlabs/datacatalog}
44
+ s.rdoc_options = ["--charset=UTF-8"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.3.5}
47
+ s.summary = %q{Wrapper for the National Data Catalog API}
48
+ s.test_files = [
49
+ "spec/api_key_spec.rb",
50
+ "spec/base_spec.rb",
51
+ "spec/source_spec.rb",
52
+ "spec/spec_helper.rb",
53
+ "spec/user_spec.rb"
54
+ ]
55
+
56
+ if s.respond_to? :specification_version then
57
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
58
+ s.specification_version = 3
59
+
60
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
61
+ s.add_development_dependency(%q<rspec>, [">= 0"])
62
+ else
63
+ s.add_dependency(%q<rspec>, [">= 0"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<rspec>, [">= 0"])
67
+ end
68
+ end
@@ -0,0 +1,54 @@
1
+ A user has a built-in, primary API key.
2
+ A user also has secondary API keys.
3
+
4
+ API keys fall into these categories:
5
+ 1. "primary" API key
6
+ - created and controlled by system
7
+ - cannot be deleted by users
8
+ - used by the Data Catalog Web Application
9
+ - can be rekeyed by user
10
+ 2. application keys
11
+ - created and controlled by users
12
+ - can be rekeyed by user
13
+ - do not provide user-specific credentials:
14
+ - favorites
15
+ - comments
16
+ 3. valet keys
17
+ - created and controlled by users
18
+ - can be rekeyed by user
19
+ - provide user-specific credentials
20
+ - favorites
21
+ - comments
22
+
23
+ GET /users/1/api_keys
24
+ # get all API keys
25
+ :type => :primary
26
+ :type => :application
27
+ :type => :valet
28
+
29
+ GET /users/1/api_keys?type=primary
30
+ # just returns the primary key
31
+
32
+ GET /users/1/api_keys?type=application
33
+ # returns an array of application keys. [] if none.
34
+
35
+ GET /users/1/api_keys?type=valet
36
+ # returns an array of the valet keys. [] if none.
37
+
38
+ POST /users/1/api_keys
39
+ # create a new API key
40
+
41
+ PUT /users/1/api_keys/12345
42
+ # update an existing API key
43
+
44
+ PUT /users/1/api_keys/12345?regenerate_api_key=true
45
+ # rekey an API key
46
+
47
+ DELETE /users/1/api_keys/12345
48
+ # delete an existing API key
49
+ # return 403 Forbidden if attempting to delete a primary key
50
+
51
+ # -----
52
+
53
+ POST /users
54
+ A a primary key API should be generated automatically.
@@ -0,0 +1,26 @@
1
+ # I want to get a user's primary API key
2
+ user = DataCatalog::User.find(user_id)
3
+ user.primary_api_key
4
+ user.application_api_keys
5
+ user.valet_api_keys
6
+
7
+ # I want to generate a new API key for a user
8
+ user.generate_api_key!(:type => "application", :purpose => "To be awesome.") # sets user to the updated object
9
+
10
+ # I want to update an API key
11
+ user.update_api_key!("173f938237a93e9393b365c3",
12
+ :type => "application",
13
+ :purpose => "To be more awesome.") # sets user to the updated object
14
+
15
+ # I want to delete an API key of a user
16
+ user.delete_api_key!("173f938237a93e9393b365c3") # returns: true / exception
17
+
18
+ # I want to get all API keys of a single user
19
+ user.api_keys # returns: array of keys
20
+
21
+ # ^^^^^^^^^^^^ DONE ^^^^^^^^^^^^^^^
22
+
23
+ # vvvvvvvvvv NOT DONE vvvvvvvvvvvvv
24
+
25
+ # Given an API key, I want to see what user it corresponds to, if any.
26
+ DataCatalog::User.find_by_api_key("7a93e93b365ca93e9393b365c3") # returns: User object
@@ -0,0 +1,20 @@
1
+ # Ruby wrapper API mocking options
2
+
3
+ ## 1. Machinist
4
+
5
+ * Use Machinist as an object factory for HTTParty::Response and DataCatalog::* objects.
6
+ * Big question: Can HTTParty::Response object methods be appropriately mocked out by Machinist?
7
+ * Will need to keep objects in sync with the live API
8
+
9
+ ## 2. FakeWeb
10
+
11
+ * Use FakeWeb to fake out the HTTP responses
12
+ * Will need to know the details of the JSON bodies, but HTTParty wraps all that for us.
13
+ * Will need to keep the JSON output (and headers + status codes) in sync with the live API
14
+
15
+ ## 3. Local Sandbox API
16
+
17
+ * Use a locally running sandbox API that mocks out the real API
18
+ * Each spec will need to build up needed objects in the API, then tear them down at the end.
19
+ * Much slower than in-memory solutions above.
20
+ * To keep in sync with live API, just need to refresh API codebase.
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'activesupport'
3
+ require 'httparty'
4
+ require 'mash'
5
+
6
+ module DataCatalog
7
+ mattr_accessor :api_key, :base_uri
8
+ class Error < RuntimeError; end
9
+ class BadRequest < Error; end # 400
10
+ class Unauthorized < Error; end # 401
11
+ class Forbidden < Error; end # 403
12
+ class NotFound < Error; end # 404
13
+ class Conflict < Error; end # 409
14
+ class InternalServerError < Error; end # 500
15
+ class ApiKeyNotConfigured < Error; end
16
+ class CannotDeletePrimaryApiKey < Error; end
17
+
18
+ def self.with_key(temp_key)
19
+ original_key = DataCatalog.api_key
20
+ DataCatalog.api_key = temp_key
21
+ yield
22
+ DataCatalog.api_key = original_key
23
+ end
24
+
25
+ end
26
+
27
+ require "#{File.dirname(__FILE__)}/datacatalog/base.rb"
28
+ Dir["#{File.dirname(__FILE__)}/datacatalog/*.rb"].each { |source_file| require source_file }
@@ -0,0 +1,7 @@
1
+ module DataCatalog
2
+
3
+ class ApiKey < DataCatalog::Base
4
+
5
+ end # class ApiKey
6
+
7
+ end # module DataCatalog
@@ -0,0 +1,68 @@
1
+ module DataCatalog
2
+
3
+ class Base < Mash
4
+
5
+ include HTTParty
6
+ format :json
7
+
8
+ def self.set_base_uri
9
+ default_options[:base_uri] = HTTParty.normalize_base_uri(DataCatalog.base_uri || 'api.nationaldatacatalog.com')
10
+ end
11
+
12
+ def self.set_api_key
13
+ if DataCatalog.api_key.blank?
14
+ raise ApiKeyNotConfigured, "Use DataCatalog.api_key = '...'"
15
+ end
16
+ default_options[:default_params] = {} if default_options[:default_params].nil?
17
+ default_options[:default_params].merge!({ :api_key => DataCatalog.api_key })
18
+ end
19
+
20
+ def self.set_up!
21
+ set_base_uri
22
+ set_api_key
23
+ end
24
+
25
+ def self.check_status_code(response)
26
+ case response.code
27
+ when 400: raise BadRequest, error_message(response)
28
+ when 401: raise Unauthorized, error_message(response)
29
+ when 403: raise Forbidden, error_message(response)
30
+ when 404: raise NotFound, error_message(response)
31
+ when 409: raise Conflict, error_message(response)
32
+ when 500: raise InternalServerError, error_message(response)
33
+ end
34
+ end
35
+
36
+ def self.error_message(response)
37
+ parsed_body = JSON.parse(response.body)
38
+ if parsed_body.empty?
39
+ "Response was empty"
40
+ elsif parsed_body["errors"]
41
+ parsed_body["errors"].inspect
42
+ else
43
+ response.body
44
+ end
45
+ rescue JSON::ParserError
46
+ "Unable to parse: #{response.body.inspect}"
47
+ end
48
+
49
+ def self.response_for
50
+ response = yield
51
+ check_status_code(response)
52
+ response
53
+ end
54
+
55
+ def self.build_object(response)
56
+ return nil if response.nil? || response.empty?
57
+ new(response)
58
+ end
59
+
60
+ def self.about
61
+ default_options[:default_params] = {}
62
+ set_base_uri
63
+ build_object(response_for { get('/') })
64
+ end
65
+
66
+ end # class Base
67
+
68
+ end # module DataCatalog
@@ -0,0 +1,30 @@
1
+ module DataCatalog
2
+
3
+ class Source < DataCatalog::Base
4
+
5
+ def self.create(params={})
6
+ set_up!
7
+ build_object(response_for { post("/sources", :query => params) })
8
+ end
9
+
10
+ def self.all
11
+ set_up!
12
+ response_for{ get("/sources") }.map do |source|
13
+ build_object(source)
14
+ end
15
+ end
16
+
17
+ def self.update(source_id, params={})
18
+ set_up!
19
+ build_object(response_for { put("/sources/#{source_id}", :query => params) })
20
+ end
21
+
22
+ def self.destroy(source_id)
23
+ set_up!
24
+ response = response_for { delete("/sources/#{source_id}") }
25
+ true
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,94 @@
1
+ module DataCatalog
2
+
3
+ class User < DataCatalog::Base
4
+
5
+ def self.all
6
+ set_up!
7
+ response_for{ get("/users") }.map do |user|
8
+ build_object(user)
9
+ end
10
+ end
11
+
12
+ def self.find(id)
13
+ set_up!
14
+ user = build_object(response_for { get("/users/#{id}") })
15
+ user.api_keys = response_for { get("/users/#{id}/keys") }.map do |key|
16
+ DataCatalog::ApiKey.build_object(key)
17
+ end if user
18
+ user
19
+ end
20
+
21
+ def self.find_by_api_key(api_key)
22
+ user = nil
23
+ DataCatalog.with_key(api_key) do
24
+ set_up!
25
+ checkup = build_object(response_for { get("/checkup") })
26
+ user = find(checkup.user_id)
27
+ end
28
+ user
29
+ end
30
+
31
+ def self.create(params={})
32
+ set_up!
33
+ user = build_object(response_for { post("/users", :query => params) })
34
+ user.api_keys = response_for { get("/users/#{user.id}/keys") }.map do |key|
35
+ DataCatalog::ApiKey.build_object(key)
36
+ end if user
37
+ user
38
+ end
39
+
40
+ def self.update(user_id, params)
41
+ set_up!
42
+ build_object(response_for { put("/users/#{user_id}", :query => params) })
43
+ end
44
+
45
+ def self.destroy(user_id)
46
+ set_up!
47
+ response = response_for { delete("/users/#{user_id}") }
48
+ true
49
+ end
50
+
51
+ def generate_api_key!(params)
52
+ self.class.set_up!
53
+
54
+ response = self.class.response_for do
55
+ self.class.post("/users/#{self.id}/keys", :query => params )
56
+ end
57
+ update_api_keys
58
+ true
59
+ end
60
+
61
+ def delete_api_key!(api_key_id)
62
+ self.class.set_up!
63
+
64
+ response = self.class.response_for do
65
+ self.class.delete("/users/#{self.id}/keys/#{api_key_id}")
66
+ end
67
+ update_api_keys
68
+ true
69
+ end
70
+
71
+ def update_api_key!(api_key_id, params)
72
+ self.class.set_up!
73
+
74
+ response = self.class.response_for do
75
+ self.class.put("/users/#{self.id}/keys/#{api_key_id}", :query => params)
76
+ end
77
+ update_api_keys
78
+ true
79
+ end
80
+
81
+ private
82
+
83
+ def update_api_keys
84
+ self.api_keys = self.class.response_for { self.class.get("/users/#{self.id}/keys") }.map do |key|
85
+ DataCatalog::ApiKey.build_object(key)
86
+ end
87
+ updated_user = DataCatalog::User.find(self.id)
88
+ self.application_api_keys = updated_user.application_api_keys
89
+ self.valet_api_keys = updated_user.valet_api_keys
90
+ end
91
+
92
+ end # class User
93
+
94
+ end # module DataCatalog
@@ -0,0 +1,2 @@
1
+ api_key: d193d1523d49e4caf46297ef94d7a2c995d3b2cb # generate with rake db:ensure_admin on datacatalog-api
2
+ base_uri: sandbox.dc-api.local
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
data/spec/base_spec.rb ADDED
@@ -0,0 +1,184 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe DataCatalog do
4
+
5
+ describe "module accessors" do
6
+
7
+ it "should access the API Key" do
8
+ DataCatalog.api_key = 'flurfeneugen'
9
+ DataCatalog.api_key.should == 'flurfeneugen'
10
+ end
11
+
12
+ it "should access the base URI" do
13
+ DataCatalog.base_uri = 'somehost.com'
14
+ DataCatalog.base_uri.should == 'somehost.com'
15
+ end
16
+
17
+ end # describe "accessors"
18
+
19
+ describe ".with_key" do
20
+
21
+ it "should set the API key within the block" do
22
+ DataCatalog.api_key = 'flurfeneugen'
23
+ temp_value = 'not_temp_key'
24
+
25
+ DataCatalog.with_key('temp_key') do
26
+ temp_value = DataCatalog.api_key
27
+ end
28
+
29
+ temp_value.should == 'temp_key'
30
+ DataCatalog.api_key.should == 'flurfeneugen'
31
+ end
32
+
33
+ end # describe ".with_key"
34
+
35
+ end # describe DataCatalog
36
+
37
+ describe DataCatalog::Base do
38
+
39
+ before(:each) { setup_api }
40
+
41
+ describe ".set_base_uri" do
42
+
43
+ it "should set and normalize the base URI" do
44
+ DataCatalog.base_uri = 'notherhost.com'
45
+ DataCatalog::Base.set_base_uri
46
+ DataCatalog::Base.default_options[:base_uri].should == 'http://notherhost.com'
47
+ end
48
+
49
+ it "should set the base URI to the default if it's not explicitly defined" do
50
+ DataCatalog.base_uri = nil
51
+ DataCatalog::Base.set_base_uri
52
+ DataCatalog::Base.default_options[:base_uri].should == 'http://api.nationaldatacatalog.com'
53
+ end
54
+
55
+ end # describe ".set_base_uri"
56
+
57
+ describe ".set_api_key" do
58
+
59
+ it "should set the API key" do
60
+ DataCatalog.api_key = 'flurfeneugen'
61
+ DataCatalog::Base.set_api_key
62
+ DataCatalog::Base.default_options[:default_params].should include(:api_key => 'flurfeneugen')
63
+ end
64
+
65
+ it "should raise exception when attempting to set the API key but none is set" do
66
+ DataCatalog.api_key = nil
67
+ executing do
68
+ DataCatalog::Base.set_api_key
69
+ end.should raise_error(DataCatalog::ApiKeyNotConfigured)
70
+ end
71
+
72
+ end # describe ".set_default_params"
73
+
74
+ describe ".set_up!" do
75
+
76
+ it "should set both the base URI and API key" do
77
+ DataCatalog.base_uri = 'somehost.com'
78
+ DataCatalog.api_key = 'flurfeneugen'
79
+ DataCatalog::Base.set_up!
80
+ DataCatalog::Base.default_options[:base_uri].should == 'http://somehost.com'
81
+ DataCatalog::Base.default_options[:default_params].should include(:api_key => 'flurfeneugen')
82
+ end
83
+
84
+ end # describe ".set_up!"
85
+
86
+ describe ".build_object" do
87
+
88
+ it "should create an object when a filled hash is passed in" do
89
+ base_object = DataCatalog::Base.build_object(:name => "John Smith", :email => "john@email.com")
90
+ base_object.should be_an_instance_of(DataCatalog::Base)
91
+ base_object.email.should == "john@email.com"
92
+ end
93
+
94
+ it "should return nil when an empty hash is passed in" do
95
+ base_object = DataCatalog::Base.build_object({})
96
+ base_object.should be_nil
97
+ end
98
+
99
+ it "should return nil when nil is passed in" do
100
+ base_object = DataCatalog::Base.build_object(nil)
101
+ base_object.should be_nil
102
+ end
103
+
104
+ end # describe ".build_object!"
105
+
106
+ describe ".about" do
107
+
108
+ it "should return information about the API" do
109
+ base_object = DataCatalog::Base.about
110
+ base_object.should be_an_instance_of(DataCatalog::Base)
111
+ base_object.name.should == "National Data Catalog API"
112
+ end
113
+
114
+ end # describe ".about"
115
+
116
+ describe ".check_status_code" do
117
+
118
+ it "should return nil on 200 OK" do
119
+ response = HTTParty::Response.new(nil, '{"foo":"bar"}', 200, 'OK', {})
120
+ DataCatalog::Base.check_status_code(response).should be_nil
121
+ end
122
+
123
+ it "should raise BadRequest on 400 Bad Request" do
124
+ response = HTTParty::Response.new(nil, '[]', 400, 'Bad Request', {})
125
+ executing do
126
+ DataCatalog::Base.check_status_code(response)
127
+ end.should raise_error(DataCatalog::BadRequest)
128
+ end
129
+
130
+ it "should raise Unauthorized on 401 Unauthorized" do
131
+ response = HTTParty::Response.new(nil, '', 401, 'Unauthorized', {})
132
+ executing do
133
+ DataCatalog::Base.check_status_code(response)
134
+ end.should raise_error(DataCatalog::Unauthorized)
135
+ end
136
+
137
+ it "should raise NotFound on 404 Not Found" do
138
+ response = HTTParty::Response.new(nil, '[]', 404, 'Not Found', {})
139
+ executing do
140
+ DataCatalog::Base.check_status_code(response)
141
+ end.should raise_error(DataCatalog::NotFound)
142
+ end
143
+
144
+ it "should raise InternalServerError on 500 Internal Server Error" do
145
+ response = HTTParty::Response.new(nil, '', 500, 'Internal Server Error', {})
146
+ executing do
147
+ DataCatalog::Base.check_status_code(response)
148
+ end.should raise_error(DataCatalog::InternalServerError)
149
+ end
150
+
151
+ end # describe ".check_status_code"
152
+
153
+ describe ".error_message" do
154
+
155
+ it "should return an 'Unable to parse:' message when body is blank" do
156
+ response = HTTParty::Response.new(nil, '', 404, 'Not Found', {})
157
+ DataCatalog::Base.error_message(response).should == %(Unable to parse: "")
158
+ end
159
+
160
+ it "should return 'Response was empty' when body is an empty JSON object" do
161
+ response = HTTParty::Response.new(nil, '{}', 404, 'Not Found', {})
162
+ DataCatalog::Base.error_message(response).should == "Response was empty"
163
+ end
164
+
165
+ it "should return 'Response was empty' when body is an empty array" do
166
+ response = HTTParty::Response.new(nil, '[]', 404, 'Not Found', {})
167
+ DataCatalog::Base.error_message(response).should == "Response was empty"
168
+ end
169
+
170
+ it "should return the contents of the errors hash when it exists" do
171
+ errors = '{"errors":["bad_error"]}'
172
+ response = HTTParty::Response.new(nil, errors, 400, 'Bad Request', {})
173
+ DataCatalog::Base.error_message(response).should == '["bad_error"]'
174
+ end
175
+
176
+ it "should return the contents of the response body when no errors hash exists" do
177
+ errors = '{"foo":["bar"]}'
178
+ response = HTTParty::Response.new(nil, errors, 400, 'Bad Request', {})
179
+ DataCatalog::Base.error_message(response).should == '{"foo":["bar"]}'
180
+ end
181
+
182
+ end # describe ".error_message"
183
+
184
+ end # describe DataCatalog::Base
@@ -0,0 +1,101 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe DataCatalog::Source do
4
+
5
+ def create_source(params={})
6
+ valid_params = {
7
+ :title => "Some FCC Data",
8
+ :url => "http://fcc.gov/somedata.csv"
9
+ }
10
+ DataCatalog::Source.create(valid_params.merge(params))
11
+ end
12
+
13
+ before(:each) do
14
+ setup_api
15
+ clean_slate
16
+ end
17
+
18
+ describe ".all" do
19
+ before do
20
+ %w(FCC NASA DOE).each do |name|
21
+ DataCatalog::Source.create({
22
+ :title => "#{name} Data",
23
+ :url => "http://#{name.downcase}.gov/data.xml"
24
+ })
25
+ end
26
+ @sources = DataCatalog::Source.all
27
+ end
28
+
29
+ it "should return an enumeration of sources" do
30
+ @sources.each do |source|
31
+ source.should be_an_instance_of(DataCatalog::Source)
32
+ end
33
+ end
34
+
35
+ it "should return correct titles" do
36
+ expected = ["FCC Data", "NASA Data", "DOE Data"]
37
+ @sources.map(&:title).sort.should == expected.sort
38
+ end
39
+ end # describe ".all"
40
+
41
+ describe ".create" do
42
+ it "should create a new source from basic params" do
43
+ source = create_source
44
+ source.should be_an_instance_of(DataCatalog::Source)
45
+ source.url.should == "http://fcc.gov/somedata.csv"
46
+ end
47
+
48
+ it "should create a new source from custom params" do
49
+ source = create_source({ :custom => {
50
+ "0" => {
51
+ :label => "License",
52
+ :description => "License",
53
+ :type => "string",
54
+ :value => "Public Domain"
55
+ }
56
+ }})
57
+ source.should be_an_instance_of(DataCatalog::Source)
58
+ source.url.should == "http://fcc.gov/somedata.csv"
59
+ source.custom.should == {
60
+ "0" => {
61
+ "label" => "License",
62
+ "description" => "License",
63
+ "type" => "string",
64
+ "value" => "Public Domain"
65
+ }
66
+ }
67
+ end
68
+ end # describe ".all"
69
+
70
+ describe ".update" do
71
+ before do
72
+ @source = create_source
73
+ end
74
+
75
+ it "should update an existing source from valid params" do
76
+ source = DataCatalog::Source.update(@source.id, {
77
+ :url => "http://fec.gov/newdata.csv"
78
+ })
79
+ source.should be_an_instance_of(DataCatalog::Source)
80
+ source.url.should == "http://fec.gov/newdata.csv"
81
+ end
82
+ end # describe ".all"
83
+
84
+ describe ".destroy" do
85
+ before do
86
+ @source = create_source
87
+ end
88
+
89
+ it "should destroy an existing source" do
90
+ result = DataCatalog::Source.destroy(@source.id)
91
+ result.should be_true
92
+ end
93
+
94
+ it "should raise NotFound when attempting to destroy non-existing source" do
95
+ executing do
96
+ DataCatalog::Source.destroy(mangle(@source.id))
97
+ end.should raise_error(DataCatalog::NotFound)
98
+ end
99
+ end # describe ".destroy"
100
+
101
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --timeout
3
+ 20
4
+ --diff
@@ -0,0 +1,48 @@
1
+ require File.dirname(__FILE__) + '/../lib/datacatalog'
2
+ require 'yaml'
3
+
4
+ Spec::Runner.configure do |config|
5
+ config.mock_with :rr
6
+ end
7
+
8
+ alias :executing :lambda
9
+
10
+ def setup_api
11
+ config = YAML.load_file(File.dirname(__FILE__) + '/../sandbox_api.yml')
12
+ DataCatalog.api_key = config['api_key']
13
+ DataCatalog.base_uri = config['base_uri']
14
+ end
15
+
16
+ def clean_slate
17
+ DataCatalog::User.all.each do |u|
18
+ DataCatalog::User.destroy(u.id) unless u.name == "Primary Admin"
19
+ end
20
+ DataCatalog::Source.all.each do |s|
21
+ DataCatalog::Source.destroy(s.id)
22
+ end
23
+ end
24
+
25
+ if RUBY_VERSION >= "1.8.7"
26
+ # Converts a valid ID into a almost-but-not-quite valid one.
27
+ # Here is an example of what it does:
28
+ # From ... 4ac2520b25b7e7056600034e
29
+ # To ... a42c25b0527b7e50660030e4
30
+ def mangle(string)
31
+ array = string.chars.to_a
32
+ sliced = []
33
+ array.each_slice(2) { |s| sliced << s.reverse }
34
+ result = sliced.flatten.join
35
+ raise "mangle failed" if result == string
36
+ result
37
+ end
38
+ else
39
+ # Converts a valid ID into a almost-but-not-quite valid one.
40
+ # Here is an example of what it does:
41
+ # From ... 4ac2520b25b7e7056600034e
42
+ # To ... e4300066507e7b52b0252ca4
43
+ def mangle(string)
44
+ result = string.reverse
45
+ raise "mangle failed" if result == string
46
+ result
47
+ end
48
+ end
data/spec/user_spec.rb ADDED
@@ -0,0 +1,208 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe DataCatalog::User do
4
+
5
+ def create_user
6
+ DataCatalog::User.create({
7
+ :name => "Ted Smith",
8
+ :email => "ted@email.com"
9
+ })
10
+ end
11
+
12
+ def create_user_with_2_keys
13
+ user = create_user
14
+ result = user.generate_api_key!(
15
+ :purpose => "Civic hacking with my awesome app",
16
+ :key_type => "application"
17
+ )
18
+ raise "generate_api_key! failed" unless result
19
+ raise "incorrect number of keys" unless user.api_keys.length == 2
20
+ user
21
+ end
22
+
23
+ before(:each) do
24
+ setup_api
25
+ clean_slate
26
+ end
27
+
28
+ describe ".all" do
29
+ before(:each) do
30
+ 3.times do |n|
31
+ DataCatalog::User.create(
32
+ :name => "User-#{n}",
33
+ :email => "user_#{n}@email.com"
34
+ )
35
+ end
36
+ @users = DataCatalog::User.all
37
+ end
38
+
39
+ it "should return an enumeration of users" do
40
+ @users.each do |u|
41
+ u.should be_an_instance_of(DataCatalog::User)
42
+ end
43
+ end
44
+
45
+ it "should include four users" do
46
+ names = @users.map(&:name)
47
+ names.should include("User-0")
48
+ names.should include("User-1")
49
+ names.should include("User-2")
50
+ end
51
+ end # describe ".all"
52
+
53
+ describe ".create" do
54
+ before do
55
+ @user = create_user
56
+ end
57
+
58
+ it "should create a new user when valid params are passed in" do
59
+ @user.should be_an_instance_of(DataCatalog::User)
60
+ @user.name.should == "Ted Smith"
61
+ @user.email.should == "ted@email.com"
62
+ end
63
+
64
+ it "should raise BadRequest when invalid params are passed in" do
65
+ executing do
66
+ DataCatalog::User.create({ :garbage_field => "junk" })
67
+ end.should raise_error(DataCatalog::BadRequest)
68
+ end
69
+ end # describe ".create"
70
+
71
+ describe ".find" do
72
+ before do
73
+ @user = create_user
74
+ end
75
+
76
+ it "should return a user" do
77
+ user = DataCatalog::User.find(@user.id)
78
+ user.should be_an_instance_of(DataCatalog::User)
79
+ user.email.should == "ted@email.com"
80
+ end
81
+
82
+ it "should raise NotFound out if no user exists" do
83
+ executing do
84
+ DataCatalog::User.find(mangle(@user.id))
85
+ end.should raise_error(DataCatalog::NotFound)
86
+ end
87
+ end # describe ".find"
88
+
89
+ describe ".find_by_api_key" do
90
+ before do
91
+ @user = create_user
92
+ end
93
+
94
+ it "should return a user" do
95
+ user = DataCatalog::User.find_by_api_key(@user.primary_api_key)
96
+ user.should be_an_instance_of(DataCatalog::User)
97
+ user.email.should == "ted@email.com"
98
+ end
99
+ end # describe ".find_by_api_key"
100
+
101
+ describe ".update" do
102
+ before do
103
+ @user = create_user
104
+ end
105
+
106
+ it "should update a user when valid params are passed in" do
107
+ user = DataCatalog::User.update(@user.id, { :name => "Jane Smith" })
108
+ user.name.should == "Jane Smith"
109
+ end
110
+
111
+ it "should raise BadRequest when invalid params are passed in" do
112
+ executing do
113
+ DataCatalog::User.update(@user.id, { :garbage => "junk" })
114
+ end.should raise_error(DataCatalog::BadRequest)
115
+ end
116
+
117
+ end # describe ".update"
118
+
119
+ describe ".destroy" do
120
+ before do
121
+ @user = create_user
122
+ end
123
+
124
+ it "should destroy an existing user" do
125
+ result = DataCatalog::User.destroy(@user.id)
126
+ result.should be_true
127
+ end
128
+
129
+ it "should raise NotFound when non-existing user" do
130
+ executing do
131
+ DataCatalog::User.destroy(mangle(@user.id))
132
+ end.should raise_error(DataCatalog::NotFound)
133
+ end
134
+ end # describe ".destroy"
135
+
136
+ describe "#generate_api_key!" do
137
+ before do
138
+ @user = create_user
139
+ end
140
+
141
+ it "should generate a new key for the user" do
142
+ @user.api_keys.length.should == 1
143
+ @user.generate_api_key!({
144
+ :purpose => "Civic hacking with my awesome app",
145
+ :key_type => "application"
146
+ }).should be_true
147
+ @user.api_keys.length.should == 2
148
+ @user.api_keys.last[:purpose].should == "Civic hacking with my awesome app"
149
+ @user.application_api_keys.length.should == 1
150
+ end
151
+
152
+ it "should raise BadRequest when attempting to create a primary key" do
153
+ executing do
154
+ @user.generate_api_key!({
155
+ :purpose => "Civic hacking with my awesome app",
156
+ :key_type => "primary"
157
+ })
158
+ end.should raise_error(DataCatalog::BadRequest)
159
+ end
160
+ end # describe "#generate_api_key!"
161
+
162
+ describe "#update_api_key!" do
163
+ before do
164
+ @user = create_user_with_2_keys
165
+ end
166
+
167
+ it "should update a key for the user" do
168
+ @user.update_api_key!(@user.api_keys[1].id, {
169
+ :key_type => "valet",
170
+ :purpose => "To be more awesome"
171
+ }).should be_true
172
+ @user.api_keys[1].purpose.should == "To be more awesome"
173
+ end
174
+
175
+ it "should raise NotFound if updating a key that doesn't exist" do
176
+ executing do
177
+ @user.update_api_key!(mangle(@user.api_keys[1].id), {})
178
+ end.should raise_error(DataCatalog::NotFound)
179
+ end
180
+
181
+ it "should raise BadRequest if primary key's type is changed" do
182
+ executing do
183
+ @user.update_api_key!(@user.api_keys[0].id, {
184
+ :key_type => "valet"
185
+ })
186
+ end.should raise_error(DataCatalog::BadRequest)
187
+ end
188
+ end # describe "#update_api_key!"
189
+
190
+ describe "#delete_api_key!" do
191
+ before do
192
+ @user = create_user_with_2_keys
193
+ end
194
+
195
+ it "should delete a key for the user" do
196
+ @user.delete_api_key!(@user.api_keys[1].id).should be_true
197
+ @user.api_keys.length.should == 1
198
+ end
199
+
200
+ it "should raise Conflict if deleting the primary key" do
201
+ executing do
202
+ @user.delete_api_key!(@user.api_keys[0].id)
203
+ end.should raise_error(DataCatalog::Conflict)
204
+ @user.api_keys.length.should == 2
205
+ end
206
+ end # describe "#delete_api_key!"
207
+
208
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: datacatalog
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Luigi Montanez
8
+ - David James
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-10-05 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ type: :development
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ version:
26
+ description: Ruby library that wraps the National Data Catalog API
27
+ email: luigi@sunlightfoundation.com
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - LICENSE.md
34
+ - README.md
35
+ files:
36
+ - .gitignore
37
+ - CHANGES.md
38
+ - LICENSE.md
39
+ - README.md
40
+ - Rakefile
41
+ - VERSION
42
+ - datacatalog.gemspec
43
+ - doc/api_key_spec_for_rest_api.txt
44
+ - doc/api_key_spec_for_ruby_api.rb
45
+ - doc/mocking_options.md
46
+ - lib/datacatalog.rb
47
+ - lib/datacatalog/api_key.rb
48
+ - lib/datacatalog/base.rb
49
+ - lib/datacatalog/source.rb
50
+ - lib/datacatalog/user.rb
51
+ - sandbox_api.yml.example
52
+ - spec/api_key_spec.rb
53
+ - spec/base_spec.rb
54
+ - spec/source_spec.rb
55
+ - spec/spec.opts
56
+ - spec/spec_helper.rb
57
+ - spec/user_spec.rb
58
+ has_rdoc: true
59
+ homepage: http://github.com/sunlightlabs/datacatalog
60
+ licenses: []
61
+
62
+ post_install_message:
63
+ rdoc_options:
64
+ - --charset=UTF-8
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.5
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Wrapper for the National Data Catalog API
86
+ test_files:
87
+ - spec/api_key_spec.rb
88
+ - spec/base_spec.rb
89
+ - spec/source_spec.rb
90
+ - spec/spec_helper.rb
91
+ - spec/user_spec.rb