you_track 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8cdefbb4147fc7cb54d35c9b16e3153efc697e64
4
- data.tar.gz: 1bffc0d9b250598b516496df7854046e76f09f39
3
+ metadata.gz: 3f471aed77522ed43c5aaf4b707e5888a2447385
4
+ data.tar.gz: b50001dd03586253963b17cad8b40940d5bc4001
5
5
  SHA512:
6
- metadata.gz: d5846ffe59330b6b1aa90c3a7048f94218203efb4e57d3dae7c06c4cd96434daa9463d3b2df6a541dad6ad5fecd8326956ed16e775fe0506927364440eb160bb
7
- data.tar.gz: 3c93e712555a4057c7c95785641b86f959a7624983a18390b8634b6ed38cdd85d159722782acd21803d05ab96cfc35b252bc8ffc3f651a73edb3aa06b24e0280
6
+ metadata.gz: c5c2791ff479e5248344fc51b82d037d17089196b5b09d06c9a78fa6751f684f787dd14833a49b6932f6d2e9901d29bb10aa78932ef2aac70ea11ea278ff6f47
7
+ data.tar.gz: 2c8f5f53a3ed1d7e83d6f7248131eabe5bc13389273dbac41fc0bb7cf70da9b11a367abd90dcdd16fadadc38a719cdd190b65009b8e42efbdaea72c99c005a28
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ gemspec
4
4
 
5
5
  gem "awesome_print"
6
6
  gem "pry-nav"
7
+ gem "guard-rspec", "~> 4.5", require: false
7
8
 
8
9
  group :test do
9
10
  gem "rspec", "~> 3.2"
data/Guardfile ADDED
@@ -0,0 +1,50 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features)
6
+
7
+ ## Uncomment to clear the screen before every task
8
+ # clearing :on
9
+
10
+ ## Guard internally checks for changes in the Guardfile and exits.
11
+ ## If you want Guard to automatically start up again, run guard in a
12
+ ## shell loop, e.g.:
13
+ ##
14
+ ## $ while bundle exec guard; do echo "Restarting Guard..."; done
15
+ ##
16
+ ## Note: if you are using the `directories` clause above and you are not
17
+ ## watching the project directory ('.'), then you will want to move
18
+ ## the Guardfile to a watched dir and symlink it back, e.g.
19
+ #
20
+ # $ mkdir config
21
+ # $ mv Guardfile config/
22
+ # $ ln -s config/Guardfile .
23
+ #
24
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
25
+
26
+ # Note: The cmd option is now required due to the increasing number of ways
27
+ # rspec may be run, below are examples of the most common uses.
28
+ # * bundler: 'bundle exec rspec'
29
+ # * bundler binstubs: 'bin/rspec'
30
+ # * spring: 'bin/rspec' (This will use spring if running and you have
31
+ # installed the spring binstubs per the docs)
32
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
33
+ # * 'just' rspec: 'rspec'
34
+
35
+ guard :rspec, cmd: "bundle exec rspec", all_on_start: true, failed_mode: :focus do
36
+ require "guard/rspec/dsl"
37
+ dsl = Guard::RSpec::Dsl.new(self)
38
+
39
+ # Feel free to open issues for suggestions and improvements
40
+
41
+ # RSpec files
42
+ rspec = dsl.rspec
43
+ watch(rspec.spec_helper) { rspec.spec_dir }
44
+ watch(rspec.spec_support) { rspec.spec_dir }
45
+ watch(rspec.spec_files)
46
+
47
+ # Ruby files
48
+ ruby = dsl.ruby
49
+ dsl.watch_spec_files_for(ruby.lib_files)
50
+ end
@@ -0,0 +1,39 @@
1
+ class YouTrack::Client::ApplyIssueCommand < YouTrack::Client::Request
2
+ def real(params={})
3
+ id = params.delete("id")
4
+
5
+ service.request(
6
+ :path => "/issue/#{id}/execute",
7
+ :query => params,
8
+ :method => :post,
9
+ )
10
+ end
11
+
12
+ def mock(params={})
13
+ id = params.delete("id")
14
+ issue = find(:issues, id)
15
+ comment_id = "#{Cistern::Mock.random_numbers(2)}-#{Cistern::Mock.random_numbers(5)}"
16
+
17
+ if params["comment"]
18
+ comment = {
19
+ "id" => comment_id,
20
+ "author" => service.username,
21
+ "deleted" => false,
22
+ "text" => params["comment"],
23
+ "shownForIssuer" => false,
24
+ "created" => Time.now.to_i * 1000,
25
+ "issueId" => id,
26
+ }
27
+ service.data[:comments][comment_id] = comment
28
+ end
29
+
30
+ if params["command"]
31
+ commands = params["command"].split.each_slice(2).map { |a| [a[0], a[1]] }
32
+ commands.each do |command|
33
+ issue["custom_fields"].detect { |f| f[0] == command[0] }[1] = command[1]
34
+ end
35
+ end
36
+
37
+ service.response
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ class YouTrack::Client::Comment < YouTrack::Client::Model
2
+ identity :id
3
+
4
+ attribute :author
5
+ attribute :issue_id, aliases: ['issueId']
6
+ attribute :deleted, type: :boolean
7
+ attribute :text
8
+ attribute :shown_for_issue_author, aliases: ['shownForIssueAuthor'], type: :boolean
9
+ attribute :created # Unix time, puke
10
+ attribute :updated # More unix time puke
11
+ end
@@ -0,0 +1,7 @@
1
+ class YouTrack::Client::Comments < YouTrack::Client::Collection
2
+ model YouTrack::Client::Comment
3
+
4
+ def all(issue_id)
5
+ service.comments.load(service.get_issue_comments(issue_id).body)
6
+ end
7
+ end
@@ -15,13 +15,15 @@ class YouTrack::Client::CreateIssue < YouTrack::Client::Request
15
15
 
16
16
  project = issue["projectShortName"] = issue.delete("project")
17
17
 
18
- identity = service.data[:issues].size + 1
18
+ index = service.data[:issues].size + 1
19
+ project_index = service.data[:issues].values.select { |i| i["projectShortName"] == project }.size
19
20
 
21
+ identity = "#{project}-#{index}"
20
22
 
21
23
  issue.merge!(
22
- "id" => "#{project}-#{identity}",
24
+ "id" => identity,
23
25
  "tag" => "",
24
- "numberInProject" => identity,
26
+ "numberInProject" => project_index,
25
27
  "created" => Time.now.to_i * 1000,
26
28
  "updated" => Time.now.to_i * 1000,
27
29
  "updaterName" => service.username,
@@ -30,10 +32,14 @@ class YouTrack::Client::CreateIssue < YouTrack::Client::Request
30
32
  "reporterFullName" => service.username.capitalize,
31
33
  "commentsCount" => "0",
32
34
  "votes" => "0",
33
- "custom_fields" => [], # @fixme need these
35
+ "custom_fields" => [
36
+ ["State", "Open"],
37
+ ], # @fixme need these
34
38
  "attachments" => [],
35
39
  )
36
40
 
41
+ service.data[:issues][identity] = issue
42
+
37
43
  service.response(
38
44
  :body => issue,
39
45
  :status => 201,
@@ -0,0 +1,14 @@
1
+ class YouTrack::Client::GetIssueComments < YouTrack::Client::Request
2
+ def real(issue_id)
3
+ service.request(
4
+ :path => "/issue/#{issue_id}/comment",
5
+ :parser => YouTrack::Parser::CommentsParser,
6
+ )
7
+ end
8
+
9
+ def mock(issue_id)
10
+ service.response(
11
+ :body => service.data[:comments].values.select { |c| c['issueId'] == issue_id }
12
+ )
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ class YouTrack::Client::GetIssues < YouTrack::Client::Request
2
+ def real(project, filters={})
3
+ service.request(
4
+ :path => "/issue/byproject/#{project}",
5
+ :parser => YouTrack::Parser::IssuesParser,
6
+ :query => filters,
7
+ )
8
+ end
9
+
10
+ def mock(project, filters={})
11
+ issues = service.data[:issues].values
12
+
13
+ # delete first n elements from the array
14
+ if filters["after"]
15
+ issues.delete_if.with_index { |x,i| i < (filters["after"].to_i - 1) }
16
+ end
17
+
18
+ service.response(
19
+ :body => issues
20
+ )
21
+ end
22
+ end
@@ -22,6 +22,25 @@ class YouTrack::Client::Issue < YouTrack::Client::Model
22
22
 
23
23
  # CREATE https://confluence.jetbrains.com/display/YTD6/Create+New+Issue
24
24
  # UPDATE https://confluence.jetbrains.com/display/YTD6/Update+an+Issue
25
+
26
+ def comments
27
+ service.comments.load(service.get_issue_comments(self.identity).body)
28
+ end
29
+
30
+ def comment(comment)
31
+ service.apply_issue_command("id" => self.identity, "comment" => comment)
32
+ comments.detect { |c| c.text == comment }
33
+ end
34
+
35
+ def state
36
+ custom_fields.detect { |f| f[0] == 'State' }.last
37
+ end
38
+
39
+ def state=(new_state)
40
+ service.apply_issue_command("id" => self.identity, "command" => "State #{new_state}")
41
+ self.reload
42
+ end
43
+
25
44
  def save
26
45
  if new_record?
27
46
  requires :project, :summary
@@ -36,7 +55,13 @@ class YouTrack::Client::Issue < YouTrack::Client::Model
36
55
  ).body
37
56
  )
38
57
  else
39
- raise NotImplementedError
58
+ requires :identity
59
+ service.update_issue(
60
+ "id" => self.identity,
61
+ "summary" => self.summary,
62
+ "description" => self.description
63
+ )
64
+ self.reload
40
65
  end
41
66
  end
42
67
  end
@@ -2,9 +2,15 @@ class YouTrack::Client::Issues < YouTrack::Client::Collection
2
2
 
3
3
  model YouTrack::Client::Issue
4
4
 
5
+ def all(project, filters={})
6
+ service.issues.load(service.get_issues(project, filters).body)
7
+ end
8
+
5
9
  def get(identity)
6
10
  service.issues.new(
7
11
  service.get_issue(identity).body
8
12
  )
13
+ rescue Faraday::ResourceNotFound
14
+ nil
9
15
  end
10
16
  end
@@ -4,7 +4,8 @@ class YouTrack::Client::Mock
4
4
  def self.data
5
5
  @data ||= Hash.new { |h,k|
6
6
  h[k] = {
7
- :issues => {},
7
+ :issues => {},
8
+ :comments => {},
8
9
  }
9
10
  }
10
11
  end
@@ -2,6 +2,10 @@ class YouTrack::Client::Real
2
2
  attr_reader :url, :connection, :adapter, :username, :authenticated
3
3
 
4
4
  def initialize(options={})
5
+ youtrack_file = YAML.load_file(File.expand_path("~/.youtrack"))
6
+
7
+ options.merge!(youtrack_file)
8
+ requires(options, :url, :username, :password)
5
9
  @url = URI.parse(options[:url])
6
10
  adapter = options[:adapter] || Faraday.default_adapter
7
11
  logger = options[:logger] || Logger.new(nil)
@@ -37,6 +41,13 @@ class YouTrack::Client::Real
37
41
  end
38
42
  end
39
43
 
44
+ def requires(options, *required)
45
+ missing = required.map do |required_param|
46
+ required_param if options[required_param].nil?
47
+ end.compact
48
+ raise RuntimeError, "Missing required options: #{missing.inspect}" unless missing.empty?
49
+ end
50
+
40
51
  def request(options={})
41
52
  # @note first request gets the cookie
42
53
  if !@authenticated && !@authenticating
@@ -55,12 +66,14 @@ class YouTrack::Client::Real
55
66
  }
56
67
  end
57
68
 
58
- method = options[:method] || :get
59
- url = URI.parse(options[:url] || File.join(self.url.to_s, "/rest", options.fetch(:path)))
60
- params = options[:params] || {}
61
- body = options[:body]
62
- headers = options[:headers] || {}
63
- parser = options[:parser]
69
+ method = options[:method] || :get
70
+ query = options[:query]
71
+ url = URI.parse(options[:url] || File.join(self.url.to_s, "/rest", options.fetch(:path)))
72
+ url.query = query.map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&') if query
73
+ params = options[:params] || {}
74
+ body = options[:body]
75
+ headers = options[:headers] || {}
76
+ parser = options[:parser]
64
77
 
65
78
  headers["Content-Type"] ||= if body.nil?
66
79
  if !params.empty?
@@ -0,0 +1,20 @@
1
+ class YouTrack::Client::UpdateIssue < YouTrack::Client::Request
2
+ def real(params={})
3
+ id = params.delete("id")
4
+ service.request(
5
+ :path => "/issue/#{id}",
6
+ :method => :post,
7
+ :query => params,
8
+ )
9
+ end
10
+
11
+ def mock(params={})
12
+ id = params.delete("id")
13
+ issue = find(:issues, id)
14
+
15
+ issue.merge!(params)
16
+ service.data[:issues][id] = issue
17
+
18
+ service.response
19
+ end
20
+ end
@@ -1,6 +1,5 @@
1
1
  class YouTrack::Client < Cistern::Service
2
- requires :url, :username, :password
3
- recognizes :logger, :adapter, :builder, :connection_options
2
+ recognizes :logger, :adapter, :builder, :connection_options, :url, :username, :password
4
3
  end
5
4
 
6
5
  require_relative "client/real"
@@ -11,7 +10,14 @@ require_relative "client/request"
11
10
 
12
11
  require_relative "client/login"
13
12
  require_relative "client/get_issue"
13
+ require_relative "client/get_issues"
14
14
  require_relative "client/create_issue"
