Skip to content

Commit

Permalink
Switch from flask_script to click, add CLI unit tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
Allen Short committed Dec 6, 2016
1 parent fcd623e commit 80491ea
Show file tree
Hide file tree
Showing 11 changed files with 682 additions and 137 deletions.
3 changes: 1 addition & 2 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ dependencies:
- pip install -r requirements_dev.txt
- pip install -r requirements.txt
- pip install pymongo==3.2.1
- if [ "$CIRCLE_BRANCH" = "master" ]; then make deps; fi
- if [ "$CIRCLE_BRANCH" = "webpack" ]; then make deps; fi
- make deps
cache_directories:
- node_modules/
- client/node_modules/
Expand Down
57 changes: 35 additions & 22 deletions manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,73 @@
"""
import json

from flask_script import Manager

from redash import settings, models, __version__
from redash.wsgi import app
import click
from flask.cli import FlaskGroup, run_command

from redash import create_app, settings, __version__
from redash.cli import users, groups, database, data_sources, organization
from redash.monitor import get_status

manager = Manager(app)
manager.add_command("database", database.manager)
manager.add_command("users", users.manager)
manager.add_command("groups", groups.manager)
manager.add_command("ds", data_sources.manager)
manager.add_command("org", organization.manager)

def create(group):
app = create_app()
group.app = app
return app


@click.group(cls=FlaskGroup, create_app=create)
def manager():
"Management script for redash"


manager.add_command(database.manager, "database")
manager.add_command(users.manager, "users")
manager.add_command(groups.manager, "groups")
manager.add_command(data_sources.manager, "ds")
manager.add_command(organization.manager, "org")
manager.add_command(run_command, "runserver")


@manager.command
@manager.command()
def version():
"""Displays re:dash version."""
print __version__

@manager.command

@manager.command()
def status():
print json.dumps(get_status(), indent=2)

@manager.command

@manager.command()
def runworkers():
"""Start workers (deprecated)."""
print "** This command is deprecated. Please use Celery's CLI to control the workers. **"


@manager.shell
def make_shell_context():
from redash.models import db
return dict(app=app, db=db, models=models)


@manager.command
@manager.command()
def check_settings():
"""Show the settings as re:dash sees them (useful for debugging)."""
for name, item in settings.all_settings().iteritems():
print "{} = {}".format(name, item)


@manager.option('email', default=None, help="Email address to send test message to (default: the address you defined in MAIL_DEFAULT_SENDER)")
@manager.command()
@click.argument('email', default=settings.MAIL_DEFAULT_SENDER, required=False)
def send_test_mail(email=None):
"""
Send test message to EMAIL (default: the address you defined in MAIL_DEFAULT_SENDER)
"""
from redash import mail
from flask_mail import Message

if email is None:
email = settings.MAIL_DEFAULT_SENDER

mail.send(Message(subject="Test Message from re:dash", recipients=[email], body="Test message."))
mail.send(Message(subject="Test Message from re:dash", recipients=[email],
body="Test message."))


if __name__ == '__main__':
manager.run()
manager()
101 changes: 65 additions & 36 deletions redash/cli/data_sources.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
from sys import exit
import json

import click
from flask_script import Manager

from redash import models
from redash.query_runner import query_runners, get_configuration_schema_for_query_runner_type
from redash.query_runner import query_runners
from redash.query_runner import get_configuration_schema_for_query_runner_type
from redash.utils.configuration import ConfigurationContainer

manager = Manager(help="Data sources management commands.")
manager = click.Group(help="Data sources management commands.")


@manager.option('--org', dest='organization', default=None, help="The organization the user belongs to (leave blank for all organizations).")
@manager.command()
@click.option('--org', 'organization', default=None,
help="The organization the user belongs to (leave blank for "
"all organizations).")
def list(organization=None):
"""List currently configured data sources."""
if organization:
Expand All @@ -20,21 +26,24 @@ def list(organization=None):
if i > 0:
print "-" * 20

print "Id: {}\nName: {}\nType: {}\nOptions: {}".format(ds.id, ds.name, ds.type, ds.options)
print "Id: {}\nName: {}\nType: {}\nOptions: {}".format(
ds.id, ds.name, ds.type, ds.options.to_json())


def validate_data_source_type(type):
if type not in query_runners.keys():
print "Error: the type \"{}\" is not supported (supported types: {}).".format(type, ", ".join(query_runners.keys()))
print ("Error: the type \"{}\" is not supported (supported types: {})."
.format(type, ", ".join(query_runners.keys())))
exit()


@manager.option('name', default=None, help="name of data source to test")
@manager.option('--org', dest='organization', default='default',
help="The organization the user belongs to "
"(leave blank for 'default').")
@manager.command()
@click.argument('name')
@click.option('--org', 'organization', default='default',
help="The organization the user belongs to "
"(leave blank for 'default').")
def test(name, organization='default'):
"""Test connection to data source."""
"""Test connection to data source by issuing a trivial query."""
try:
org = models.Organization.get_by_slug(organization)
data_source = models.DataSource.get(
Expand All @@ -47,18 +56,26 @@ def test(name, organization='default'):
data_source.query_runner.test_connection()
except Exception, e:
print "Failure: {}".format(e)
exit(1)
else:
print "Success"
except models.DataSource.DoesNotExist:
print "Couldn't find data source named: {}".format(name)


@manager.option('name', default=None, help="name of data source to create")
@manager.option('--type', dest='type', default=None, help="new type for the data source")
@manager.option('--options', dest='options', default=None, help="updated options for the data source")
@manager.option('--org', dest='organization', default='default', help="The organization the user belongs to (leave blank for 'default').")
exit(1)


@manager.command()
@click.argument('name', default=None, required=False)
@click.option('--type', default=None,
help="new type for the data source")
@click.option('--options', default=None,
help="updated options for the data source")
@click.option('--org', 'organization', default='default',
help="The organization the user belongs to (leave blank for "
"'default').")
def new(name=None, type=None, options=None, organization='default'):
"""Create new data source."""

if name is None:
name = click.prompt("Name")

Expand All @@ -69,7 +86,8 @@ def new(name=None, type=None, options=None, organization='default'):

idx = 0
while idx < 1 or idx > len(query_runners.keys()):
idx = click.prompt("[{}-{}]".format(1, len(query_runners.keys())), type=int)
idx = click.prompt("[{}-{}]".format(1, len(query_runners.keys())),
type=int)

type = query_runners.keys()[idx - 1]
else:
Expand Down Expand Up @@ -99,7 +117,8 @@ def new(name=None, type=None, options=None, organization='default'):
else:
prompt = "{} (optional)".format(prompt)

value = click.prompt(prompt, default=default_value, type=types[prop['type']], show_default=False)
value = click.prompt(prompt, default=default_value,
type=types[prop['type']], show_default=False)
if value != default_value:
options_obj[k] = value

Expand All @@ -111,17 +130,20 @@ def new(name=None, type=None, options=None, organization='default'):
print "Error: invalid configuration."
exit()

print "Creating {} data source ({}) with options:\n{}".format(type, name, options.to_json())
print "Creating {} data source ({}) with options:\n{}".format(
type, name, options.to_json())

data_source = models.DataSource.create_with_group(name=name,
type=type,
options=options,
org=models.Organization.get_by_slug(organization))
data_source = models.DataSource.create_with_group(
name=name, type=type, options=options,
org=models.Organization.get_by_slug(organization))
print "Id: {}".format(data_source.id)


@manager.option('name', default=None, help="name of data source to delete")
@manager.option('--org', dest='organization', default='default', help="The organization the user belongs to (leave blank for 'default').")
@manager.command()
@click.argument('name')
@click.option('--org', 'organization', default='default',
help="The organization the user belongs to (leave blank for "
"'default').")
def delete(name, organization='default'):
"""Delete data source by name."""
try:
Expand All @@ -134,6 +156,7 @@ def delete(name, organization='default'):
data_source.delete_instance(recursive=True)
except models.DataSource.DoesNotExist:
print "Couldn't find data source named: {}".format(name)
exit(1)


def update_attr(obj, attr, new_value):
Expand All @@ -143,31 +166,37 @@ def update_attr(obj, attr, new_value):
setattr(obj, attr, new_value)


@manager.option('name', default=None, help="name of data source to edit")
@manager.option('--name', dest='new_name', default=None, help="new name for the data source")
@manager.option('--options', dest='options', default=None, help="updated options for the data source")
@manager.option('--type', dest='type', default=None, help="new type for the data source")
@manager.option('--org', dest='organization', default='default', help="The organization the user belongs to (leave blank for 'default').")
@manager.command()
@click.argument('name')
@click.option('--name', 'new_name', default=None,
help="new name for the data source")
@click.option('--options', default=None,
help="updated options for the data source")
@click.option('--type', default=None,
help="new type for the data source")
@click.option('--org', 'organization', default='default',
help="The organization the user belongs to (leave blank for "
"'default').")
def edit(name, new_name=None, options=None, type=None, organization='default'):
"""Edit data source settings (name, options, type)."""
try:
if type is not None:
validate_data_source_type(type)

org = models.Organization.get_by_slug(organization)
data_source = models.DataSource.get(
models.DataSource.name==name,
models.DataSource.org==org,
)
update_attr(data_source, "name", new_name)
update_attr(data_source, "type", type)

if options is not None:
schema = get_configuration_schema_for_query_runner_type(data_source.type)
schema = get_configuration_schema_for_query_runner_type(
data_source.type)
options = json.loads(options)
data_source.options.set_schema(schema)
data_source.options.update(options)

update_attr(data_source, "name", new_name)
update_attr(data_source, "type", type)
update_attr(data_source, "options", options)
data_source.save()

except models.DataSource.DoesNotExist:
Expand Down
10 changes: 6 additions & 4 deletions redash/cli/database.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
from flask_script import Manager
from click import Group

manager = Manager(help="Manage the database (create/drop tables).")
manager = Group(help="Manage the database (create/drop tables).")

@manager.command

@manager.command()
def create_tables():
"""Create the database tables."""
from redash.models import create_db, init_db

create_db(True, False)
init_db()

@manager.command

@manager.command()
def drop_tables():
"""Drop the database tables."""
from redash.models import create_db
Expand Down
Loading

0 comments on commit 80491ea

Please sign in to comment.