confluence_helper 0.0.1
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 +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/confluence_helper.gemspec +25 -0
- data/lib/confluence_helper.rb +220 -0
- data/lib/confluence_helper/version.rb +3 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 871a0cbe9392a85d9fbcc1dbee5d85ccad98f8da
|
4
|
+
data.tar.gz: 1089c0962a376f79dc98dfb6d2b7d4c8ee930e56
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 51355352fd4557663437a81280ce8ded2507b538b282df80699c60a264a5b54a35caead2d117fa90f3684c6009422725ff4dec7368e1acfddca4e5619d99d0d1
|
7
|
+
data.tar.gz: 4d8414608b8f4be1d5af6563ac8b87077cd2cb48a16ba0122b791c34238dac19f612decba21a6321cde889b0a352df1e26d7aab1a05575797b979a3e163b1c38
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Nigel Thorne
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# ConfluenceHelper
|
2
|
+
|
3
|
+
Confluence Helper wraps up some conflience API operations.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'confluence_helper'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install confluence_helper
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/confluence_helper/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'confluence_helper/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "confluence_helper"
|
8
|
+
spec.version = ConfluenceHelper::VERSION
|
9
|
+
spec.authors = ["Nigel Thorne"]
|
10
|
+
spec.email = ["github@nigelthorne.com"]
|
11
|
+
spec.summary = %q{Helper for accessing data from within your Confluence wiki}
|
12
|
+
spec.description = %q{I needed to pull data from Confluence for several internal web services. This is intended to pull together all helper logic I need to get the data I want.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_runtime_dependency "nokogiri", "~> 1.6.4"
|
24
|
+
spec.add_runtime_dependency "envied", "~> 0.8.1"
|
25
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require "confluence_helper/version"
|
2
|
+
|
3
|
+
module ConfluenceHelper
|
4
|
+
|
5
|
+
class HtmlTable
|
6
|
+
def initialize(node)
|
7
|
+
@node = node
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_columns?(columns:)
|
11
|
+
column_names.sort == columns.sort
|
12
|
+
end
|
13
|
+
|
14
|
+
def column_names
|
15
|
+
@column_names ||= @node.xpath('.//tr/th').map{|n| n.text.strip}
|
16
|
+
end
|
17
|
+
|
18
|
+
def data
|
19
|
+
@data ||=
|
20
|
+
@node
|
21
|
+
.xpath('.//tr')
|
22
|
+
.map{|r| r.xpath('.//td').map{|cell| cell_value(cell)} }
|
23
|
+
.reject{|r| r.empty? }
|
24
|
+
.map{|r| column_names
|
25
|
+
.each
|
26
|
+
.with_index
|
27
|
+
.with_object({}){ |(c,i),o| o[c] = r[i] }
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def cell_value(cell)
|
34
|
+
(cell.xpath(".//a").empty? ? cell.text : cell.inner_html).gsub("\u00C2", " ").gsub("\u00A0", " ").strip
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
class Confluence
|
40
|
+
|
41
|
+
def get_labels_for_each_child_page(rootpageid)
|
42
|
+
child_pages_in_space(rootpageid, {:expand => "metadata.labels"} ).
|
43
|
+
each_with_object({}) do |page, hash|
|
44
|
+
hash[page["title"]] =
|
45
|
+
page.
|
46
|
+
fetch("metadata", {}).
|
47
|
+
fetch("labels",{}).
|
48
|
+
fetch("results",[]).
|
49
|
+
map{|l| l["name"]}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_url_by_title_and_space(spacekey, title)
|
54
|
+
page_by_title_in_space(spacekey, title, {})["_links"]["self"]
|
55
|
+
end
|
56
|
+
|
57
|
+
def save_page_attachment( spacekey, page_title, attachment_name, destination )
|
58
|
+
response = query_rest_api( get_attachment_url( spacekey, page_title, attachment_name ) )
|
59
|
+
File.open(destination, 'wb') { |fp| fp.write(response.body) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def deliverables_pages(rootpageid)
|
63
|
+
child_pages_in_space(rootpageid, {:expand => "metadata.labels,body.export_view"} ).
|
64
|
+
each_with_object({}) do |page, hash|
|
65
|
+
hash[page["title"]] = DeliverablePage.new(
|
66
|
+
labels: page.fetch("metadata", {}).fetch("labels", {}).fetch("results",[]).map{|l| l["name"]},
|
67
|
+
body: page.fetch("body", {}).fetch("export_view", {}).fetch("value", "")
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
@@url = {}
|
75
|
+
|
76
|
+
class DeliverablePage
|
77
|
+
def initialize(labels:, body:)
|
78
|
+
@node = Nokogiri::HTML(body)
|
79
|
+
end
|
80
|
+
|
81
|
+
def tables
|
82
|
+
@tables ||= @node.xpath('//table').map{|t| HtmlTable.new(t) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def deliverables
|
86
|
+
tables.select{|t| is_deliverables_table? t } .map{|table| table.data }.flatten
|
87
|
+
end
|
88
|
+
|
89
|
+
#Name DHFR Status Location Comments
|
90
|
+
def is_deliverables_table?(table)
|
91
|
+
table.has_columns? columns: %w{Name DHFR Status Location Comments}
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_attachment_url(spacekey, page_title, attachment_name)
|
97
|
+
"#{ENVied.CONSTASH_URL}#{ get_attachment_data(spacekey, page_title, attachment_name)["_links"]["download"]}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_attachment_data(spacekey, page_title, attachment_name)
|
101
|
+
get_attachments_data(spacekey, page_title).find{|x| x["title"] == attachment_name}
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_attachments_data(spacekey, page_title)
|
105
|
+
page_by_title_in_space(spacekey, page_title, {expand:"children.attachment"})["children"]["attachment"]["results"]
|
106
|
+
end
|
107
|
+
|
108
|
+
def page_by_title_in_space(spacekey, page_title, options)
|
109
|
+
all_pages = pages_in_space(spacekey, options).lazy
|
110
|
+
all_pages.find {|p| p['title'] == page_title}.
|
111
|
+
tap{|page| raise "Can't find page with title #{page_title} in that space #{spacekey}\n\tFound pages: #{all_pages.map{|p| p['title']}.join("\n\t")}" unless page }
|
112
|
+
end
|
113
|
+
|
114
|
+
def pages_in_space(spacekey, options)
|
115
|
+
enum_for(:for_all_pages_of_results, "rest/api/space/#{spacekey}/content/page", options)
|
116
|
+
end
|
117
|
+
|
118
|
+
def child_pages_in_space(pageid, options)
|
119
|
+
enum_for(:for_all_pages_of_results, "rest/api/content/#{pageid}/child/page", options)
|
120
|
+
end
|
121
|
+
|
122
|
+
def for_all_pages_of_results(url, options, &block)
|
123
|
+
full_options = {start:0, limit:25}.merge(options)
|
124
|
+
response = query_rest_api(url, full_options)
|
125
|
+
if response && response.success?
|
126
|
+
results = response.body.fetch('results',[])
|
127
|
+
results.each{ |p| block.call(p) }
|
128
|
+
if(results.count == full_options[:limit])
|
129
|
+
start = full_options[:start] + results.count
|
130
|
+
for_all_pages_of_results(url, options.merge({start:start}), &block)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_logged_in_connection(request = :json)
|
136
|
+
# TODO: move to memcache
|
137
|
+
# store = ActiveSupport::Cache.lookup_store(:mem_cache_store, ['localhost:11211'])
|
138
|
+
|
139
|
+
return Faraday.new(:url => ENVied.CONSTASH_URL) do |faraday|
|
140
|
+
faraday.use :http_cache # , store: store
|
141
|
+
faraday.request :url_encoded # form-encode POST params
|
142
|
+
faraday.basic_auth(ENVied.CONSTASH_USERNAME, ENVied.CONSTASH_PASSWORD)
|
143
|
+
faraday.request request
|
144
|
+
|
145
|
+
faraday.response :json, :content_type => /\bjson$/
|
146
|
+
# faraday.response :logger # log requests to STDOUT
|
147
|
+
|
148
|
+
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def query_rest_api(url, options = {})
|
153
|
+
puts "-->#{url} #{options.inspect}"
|
154
|
+
get_logged_in_connection().
|
155
|
+
get(url, options).
|
156
|
+
tap{ |response|
|
157
|
+
raise "Error while attempting to query '#{ENVied.CONSTASH_URL}/#{url}'\n Status: #{response.status} \n #{response.body}" unless [200, 302].include? response.status
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
def get_url(space, title)
|
162
|
+
@@url[ [space,title] ] ||= find_url_by_title_and_space(space, title)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class WikiPage < Confluence
|
167
|
+
|
168
|
+
def initialize(spacekey, title)
|
169
|
+
@spacekey, @title = spacekey, title
|
170
|
+
end
|
171
|
+
|
172
|
+
def to_nodes
|
173
|
+
parse_wiki_table(doc)
|
174
|
+
end
|
175
|
+
|
176
|
+
def attachment(title)
|
177
|
+
page = query_rest_api( url , {expand:"children.attachment"})
|
178
|
+
results = page.body["children"]["attachment"]["results"]
|
179
|
+
result = results.find{|x| x["title"] == title}
|
180
|
+
attachment_url = "#{ENVied.CONSTASH_URL}#{result["_links"]["download"]}"
|
181
|
+
query_rest_api(attachment_url, options = {}).body
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def url
|
187
|
+
@url ||= get_url(@spacekey, @title)
|
188
|
+
end
|
189
|
+
|
190
|
+
def doc
|
191
|
+
@doc ||= get_page_body()
|
192
|
+
end
|
193
|
+
|
194
|
+
def get_page_body()
|
195
|
+
page = query_rest_api( url , {expand:"body.export_view"} )
|
196
|
+
return Nokogiri::HTML( page.body["body"]["export_view"]["value"] )
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
def parse_wiki_table(doc)
|
201
|
+
rows = doc.xpath("//table/tbody/tr")
|
202
|
+
headers = rows.xpath("th").map{|n| n.content}
|
203
|
+
rows = rows - rows.xpath("th/..")
|
204
|
+
documents = rows.map{ | row | row_to_hash(row, headers) } #convert to hashes
|
205
|
+
documents.each_with_object({}){|doc, o| o[doc["ID"].to_i] = doc } # index by id
|
206
|
+
end
|
207
|
+
|
208
|
+
def row_to_hash(row, headers)
|
209
|
+
row.xpath( "td" ).
|
210
|
+
to_a.
|
211
|
+
each.
|
212
|
+
with_index.
|
213
|
+
with_object( {} ) { | (cell , index) , row_hash |
|
214
|
+
row_hash[ headers[ index ] ] = cell.content.gsub( /\u00A0/,"" )
|
215
|
+
}
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: confluence_helper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nigel Thorne
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-10 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.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: nokogiri
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.6.4
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.6.4
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: envied
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.8.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.8.1
|
69
|
+
description: I needed to pull data from Confluence for several internal web services.
|
70
|
+
This is intended to pull together all helper logic I need to get the data I want.
|
71
|
+
email:
|
72
|
+
- github@nigelthorne.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- confluence_helper.gemspec
|
83
|
+
- lib/confluence_helper.rb
|
84
|
+
- lib/confluence_helper/version.rb
|
85
|
+
homepage: ''
|
86
|
+
licenses:
|
87
|
+
- MIT
|
88
|
+
metadata: {}
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
requirements: []
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 2.4.2
|
106
|
+
signing_key:
|
107
|
+
specification_version: 4
|
108
|
+
summary: Helper for accessing data from within your Confluence wiki
|
109
|
+
test_files: []
|