tracker_api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +12 -0
  4. data/CHANGELOG.md +3 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +57 -0
  8. data/Rakefile +18 -0
  9. data/lib/tracker_api.rb +39 -0
  10. data/lib/tracker_api/client.rb +71 -0
  11. data/lib/tracker_api/endpoints/epic.rb +20 -0
  12. data/lib/tracker_api/endpoints/epics.rb +22 -0
  13. data/lib/tracker_api/endpoints/iterations.rb +22 -0
  14. data/lib/tracker_api/endpoints/project.rb +21 -0
  15. data/lib/tracker_api/endpoints/projects.rb +22 -0
  16. data/lib/tracker_api/endpoints/stories.rb +22 -0
  17. data/lib/tracker_api/endpoints/story.rb +20 -0
  18. data/lib/tracker_api/error.rb +18 -0
  19. data/lib/tracker_api/logger.rb +31 -0
  20. data/lib/tracker_api/resources/account.rb +18 -0
  21. data/lib/tracker_api/resources/epic.rb +19 -0
  22. data/lib/tracker_api/resources/iteration.rb +23 -0
  23. data/lib/tracker_api/resources/label.rb +14 -0
  24. data/lib/tracker_api/resources/project.rb +85 -0
  25. data/lib/tracker_api/resources/story.rb +33 -0
  26. data/lib/tracker_api/resources/time_zone.rb +13 -0
  27. data/lib/tracker_api/version.rb +3 -0
  28. data/test/client_test.rb +61 -0
  29. data/test/minitest_helper.rb +30 -0
  30. data/test/project_test.rb +81 -0
  31. data/test/vcr/cassettes/get_all_projects.json +1 -0
  32. data/test/vcr/cassettes/get_current_iteration.json +1 -0
  33. data/test/vcr/cassettes/get_done_iterations.json +1 -0
  34. data/test/vcr/cassettes/get_epics.json +1 -0
  35. data/test/vcr/cassettes/get_project.json +1 -0
  36. data/test/vcr/cassettes/get_project_with_epics.json +1 -0
  37. data/test/vcr/cassettes/get_unscheduled_stories.json +1 -0
  38. data/tracker_api.gemspec +36 -0
  39. metadata +273 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8407c876c55351601c4fd2648ced15f1af1336b3