15
+ require_relative "client/update_issue"
16
+ require_relative "client/apply_issue_command"
17
+
18
+ require_relative "client/get_issue_comments"
15
19
 
16
20
  require_relative "client/issue"
17
21
  require_relative "client/issues"
22
+ require_relative "client/comment"
23
+ require_relative "client/comments"
@@ -0,0 +1,13 @@
1
+ class YouTrack::Parser::Base
2
+ attr_reader :raw
3
+
4
+ def initialize(raw)
5
+ @raw = raw
6
+ end
7
+
8
+ def parse_fields(fields)
9
+ fields.inject({}) { |r, f|
10
+ r.merge(f["name"] => f["value"])
11
+ }
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ class YouTrack::Parser::CommentsParser < YouTrack::Parser::Base
2
+ def parse
3
+ return [] if raw["comments"].nil?
4
+ raw["comments"]["comment"]
5
+ end
6
+ end
@@ -1,16 +1,4 @@
1
- class YouTrack::Parser::IssueParser
2
- attr_reader :raw
3
-
4
- def initialize(raw)
5
- @raw = raw
6
- end
7
-
8
- def parse_fields(fields)
9
- fields.inject({}) { |r, f|
10
- r.merge(f["name"] => f["value"])
11
- }
12
- end
13
-
1
+ class YouTrack::Parser::IssueParser < YouTrack::Parser::Base
14
2
  def parse_attachments(attachments)
15
3
  attachments.inject([]) { |r, a|
16
4
  value = a["value"]
@@ -0,0 +1,21 @@
1
+ class YouTrack::Parser::IssuesParser < YouTrack::Parser::IssueParser
2
+ def parse
3
+ return [] if raw["issues"].nil? # i hate xml
4
+ results = raw["issues"]["issue"].dup # i really hate xml
5
+
6
+ results.each do |result|
7
+ fields = result.delete("field")
8
+ standard_fields = fields.select { |k| k["xsi:type"] == "SingleField" }
9
+ fields = fields - standard_fields
10
+ attachments = fields.select { |k| k["xsi:type"] == "AttachmentField" }
11
+ custom_fields = fields - attachments
12
+
13
+ result.merge!(parse_fields(standard_fields))
14
+ result["custom_fields"] = parse_fields(custom_fields)
15
+ result["attachments"] = parse_attachments(attachments)
16
+ result["comments"] = results.delete("comment")
17
+ end
18
+
19
+ results
20
+ end
21
+ end
@@ -1,4 +1,7 @@
1
1
  module YouTrack::Parser
2
2
  end
3
3
 
4
+ require_relative "parser/base"
4
5
  require_relative "parser/issue_parser"
6
+ require_relative "parser/issues_parser"
7
+ require_relative "parser/comments_parser"
@@ -1,3 +1,3 @@
1
1
  module YouTrack
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: you_track
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Lane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-13 00:00:00.000000000 Z
11
+ date: 2015-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cistern
@@ -122,6 +122,7 @@ files:
122
122
  - ".travis.yml"
123
123
  - CODE_OF_CONDUCT.md
124
124
  - Gemfile
125
+ - Guardfile
125
126
  - LICENSE.txt
126
127
  - README.md
127
128
  - Rakefile
@@ -129,8 +130,13 @@ files:
129
130
  - bin/setup
130
131
  - lib/you_track.rb
131
132
  - lib/you_track/client.rb
133
+ - lib/you_track/client/apply_issue_command.rb
134
+ - lib/you_track/client/comment.rb
135
+ - lib/you_track/client/comments.rb
132
136
  - lib/you_track/client/create_issue.rb
133
137
  - lib/you_track/client/get_issue.rb
138
+ - lib/you_track/client/get_issue_comments.rb
139
+ - lib/you_track/client/get_issues.rb
134
140
  - lib/you_track/client/issue.rb
135
141
  - lib/you_track/client/issues.rb
136
142
  - lib/you_track/client/login.rb
@@ -138,8 +144,12 @@ files:
138
144
  - lib/you_track/client/model.rb
139
145
  - lib/you_track/client/real.rb
140
146
  - lib/you_track/client/request.rb
147
+ - lib/you_track/client/update_issue.rb
141
148
  - lib/you_track/parser.rb
149
+ - lib/you_track/parser/base.rb
150
+ - lib/you_track/parser/comments_parser.rb
142
151
  - lib/you_track/parser/issue_parser.rb
152
+ - lib/you_track/parser/issues_parser.rb
143
153
  - lib/you_track/version.rb
144
154
  - you_track.gemspec
145
155
  homepage: https://github.com/lanej/you_track.git