Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sharing feature #410

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
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])
abuisman marked this conversation as resolved.
Show resolved Hide resolved
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}")
abuisman marked this conversation as resolved.
Show resolved Hide resolved
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