dbdoc 0.2.0 → 0.3.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 +4 -4
- data/.rubocop.yml +25 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +1 -1
- data/Rakefile +2 -0
- data/bin/dbdoc +2 -0
- data/dbdoc.gemspec +2 -0
- data/lib/confluence/api.rb +6 -4
- data/lib/confluence/markdown_converter.rb +10 -3
- data/lib/{dbdoc → confluence}/uploader.rb +50 -24
- data/lib/dbdoc.rb +3 -1
- data/lib/dbdoc/cli.rb +81 -122
- data/lib/dbdoc/config.rb +11 -18
- data/lib/dbdoc/constants.rb +2 -0
- data/lib/dbdoc/folder_initializer.rb +61 -0
- data/lib/dbdoc/manager.rb +73 -57
- data/lib/dbdoc/version.rb +3 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e952e625ed6d66c0643f58a94be599f509e9c73827013a0f39a54145281b3951
|
4
|
+
data.tar.gz: dcb4a347c77a5f7524ca194f702cc5412b36cdb91b7363e871d96197fc9aff93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3fd633a549a92f0bc6194bba2a27c93d0275e65b149cdeeec4592bd88ce66a41a695e2b2a35db8607f7476d4d85c5d4c7535441bf6ab58f7e67b2865116e45d
|
7
|
+
data.tar.gz: d61c04bc79f94523e3a7a3a19711f2778d99be2346c02998d15036b079844f942f910f4a16222a124ba1edd9348ea28a4cb627a8aec58aab4517575def440ef0
|
data/.rubocop.yml
CHANGED
@@ -1,2 +1,27 @@
|
|
1
1
|
Style/StringLiterals:
|
2
2
|
EnforcedStyle: double_quotes
|
3
|
+
|
4
|
+
Metrics/ClassLength:
|
5
|
+
Max: 200
|
6
|
+
|
7
|
+
Metrics/MethodLength:
|
8
|
+
Max: 50
|
9
|
+
|
10
|
+
Style/Documentation:
|
11
|
+
Exclude:
|
12
|
+
- lib/confluence/api.rb
|
13
|
+
- lib/dbdoc/cli.rb
|
14
|
+
|
15
|
+
Metrics/AbcSize:
|
16
|
+
Exclude:
|
17
|
+
- lib/dbdoc/cli.rb
|
18
|
+
|
19
|
+
Layout/FirstHashElementIndentation:
|
20
|
+
EnforcedStyle: consistent
|
21
|
+
|
22
|
+
Metrics/BlockLength:
|
23
|
+
Exclude:
|
24
|
+
- spec/**/*_spec.rb
|
25
|
+
|
26
|
+
Security/YAMLLoad:
|
27
|
+
Enabled: false
|
data/Gemfile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source "https://rubygems.org"
|
2
4
|
|
3
5
|
gemspec
|
@@ -8,7 +10,7 @@ gem "kramdown"
|
|
8
10
|
group :development do
|
9
11
|
gem "byebug"
|
10
12
|
gem "coveralls", require: false
|
11
|
-
gem "rubocop"
|
12
13
|
gem "rake", "~> 12.0"
|
13
14
|
gem "rspec", "~> 3.0"
|
15
|
+
gem "rubocop"
|
14
16
|
end
|
data/Gemfile.lock
CHANGED
data/Rakefile
CHANGED
data/bin/dbdoc
CHANGED
data/dbdoc.gemspec
CHANGED
data/lib/confluence/api.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "httparty"
|
2
4
|
require "json"
|
3
5
|
require "yaml"
|
@@ -22,7 +24,7 @@ module Confluence
|
|
22
24
|
"https://dbdoc.atlassian.net/wiki/rest/api/content/#{page_id}", {
|
23
25
|
headers: {
|
24
26
|
"Authorization" => "Basic #{basic_auth}",
|
25
|
-
"Content-Type"
|
27
|
+
"Content-Type" => "application/json"
|
26
28
|
}
|
27
29
|
}
|
28
30
|
)
|
@@ -36,7 +38,7 @@ module Confluence
|
|
36
38
|
"https://dbdoc.atlassian.net/wiki/rest/api/content/?&spaceKey=#{@space}", {
|
37
39
|
headers: {
|
38
40
|
"Authorization" => "Basic #{basic_auth}",
|
39
|
-
"Content-Type"
|
41
|
+
"Content-Type" => "application/json"
|
40
42
|
}
|
41
43
|
}
|
42
44
|
)
|
@@ -67,7 +69,7 @@ module Confluence
|
|
67
69
|
"https://dbdoc.atlassian.net/wiki/rest/api/content/#{page_id}", {
|
68
70
|
headers: {
|
69
71
|
"Authorization" => "Basic #{basic_auth}",
|
70
|
-
"Content-Type"
|
72
|
+
"Content-Type" => "application/json"
|
71
73
|
},
|
72
74
|
body: payload.to_json
|
73
75
|
}
|
@@ -115,7 +117,7 @@ module Confluence
|
|
115
117
|
"https://dbdoc.atlassian.net/wiki/rest/api/content/", {
|
116
118
|
headers: {
|
117
119
|
"Authorization" => "Basic #{basic_auth}",
|
118
|
-
"Content-Type"
|
120
|
+
"Content-Type" => "application/json"
|
119
121
|
},
|
120
122
|
body: payload.to_json
|
121
123
|
}
|
@@ -1,8 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "kramdown"
|
2
4
|
|
3
5
|
module Confluence
|
6
|
+
# By default, all documentation should be written in a normal
|
7
|
+
# Markdown format. Later, when we're generating/uploading final documentation,
|
8
|
+
# we'll use Confluence::MarkdownConverter to convert normal Markdown
|
9
|
+
# into Confluence specific Markdown
|
10
|
+
# https://confluence.atlassian.com/bitbucketserver/markdown-syntax-guide-776639995.html
|
4
11
|
class MarkdownConverter
|
5
|
-
CONVERTERS = %w
|
12
|
+
CONVERTERS = %w[
|
6
13
|
convert_h1
|
7
14
|
convert_h2
|
8
15
|
convert_h3
|
@@ -12,7 +19,7 @@ module Confluence
|
|
12
19
|
convert_italic
|
13
20
|
convert_code
|
14
21
|
convert_unordered_list
|
15
|
-
|
22
|
+
].freeze
|
16
23
|
|
17
24
|
def convert(original_markdown)
|
18
25
|
return if original_markdown.nil?
|
@@ -81,7 +88,7 @@ module Confluence
|
|
81
88
|
# TODO: add nested list convertion
|
82
89
|
# TODO: add numbered list convertion
|
83
90
|
|
84
|
-
# TODO
|
91
|
+
# TODO: add emoji
|
85
92
|
# https://support.atlassian.com/confluence-cloud/docs/use-symbols-emojis-and-special-characters/
|
86
93
|
end
|
87
94
|
end
|
@@ -1,11 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "dbdoc/constants"
|
2
4
|
require_relative "../confluence/markdown_converter"
|
3
5
|
require_relative "../confluence/api"
|
4
6
|
|
5
|
-
module
|
7
|
+
module Confluence
|
8
|
+
# Confluence::Uploader class knows how to uploaded
|
9
|
+
# documentation to the Confluence Space provided in the config.yml.
|
10
|
+
#
|
11
|
+
# Uploader creates a root page with the database name from config.yml
|
12
|
+
# and creates nested pages for schemas and tables.
|
13
|
+
#
|
14
|
+
# A Confluence ID of each page created by dbdoc
|
15
|
+
# is logged to pages.yml file stored in the user's folder
|
16
|
+
# with the documentation.
|
6
17
|
class Uploader
|
7
|
-
def initialize(
|
8
|
-
@config =
|
18
|
+
def initialize(local_path: Dir.pwd)
|
19
|
+
@config = Dbdoc::Config.new(local_path: local_path).load
|
9
20
|
@confluence_api = Confluence::Api.new
|
10
21
|
@doc_folder = File.join(Dir.pwd, "doc")
|
11
22
|
end
|
@@ -19,8 +30,17 @@ module Dbdoc
|
|
19
30
|
@confluence_api.existing_pages["results"]
|
20
31
|
end
|
21
32
|
|
33
|
+
def print_space_pages
|
34
|
+
space_pages.each do |page|
|
35
|
+
page_title = page["title"]
|
36
|
+
page_id = page["id"]
|
37
|
+
|
38
|
+
puts "#{page_title}: #{page_id}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
22
42
|
def clear_confluence_space
|
23
|
-
|
43
|
+
YAML.load(File.read(page_ids_file))
|
24
44
|
|
25
45
|
space_pages.each do |page|
|
26
46
|
page_key = page["title"]
|
@@ -28,24 +48,23 @@ module Dbdoc
|
|
28
48
|
|
29
49
|
puts "--> Deleting #{page_key} #{page_id}"
|
30
50
|
|
31
|
-
if @confluence_api.delete_page(page_id: page_id)
|
32
|
-
unlog_page_id(key: page_key)
|
33
|
-
end
|
51
|
+
unlog_page_id(key: page_key) if @confluence_api.delete_page(page_id: page_id)
|
34
52
|
end
|
35
53
|
end
|
36
54
|
|
37
55
|
private
|
38
56
|
|
57
|
+
# rubocop:disable Metrics/AbcSize
|
39
58
|
def delete_pages_for_dropped_schemas_or_tables
|
40
59
|
uploaded_pages = YAML.load(File.read(page_ids_file))
|
41
60
|
|
42
|
-
uploaded_pages.each do |key,
|
61
|
+
uploaded_pages.each do |key, _params|
|
43
62
|
next if key == "root"
|
44
63
|
|
45
64
|
if key.start_with?("schema:")
|
46
65
|
schema_name = key.gsub("schema:", "")
|
47
66
|
|
48
|
-
unless Dir.
|
67
|
+
unless Dir.exist?(File.join(@doc_folder, schema_name))
|
49
68
|
page_id = uploaded_pages[key][:page_id]
|
50
69
|
puts "--> delete page #{key}: #{page_id}"
|
51
70
|
@confluence_api.delete_page(page_id: page_id)
|
@@ -54,7 +73,7 @@ module Dbdoc
|
|
54
73
|
elsif key.start_with?("table:")
|
55
74
|
schema_name, table_name = key.gsub("table:", "").split(".")
|
56
75
|
|
57
|
-
unless Dir.
|
76
|
+
unless Dir.exist?(File.join(@doc_folder, schema_name, table_name))
|
58
77
|
page_id = uploaded_pages[key][:page_id]
|
59
78
|
puts "--> delete page #{key}: #{page_id}"
|
60
79
|
@confluence_api.delete_page(page_id: page_id)
|
@@ -63,6 +82,7 @@ module Dbdoc
|
|
63
82
|
end
|
64
83
|
end
|
65
84
|
end
|
85
|
+
# rubocop:enable Metrics/AbcSize
|
66
86
|
|
67
87
|
def create_or_updates_pages
|
68
88
|
root_page_id = create_root_db_page[:page_id]
|
@@ -85,7 +105,7 @@ module Dbdoc
|
|
85
105
|
def page_ids_file
|
86
106
|
file = File.join(Dir.pwd, "page_ids.yml")
|
87
107
|
|
88
|
-
unless File.
|
108
|
+
unless File.exist?(file)
|
89
109
|
File.open(file, "w") do |f|
|
90
110
|
f.puts("--- {}")
|
91
111
|
end
|
@@ -111,7 +131,7 @@ module Dbdoc
|
|
111
131
|
version: 0
|
112
132
|
}
|
113
133
|
|
114
|
-
if page_ids
|
134
|
+
if page_ids.dig(key, :version).zero?
|
115
135
|
puts "--> create page #{key}: #{page_id}"
|
116
136
|
else
|
117
137
|
puts "--> update page #{key}: #{page_id}"
|
@@ -137,9 +157,11 @@ module Dbdoc
|
|
137
157
|
def create_root_db_page
|
138
158
|
page_id = latest_page_id(key: "root")
|
139
159
|
|
140
|
-
|
141
|
-
|
142
|
-
|
160
|
+
if page_id
|
161
|
+
return {
|
162
|
+
page_id: page_id
|
163
|
+
}
|
164
|
+
end
|
143
165
|
|
144
166
|
db_name = @config["db"]["name"]
|
145
167
|
@confluence_api.create_page(
|
@@ -180,13 +202,16 @@ module Dbdoc
|
|
180
202
|
Confluence::MarkdownConverter.new.convert(input)
|
181
203
|
end
|
182
204
|
|
205
|
+
# rubocop:disable Metrics/AbcSize
|
183
206
|
def upload_table(schema_name:, table_name:, schema_page_id:)
|
184
207
|
table_folder = File.join(@doc_folder, schema_name, table_name)
|
185
208
|
|
186
209
|
table_description = markdown(File.read(File.join(table_folder, "description.md")))
|
187
210
|
|
188
211
|
examples_folder = File.join(table_folder, "examples")
|
189
|
-
table_examples = Dir[File.join(examples_folder, "*.md")].map
|
212
|
+
table_examples = Dir[File.join(examples_folder, "*.md")].map do |f|
|
213
|
+
markdown(File.read(f))
|
214
|
+
end
|
190
215
|
|
191
216
|
columns_markdown_template_file = File.join(DBDOC_HOME, "doc_files", "columns.md.erb")
|
192
217
|
|
@@ -205,18 +230,18 @@ module Dbdoc
|
|
205
230
|
columns: columns_doc
|
206
231
|
})
|
207
232
|
|
208
|
-
page_body =
|
209
|
-
h2. Description
|
233
|
+
page_body = <<~MARKDOWN
|
234
|
+
h2. Description
|
210
235
|
|
211
|
-
#{table_description}
|
236
|
+
#{table_description}
|
212
237
|
|
213
|
-
h2. Columns
|
238
|
+
h2. Columns
|
214
239
|
|
215
|
-
#{columns_table}
|
240
|
+
#{columns_table}
|
216
241
|
|
217
|
-
h2. Examples
|
242
|
+
h2. Examples
|
218
243
|
|
219
|
-
#{table_examples.join("\n")
|
244
|
+
#{table_examples.join("\n")}
|
220
245
|
MARKDOWN
|
221
246
|
|
222
247
|
page_title = schema_name == "public" ? table_name : "#{schema_name}.#{table_name}"
|
@@ -225,7 +250,7 @@ h2. Examples
|
|
225
250
|
page_id = latest_page_id(key: page_key)
|
226
251
|
|
227
252
|
if page_id
|
228
|
-
|
253
|
+
@confluence_api.update_page(
|
229
254
|
page_id: page_id,
|
230
255
|
page_title: page_title,
|
231
256
|
body: page_body,
|
@@ -245,5 +270,6 @@ h2. Examples
|
|
245
270
|
log_page_id(key: "table:#{schema_name}.#{table_name}", page_id: table_page_id)
|
246
271
|
end
|
247
272
|
end
|
273
|
+
# rubocop:enable Metrics/AbcSize
|
248
274
|
end
|
249
275
|
end
|
data/lib/dbdoc.rb
CHANGED
data/lib/dbdoc/cli.rb
CHANGED
@@ -1,157 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "optparse"
|
2
4
|
|
3
5
|
$LOAD_PATH << File.expand_path(__dir__)
|
4
6
|
|
5
7
|
module Dbdoc
|
6
8
|
class CLI
|
9
|
+
COMMANDS = {
|
10
|
+
install: %(
|
11
|
+
Generates all necessary config files and documentation folders.
|
12
|
+
|
13
|
+
Run this command in an empty directory.
|
14
|
+
),
|
15
|
+
query: %(
|
16
|
+
Prints a query that you need to run in the database \
|
17
|
+
you're going to document.
|
18
|
+
|
19
|
+
Export the result of this query to the "schema.csv" file \
|
20
|
+
and copy it over to the "schema" folder for processing.
|
21
|
+
),
|
22
|
+
plan: %(
|
23
|
+
Shows you what columns/tables/schemas are new and \
|
24
|
+
going to be added/deleted from the documentation.
|
25
|
+
),
|
26
|
+
apply: %(
|
27
|
+
Generates boilerplate documentation for newly added \
|
28
|
+
columns/tables/schemas.
|
29
|
+
|
30
|
+
Drops documentation for columns/tables/schemas that \
|
31
|
+
were deleted from the database.
|
32
|
+
),
|
33
|
+
"confluence:upload": %(
|
34
|
+
Uploads current documentation to Confluence: pages for \
|
35
|
+
new tables/schemas will be added, pages for dropped tables/schemas \
|
36
|
+
will be deleted from your Confluence space.
|
37
|
+
),
|
38
|
+
"confluece:pages": %(
|
39
|
+
Lists all pages in your dbdoc Confluence space \
|
40
|
+
(created manually or via dbdoc).
|
41
|
+
),
|
42
|
+
"confluence:clear": %(
|
43
|
+
IMPORTANT This command will delete ALL pages from \
|
44
|
+
the Confluence space (pages created via dbdoc AND pages that were added manually).
|
45
|
+
),
|
46
|
+
todo: %(
|
47
|
+
Shows you the documentation that needs to be written.
|
48
|
+
)
|
49
|
+
}.freeze
|
50
|
+
|
51
|
+
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
7
52
|
def run(args = [])
|
8
|
-
if args.first == "
|
9
|
-
|
10
|
-
|
11
|
-
schema_folder = File.join(Dir.pwd, "schema")
|
12
|
-
unless Dir.exists?(schema_folder)
|
13
|
-
Dir.mkdir(schema_folder)
|
14
|
-
end
|
15
|
-
|
16
|
-
doc_folder = File.join(Dir.pwd, "doc")
|
17
|
-
unless Dir.exists?(doc_folder)
|
18
|
-
Dir.mkdir(doc_folder)
|
19
|
-
end
|
20
|
-
|
21
|
-
target_file = File.join(Dir.pwd, "config.yml")
|
22
|
-
config_file = File.join(File.expand_path(__dir__), "../..", "config", "default.yml")
|
23
|
-
|
24
|
-
FileUtils.cp(config_file, target_file) unless File.exists?(target_file)
|
25
|
-
|
26
|
-
target_file = File.join(Dir.pwd, ".gitignore")
|
27
|
-
config_file = File.join(File.expand_path(__dir__), "../..", "config", "gitignore.template")
|
28
|
-
|
29
|
-
FileUtils.cp(config_file, target_file) unless File.exists?(target_file)
|
30
|
-
|
31
|
-
target_file = File.join(Dir.pwd, "confluence.yml")
|
32
|
-
config_file = File.join(File.expand_path(__dir__), "../..", "config", "confluence.yml")
|
33
|
-
|
34
|
-
FileUtils.cp(config_file, target_file) unless File.exists?(target_file)
|
35
|
-
|
36
|
-
0
|
53
|
+
if args.first == "install"
|
54
|
+
Dbdoc::FolderInitializer.new.init
|
37
55
|
elsif args.first == "query"
|
38
|
-
|
39
|
-
|
40
|
-
config = Dbdoc::Config.load
|
41
|
-
config.merge!(options)
|
42
|
-
|
43
|
-
db_type = config["db"]["type"]
|
44
|
-
query_file = File.join(File.expand_path(__dir__), "../..", "config", "schema_queries", "#{db_type}.sql")
|
45
|
-
query = File.read(query_file)
|
46
|
-
|
47
|
-
puts query
|
48
|
-
|
49
|
-
0
|
56
|
+
puts manager.query
|
50
57
|
elsif args.first == "plan"
|
51
|
-
|
52
|
-
|
53
|
-
config = Dbdoc::Config.load
|
54
|
-
config.merge!(options)
|
58
|
+
plan = manager.plan
|
55
59
|
|
56
|
-
|
57
|
-
|
60
|
+
puts "--> New columns:"
|
61
|
+
plan[:new_columns].each do |column|
|
62
|
+
puts column
|
63
|
+
end
|
58
64
|
|
59
|
-
|
65
|
+
puts "--> Columns to drop:"
|
66
|
+
plan[:columns_to_drop].each do |column|
|
67
|
+
puts column
|
68
|
+
end
|
60
69
|
elsif args.first == "apply"
|
61
|
-
options = extract_options(args)
|
62
|
-
|
63
|
-
config = Dbdoc::Config.load
|
64
|
-
config.merge!(options)
|
65
|
-
|
66
|
-
manager = Dbdoc::Manager.new(config: config)
|
67
70
|
manager.apply
|
68
|
-
|
69
|
-
0
|
70
|
-
elsif args.first == "upload"
|
71
|
-
options = extract_options(args)
|
72
|
-
|
73
|
-
config = Dbdoc::Config.load
|
74
|
-
config.merge!(options)
|
75
|
-
|
76
|
-
uploader = Dbdoc::Uploader.new(config: config)
|
71
|
+
elsif args.first == "confluence:upload"
|
77
72
|
uploader.upload
|
78
|
-
|
79
|
-
|
80
|
-
elsif args.first == "
|
81
|
-
options = extract_options(args)
|
82
|
-
|
83
|
-
config = Dbdoc::Config.load
|
84
|
-
config.merge!(options)
|
85
|
-
|
86
|
-
uploader = Dbdoc::Uploader.new(config: config)
|
87
|
-
pages = uploader.space_pages
|
88
|
-
|
89
|
-
pages.each do |page|
|
90
|
-
page_title = page["title"]
|
91
|
-
page_id = page["id"]
|
92
|
-
|
93
|
-
puts "#{page_title}: #{page_id}"
|
94
|
-
end
|
95
|
-
|
96
|
-
0
|
97
|
-
elsif args.first == "clear_space"
|
98
|
-
options = extract_options(args)
|
99
|
-
|
100
|
-
config = Dbdoc::Config.load
|
101
|
-
config.merge!(options)
|
102
|
-
|
103
|
-
uploader = Dbdoc::Uploader.new(config: config)
|
73
|
+
elsif args.first == "confluence:pages"
|
74
|
+
uploader.print_space_pages
|
75
|
+
elsif args.first == "confluence:clear"
|
104
76
|
uploader.clear_confluence_space
|
105
|
-
|
106
|
-
0
|
107
77
|
elsif args.first == "todo"
|
108
|
-
options = extract_options(args)
|
109
|
-
|
110
|
-
config = Dbdoc::Config.load
|
111
|
-
config.merge!(options)
|
112
|
-
|
113
|
-
manager = Dbdoc::Manager.new(config: config)
|
114
78
|
manager.todo
|
115
|
-
|
116
|
-
0
|
117
79
|
elsif args.first == "help"
|
118
|
-
|
119
|
-
|
120
|
-
|
80
|
+
print_help
|
81
|
+
elsif args.first == "version"
|
82
|
+
puts Dbdoc::VERSION
|
121
83
|
end
|
122
84
|
|
123
85
|
0
|
124
86
|
end
|
87
|
+
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
125
88
|
|
126
89
|
private
|
127
90
|
|
128
|
-
# This method is needed to unindent
|
129
|
-
# ["here document"](https://en.wikibooks.org/wiki/Ruby_Programming/Here_documents)
|
130
|
-
# help description.
|
131
|
-
#
|
132
91
|
def unindent(str)
|
133
92
|
str.gsub(/^#{str.scan(/^[ \t]+(?=\S)/).min}/, "")
|
134
93
|
end
|
135
94
|
|
136
|
-
def
|
137
|
-
|
138
|
-
|
139
|
-
OptionParser.new do |opts|
|
140
|
-
opts.banner = unindent(<<-TEXT)
|
141
|
-
dbdoc help
|
95
|
+
def manager
|
96
|
+
@manager ||= Dbdoc::Manager.new
|
97
|
+
end
|
142
98
|
|
143
|
-
|
99
|
+
def uploader
|
100
|
+
@uploader ||= Confluence::Uploader.new
|
101
|
+
end
|
144
102
|
|
145
|
-
|
146
|
-
|
103
|
+
def print_help
|
104
|
+
puts unindent <<-TEXT
|
105
|
+
Usage: dbdoc [command]
|
106
|
+
TEXT
|
107
|
+
puts
|
147
108
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
153
|
-
|
154
|
-
options
|
109
|
+
COMMANDS.each do |command, description|
|
110
|
+
puts "dbdoc #{command}"
|
111
|
+
puts
|
112
|
+
puts unindent(description)
|
113
|
+
end
|
155
114
|
end
|
156
115
|
end
|
157
116
|
end
|
data/lib/dbdoc/config.rb
CHANGED
@@ -1,28 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "yaml"
|
2
4
|
require "dbdoc/constants"
|
3
5
|
|
4
6
|
module Dbdoc
|
7
|
+
# Dbdoc::Config class knows how to load the default config from the
|
8
|
+
# dbdoc gem folder. Later, if needed, this class could be used to merge
|
9
|
+
# user-defined config with the default one.
|
5
10
|
class Config
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
class << self
|
10
|
-
# Be default gem will try to load config file in user's project folder.
|
11
|
-
# Then user's config (or empty object) will be merge with the default config
|
12
|
-
# from gem's folder.
|
13
|
-
#
|
14
|
-
def load
|
15
|
-
user_config = File.exist?(user_file) ? YAML.safe_load(File.read(user_file)) : {}
|
16
|
-
default_config = YAML.safe_load(File.read(DEFAULT_FILE))
|
17
|
-
|
18
|
-
default_config.merge(user_config)
|
19
|
-
end
|
11
|
+
def initialize(local_path: Dir.pwd)
|
12
|
+
@local_path = local_path
|
13
|
+
end
|
20
14
|
|
21
|
-
|
15
|
+
def load
|
16
|
+
config_file = File.join(@local_path, "config.yml")
|
22
17
|
|
23
|
-
|
24
|
-
File.join(Dir.pwd, FILE_NAME)
|
25
|
-
end
|
18
|
+
YAML.load(File.read(config_file))
|
26
19
|
end
|
27
20
|
end
|
28
21
|
end
|
data/lib/dbdoc/constants.rb
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "fileutils"
|
5
|
+
require "dbdoc/constants"
|
6
|
+
|
7
|
+
module Dbdoc
|
8
|
+
# Dbdoc::FolderInitializer class knows how to turn an emtpy
|
9
|
+
# folder into a dbdoc folder.
|
10
|
+
#
|
11
|
+
class FolderInitializer
|
12
|
+
def initialize(local_path: Dir.pwd)
|
13
|
+
@config = Dbdoc::Config.new(local_path: local_path).load
|
14
|
+
end
|
15
|
+
|
16
|
+
def init
|
17
|
+
create_schema_folder
|
18
|
+
create_doc_folder
|
19
|
+
create_config_file
|
20
|
+
create_gitignore_file
|
21
|
+
create_confluence_config_file
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# schema folder is the one user copies the schema.csv to
|
27
|
+
# before updating/generating documentation
|
28
|
+
def create_schema_folder
|
29
|
+
schema_folder = File.join(Dir.pwd, "schema")
|
30
|
+
Dir.mkdir(schema_folder) unless Dir.exist?(schema_folder)
|
31
|
+
end
|
32
|
+
|
33
|
+
# doc folder stores all the database documentation files
|
34
|
+
def create_doc_folder
|
35
|
+
doc_folder = File.join(Dir.pwd, "doc")
|
36
|
+
Dir.mkdir(doc_folder) unless Dir.exist?(doc_folder)
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_file(default_file_name, target_file_name)
|
40
|
+
target_file = File.join(Dir.pwd, target_file_name)
|
41
|
+
|
42
|
+
return if File.exist?(target_file)
|
43
|
+
|
44
|
+
config_file = File.join(File.expand_path(__dir__), "../..", "config", default_file_name)
|
45
|
+
|
46
|
+
FileUtils.cp(config_file, target_file)
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_config_file
|
50
|
+
create_file("default.yml", "config.yml")
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_gitignore_file
|
54
|
+
create_file("gitignore.template", ".gitignore")
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_confluence_config_file
|
58
|
+
create_file("confluence.yml", "confluence.yml")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/dbdoc/manager.rb
CHANGED
@@ -1,31 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "yaml"
|
4
|
+
require "erb"
|
5
|
+
require "fileutils"
|
2
6
|
require "dbdoc/constants"
|
3
7
|
|
4
8
|
module Dbdoc
|
9
|
+
# Dbdoc::Manager class manages database schema documentation.
|
10
|
+
#
|
11
|
+
# It knows how to generate and update documentation based on the database
|
12
|
+
# schema generated by the schema query for user's database.
|
5
13
|
class Manager
|
6
|
-
def initialize(
|
7
|
-
@
|
14
|
+
def initialize(local_path: Dir.pwd)
|
15
|
+
@local_path = local_path
|
16
|
+
@config = Dbdoc::Config.new(local_path: local_path).load
|
8
17
|
end
|
9
18
|
|
10
|
-
def plan
|
11
|
-
|
12
|
-
puts
|
13
|
-
puts
|
14
|
-
|
15
|
-
input_schema = read_input_schema.map { |r| r.first(4) }.map { |r| r.join(":") }
|
19
|
+
def plan
|
20
|
+
input_schema = read_input_schema.map { |r| r.first(4).join(":") }
|
16
21
|
current_schema = read_documented_schema
|
17
22
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
puts "--> Columns to drop:"
|
24
|
-
pp current_schema - input_schema
|
23
|
+
{
|
24
|
+
new_columns: input_schema - current_schema,
|
25
|
+
columns_to_drop: current_schema - input_schema
|
26
|
+
}
|
25
27
|
end
|
26
28
|
|
27
29
|
def todo
|
28
|
-
doc_folder_files = File.join(
|
30
|
+
doc_folder_files = File.join(@local_path, "doc", "**/*")
|
29
31
|
|
30
32
|
Dir[doc_folder_files].each do |file|
|
31
33
|
next if file == "."
|
@@ -33,16 +35,24 @@ module Dbdoc
|
|
33
35
|
next if File.directory?(file)
|
34
36
|
|
35
37
|
File.read(file).split("\n").each_with_index do |line, i|
|
36
|
-
|
37
|
-
relative_path = file.gsub(Dir.pwd, "")
|
38
|
+
next unless line.include?("TODO")
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
relative_path = file.gsub(@local_path, "")
|
41
|
+
|
42
|
+
puts "#{relative_path}:#{i + 1}"
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
45
|
-
def
|
47
|
+
def query
|
48
|
+
db_type = @config["db"]["type"]
|
49
|
+
query_file = File.join(File.expand_path(__dir__), "../..", "config", "schema_queries", "#{db_type}.sql")
|
50
|
+
|
51
|
+
File.read(query_file)
|
52
|
+
end
|
53
|
+
|
54
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
|
55
|
+
def apply
|
46
56
|
puts "--> APPLY"
|
47
57
|
puts
|
48
58
|
puts
|
@@ -53,14 +63,16 @@ module Dbdoc
|
|
53
63
|
added_columns = input_schema - current_schema
|
54
64
|
dropped_columns = current_schema - input_schema
|
55
65
|
|
56
|
-
doc_folder = File.join(
|
66
|
+
doc_folder = File.join(@local_path, "doc")
|
67
|
+
|
68
|
+
Dir.mkdir(doc_folder) unless Dir.exist?(doc_folder)
|
57
69
|
|
58
70
|
## DROP COLUMNS
|
59
71
|
dropped_columns.each do |column|
|
60
|
-
schema_name, table_name, column_name
|
72
|
+
schema_name, table_name, column_name = column.split(":")
|
61
73
|
|
62
74
|
columns_file = File.join(doc_folder, schema_name, table_name, "columns.yml")
|
63
|
-
next unless File.
|
75
|
+
next unless File.exist?(columns_file)
|
64
76
|
|
65
77
|
columns = YAML.load(File.read(columns_file))
|
66
78
|
columns.reject! { |c| c[:name] == column_name }
|
@@ -87,7 +99,7 @@ module Dbdoc
|
|
87
99
|
next unless File.directory?(table_folder)
|
88
100
|
|
89
101
|
columns_file = File.join(table_folder, "columns.yml")
|
90
|
-
next unless File.
|
102
|
+
next unless File.exist?(columns_file)
|
91
103
|
|
92
104
|
columns = YAML.load(File.read(columns_file))
|
93
105
|
|
@@ -111,16 +123,18 @@ module Dbdoc
|
|
111
123
|
|
112
124
|
create_new_columns(added_columns)
|
113
125
|
end
|
126
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
|
114
127
|
|
115
128
|
private
|
116
129
|
|
117
130
|
def input_schema
|
118
|
-
File.read(File.join(
|
131
|
+
File.read(File.join(@local_path, "schema", "schema.csv"))
|
119
132
|
end
|
120
133
|
|
134
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
|
121
135
|
def read_input_schema
|
122
136
|
rows = input_schema.split("\n")
|
123
|
-
with_header = rows[0].include?("
|
137
|
+
with_header = rows[0].include?("table_schema")
|
124
138
|
|
125
139
|
rows.shift if with_header
|
126
140
|
|
@@ -128,38 +142,37 @@ module Dbdoc
|
|
128
142
|
r.split(",").map(&:strip).map { |c| c.gsub('"', "") }.first(5)
|
129
143
|
end
|
130
144
|
|
131
|
-
config
|
132
|
-
|
133
|
-
if @config["ignorelist"]
|
134
|
-
@config["ignorelist"].map { |r| r.split(/[\.\#]/) }.each do |b|
|
135
|
-
schema_pattern, table_pattern, column_pattern = b
|
136
|
-
|
137
|
-
rows.reject! do |row|
|
138
|
-
schema_name, table_name, column_name, _, _ = row
|
145
|
+
@config["ignorelist"]&.map { |r| r.split(/[\.\#]/) }&.each do |b|
|
146
|
+
schema_pattern, table_pattern, column_pattern = b
|
139
147
|
|
140
|
-
|
141
|
-
|
142
|
-
end
|
148
|
+
rows.reject! do |row|
|
149
|
+
schema_name, table_name, column_name, = row
|
143
150
|
|
144
|
-
|
145
|
-
|
146
|
-
|
151
|
+
if column_pattern
|
152
|
+
next unless column_name =~ Regexp.new(column_pattern.gsub("*", ".*"))
|
153
|
+
end
|
147
154
|
|
148
|
-
|
149
|
-
|
150
|
-
|
155
|
+
if table_pattern
|
156
|
+
next unless table_name =~ Regexp.new(table_pattern.gsub("*", ".*"))
|
157
|
+
end
|
151
158
|
|
152
|
-
|
159
|
+
if schema_pattern
|
160
|
+
next unless schema_name =~ Regexp.new(schema_pattern.gsub("*", ".*"))
|
153
161
|
end
|
162
|
+
|
163
|
+
true
|
154
164
|
end
|
155
165
|
end
|
156
166
|
|
157
167
|
rows
|
158
168
|
end
|
169
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
|
159
170
|
|
171
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
|
160
172
|
def read_documented_schema
|
161
|
-
doc_folder = File.join(
|
173
|
+
doc_folder = File.join(@local_path, "doc")
|
162
174
|
|
175
|
+
return [] unless Dir.exist?(doc_folder)
|
163
176
|
return [] if Dir.empty?(doc_folder)
|
164
177
|
|
165
178
|
keys = []
|
@@ -178,9 +191,9 @@ module Dbdoc
|
|
178
191
|
next unless File.directory?(table_folder)
|
179
192
|
|
180
193
|
columns_file = File.join(table_folder, "columns.yml")
|
181
|
-
next unless File.
|
194
|
+
next unless File.exist?(columns_file)
|
182
195
|
|
183
|
-
columns = YAML.load(File.read(columns_file))
|
196
|
+
columns = YAML.load(File.read(columns_file), [Symbol])
|
184
197
|
columns.each do |column|
|
185
198
|
keys.push([
|
186
199
|
schema_name,
|
@@ -194,9 +207,11 @@ module Dbdoc
|
|
194
207
|
|
195
208
|
keys
|
196
209
|
end
|
210
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
|
197
211
|
|
212
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
|
198
213
|
def create_new_columns(added_columns)
|
199
|
-
doc_folder = File.join(
|
214
|
+
doc_folder = File.join(@local_path, "doc")
|
200
215
|
|
201
216
|
added_columns.map! { |r| r.split(":") }
|
202
217
|
new_columns = read_input_schema.select do |row|
|
@@ -242,17 +257,18 @@ module Dbdoc
|
|
242
257
|
# 4. create table columns.yml
|
243
258
|
columns_yaml = File.join(table_folder, "columns.yml")
|
244
259
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
260
|
+
next if File.exist?(columns_yaml)
|
261
|
+
|
262
|
+
columns_erb_tamplate_file = File.join(DBDOC_HOME, "doc_files", "columns.yml.erb")
|
263
|
+
columns_yaml_template = ERB.new(File.read(columns_erb_tamplate_file), nil, "-")
|
264
|
+
File.open(columns_yaml, "w") do |f|
|
265
|
+
f.puts columns_yaml_template.result_with_hash({
|
266
|
+
columns: columns
|
267
|
+
})
|
253
268
|
end
|
254
269
|
end
|
255
270
|
end
|
256
271
|
end
|
272
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize
|
257
273
|
end
|
258
274
|
end
|
data/lib/dbdoc/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbdoc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anatoli Makarevich
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Dbdoc is a tool to keep your database documentation up-to-date and version
|
14
14
|
controlled.
|
@@ -45,12 +45,13 @@ files:
|
|
45
45
|
- doc_files/table_example.md
|
46
46
|
- lib/confluence/api.rb
|
47
47
|
- lib/confluence/markdown_converter.rb
|
48
|
+
- lib/confluence/uploader.rb
|
48
49
|
- lib/dbdoc.rb
|
49
50
|
- lib/dbdoc/cli.rb
|
50
51
|
- lib/dbdoc/config.rb
|
51
52
|
- lib/dbdoc/constants.rb
|
53
|
+
- lib/dbdoc/folder_initializer.rb
|
52
54
|
- lib/dbdoc/manager.rb
|
53
|
-
- lib/dbdoc/uploader.rb
|
54
55
|
- lib/dbdoc/version.rb
|
55
56
|
homepage: https://github.com/sqlhabit/dbdoc
|
56
57
|
licenses:
|