Skip to content
Open
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,47 @@ cache:

Of course, you can force a refresh at any time.

### Sharing (optional)

You can generate sharing urls for queries. You can download up-to-date results for each query in CSV directly.

This is useful for scripts or for automatic importing into spreadsheets.

There are 2 steps necessary for setting up sharing:

1. Configuring an API key
2. Make the sharing endpoint accessible in your routes

First configure an API key in `blazer.yml`:

```yml
sharing:
api_key: 'secret'
```

Alternatively you can set the `BLAZER_DOWNLOAD_API_KEY` ENV var which blazer uses by default.

Now routes: we assume you have secured blazer so you will need to expose a new route outside of the mount.

The default path for shares is `/blazer_share`. You can change this in `blazer.yml`:

```yml
sharing:
path: /another_path
```

This config is only so that blazer can generate the correct url.

Now add this route to your `routes.rb`:

```ruby
get Blazer.sharing.route_path, to: Blazer.sharing.to_controller if Blazer.sharing.enabled?
```

Now restart your server and each query page will have a `share` button which will open up a modal that allows you to copy sharing urls.

Each url has a unique token based on a hash of the query's id and the API key, so the token can't be reused for other queries.

## Charts

Blazer will automatically generate charts based on the types of the columns returned in your query.
Expand Down
11 changes: 10 additions & 1 deletion app/controllers/blazer/queries_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ def show
def edit
end

def share
if params[:token] && params[:query_id] && params[:token] == Blazer.sharing.query_token(params[:query_id])
Comment thread
abuisman marked this conversation as resolved.
Outdated
run
else
render_forbidden
end
end

def run
@query = Query.find_by(id: params[:query_id]) if params[:query_id]

Expand All @@ -87,7 +95,8 @@ def run
data_source ||= params[:data_source]
@data_source = Blazer.data_sources[data_source]

@statement = Blazer::Statement.new(params[:statement], @data_source)
sql_statement = params[:statement] || @query.statement
@statement = Blazer::Statement.new(sql_statement, @data_source)
# before process_vars
@cohort_analysis = @statement.cohort_analysis?

Expand Down
25 changes: 25 additions & 0 deletions app/views/blazer/queries/_sharing_modal.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div class="modal fade" id="sharingModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">Sharing</h4>
</div>
<div class="modal-body">
Below are some shareable links to this query. Copy and share them.

Anyone with these urls can access the results of this query!

<div class="form-group">
<label for="csvShare">CSV</label>
<input type="text" class="form-control" id="csvShare" readonly value="<%= Blazer.sharing.url_for(@query.id, request.url, format: 'csv') %>">
</div>

<div class="form-group">
<label for="csvShare">Google sheets</label>
<input type="text" class="form-control" id="googleShare" readonly value='=IMPORTDATA("<%= Blazer.sharing.url_for(@query.id, request.url, format: 'csv') %>")'>
</div>
</div>
</div>
</div>
</div>
12 changes: 10 additions & 2 deletions app/views/blazer/queries/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@
<div class="topbar">
<div class="container">
<div class="row" style="padding-top: 13px;">
<div class="col-sm-9">
<div class="col-sm-8">
<%= render partial: "blazer/nav" %>
<h3 style="line-height: 34px; display: inline; margin-left: 5px;">
<%= @query.name %>
</h3>
</div>
<div class="col-sm-3 text-right">
<div class="col-sm-4 text-right">
<%= link_to "Edit", edit_query_path(@query, params: variable_params(@query)), class: "btn btn-default", disabled: !@query.editable?(blazer_user) %>
<%= link_to "Fork", new_query_path(params: variable_params(@query).merge(fork_query_id: @query.id, data_source: @query.data_source, name: @query.name)), class: "btn btn-info" %>

<% if !@error && @success %>
<% if Blazer.sharing.enabled? %>
<span class='btn btn-success' data-toggle="modal" data-target="#sharingModal">Share</span>
<% end %>

<%= button_to "Download", run_queries_path(format: "csv"), params: run_data, class: "btn btn-primary" %>
<% end %>
</div>
Expand Down Expand Up @@ -78,3 +82,7 @@
}
</script>
<% end %>

<% if Blazer.sharing.enabled? %>
<%= render(partial: 'sharing_modal') %>
<% end %>
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
get :tables, on: :collection
get :schema, on: :collection
get :docs, on: :collection
get Blazer.sharing.route_path, to: 'queries#share', as: :share
end

resources :checks, except: [:show] do
Expand Down
8 changes: 8 additions & 0 deletions lib/blazer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
require "blazer/data_source"
require "blazer/result"
require "blazer/run_statement"
require "blazer/sharing"
require "blazer/statement"

# adapters
Expand Down Expand Up @@ -135,6 +136,13 @@ def self.data_sources
end
end

def self.sharing
@sharing ||= begin
sharing_settings = settings["sharing"] || {}
Blazer::Sharing.new(**sharing_settings.symbolize_keys)
end
end

# TODO move to Statement and remove in 3.0.0
def self.extract_vars(statement)
# strip commented out lines
Expand Down
36 changes: 36 additions & 0 deletions lib/blazer/sharing.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module Blazer
class Sharing
attr_accessor :api_key, :path

def initialize(api_key: ENV.fetch('BLAZER_DOWNLOAD_API_KEY', nil), path: '/blazer_share')
@api_key = api_key
@path = path.sub(/\/$/, '') # Strip trailing /
end

def route_path
@route_path ||= "#{path}/:token/:query_id"
end

def to_controller
'blazer/queries#share'
end

def query_token(query_id)
Digest::SHA1.hexdigest("#{query_id}-#{api_key}")
Comment thread
abuisman marked this conversation as resolved.
Outdated
end

def enabled?
api_key.present?
end

def share_path(query_id, format: nil)
"#{path}/#{query_token(query_id)}/#{query_id}#{".#{format}" if format}"
end

def url_for(query_id, current_url, format: 'csv')
url = URI.parse(current_url)
url.path = share_path(query_id, format: format)
url.to_s
end
end
end
9 changes: 9 additions & 0 deletions test/queries_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ def test_csv
assert_equal "id,city\n1,Chicago\n", response.body
end

def test_share
Blazer.sharing.api_key = "123"
query = create_query
get blazer.query_share_path(query_id: query.id, token: Digest::SHA1.hexdigest("#{query.id}-123"), format: 'csv')
assert_response :success
assert_match query.name, response.body
Blazer.sharing.api_key = nil
end

def test_url
run_query "SELECT 'http://localhost:3000/'"
assert_match %{<a target="_blank" href="http://localhost:3000/">http://localhost:3000/</a>}, response.body
Expand Down