you_track 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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