pivotal-api 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f8eb153b9cf748c9d9d0f2ecbdeec3ef341963f1
4
+ data.tar.gz: 02383e644883c0bf292e1cfb5e58bfc223c2c58b
5
+ SHA512:
6
+ metadata.gz: 32dd22449130691138f408a00626ef3a0826903b23f2a5017583619a4f8d7f06adc831f3d30abd2db08e41b2d0fb553ae4d389bd720bd49e42ee3aed1b0fce9d
7
+ data.tar.gz: 03716a31c4bf552241b0c3940db729483843662efb31d33461a4d57d52b4dbef6f8b3e6501ec600c4046c2b6f064be5f7ef12be95b532992305c9a9e769d76f7
@@ -0,0 +1,51 @@
1
+ require_relative './pivotal_api/version.rb'
2
+
3
+ # dependencies
4
+ require 'virtus'
5
+ require 'faraday'
6
+ require 'faraday_middleware'
7
+
8
+ # stdlib
9
+ require 'addressable/uri'
10
+ require 'forwardable'
11
+ require 'logger'
12
+
13
+ module PivotalApi
14
+ autoload :Error, 'pivotal_api/error'
15
+ autoload :Client, 'pivotal_api/client'
16
+ autoload :Logger, 'pivotal_api/logger'
17
+
18
+ module Errors
19
+ class UnexpectedData < StandardError; end
20
+ end
21
+
22
+ module Endpoints
23
+ autoload :Comments, 'pivotal_api/endpoints/comments'
24
+ autoload :Epics, 'pivotal_api/endpoints/epics'
25
+ autoload :Iterations, 'pivotal_api/endpoints/iterations'
26
+ autoload :Labels, 'pivotal_api/endpoints/labels'
27
+ autoload :Me, 'pivotal_api/endpoints/me'
28
+ autoload :Memberships, 'pivotal_api/endpoints/memberships'
29
+ autoload :Projects, 'pivotal_api/endpoints/projects'
30
+ autoload :Stories, 'pivotal_api/endpoints/stories'
31
+ autoload :Tasks, 'pivotal_api/endpoints/tasks'
32
+ autoload :Webhooks, 'pivotal_api/endpoints/webhooks'
33
+ end
34
+
35
+ module Resources
36
+ autoload :Account, 'pivotal_api/resources/account'
37
+ autoload :Comment, 'pivotal_api/resources/comment'
38
+ autoload :Epic, 'pivotal_api/resources/epic'
39
+ autoload :Iteration, 'pivotal_api/resources/iteration'
40
+ autoload :Me, 'pivotal_api/resources/me'
41
+ autoload :MembershipSummary, 'pivotal_api/resources/membership_summary'
42
+ autoload :Label, 'pivotal_api/resources/label'
43
+ autoload :Person, 'pivotal_api/resources/person'
44
+ autoload :Project, 'pivotal_api/resources/project'
45
+ autoload :ProjectMembership, 'pivotal_api/resources/project_membership'
46
+ autoload :Story, 'pivotal_api/resources/story'
47
+ autoload :Task, 'pivotal_api/resources/task'
48
+ autoload :TimeZone, 'pivotal_api/resources/time_zone'
49
+ autoload :Webhook, 'pivotal_api/resources/webhook'
50
+ end
51
+ end
@@ -0,0 +1,218 @@
1
+ module PivotalApi
2
+ class Client
3
+ USER_AGENT = "Ruby/#{RUBY_VERSION} (#{RUBY_PLATFORM}; #{RUBY_ENGINE}) PivotalApi/#{PivotalApi::VERSION} Faraday/#{Faraday::VERSION}".freeze
4
+
5
+ # Header keys that can be passed in options hash to {#get},{#paginate}
6
+ CONVENIENCE_HEADERS = Set.new([:accept, :content_type])
7
+
8
+ attr_reader :url, :api_version, :token, :logger, :connection, :auto_paginate, :last_response
9
+
10
+ # Create Pivotal Tracker API client.
11
+ #
12
+ # @param [Hash] options the connection options
13
+ # @option options [String] :token API token to use for requests
14
+ # @option options [String] :url Main HTTP API root
15
+ # @option options [Boolean] :auto_paginate Client should perform pagination automatically. Default true.
16
+ # @option options [String] :api_version The API version URL path
17
+ # @option options [String] :logger Custom logger
18
+ # @option options [String] :adapter Custom http adapter to configure Faraday with
19
+ # @option options [String] :connection_options Connection options to pass to Faraday
20
+ #
21
+ # @example Creating a Client
22
+ # Client.new token: 'my-super-special-token'
23
+ def initialize(options={})
24
+ url = options.fetch(:url, 'https://www.pivotaltracker.com')
25
+ @url = Addressable::URI.parse(url).to_s
26
+ @api_version = options.fetch(:api_version, '/services/v5')
27
+ @logger = options.fetch(:logger, ::Logger.new(nil))
28
+ adapter = options.fetch(:adapter, :excon)
29
+ connection_options = options.fetch(:connection_options, { ssl: { verify: true } })
30
+ @auto_paginate = options.fetch(:auto_paginate, true)
31
+ @token = options[:token]
32
+ raise 'Missing required options: :token' unless @token
33
+
34
+ @connection = Faraday.new({ url: @url }.merge(connection_options)) do |builder|
35
+ # response
36
+ builder.use Faraday::Response::RaiseError
37
+ builder.response :json
38
+
39
+ # request
40
+ builder.request :multipart
41
+ builder.request :json
42
+
43
+ builder.use PivotalApi::Logger, @logger
44
+ builder.adapter adapter
45
+ end
46
+ end
47
+
48
+ # Make a HTTP GET request
49
+ #
50
+ # @param path [String] The path, relative to api endpoint
51
+ # @param options [Hash] Query and header params for request
52
+ # @return [Faraday::Response]
53
+ def get(path, options = {})
54
+ request(:get, parse_query_and_convenience_headers(path, options))
55
+ end
56
+
57
+ # Make a HTTP DELETE request
58
+ #
59
+ # @param path [String] The path, relative to api endpoint
60
+ # @param options [Hash] Query and header params for request
61
+ # @return [Faraday::Response]
62
+ def delete(path, options = {})
63
+ request(:delete, parse_query_and_convenience_headers(path, options))
64
+ end
65
+
66
+ # Make a HTTP POST request
67
+ #
68
+ # @param path [String] The path, relative to api endpoint
69
+ # @param options [Hash] Query and header params for request
70
+ # @return nil
71
+ def post(path, options = {})
72
+ request(:post, parse_query_and_convenience_headers(path, options))
73
+ end
74
+
75
+ # Make a HTTP PUT request
76
+ #
77
+ # @param path [String] The path, relative to api endpoint
78
+ # @param options [Hash] Query and header params for request
79
+ # @return nil
80
+ def put(path, options = {})
81
+ request(:put, parse_query_and_convenience_headers(path, options))
82
+ end
83
+
84
+ # Make one or more HTTP GET requests, optionally fetching
85
+ # the next page of results from information passed back in headers
86
+ # based on value in {#auto_paginate}.
87
+ #
88
+ # @param path [String] The path, relative to {#api_endpoint}
89
+ # @param options [Hash] Query and header params for request
90
+ # @param block [Block] Block to perform the data concatenation of the
91
+ # multiple requests. The block is called with two parameters, the first
92
+ # contains the contents of the requests so far and the second parameter
93
+ # contains the latest response.
94
+ # @return [Array]
95
+ def paginate(path, options = {}, &block)
96
+ opts = parse_query_and_convenience_headers path, options.dup
97
+ auto_paginate = opts[:params].delete(:auto_paginate) { |k| @auto_paginate }
98
+ @last_response = request :get, opts
99
+ data = @last_response.body
100
+ raise PivotalApi::Errors::UnexpectedData, 'Array expected' unless data.is_a? Array
101
+
102
+ if auto_paginate
103
+ pager = Pagination.new @last_response.headers
104
+
105
+ while pager.more?
106
+ opts[:params].update(pager.next_page_params)
107
+
108
+ @last_response = request :get, opts
109
+ pager = Pagination.new @last_response.headers
110
+ if block_given?
111
+ yield(data, @last_response)
112
+ else
113
+ data.concat(@last_response.body) if @last_response.body.is_a?(Array)
114
+ end
115
+ end
116
+ end
117
+
118
+ data
119
+ end
120
+
121
+ # Get projects
122
+ #
123
+ # @return [PivotalApi::Endpoints::Projects]
124
+ def projects
125
+ Endpoints::Projects.new(self)
126
+ end
127
+
128
+ # Get information about the authenticated user
129
+ #
130
+ # @return [PivotalApi::Resources::Me]
131
+ def me
132
+ data = get("/me").body
133
+
134
+ Resources::Me.new({ client: self }.merge(data))
135
+ end
136
+
137
+ # Get information about a client story without knowing what project the story belongs to
138
+ #
139
+ # @param [String] story_id
140
+ # @return [PivotalApi::Resources::Story]
141
+
142
+
143
+ private
144
+
145
+ def parse_query_and_convenience_headers(path, options)
146
+ raise 'Path can not be blank.' if path.to_s.empty?
147
+
148
+ opts = { body: options[:body] }
149
+
150
+ opts[:url] = options[:url] || File.join(@url, @api_version, path.to_s)
151
+ opts[:method] = options[:method] || :get
152
+ opts[:params] = options[:params] || {}
153
+ opts[:token] = options[:token] || @token
154
+ headers = { 'User-Agent' => USER_AGENT,
155
+ 'X-TrackerToken' => opts.fetch(:token) }.merge(options.fetch(:headers, {}))
156
+
157
+ CONVENIENCE_HEADERS.each do |h|
158
+ if header = options[h]
159
+ headers[h] = header
160
+ end
161
+ end
162
+ opts[:headers] = headers
163
+
164
+ opts
165
+ end
166
+
167
+ def request(method, options = {})
168
+ url = options.fetch(:url)
169
+ params = options[:params] || {}
170
+ body = options[:body]
171
+ headers = options[:headers]
172
+
173
+ @last_response = response = connection.send(method) do |req|
174
+ req.url(url)
175
+ req.headers.merge!(headers)
176
+ req.params.merge!(params)
177
+ req.body = body
178
+ end
179
+ response
180
+ rescue Faraday::Error::ClientError => e
181
+ raise PivotalApi::Error.new(e)
182
+ end
183
+
184
+ class Pagination
185
+ attr_accessor :headers, :total, :limit, :offset, :returned
186
+
187
+ def initialize(headers)
188
+ @headers = headers
189
+ @total = headers['x-tracker-pagination-total'].to_i
190
+ @limit = headers['x-tracker-pagination-limit'].to_i
191
+ @offset = headers['x-tracker-pagination-offset'].to_i
192
+ @returned = headers['x-tracker-pagination-returned'].to_i
193
+
194
+ # if offset is negative (e.g. Iterations Endpoint).
195
+ # For the 'Done' scope, negative numbers can be passed, which
196
+ # specifies the number of iterations preceding the 'Current' iteration.
197
+ # then need to adjust the negative offset to account for a smaller total,
198
+ # and set total to zero since we are paginating from -X to 0.
199
+ if @offset < 0
200
+ @offset = -@total if @offset.abs > @total
201
+ @total = 0
202
+ end
203
+ end
204
+
205
+ def more?
206
+ (offset + limit) < total
207
+ end
208
+
209
+ def next_offset
210
+ offset + limit
211
+ end
212
+
213
+ def next_page_params
214
+ { limit: limit, offset: next_offset }
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,42 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Comments
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, story_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/stories/#{story_id}/comments", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
13
+
14
+ data.map do |story|
15
+ Resources::Comment.new({ project_id: project_id }.merge(story))
16
+ end
17
+ end
18
+
19
+ def get(project_id, story_id, id)
20
+ data = client.get("/projects/#{project_id}/stories/#{story_id}/comments/#{id}").body
21
+
22
+ Resources::Comment.new({ project_id: project_id }.merge(data))
23
+ end
24
+
25
+ def create(project_id, story_id, params)
26
+ data = client.post("/projects/#{project_id}/stories/#{story_id}/comments", params: params).body
27
+
28
+ Resources::Comment.new({ project_id: project_id }.merge(data))
29
+ end
30
+
31
+ def update(project_id, story_id, id, params)
32
+ data = client.put("/projects/#{project_id}/stories/#{story_id}/comments/#{id}", params: params).body
33
+
34
+ Resources::Comment.new({ }.merge(data))
35
+ end
36
+
37
+ def delete(project_id, story_id, id)
38
+ client.delete("/projects/#{project_id}/stories/#{story_id}/comments/#{id}").body
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Epics
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/epics", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of epics expected' unless data.is_a? Array
13
+
14
+ data.map do |epic|
15
+ Resources::Epic.new({ project_id: project_id }.merge(epic))
16
+ end
17
+ end
18
+
19
+ def get(project_id, id)
20
+ data = client.get("/projects/#{project_id}/epics/#{id}").body
21
+
22
+ Resources::Epic.new({ project_id: project_id }.merge(data))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Iterations
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/iterations", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of iterations expected' unless data.is_a? Array
13
+
14
+ data.map do |iteration|
15
+ Resources::Iteration.new({ project_id: project_id }.merge(iteration))
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Labels
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, story_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/stories/#{story_id}/labels", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
13
+
14
+ data.map do |story|
15
+ Resources::Label.new({ project_id: project_id }.merge(story))
16
+ end
17
+ end
18
+
19
+ def create(project_id, story_id, params)
20
+ data = client.post("/projects/#{project_id}/stories/#{story_id}/labels", params: params).body
21
+
22
+ Resources::Label.new({ project_id: project_id }.merge(data))
23
+ end
24
+
25
+ def update(project_id, story_id, id, params)
26
+ data = client.put("/projects/#{project_id}/stories/#{story_id}/labels/#{id}", params: params).body
27
+
28
+ Resources::Label.new({ }.merge(data))
29
+ end
30
+
31
+ def delete(project_id, story_id, id)
32
+ client.delete("/projects/#{project_id}/stories/#{story_id}/labels/#{id}").body
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Memberships
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/memberships", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of memberships expected' unless data.is_a? Array
13
+
14
+ data.map do |membership|
15
+ Resources::ProjectMembership.new({ project_id: project_id }.merge(membership))
16
+ end
17
+ end
18
+
19
+ def get(project_id, id)
20
+ data = client.get("/projects/#{project_id}/memberships/#{id}").body
21
+
22
+ Resources::ProjectMembership.new({ project_id: project_id }.merge(data))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Projects
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ # @param [Hash] params
11
+ # @return [Array[PivotalApi::Resources::Project]]
12
+ def all(params={})
13
+ data = client.paginate('/projects', params: params)
14
+ raise PivotalApi::Errors::UnexpectedData, 'Array of projects expected' unless data.is_a? Array
15
+
16
+ data.map { |project| Resources::Project.new({ }.merge(project)) }
17
+ end
18
+
19
+
20
+ # @param [Hash] params
21
+ # @return [PivotalApi::Resources::Project]
22
+ def get(id, params={})
23
+ # Endpoints::Project.new(self).get(id, params)
24
+ data = client.get("/projects/#{id}", params: params).body
25
+
26
+ Resources::Project.new({ }.merge(data))
27
+ end
28
+
29
+ def epics
30
+ Endpoints::Epics.new(client)
31
+ end
32
+
33
+ def iterations
34
+ Endpoints::Iterations.new(client)
35
+ end
36
+
37
+ def stories
38
+ Endpoints::Stories.new(client)
39
+ end
40
+
41
+ def memberships
42
+ Endpoints::Memberships.new(client)
43
+ end
44
+
45
+ def webhooks
46
+ Endpoints::Webhooks.new(client)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,54 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Stories
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/stories", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
13
+
14
+ data.map do |story|
15
+ Resources::Story.new({ project_id: project_id }.merge(story))
16
+ end
17
+ end
18
+
19
+ def get(project_id, id)
20
+ data = client.get("/projects/#{project_id}/stories/#{id}").body
21
+
22
+ Resources::Story.new({ project_id: project_id }.merge(data))
23
+ end
24
+
25
+ def create(project_id, params)
26
+ data = client.post("/projects/#{project_id}/stories", params: params).body
27
+
28
+ Resources::Story.new({ }.merge(data))
29
+ end
30
+
31
+ def update(project_id, id, params)
32
+ data = client.put("/projects/#{project_id}/stories/#{id}", params: params).body
33
+
34
+ Resources::Story.new({ }.merge(data))
35
+ end
36
+
37
+ def delete(project_id, id)
38
+ client.delete("/projects/#{project_id}/stories/#{id}").body
39
+ end
40
+
41
+ def tasks
42
+ Endpoints::Tasks.new(client)
43
+ end
44
+
45
+ def comments
46
+ Endpoints::Comments.new(client)
47
+ end
48
+
49
+ def labels
50
+ Endpoints::Labels.new(client)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,26 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Tasks
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, story_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/stories/#{story_id}/tasks", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of tasks expected' unless data.is_a? Array
13
+
14
+ data.map do |task|
15
+ Resources::Task.new({project_id: project_id }.merge(task))
16
+ end
17
+ end
18
+
19
+ def get(project_id, story_id, id)
20
+ data = client.get("/projects/#{project_id}/stories/#{story_id}/tasks/#{id}").body
21
+
22
+ Resources::Task.new({project_id: project_id }.merge(data))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Webhooks
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/webhooks", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of webhooks expected' unless data.is_a? Array
13
+
14
+ data.map do |story|
15
+ Resources::Webhook.new({ project_id: project_id }.merge(story))
16
+ end
17
+ end
18
+
19
+ def get(project_id, id)
20
+ data = client.get("/projects/#{project_id}/webhooks/#{id}").body
21
+
22
+ Resources::Webhook.new({ project_id: project_id }.merge(data))
23
+ end
24
+
25
+ def create(project_id, params)
26
+ data = client.post("/projects/#{project_id}/webhooks", params: params).body
27
+
28
+ Resources::Webhook.new({ }.merge(data))
29
+ end
30
+
31
+ def update(project_id, id, params)
32
+ data = client.put("/projects/#{project_id}/webhooks/#{id}", params: params).body
33
+
34
+ Resources::Webhook.new({ }.merge(data))
35
+ end
36
+
37
+ def delete(project_id, id)
38
+ client.delete("/projects/#{project_id}/webhooks/#{id}").body
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ module PivotalApi
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
@@ -0,0 +1,31 @@
1
+ module PivotalApi
2
+ class Logger < Faraday::Response::Middleware
3
+ extend Forwardable
4
+
5
+ def initialize(app, logger = nil)
6
+ super(app)
7
+ @logger = logger || ::Logger.new(STDOUT)
8
+ end
9
+
10
+ def_delegators :@logger, :debug, :info, :warn, :error, :fatal
11
+
12
+ def call(env)
13
+ info("#{env[:method]} => #{env[:url].to_s}")
14
+ debug('request') { dump_headers env[:request_headers] }
15
+ debug('request.body') { env[:body] }
16
+ super
17
+ end
18
+
19
+ def on_complete(env)
20
+ info("#{env[:status]} <= #{env[:url].to_s}")
21
+ debug('response') { dump_headers env[:response_headers] }
22
+ debug('response.body') { env[:body] }
23
+ end
24
+
25
+ private
26
+
27
+ def dump_headers(headers)
28
+ headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Account
4
+ include Virtus.model
5
+
6
+ attribute :id, Integer
7
+ attribute :created_at, DateTime
8
+ attribute :status, String
9
+ attribute :kind, String
10
+ attribute :name, String
11
+ attribute :updated_at, DateTime
12
+ attribute :url, String
13
+ attribute :plan, String
14
+ attribute :days_left, Integer
15
+ attribute :over_the_limit, Boolean
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Comment
4
+ include Virtus.model
5
+
6
+ attribute :id, Integer
7
+ attribute :story_id, Integer
8
+ attribute :epic_id, Integer
9
+ attribute :text, String
10
+ attribute :person_id, Integer
11
+ attribute :created_at, DateTime
12
+ attribute :updated_at, DateTime
13
+ attribute :file_attachment_ids, Array[Integer]
14
+ attribute :google_attachment_ids, Array[Integer]
15
+ attribute :commit_identifier, String
16
+ attribute :commit_type, String
17
+ attribute :kind, String
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Epic
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :id, Integer
9
+ attribute :created_at, DateTime
10
+ attribute :description, String
11
+ attribute :kind, String
12
+ attribute :label, PivotalApi::Resources::Label
13
+ attribute :name, String
14
+ attribute :project_id, Integer
15
+ attribute :updated_at, DateTime
16
+ attribute :url, String
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Iteration
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :finish, DateTime
9
+ attribute :kind, String
10
+ attribute :length, Integer
11
+ attribute :number, Integer
12
+ attribute :planned, Boolean
13
+ attribute :project_id, Integer
14
+ attribute :start, DateTime
15
+ attribute :stories, [PivotalApi::Resources::Story]
16
+ attribute :story_ids, [Integer]
17
+ attribute :team_strength, Float
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Label
4
+ include Virtus.model
5
+
6
+ attribute :id, Integer
7
+ attribute :created_at, DateTime
8
+ attribute :kind, String
9
+ attribute :name, String
10
+ attribute :project_id, Integer
11
+ attribute :updated_at, DateTime
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Me
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :id, Integer
9
+ attribute :name, String
10
+ attribute :initials, String
11
+ attribute :username, String
12
+ attribute :time_zone, PivotalApi::Resources::TimeZone
13
+ attribute :api_token, String
14
+ attribute :has_google_identity, Boolean
15
+ attribute :project_ids, Array[Integer]
16
+ attribute :projects, [PivotalApi::Resources::MembershipSummary]
17
+ attribute :workspace_ids, Array[Integer]
18
+ attribute :email, String
19
+ attribute :receives_in_app_notifications, Boolean
20
+ attribute :kind, String
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class MembershipSummary
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :id, Integer
9
+ attribute :kind, String
10
+ attribute :last_viewed_at, DateTime
11
+ attribute :project_color, String
12
+ attribute :project_id, Integer
13
+ attribute :project_name, String
14
+ attribute :role, String
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Person
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :kind, String
9
+ attribute :id, Integer
10
+ attribute :name, String
11
+ attribute :email, String
12
+ attribute :initials, String
13
+ attribute :username, String
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,49 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Project
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :account, PivotalApi::Resources::Account
9
+ attribute :account_id, Integer
10
+ attribute :atom_enabled, Boolean
11
+ attribute :bugs_and_chores_are_estimatable, Boolean
12
+ attribute :created_at, DateTime
13
+ attribute :current_iteration_number, Integer
14
+ attribute :current_velocity, Integer
15
+ attribute :description, String
16
+ attribute :enable_following, Boolean
17
+ attribute :enable_incoming_emails, Boolean
18
+ attribute :enable_planned_mode, Boolean
19
+ attribute :enable_tasks, Boolean
20
+ attribute :epic_ids, Array[Integer]
21
+ attribute :epics, Array[PivotalApi::Resources::Epic]
22
+ attribute :has_google_domain, Boolean
23
+ attribute :id, Integer
24
+ attribute :initial_velocity, Integer
25
+ attribute :iteration_length, Integer
26
+ attribute :kind, String
27
+ attribute :label_ids, Array[Integer]
28
+ attribute :labels, Array[PivotalApi::Resources::Label]
29
+ attribute :name, String
30
+ attribute :number_of_done_iterations_to_show, Integer
31
+ attribute :point_scale, String
32
+ attribute :point_scale_is_custom, Boolean
33
+ attribute :profile_content, String
34
+ attribute :public, Boolean
35
+ attribute :start_date, DateTime
36
+ attribute :start_time, DateTime
37
+ attribute :time_zone, PivotalApi::Resources::TimeZone
38
+ attribute :updated_at, DateTime
39
+ attribute :velocity_averaged_over, Integer
40
+ attribute :version, Integer
41
+ attribute :week_start_day, String
42
+
43
+ # @return [String] comma separated list of labels
44
+ def label_list
45
+ @label_list ||= labels.collect(&:name).join(',')
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,18 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class ProjectMembership
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :id, Integer
9
+ attribute :person_id, Integer
10
+ attribute :project_id, Integer
11
+ attribute :role, String
12
+ attribute :project_color, String
13
+ attribute :wants_comment_notification_emails, Boolean
14
+ attribute :kind, String
15
+ attribute :person, PivotalApi::Resources::Person
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Story
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :accepted_at, DateTime
9
+ attribute :comment_ids, Array[Integer]
10
+ attribute :created_at, DateTime
11
+ attribute :current_state, String # (accepted, delivered, finished, started, rejected, planned, unstarted, unscheduled)
12
+ attribute :deadline, DateTime
13
+ attribute :description, String
14
+ attribute :estimate, Float
15
+ attribute :external_id, String
16
+ attribute :follower_ids, Array[Integer]
17
+ attribute :id, Integer
18
+ attribute :integration_id, Integer
19
+ attribute :kind, String
20
+ attribute :label_ids, Array[Integer]
21
+ attribute :labels, Array[PivotalApi::Resources::Label]
22
+ attribute :name, String
23
+ attribute :owned_by_id, Integer # deprecated!
24
+ attribute :owner_ids, Array[Integer]
25
+ attribute :owners, Array[PivotalApi::Resources::Person]
26
+ attribute :planned_iteration_number, Integer
27
+ attribute :project_id, Integer
28
+ attribute :requested_by_id, Integer
29
+ attribute :story_type, String # (feature, bug, chore, release)
30
+ attribute :task_ids, Array[Integer]
31
+ attribute :tasks, Array[PivotalApi::Resources::Task]
32
+ attribute :updated_at, DateTime
33
+ attribute :url, String
34
+
35
+ # @return [String] Comma separated list of labels.
36
+ def label_list
37
+ @label_list ||= labels.collect(&:name).join(',')
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Task
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :id, Integer
9
+ attribute :story_id, Integer
10
+ attribute :project_id, Integer
11
+ attribute :description, String
12
+ attribute :complete, Boolean
13
+ attribute :position, Integer
14
+ attribute :created_at, DateTime
15
+ attribute :updated_at, DateTime
16
+ attribute :kind, String
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class TimeZone
4
+ include Virtus.value_object
5
+
6
+ values do
7
+ attribute :offset, String
8
+ attribute :olson_name, String
9
+ attribute :kind, String
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Webhook
4
+ include Virtus.model
5
+
6
+ attribute :id, Integer
7
+ attribute :project_id, Integer
8
+ attribute :webhook_url, String
9
+ attribute :webhook_version, String, default: 'v5'
10
+ attribute :created_at, DateTime
11
+ attribute :updated_at, DateTime
12
+ attribute :kind, String
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module PivotalApi
2
+ VERSION = '0.3.1'
3
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pivotal-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Maciej Skierkowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: addressable
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: virtus
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: faraday
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.9.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.9.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: faraday_middleware
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: excon
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: This gem allows you to easily use the Pivotal Tracker v5 API.
112
+ email:
113
+ - maciej@factor.io
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - "./lib/pivotal-api.rb"
119
+ - "./lib/pivotal_api/client.rb"
120
+ - "./lib/pivotal_api/endpoints/comments.rb"
121
+ - "./lib/pivotal_api/endpoints/epics.rb"
122
+ - "./lib/pivotal_api/endpoints/iterations.rb"
123
+ - "./lib/pivotal_api/endpoints/labels.rb"
124
+ - "./lib/pivotal_api/endpoints/memberships.rb"
125
+ - "./lib/pivotal_api/endpoints/projects.rb"
126
+ - "./lib/pivotal_api/endpoints/stories.rb"
127
+ - "./lib/pivotal_api/endpoints/tasks.rb"
128
+ - "./lib/pivotal_api/endpoints/webhooks.rb"
129
+ - "./lib/pivotal_api/error.rb"
130
+ - "./lib/pivotal_api/logger.rb"
131
+ - "./lib/pivotal_api/resources/account.rb"
132
+ - "./lib/pivotal_api/resources/comment.rb"
133
+ - "./lib/pivotal_api/resources/epic.rb"
134
+ - "./lib/pivotal_api/resources/iteration.rb"
135
+ - "./lib/pivotal_api/resources/label.rb"
136
+ - "./lib/pivotal_api/resources/me.rb"
137
+ - "./lib/pivotal_api/resources/membership_summary.rb"
138
+ - "./lib/pivotal_api/resources/person.rb"
139
+ - "./lib/pivotal_api/resources/project.rb"
140
+ - "./lib/pivotal_api/resources/project_membership.rb"
141
+ - "./lib/pivotal_api/resources/story.rb"
142
+ - "./lib/pivotal_api/resources/task.rb"
143
+ - "./lib/pivotal_api/resources/time_zone.rb"
144
+ - "./lib/pivotal_api/resources/webhook.rb"
145
+ - "./lib/pivotal_api/version.rb"
146
+ homepage: https://github.com/factor-io/pivotal-api
147
+ licenses:
148
+ - MIT
149
+ metadata: {}
150
+ post_install_message:
151
+ rdoc_options: []
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubyforge_project:
166
+ rubygems_version: 2.2.2
167
+ signing_key:
168
+ specification_version: 4
169
+ summary: API client for the Pivotal Tracker v5 API
170
+ test_files: []
171
+ has_rdoc: