goodot 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Binary file
@@ -0,0 +1,7 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2010-2016 GoodData Corporation. All rights reserved.
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ $LOAD_PATH.unshift File.dirname(__FILE__)
@@ -0,0 +1,33 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2010-2016 GoodData Corporation. All rights reserved.
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ require 'gli'
8
+ require 'pathname'
9
+ require 'pp'
10
+
11
+ require_relative 'shared'
12
+ require_relative '../version'
13
+
14
+ module GoodData
15
+ module GoodotCLI
16
+ include GLI::App
17
+ extend self
18
+
19
+ def self.launch(args)
20
+ exit run args
21
+ end
22
+
23
+ subcommand_option_handling :normal
24
+ program_desc 'Toolset for helping with lifecycle management of the project'
25
+ end
26
+ end
27
+
28
+ cmds = File.absolute_path(File.join(File.dirname(__FILE__), 'cmd'))
29
+ Dir.glob(cmds + '/*.rb').each do |file|
30
+ require file
31
+ end
32
+
33
+ GoodData::GoodotCLI.launch(ARGV)
@@ -0,0 +1,7 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2010-2016 GoodData Corporation. All rights reserved.
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ require_relative 'app'
@@ -0,0 +1,170 @@
1
+ module GoodData
2
+ module GoodotCLI
3
+ desc 'Manage app'
4
+ command :app do |c|
5
+ c.desc 'Synchronize clients'
6
+ c.command :synchronize do |sync|
7
+ sync.desc 'Segment'
8
+ sync.flag [:s, :segment]
9
+
10
+ sync.action do |global_options, options, _args|
11
+ ok_key = 'OK'
12
+
13
+ domain = global_options[:gd_domain]
14
+ results = if options[:segment]
15
+ segment = domain.segments(options[:segment])
16
+ [segment.synchronize_clients]
17
+ else
18
+ domain.synchronize_clients
19
+ end
20
+ res = results.pmapcat { |r| r.details.items }.group_by { |x| x['status'] }
21
+ if res.key?(ok_key) && res.keys.count == 1
22
+ puts HighLine.color("#{res[ok_key].count} syncs ended up in state #{ok_key}", :green)
23
+ else
24
+ res.except(ok_key).each do |k, v|
25
+ puts "#{v.count} syncs ended up in state #{k}."
26
+ puts 'Printing first 10'
27
+ v.each { |x| puts x['id'] }
28
+ end
29
+ fail HighLine.color("Some synchronizations ended up in different state than #{ok_key}", :red) unless res.except(ok_key).empty?
30
+ end
31
+ end
32
+ end
33
+
34
+ c.desc 'Export association for backup'
35
+ c.command :'export-association' do |export|
36
+ export.desc 'Without project ids'
37
+ export.switch [:'without-project']
38
+ export.default_value false
39
+
40
+ export.desc 'File for output'
41
+ export.flag [:f, :file]
42
+
43
+ export.action do |global_options, options, _args|
44
+ domain = global_options[:gd_domain]
45
+ results = domain.segments.pmapcat { |s| s.clients.map { |cl| [s, cl] } }
46
+ res = results.map do |s, cl|
47
+ {
48
+ segment: s.id,
49
+ id: cl.id
50
+ }.tap do |h|
51
+ h[:project] = cl.project.uri unless options['without-project']
52
+ end
53
+ end
54
+ file = options[:file]
55
+ if file
56
+ File.open(file, 'w') { |f| f << MultiJson.dump(res, pretty: true) }
57
+ else
58
+ puts MultiJson.dump(res, pretty: true)
59
+ end
60
+ end
61
+ end
62
+
63
+ c.desc 'Import association from backup'
64
+ c.command :'import-association' do |export|
65
+ export.desc 'File for output'
66
+ export.flag [:f, :file]
67
+
68
+ export.action do |global_options, options, _args|
69
+ domain = global_options[:gd_domain]
70
+ specification = MultiJson.load(File.read(options[:file]), symbolize_keys: true)
71
+ domain.update_clients(specification, delete_extra: true)
72
+ end
73
+ end
74
+
75
+ c.desc 'Spin up projects'
76
+ c.command :'spin-up-projects' do |spin|
77
+ spin.desc 'Segment'
78
+ spin.flag [:s, :segment]
79
+
80
+ spin.action do |global_options, _options, _args|
81
+ domain = global_options[:gd_domain]
82
+ results = domain.provision_client_projects
83
+ if results.to_a.empty?
84
+ puts HighLine.color('Everything seems to be up to date. Nothing was done!', :green)
85
+ else
86
+ puts HighLine.color("#{results.count} operations were performed", :green)
87
+ puts
88
+ results.group_by(&:status).each do |status, group|
89
+ puts HighLine.color("#{group.count} #{status} operations were performed", :green)
90
+ puts table_with_headers(group, [:id, :project_uri])
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ c.desc 'Add segment'
97
+ c.command :'create-segment' do |seg|
98
+ seg.desc 'Segment'
99
+ seg.flag [:s, :segment]
100
+
101
+ seg.desc 'Master project'
102
+ seg.flag [:p, :project]
103
+
104
+ seg.action do |global_options, options, _args|
105
+ client = global_options[:gd_client]
106
+ domain = global_options[:gd_domain]
107
+ segment_id = options[:segment]
108
+ project_id = options[:project]
109
+ fail 'Segment needs to be provided' unless segment_id
110
+ fail 'Project id needs to be provided' unless project_id
111
+ project = client.projects(project_id)
112
+
113
+ begin
114
+ s = domain.create_segment(segment_id: segment_id, master_project: project)
115
+ puts HighLine.color("Segment #{s.id} created!", :green)
116
+ rescue RuntimeError => e
117
+ raise create_api_error_message(e)
118
+ end
119
+ end
120
+ end
121
+
122
+ c.desc 'Add client'
123
+ c.command :'add-client' do |add|
124
+ add.desc 'Segment'
125
+ add.flag [:s, :segment]
126
+
127
+ add.desc 'Client ID'
128
+ add.flag [:c, :client]
129
+
130
+ add.action do |global_options, options, _args|
131
+ domain = global_options[:gd_domain]
132
+ segment_id = options[:segment]
133
+ client_id = options[:client]
134
+ fail 'Segment needs to be provided' unless segment_id
135
+ fail 'client ID needs to be provided.' unless client_id
136
+
137
+ s = domain.segments(segment_id)
138
+ begin
139
+ c = s.create_client(id: client_id)
140
+ puts HighLine.color("client #{c.id} created!", :green)
141
+ rescue RuntimeError => e
142
+ raise create_api_error_message(e)
143
+ end
144
+ end
145
+ end
146
+
147
+ c.desc 'App stats'
148
+ c.command :stats do |add|
149
+ add.action do |global_options, _options, _args|
150
+ client = global_options[:gd_client]
151
+ domain = global_options[:gd_domain]
152
+
153
+ all_projects = client.projects.to_a
154
+ master_projects = domain.segments.pmap(&:master_project)
155
+ client_projects = domain.segments.pmapcat { |s| s.clients.map(&:project).compact }
156
+
157
+ puts HighLine.color('Projects', :bold)
158
+ puts "This app contains #{all_projects.count} projects"
159
+ puts "Out of those #{master_projects.count} are linked as masters"
160
+ puts "Out of those #{client_projects.count} are linked as clients"
161
+
162
+ unused_projects_pids = (all_projects.map(&:pid) - master_projects.map(&:pid) - client_projects.map(&:pid))
163
+ puts "Additional #{unused_projects_pids.count} projects are in this account"
164
+ puts 'Printing first 10'
165
+ puts table_with_headers(unused_projects_pids.take(10).pmap { |pid| client.projects(pid) }, [:pid, :title])
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+
3
+ # Copyright (c) 2015, Tomas Korcak <korczis@gmail.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative '../shared'
24
+
25
+ module GoodData
26
+ module GoodotCLI
27
+ desc 'Manage clients in your application'
28
+ command :clients do |c|
29
+ c.desc 'List clients'
30
+ c.command :list do |add|
31
+ add.desc 'Filter on particular segment'
32
+ add.default_value :all
33
+ add.flag [:s, :segment]
34
+
35
+ add.action do |global_options, options, _args|
36
+ domain = global_options[:gd_domain]
37
+ segment_id = options[:segment]
38
+ table = Terminal::Table.new do |t|
39
+ t << ['Client Id', 'Segment URI', 'Project URI']
40
+ t << :separator
41
+ Array(domain.segments(segment_id)).pmapcat(&:clients).each { |s| t.add_row([s.client_id, s.segment_uri, s.project_uri]) }
42
+ end
43
+ puts table
44
+ end
45
+ end
46
+
47
+ c.desc 'Remove client'
48
+ c.command :remove do |remove|
49
+ remove.desc 'Client'
50
+ remove.flag [:c, :client]
51
+
52
+ remove.desc 'Delete the project'
53
+ remove.default_value false
54
+ remove.switch [:d, :'delete-project']
55
+
56
+ remove.action do |global_options, options, _args|
57
+ domain = global_options[:gd_domain]
58
+ client_name = options[:client]
59
+ fail 'Client needs to be provided' unless client_name
60
+ client = domain.clients(segment_name)
61
+ project = client.project
62
+ client.delete
63
+ project.delete if options[:d]
64
+ end
65
+ end
66
+
67
+ c.desc 'Reset client. Removes the project from the client.'
68
+ c.command :reset do |reset|
69
+ reset.desc 'Client'
70
+ reset.flag [:c, :client]
71
+
72
+ reset.desc 'Delete the project'
73
+ reset.default_value false
74
+ reset.switch [:d, :'delete-project']
75
+
76
+ reset.action do |global_options, options, _args|
77
+ domain = global_options[:gd_domain]
78
+ client_name = options[:client]
79
+ fail 'Client needs to be provided' unless client_name
80
+ client = domain.clients.find { |cl| cl.id == client_name }
81
+ project = client.project
82
+ client.project = nil
83
+ client.save
84
+ project.delete if options[:d]
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../shared'
2
+
3
+ module GoodData
4
+ module GoodotCLI
5
+ desc 'Jacks you in to work interactively with your project.'
6
+ command ['jack-in', 'jack_in'] do |c|
7
+ c.action do |global_options, _options, _args|
8
+ client = global_options[:gd_client]
9
+ domain = global_options[:gd_domain]
10
+ binding.pry(quiet: true, # rubocop:disable Lint/Debugger
11
+ prompt: [proc do |_target_self, _nest_level, _pry|
12
+ 'app_live_session: '
13
+ end])
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,67 @@
1
+ # # encoding: utf-8
2
+ #
3
+ # # Copyright (c) 2015, Tomas Korcak <korczis@gmail.com>
4
+ # #
5
+ # # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # # of this software and associated documentation files (the "Software"), to deal
7
+ # # in the Software without restriction, including without limitation the rights
8
+ # # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # # copies of the Software, and to permit persons to whom the Software is
10
+ # # furnished to do so, subject to the following conditions:
11
+ # #
12
+ # # The above copyright notice and this permission notice shall be included in
13
+ # # all copies or substantial portions of the Software.
14
+ # #
15
+ # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # # THE SOFTWARE.
22
+
23
+ require_relative '../shared'
24
+
25
+ module GoodData
26
+ module GoodotCLI
27
+ desc 'Manage masters'
28
+ command :masters do |c|
29
+ c.desc 'List masters'
30
+ c.command :list do |add|
31
+ add.action do |global_options, _options, _args|
32
+ domain = global_options[:gd_domain]
33
+ currently_used = domain.segments.pmap { |s| [s, s.master_project] }.compact
34
+
35
+ puts table_with_headers(currently_used,
36
+ [
37
+ proc { |_, p| p && p.pid },
38
+ proc { |_, p| p && p.title },
39
+ proc { |s, _| s.id },
40
+ proc { |_, p| p && p.metadata['GD_LCM_VERSION'] }
41
+ ],
42
+ headers: ['PID', 'Title', 'Segment ID', 'Version'])
43
+ end
44
+ end
45
+
46
+ c.desc 'Clone master of particular segment'
47
+ c.command :clone do |add|
48
+ add.desc 'Segment which master will be cloned'
49
+ add.default_value nil
50
+ add.flag [:s, :segment]
51
+
52
+ add.desc 'Authorization token'
53
+ add.default_value nil
54
+ add.flag [:t, :token]
55
+
56
+ add.action do |global_options, options, _args|
57
+ domain = global_options[:gd_domain]
58
+ fail 'You have to specify segment' unless options[:segment]
59
+ master_project = domain.segments(options[:segment]).master_project
60
+ clone = GoodData::Project.clone_with_etl(master_project, auth_token: options[:token])
61
+ puts "Project \"#{master_project.title}\" (#{master_project.pid}) was cloned."
62
+ puts "\"#{clone.title}\" (#{clone.pid}) was created."
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,81 @@
1
+ module GoodData
2
+ module GoodotCLI
3
+ desc 'Manage segments'
4
+ command :segments do |c|
5
+ c.desc 'List segments'
6
+ c.command :list do |add|
7
+ add.desc 'Format'
8
+ add.default_value :table
9
+ add.flag [:f, :format]
10
+
11
+ add.action do |global_options, options, _args|
12
+ domain = global_options[:gd_domain]
13
+
14
+ segments = domain.segments
15
+ case options[:format].to_sym
16
+ when :table
17
+ puts table_with_headers(segments, [:id, :master_project_id])
18
+ when :json
19
+ output = segments.map do |s|
20
+ {
21
+ name: s.id,
22
+ master_project_id: s.master_project_id
23
+ }
24
+ end
25
+ puts MultiJson.dump(output, pretty: true)
26
+ end
27
+ end
28
+ end
29
+
30
+ c.desc 'Remove segment'
31
+ c.command [:remove, :delete] do |add|
32
+ add.desc 'Segment'
33
+ add.flag [:s, :segment]
34
+
35
+ add.desc 'Delete also the clients in that particular segment'
36
+ add.default_value false
37
+ add.switch [:c, :cascade]
38
+
39
+ add.action do |global_options, options, _args|
40
+ domain = global_options[:gd_domain]
41
+ segment_name = options[:segment]
42
+ fail 'Segment needs to be provided' unless segment_name
43
+ segment = domain.segments(segment_name)
44
+ segment.clients.peach(&:delete) if options[:cascade]
45
+ segment.delete
46
+ end
47
+ end
48
+
49
+ c.desc 'Exchange master on segment.'
50
+ c.command :exchange do |exchange|
51
+ exchange.desc 'Segment'
52
+ exchange.flag [:s, :segment]
53
+
54
+ exchange.desc 'Master project ID'
55
+ exchange.flag [:p, :project]
56
+
57
+ exchange.desc 'Delete the old project'
58
+ exchange.default_value false
59
+ exchange.switch [:d, :delete]
60
+
61
+ exchange.action do |global_options, options, _args|
62
+ client = global_options[:gd_client]
63
+ domain = global_options[:gd_domain]
64
+
65
+ segment_name = options[:segment]
66
+ fail 'Segment needs to be provided' unless segment_name
67
+ segment = domain.segments(segment_name)
68
+
69
+ project_id = options[:project]
70
+ fail 'Project Master id needs to be provided' unless project_id
71
+ old_master = segment.master_project
72
+ new_master_project = client.projects(project_id)
73
+
74
+ segment.master_project = new_master_project
75
+ segment.save
76
+ old_master.delete if options[:delete]
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end