redash_to_google_docs 0.1.0 → 0.1.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/exe/redash2gsheet +177 -0
- data/lib/redash_to_google_docs/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb72009d25157a7ac5cf84df77bfb678105da72d4f681f6bcc1cc1b159e34da6
|
4
|
+
data.tar.gz: 6fdf0ff0bc5a329c328fbfc5bf9f729eb30d90e0088cfc7a07039af48397a354
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
+
|
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.
|
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
|