Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into i18n
Browse files Browse the repository at this point in the history
  • Loading branch information
Grace Guo committed Sep 19, 2017
2 parents dfcc510 + ed9f564 commit 2d977b9
Show file tree
Hide file tree
Showing 42 changed files with 896 additions and 314 deletions.
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Before you start changing the docs, you'll want to
[fork the Superset project on Github](https://help.github.com/articles/fork-a-repo/).
Once that new repository has been created, clone it on your local machine:

git clone git@github.com:your_username/superset.git
git clone git@github.com:your_username/incubator-superset.git

At this point, you may also want to create a
[Python virtual environment](http://docs.python-guide.org/en/latest/dev/virtualenvs/)
Expand All @@ -97,7 +97,7 @@ to manage the Python packages you're about to install:
Finally, to make changes to the rst files and build the docs using Sphinx,
you'll need to install a handful of dependencies from the repo you cloned:

cd superset
cd incubator-superset
pip install -r dev-reqs-for-docs.txt

To get the feel for how to edit and build the docs, let's edit a file, build
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ the world know they are using Superset. Join our growing community!
- [Konfío](http://konfio.mx)
- [Maieutical Labs](https://cloudschooling.it)
- [Qunar](https://www.qunar.com/)
- [Shopee](https://shopee.sg)
- [Shopkick](https://www.shopkick.com)
- [Tails.com](https://tails.com)
- [Tobii](http://www.tobii.com/)
Expand Down
1 change: 0 additions & 1 deletion dev-reqs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ mysqlclient
nose
psycopg2
pylint
pythrifthiveapi
pyyaml
redis
statsd
Expand Down
93 changes: 85 additions & 8 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,32 @@ Superset is tested against Python ``2.7`` and Python ``3.4``.
Airbnb currently uses 2.7.* in production. We do not plan on supporting
Python ``2.6``.

Cloud-native!
-------------

Superset is designed to be highly available. It is
"cloud-native" as it has been designed scale out in large,
distributed environments, and works well inside containers.
While you can easily
test drive Superset on a modest setup or simply on your laptop,
there's virtually no limit around scaling out the platform.
Superset is also cloud-native in the sense that it is
flexible and lets you choose your web server (Gunicorn, Nginx, Apache),
your metadata database engine (MySQL, Postgres, MariaDB, ...),
your message queue (Redis, RabbitMQ, SQS, ...),
your results backend (S3, Redis, Memcached, ...), your caching layer
(memcached, Redis, ...), works well with services like NewRelic, StatsD and
DataDog, and has the ability to run analytic workloads against
most popular database technologies.

Superset is battle tested in large environments with hundreds
of concurrent users. Airbnb's production environment runs inside
Kubernetes and serves 600+ daily active users viewing over 100K charts a
day.

The Superset web server and the Superset Celery workers (optional)
are stateless, so you can scale out by running on as many servers
as needed.

OS dependencies
---------------
Expand Down Expand Up @@ -107,10 +133,40 @@ the credential you entered while creating the admin account, and navigate to
your datasources for Superset to be aware of, and they should show up in
`Menu -> Datasources`, from where you can start playing with your data!

Please note that *gunicorn*, Superset default application server, does not
work on Windows so you need to use the development web server.
The development web server though is not intended to be used on production systems
so better use a supported platform that can run *gunicorn*.
A proper WSGI HTTP Server
-------------------------

While you can setup Superset to run on Nginx or Apache, many use
Gunicorn, preferably in **async mode**, which allows for impressive
concurrency even and is fairly easy to install and configure. Please
refer to the
documentation of your preferred technology to set up this Flask WSGI
application in a way that works well in your environment.

While the `superset runserver` command act as an quick wrapper
around `gunicorn`, it doesn't expose all the options you may need,
so you'll want to craft your own `gunicorn` command in your production
environment. Here's an **async** setup known to work well: ::

gunicorn \
-w 10 \
-k gevent \
--timeout 120 \
-b 0.0.0.0:6666 \
--limit-request-line 0 \
--limit-request-field_size 0 \
--statsd-host localhost:8125 \
superset:app

Refer to the
[Gunicorn documentation](http://docs.gunicorn.org/en/stable/design.html)
for more information.

Note that *gunicorn* does not
work on Windows so the `superser runserver` command is not expected to work
in that context. Also note that the development web
server (`superset runserver -d`) is not intended for production use.


Configuration behind a load balancer
------------------------------------
Expand Down Expand Up @@ -157,6 +213,8 @@ of the parameters you can copy / paste in that configuration module: ::

# Flask-WTF flag for CSRF
WTF_CSRF_ENABLED = True
# Add endpoints that need to be exempt from CSRF protection
WTF_CSRF_EXEMPT_LIST = []

# Set this API key to enable Mapbox visualizations
MAPBOX_API_KEY = ''
Expand All @@ -172,6 +230,11 @@ Please make sure to change:
* *SQLALCHEMY_DATABASE_URI*, by default it is stored at *~/.superset/superset.db*
* *SECRET_KEY*, to a long random string

In case you need to exempt endpoints from CSRF, e.g. you are running a custom
auth postback endpoint, you can add them to *WTF_CSRF_EXEMPT_LIST*

WTF_CSRF_EXEMPT_LIST = ['']

Database dependencies
---------------------

Expand Down Expand Up @@ -223,10 +286,6 @@ database you want to connect to should get you to the right place.
(AWS) Athena
------------

This currently relies on an unreleased future version of `PyAthenaJDBC <https://github.com/laughingman7743/PyAthenaJDBC>`_. If you're adventurous or simply impatient, you can install directly from git: ::

pip install git+https://github.com/laughingman7743/PyAthenaJDBC@support_sqlalchemy

The connection string for Athena looks like this ::

awsathena+jdbc://{aws_access_key_id}:{aws_secret_access_key}@athena.{region_name}.amazonaws.com/{schema_name}?s3_staging_dir={s3_staging_dir}&...
Expand Down Expand Up @@ -284,6 +343,24 @@ on top of the **database**. For Superset to connect to a specific schema,
there's a **schema** parameter you can set in the table form.


External Password store for SQLAlchemy connections
--------------------------------------------------
It is possible to use an external store for you database passwords. This is
useful if you a running a custom secret distribution framework and do not wish
to store secrets in Superset's meta database.

Example:
Write a function that takes a single argument of type ``sqla.engine.url`` and returns
the password for the given connection string. Then set ``SQLALCHEMY_CUSTOM_PASSWORD_STORE``
in your config file to point to that function. ::

def example_lookup_password(url):
secret = <<get password from external framework>>
return 'secret'

SQLALCHEMY_CUSTOM_PASSWORD_STORE = example_lookup_password


SSL Access to databases
-----------------------
This example worked with a MySQL database that requires SSL. The configuration
Expand Down
2 changes: 1 addition & 1 deletion docs/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ own. Alpha users can add and alter data sources.
Gamma
"""""
Gamma have limited access. They can only consume data coming from data sources
they have been giving access to through another complementary role.
they have been given access to through another complementary role.
They only have access to view the slices and
dashboards made from data sources that they have access to. Currently Gamma
users are not able to alter or add data sources. We assume that they are
Expand Down
Binary file added dump.rdb
Binary file not shown.
27 changes: 17 additions & 10 deletions superset/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@
from superset.connectors.connector_registry import ConnectorRegistry
from superset import utils, config # noqa


APP_DIR = os.path.dirname(__file__)
CONFIG_MODULE = os.environ.get('SUPERSET_CONFIG', 'superset.config')

with open(APP_DIR + '/static/assets/backendSync.json', 'r') as f:
frontend_config = json.load(f)


app = Flask(__name__)
app.config.from_object(CONFIG_MODULE)
conf = app.config
Expand All @@ -53,14 +51,16 @@ def get_manifest_file(filename):
parse_manifest_json()
return '/static/assets/dist/' + manifest.get(filename, '')


parse_manifest_json()


@app.context_processor
def get_js_manifest():
return dict(js_manifest=get_manifest_file)

#################################################################

#################################################################

for bp in conf.get('BLUEPRINTS'):
try:
Expand All @@ -83,6 +83,9 @@ def get_js_manifest():

if conf.get('WTF_CSRF_ENABLED'):
csrf = CSRFProtect(app)
csrf_exempt_list = conf.get('WTF_CSRF_EXEMPT_LIST', [])
for ex in csrf_exempt_list:
csrf.exempt(ex)

utils.pessimistic_connection_handling(db.engine)

Expand All @@ -97,10 +100,11 @@ def get_js_manifest():

if app.config.get('ENABLE_TIME_ROTATE'):
logging.getLogger().setLevel(app.config.get('TIME_ROTATE_LOG_LEVEL'))
handler = TimedRotatingFileHandler(app.config.get('FILENAME'),
when=app.config.get('ROLLOVER'),
interval=app.config.get('INTERVAL'),
backupCount=app.config.get('BACKUP_COUNT'))
handler = TimedRotatingFileHandler(
app.config.get('FILENAME'),
when=app.config.get('ROLLOVER'),
interval=app.config.get('INTERVAL'),
backupCount=app.config.get('BACKUP_COUNT'))
logging.getLogger().addHandler(handler)

if app.config.get('ENABLE_CORS'):
Expand All @@ -111,17 +115,18 @@ def get_js_manifest():
app.wsgi_app = ProxyFix(app.wsgi_app)

if app.config.get('ENABLE_CHUNK_ENCODING'):
class ChunkedEncodingFix(object):

class ChunkedEncodingFix(object):
def __init__(self, app):
self.app = app

def __call__(self, environ, start_response):
# Setting wsgi.input_terminated tells werkzeug.wsgi to ignore
# content-length and read the stream till the end.
if 'chunked' == environ.get('HTTP_TRANSFER_ENCODING', '').lower():
if environ.get('HTTP_TRANSFER_ENCODING', '').lower() == u'chunked':
environ['wsgi.input_terminated'] = True
return self.app(environ, start_response)

app.wsgi_app = ChunkedEncodingFix(app.wsgi_app)

if app.config.get('UPLOAD_FOLDER'):
Expand All @@ -139,8 +144,10 @@ class MyIndexView(IndexView):
def index(self):
return redirect('/superset/welcome')


appbuilder = AppBuilder(
app, db.session,
app,
db.session,
base_template='superset/base.html',
indexview=MyIndexView,
security_manager_class=app.config.get("CUSTOM_SECURITY_MANAGER"))
Expand Down
3 changes: 3 additions & 0 deletions superset/assets/javascripts/SqlLab/components/SqlEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ class SqlEditor extends React.PureComponent {
this.props.actions.queryEditorSetSql(this.props.queryEditor, sql);
}
runQuery(runAsync = false) {
if (!this.props.queryEditor.sql) {
return;
}
let effectiveRunAsync = runAsync;
if (!this.props.database.allow_run_sync) {
effectiveRunAsync = true;
Expand Down
16 changes: 13 additions & 3 deletions superset/assets/javascripts/dashboard/components/SliceCell.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function SliceCell({ expandedSlices, removeSlice, slice }) {
<span>{slice.slice_name}</span>
</div>
<div className="col-md-12 chart-controls">
<div className="pull-right">
<div id={'controls_' + slice.slice_id} className="pull-right">
<a title={t('Move chart')} data-toggle="tooltip">
<i className="fa fa-arrows drag" />
</a>
Expand All @@ -43,10 +43,20 @@ function SliceCell({ expandedSlices, removeSlice, slice }) {
>
<i className="fa fa-pencil" />
</a>
<a href={getExploreUrl(slice.form_data, 'csv')} title={t('Export CSV')} data-toggle="tooltip">
<a
className="exportCSV"
href={getExploreUrl(slice.form_data, 'csv')}
title={t('Export CSV')}
data-toggle="tooltip"
>
<i className="fa fa-table" />
</a>
<a href={getExploreUrl(slice.form_data)} title={t('Explore chart')} data-toggle="tooltip">
<a
className="exploreChart"
href={getExploreUrl(slice.form_data)}
title={t('Explore chart')}
data-toggle="tooltip"
>
<i className="fa fa-share" />
</a>
<a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ class ControlPanelsContainer extends React.Component {
</Alert>
}
{this.sectionsToRender().map((section) => {
const hasErrors = section.controlSetRows.some(rows => rows.some((s) => {
const errors = ctrls[s].validationErrors;
return errors && (errors.length > 0);
}));
const hasErrors = section.controlSetRows.some(rows => rows.some(s => (
ctrls[s] &&
ctrls[s].validationErrors &&
(ctrls[s].validationErrors.length > 0)
)));
return (
<ControlPanelSection
key={section.label}
Expand Down
Loading

0 comments on commit 2d977b9

Please sign in to comment.