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 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