redash_to_google_docs 0.1.0 → 0.1.1

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
  SHA256:
3
- metadata.gz: cd0281de00fe298c934be62faf1a44cea5120d4a11f5db58c14f0c1b3b2a7d4c
4
- data.tar.gz: 9fa192ef2a18a096935eb34f5c4c086c8a3edc7524f5630fe2301d5271f30a0e
3
+ metadata.gz: eb72009d25157a7ac5cf84df77bfb678105da72d4f681f6bcc1cc1b159e34da6
4
+ data.tar.gz: 6fdf0ff0bc5a329c328fbfc5bf9f729eb30d90e0088cfc7a07039af48397a354
5
5
  SHA512:
6
- metadata.gz: 5bfca653c5137a96f2aa5aa41dd7b73ab6601818a422fa9b2c7f1b723803d59bb5c85b4bc6c77536d3c1288d60d8cb7b7dea464edcc74724904fa937149c0f1b
7
- data.tar.gz: 89fd59052cd45901a647d29a0c45d1e20360707e9489478dd0a12ab14c383232941a00977aab6f19c4c028d51a70fc3191bfe5cf22eb758d4a79fbc582de278a
6
+ metadata.gz: dd3cc46dff7f60ac29a5ecadcc83f6c2bfd5d5c9a52e36f233c2d79d55e3c079a7a0849f1d22b7c0545be0a17045d0cb4801f86f558b062953d98057b742a70e
7
+ data.tar.gz: ac14731ceb830f4609de5e5ed77d9f54ff1b1d3ecb784be973311c260764fd9421a56631d98abecc5c22ed4a105b8b5ae1a5481de5c283500ff3acf0bc910189
data/exe/redash2gsheet ADDED
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+
5
+ require 'dry/cli'
6
+ require 'net/http'
7
+ require 'uri'
8
+ require 'redash_to_google_docs'
9
+ require 'json'
10
+
11
+ require 'tty-prompt'
12
+
13
+
14
+ require 'google/apis/sheets_v4'
15
+ require 'googleauth'
16
+ require 'googleauth/stores/file_token_store'
17
+
18
+
19
+ require "fileutils"
20
+
21
+ Prompt = TTY::Prompt.new
22
+
23
+
24
+
25
+ module RedashToGoogleSheet
26
+ module Clients
27
+ class Redash
28
+ ResultPending = Struct.new(:job_id)
29
+ ResultAvailable = Struct.new(:rows)
30
+
31
+ attr_reader :client, :api_host, :api_key
32
+
33
+ def initialize(api_host, api_key)
34
+ @api_host = api_host
35
+ @api_key = api_key
36
+ @client = Net::HTTP
37
+ end
38
+
39
+ def query_result(query_id)
40
+ response = client.post(query_results_url(query_id), '', { 'Authorization' => "Key #{api_key}" })
41
+
42
+ data = JSON.load(response.body)
43
+
44
+ if data.key?('job')
45
+ ResultPending.new(data.dig('job', 'id'))
46
+ else
47
+ ResultAvailable.new(data.dig('query_result', 'data', 'rows'))
48
+ end
49
+ end
50
+
51
+ def query_results_url(query_id)
52
+ URI("#{api_host}/api/queries/#{query_id}/results")
53
+ end
54
+ end
55
+
56
+ class Sheets
57
+ OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
58
+ CREDENTIALS_PATH = "credentials.json".freeze
59
+ TOKEN_PATH = "token.yaml".freeze
60
+ SCOPE = Google::Apis::SheetsV4::AUTH_SPREADSHEETS
61
+
62
+ attr_reader :prompt
63
+
64
+ def initialize(prompt)
65
+ @prompt = prompt
66
+ end
67
+
68
+ ##
69
+ # Ensure valid credentials, either by restoring from the saved credentials
70
+ # files or intitiating an OAuth2 authorization. If authorization is required,
71
+ # the user's default browser will be launched to approve the request.
72
+ #
73
+ # @return [Google::Auth::UserRefreshCredentials] OAuth2 credentials
74
+ def authorize
75
+ client_id = Google::Auth::ClientId.from_file CREDENTIALS_PATH
76
+ token_store = Google::Auth::Stores::FileTokenStore.new file: TOKEN_PATH
77
+ authorizer = Google::Auth::UserAuthorizer.new client_id, SCOPE, token_store
78
+ user_id = "default"
79
+ credentials = authorizer.get_credentials user_id
80
+
81
+ if credentials.nil?
82
+ url = authorizer.get_authorization_url base_url: OOB_URI
83
+ code = prompt.ask(<<~PROMPT)
84
+ Open the following URL in the browser and enter the resulting code after authorization:
85
+ #{url}
86
+ PROMPT
87
+
88
+ credentials = authorizer.get_and_store_credentials_from_code(
89
+ user_id: user_id, code: code, base_url: OOB_URI
90
+ )
91
+ end
92
+ credentials
93
+ end
94
+ end
95
+ end
96
+
97
+ module Commands
98
+ extend Dry::CLI::Registry
99
+
100
+ require 'redash_to_google_docs/version'
101
+ class Version < Dry::CLI::Command
102
+ def call(**)
103
+ puts "redash2gsheet version #{RedashToGoogleDocs::VERSION}"
104
+ end
105
+ end
106
+
107
+ class Sync < Dry::CLI::Command
108
+ option :redash_key, desc: "Redash API key"
109
+ option :redash_url, desc: 'Redash URL. Example: https://app.redash.io/company'
110
+ option :query_id, desc: "Redash query ID. Example: for https://app.redash.io/company/queries/444111, ID is 444111"
111
+ option :spreadsheet_id, desc: "Spreadsheet URL"
112
+
113
+ def call(redash_key:, query_id:, spreadsheet_id:, redash_url: , **)
114
+ redash_response = fetch_result_from_redash(redash_url, redash_key, query_id)
115
+
116
+ case redash_response
117
+ when Clients::Redash::ResultPending
118
+ prompt.say "Result is refreshing. Please run this command in a minute"
119
+ when Clients::Redash::ResultAvailable
120
+ prompt.say "Alright, we've got the result. Upload it to google sheets"
121
+
122
+ service = authenticate_and_authorize
123
+ result = replace_google_sheet(service, spreadsheet_id, redash_response)
124
+
125
+ Prompt.say("Updated #{result.updated_range}")
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ def prompt
132
+ Prompt
133
+ end
134
+
135
+ def fetch_result_from_redash(redash_url, redash_key, query_id)
136
+ client = Clients::Redash.new(redash_url, redash_key)
137
+ client.query_result(query_id)
138
+ end
139
+
140
+
141
+ def authenticate_and_authorize
142
+ Google::Apis::SheetsV4::SheetsService.new.tap do |service|
143
+ service.authorization = Clients::Sheets.new(prompt).authorize
144
+ end
145
+ end
146
+
147
+ def replace_google_sheet(service, spreadsheet_id, result)
148
+ values = [
149
+ result.rows.first.keys,
150
+ *result.rows.map(&:values)
151
+ ]
152
+
153
+ clear_values(service, spreadsheet_id)
154
+
155
+ upload_values(service, spreadsheet_id, values)
156
+ end
157
+
158
+ def clear_values(service, spreadsheet_id)
159
+ request_body = Google::Apis::SheetsV4::ClearValuesRequest.new
160
+ service.clear_values(spreadsheet_id, 'A:Z', request_body)
161
+ end
162
+
163
+ def upload_values(service, spreadsheet_id, values)
164
+ value_range_object = Google::Apis::SheetsV4::ValueRange.new(values: values)
165
+ service.update_spreadsheet_value(spreadsheet_id, 'A1', value_range_object, value_input_option: 'RAW')
166
+ end
167
+ end
168
+
169
+ register 'version', Version, aliases: %w[v]
170
+
171
+ register 'sync', Sync, aliases: %w[s]
172
+ end
173
+ end
174
+
175
+
176
+ Dry::CLI.new(RedashToGoogleSheet::Commands).call
177
+
@@ -1,3 +1,3 @@
1
1
  module RedashToGoogleDocs
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redash_to_google_docs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor S. Morozov
@@ -55,7 +55,8 @@ dependencies:
55
55
  description:
56
56
  email:
57
57
  - igor@morozov.is
58
- executables: []
58
+ executables:
59
+ - redash2gsheet
59
60
  extensions: []
60
61
  extra_rdoc_files: []
61
62
  files:
@@ -67,6 +68,7 @@ files:
67
68
  - Rakefile
68
69
  - bin/console
69
70
  - bin/setup
71
+ - exe/redash2gsheet
70
72
  - lib/redash_to_google_docs.rb
71
73
  - lib/redash_to_google_docs/version.rb
72
74
  - redash_to_google_docs.gemspec