4
+ data.tar.gz: 983fcb13633fd259089a96eab64accc1ea49a777
5
+ SHA512:
6
+ metadata.gz: d5cb62b0d1e08ea319e1b88281216a5874e13c8d431f391b43002d46571db29d210cd8294ed2cea1ff58608814145a4092ded83d527d84584a6fc0736b301f6c
7
+ data.tar.gz: 777c67421852258ef4a05e0165f4aa6de55c9ea207b03966fb383ba6f520c34cec821107f0dd2e9f14a3f5553fed7149cd57959940ea2a970c06f2fd9cefda9d
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .idea
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ - "2.1.0"
6
+ # Faraday::ConnectionFailed: Could not generate DH keypair (Java::JavaLang::RuntimeException)
7
+ # http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7044060
8
+ # - jruby-19mode # JRuby in 1.9 mode
9
+ - rbx
10
+ # - "1.8.7"
11
+ # uncomment this line if your project needs to run something other than `rake`:
12
+ # script: bundle exec rspec spec
@@ -0,0 +1,3 @@
1
+ 0.1.0
2
+ ---
3
+ - Initial release.
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tracker_api.gemspec
4
+ gemspec
5
+
6
+ gem 'coveralls', group: :test, require: false
7
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Dash of Code
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,57 @@
1
+ # TrackerApi
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/tracker_api.png)](http://badge.fury.io/rb/tracker_api)
4
+ [![Build Status](https://travis-ci.org/dashofcode/tracker_api.png?branch=master)](https://travis-ci.org/dashofcode/tracker_api)
5
+ [![Code Climate](https://codeclimate.com/github/dashofcode/tracker_api.png)](https://codeclimate.com/github/dashofcode/tracker_api)
6
+ [![Coverage Status](https://coveralls.io/repos/dashofcode/tracker_api/badge.png?branch=master)](https://coveralls.io/r/dashofcode/tracker_api?branch=master)
7
+ [![Dependency Status](https://gemnasium.com/dashofcode/tracker_api.png)](https://gemnasium.com/dashofcode/tracker_api)
8
+
9
+ This gem allows you to easily use the [Pivotal Tracker v5 API](https://www.pivotaltracker.com/help/api/rest/v5).
10
+
11
+ It’s powered by [Faraday](https://github.com/lostisland/faraday) and [Virtus](https://github.com/solnic/virtus).
12
+
13
+
14
+ ## Basic Usage
15
+
16
+ ```ruby
17
+ client = TrackerApi::Client.new(token: 'my-api-token') # Create API client
18
+
19
+ projects = client.projects # Get all projects
20
+ project = client.project(123456) # Find project with given ID
21
+
22
+ project.stories # Get all stories for a project
23
+ project.stories(with_state: :unscheduled, limit: 10) # Get 10 unscheduled stories for a project
24
+ project.stories(filter: 'requester:OWK label:"jedi stuff"') # Get all stories that match the given filters
25
+ project.story(847762630) # Find a story with the given ID
26
+
27
+ epics = project.epics # Get all epics for a project
28
+ epic = epics.first
29
+ label = epic.label # Get an epic's label
30
+ ```
31
+
32
+ ## Eager Loading
33
+
34
+ See Pivotal Tracker API [documentation](https://www.pivotaltracker.com/help/api#Response_Controlling_Parameters) for how to use the `fields` parameter.
35
+
36
+ ```ruby
37
+ client = TrackerApi::Client.new(token: 'my-api-token') # Create API client
38
+
39
+ client.project(project_id, fields: ':default,labels(name)') # Eagerly get labels with a project
40
+ client.project(project_id, fields: ':default,epics') # Eagerly get epics with a project
41
+ ```
42
+
43
+ ## TODO
44
+
45
+ - Pagination
46
+ - Create, Update, Delete of Resources
47
+
48
+ ## Contributing
49
+
50
+ Currently this client supports read-only access to Pivotal Tracker.
51
+ We will be extending it as our use cases require, but are always happy to accept contributions.
52
+
53
+ 1. Fork it
54
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
55
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
56
+ 4. Push to the branch (`git push origin my-new-feature`)
57
+ 5. Create new Pull Request
@@ -0,0 +1,18 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'bundler/gem_tasks'
8
+
9
+ require 'rake/testtask'
10
+
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib'
13
+ t.libs << 'test'
14
+ t.test_files = FileList['test/**/*_test.rb']
15
+ t.verbose = true
16
+ end
17
+
18
+ task :default => :test
@@ -0,0 +1,39 @@
1
+ require 'tracker_api/version'
2
+
3
+ # dependencies
4
+ require 'virtus'
5
+ require 'faraday'
6
+ require 'faraday_middleware'
7
+
8
+ # stdlib
9
+ require 'forwardable'
10
+ require 'logger'
11
+
12
+ module TrackerApi
13
+ autoload :Error, 'tracker_api/error'
14
+ autoload :Client, 'tracker_api/client'
15
+ autoload :Logger, 'tracker_api/logger'
16
+
17
+ module Errors
18
+ class UnexpectedData < StandardError; end
19
+ end
20
+
21
+ module Endpoints
22
+ autoload :Epic, 'tracker_api/endpoints/epic'
23
+ autoload :Epics, 'tracker_api/endpoints/epics'
24
+ autoload :Iterations, 'tracker_api/endpoints/iterations'
25
+ autoload :Project, 'tracker_api/endpoints/project'
26
+ autoload :Projects, 'tracker_api/endpoints/projects'
27
+ autoload :Stories, 'tracker_api/endpoints/stories'
28
+ end
29
+
30
+ module Resources
31
+ autoload :Account, 'tracker_api/resources/account'
32
+ autoload :Epic, 'tracker_api/resources/epic'
33
+ autoload :Iteration, 'tracker_api/resources/iteration'
34
+ autoload :Label, 'tracker_api/resources/label'
35
+ autoload :Project, 'tracker_api/resources/project'
36
+ autoload :Story, 'tracker_api/resources/story'
37
+ autoload :TimeZone, 'tracker_api/resources/time_zone'
38
+ end
39
+ end
@@ -0,0 +1,71 @@
1
+ module TrackerApi
2
+ class Client
3
+ USER_AGENT = "Ruby/#{RUBY_VERSION} (#{RUBY_PLATFORM}; #{RUBY_ENGINE}) TrackerApi/#{TrackerApi::VERSION} Faraday/#{Faraday::VERSION}".freeze
4
+
5
+ attr_accessor :url, :api_version, :token, :logger, :connection
6
+
7
+ # Create Pivotal Tracker API client.
8
+ #
9
+ # @param [Hash] options the connection options
10
+ # @option options [String] :token API token to use for requests
11
+ # @option options [String] :url Main HTTP API root
12
+ # @option options [String] :api_version The API version URL path
13
+ # @option options [String] :logger Custom logger
14
+ # @option options [String] :adapter Custom http adapter to configure Faraday with
15
+ # @option options [String] :connection_options Connection options to pass to Faraday
16
+ #
17
+ # @example Creating a Client
18
+ # Client.new token: 'my-super-special-token'
19
+ def initialize(options={})
20
+ url = options[:url] || 'https://www.pivotaltracker.com'
21
+ @url = URI.parse(url).to_s
22
+
23
+ @api_version = options[:api_version] || '/services/v5'
24
+ @logger = options[:logger] || Logger.new(nil)
25
+ adapter = options[:adapter] || :net_http
26
+ connection_options = options[:connection_options] || { ssl: { verify: true } }
27
+ @token = options[:token]
28
+
29
+ raise 'Missing required options: :token' unless @token
30
+
31
+ @connection = Faraday.new({ url: @url }.merge(connection_options)) do |builder|
32
+ # response
33
+ builder.use Faraday::Response::RaiseError
34
+ builder.response :json
35
+
36
+ # request
37
+ builder.request :multipart
38
+ builder.request :json
39
+
40
+ builder.use TrackerApi::Logger, @logger
41
+ builder.adapter adapter
42
+ end
43
+ end
44
+
45
+ def request(options={})
46
+ method = options[:method] || :get
47
+ url = options[:url] || File.join(@url, @api_version, options[:path])
48
+ token = options[:token] || @token
49
+ params = options[:params] || {}
50
+ body = options[:body]
51
+ headers = { 'User-Agent' => USER_AGENT, 'X-TrackerToken' => token }.merge(options[:headers] || {})
52
+
53
+ connection.send(method) do |req|
54
+ req.url url
55
+ req.headers.merge!(headers)
56
+ req.params.merge!(params)
57
+ req.body = body
58
+ end
59
+ rescue Faraday::Error::ClientError => e
60
+ raise TrackerApi::Error.new(e)
61
+ end
62
+
63
+ def projects(params={})
64
+ Endpoints::Projects.new(self).get(params)
65
+ end
66
+
67
+ def project(id, params={})
68
+ Endpoints::Project.new(self).get(id, params)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,20 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Epic
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, id)
11
+ data = client.request(
12
+ method: :get,
13
+ :path => "/projects/#{project_id}/epics/#{id}"
14
+ ).body
15
+
16
+ Resources::Epic.new({ client: client }.merge(data))
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Epics
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, params={})
11
+ data = client.request(
12
+ method: :get,
13
+ path: "/projects/#{project_id}/epics",
14
+ params: params
15
+ ).body
16
+ raise TrackerApi::Errors::UnexpectedData, 'Array of epics expected' unless data.is_a? Array
17
+
18
+ data.map { |epic| Resources::Epic.new({ client: client }.merge(epic)) }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Iterations
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, params={})
11
+ data = client.request(
12
+ method: :get,
13
+ path: "/projects/#{project_id}/iterations",
14
+ params: params
15
+ ).body
16
+ raise TrackerApi::Errors::UnexpectedData, 'Array of iterations expected' unless data.is_a? Array
17
+
18
+ data.map { |iteration| Resources::Iteration.new({ client: client }.merge(iteration)) }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Project
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(id, params={})
11
+ data = client.request(
12
+ method: :get,
13
+ path: "/projects/#{id}",
14
+ params: params
15
+ ).body
16
+
17
+ Resources::Project.new({ client: client }.merge(data))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Projects
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(params={})
11
+ data = client.request(
12
+ method: :get,
13
+ path: '/projects',
14
+ params: params
15
+ ).body
16
+ raise TrackerApi::Errors::UnexpectedData, 'Array of projects expected' unless data.is_a? Array
17
+
18
+ data.map { |project| Resources::Project.new({ client: client }.merge(project)) }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Stories
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, params={})
11
+ data = client.request(
12
+ method: :get,
13
+ path: "/projects/#{project_id}/stories",
14
+ params: params
15
+ ).body
16
+ raise TrackerApi::Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
17
+
18
+ data.map { |story| Resources::Story.new({ client: client }.merge(story)) }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Story
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, id)
11
+ data = client.request(
12
+ method: :get,
13
+ :path => "/projects/#{project_id}/stories/#{id}"
14
+ ).body
15
+
16
+ Resources::Story.new({ client: client }.merge(data))
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ module TrackerApi
2
+ class Error < StandardError
3
+ attr_reader :wrapped_exception, :response
4
+
5
+ def initialize(wrapped_exception)
6
+ @wrapped_exception = wrapped_exception
7
+ @response = wrapped_exception.response
8
+ message = if wrapped_exception.is_a?(Faraday::Error::ParsingError)
9
+ wrapped_exception.message
10
+ elsif wrapped_exception.is_a?(Faraday::Error::ClientError)
11
+ wrapped_exception.response.inspect
12
+ else
13
+ wrapped_exception.instance_variable_get(:@wrapped_exception).inspect
14
+ end
15
+ super(message)
16
+ end
17
+ end
18
+ end