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

Change: Replace Peewee with SQLAlchemy/Alembic #1417

Merged
merged 80 commits into from
Dec 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
24217d9
schema for sqlalchemy, basic test support
Nov 21, 2016
d2aef54
properly handle view_only permission in groups API
Nov 21, 2016
ea16666
test_models passes
Nov 23, 2016
f00d77d
auth tests wip
Nov 28, 2016
982667f
Make draft status for queries and dashboards toggleable.
Oct 14, 2016
f55b836
Fix: fix database URL
arikfr Nov 28, 2016
b390cd2
Close DB connection between tests.
arikfr Nov 28, 2016
2bff12b
Update all tests to use the same test_client
arikfr Nov 28, 2016
55cb374
Use db.drop_all/create_all directly
arikfr Nov 28, 2016
04447e0
Fix: connections leaking during tests.
arikfr Nov 28, 2016
2a52521
Start fixing visualizations tests
arikfr Nov 28, 2016
90879c9
Simplify query api key generation
arikfr Nov 28, 2016
c2378d8
test_handlers passes
Nov 29, 2016
9210f5f
Remove unused code
arikfr Nov 29, 2016
d59299b
Fix Alert model tests
arikfr Nov 29, 2016
8680ebe
Change Dashboard factory to generate non drafts
arikfr Nov 29, 2016
c386ff9
Fix data source models tests
arikfr Nov 29, 2016
811a4ef
Fix accesspemrissions tests
arikfr Nov 29, 2016
d1fcb43
test_alerts passes
Nov 29, 2016
bb755b5
test_models fixes
Nov 29, 2016
dff39a6
test_dashboards passes
Nov 29, 2016
c6ef604
test_data_sources passes
Nov 30, 2016
c355eef
Fix test_permissions tests
arikfr Nov 30, 2016
6b2d6a2
Change groups property of ApiUser to be group_ids
arikfr Nov 30, 2016
b61dbfa
Fix test_authentication tests
arikfr Nov 30, 2016
419234a
Fix widget tests
arikfr Nov 30, 2016
f4c7652
Fix refresh queries tests
arikfr Nov 30, 2016
4459c46
use Query#query_text instead of Query#query
arikfr Nov 30, 2016
9c1450f
Fix users handlers tests
arikfr Nov 30, 2016
802b812
Fix query results tests
arikfr Nov 30, 2016
c5548e9
Fix query handlers test
arikfr Nov 30, 2016
d103e3f
Fix groups tests (except for delete)
arikfr Nov 30, 2016
29cdfcd
test_embed, test_groups
Nov 30, 2016
9f789d3
test_paginate
Nov 30, 2016
261b374
test_permissions
Nov 30, 2016
6c3d5d1
test_queries
Nov 30, 2016
271b468
test_alerts
Nov 30, 2016
9b5aaa7
test_permissions, test_changes, test_queries
Dec 1, 2016
c0f4890
Fix destinations handlers code
arikfr Dec 1, 2016
3f54799
More destination handlers test fixes
arikfr Dec 4, 2016
7d45812
Add setting to enable SQLA echo mode
arikfr Dec 4, 2016
03b2a41
Fix queries update handler
arikfr Dec 4, 2016
a5805d0
Fix session api (used groups instead of group_ids)
arikfr Dec 4, 2016
520835d
Fix test_cli
Dec 6, 2016
f3d8134
Fix data source models tests
arikfr Nov 29, 2016
8280859
add test coverage for remaining queries to convert
Dec 7, 2016
4edc3e3
gratuitous admin query fix
Dec 7, 2016
dc6bc07
work around flask proxy object
Dec 7, 2016
74e1b31
Remove dead code
arikfr Dec 6, 2016
6b0e454
Update QuerySnippets code to SQLA
arikfr Dec 6, 2016
8c2b310
Add tests to alerts celery task
arikfr Dec 6, 2016
ecbed00
Update create_and_login_user not to call save
arikfr Dec 6, 2016
463da02
remove group_hack method (unused)
arikfr Dec 6, 2016
0c974bd
Update User.find_by_email to SQLA
arikfr Dec 6, 2016
045e880
Add dedicated delete method to widgets instead of using an event
arikfr Dec 6, 2016
fb75626
Create default visualization using a method instead of signal
arikfr Dec 6, 2016
51117e8
Update dependencies to more latest versions
arikfr Dec 7, 2016
7312189
Remove usage of the deprecated flask.ext package
arikfr Dec 7, 2016
80a7d37
Fix: set the column name of QueryResult.query_text
arikfr Dec 7, 2016
bcce9cf
Update circle.yml to skip docker builds for now
arikfr Dec 7, 2016
b9024b1
Remove unused code
arikfr Dec 7, 2016
a84c3e2
Move CLI logic into redash.cli and uses manager for tests.
arikfr Dec 7, 2016
2b33963
Add missing db.session.commit calls in CLI
arikfr Dec 7, 2016
2d206ef
Switch to flask.cli.AppGroup instead of flask.cli.with_app_context
arikfr Dec 7, 2016
abf57e4
Upgrade setuptools to install mock
arikfr Dec 7, 2016
74e6ef5
Fix users CLI tests
arikfr Dec 7, 2016
70d5454
Add Flask-Migrate to the project
arikfr Dec 7, 2016
923c463
Add migration for the is_draft column
arikfr Dec 7, 2016
da31d98
Fix test_cli tests
Dec 7, 2016
4ba399a
fix basic query execution from UI
Dec 7, 2016
4945d0b
fix cleanup_query_results task
Dec 8, 2016
3cce4d0
Add warning script to migrations folder
arikfr Dec 8, 2016
12cbfe1
Stamp database on first creation
arikfr Dec 8, 2016
106c743
Keep same logging format in ALembic
arikfr Dec 8, 2016
c380596
Fix cases where we used User.groups instead of User.group_ids
arikfr Dec 8, 2016
1d18109
Fix tests that used Query.all_queries
arikfr Dec 8, 2016
81fb139
Add shell_context_processor to inject models to shell.
arikfr Dec 8, 2016
e524db0
Measure query time with statsd
Dec 9, 2016
1978e07
Use group ids instead of groups in Queries.search/recent
arikfr Dec 11, 2016
b3bfc3b
Bring back support for total query time/queries count
arikfr Dec 11, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ machine:
2.7.3
dependencies:
pre:
- pip install --upgrade setuptools
- pip install -r requirements_dev.txt
- pip install -r requirements.txt
- pip install pymongo==3.2.1
Expand All @@ -26,11 +27,11 @@ deployment:
- make pack
# Skipping uploads for now, until master is stable.
# - make upload
- echo "client/app" >> .dockerignore
- docker pull redash/redash:latest
- docker build -t redash/redash:$(./manage.py version | sed -e "s/\+/./") .
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
- docker push redash/redash:$(./manage.py version | sed -e "s/\+/./")
#- echo "client/app" >> .dockerignore
#- docker pull redash/redash:latest
#- docker build -t redash/redash:$(./manage.py version | sed -e "s/\+/./") .
#- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
#- docker push redash/redash:$(./manage.py version | sed -e "s/\+/./")
notify:
webhooks:
- url: https://webhooks.gitter.im/e/895d09c3165a0913ac2f
Expand Down
69 changes: 1 addition & 68 deletions manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,75 +2,8 @@
"""
CLI to manage redash.
"""
import json


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


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()
def version():
"""Displays re:dash version."""
print __version__


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


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


@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.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."))

from redash.cli import manager

if __name__ == '__main__':
manager()
9 changes: 9 additions & 0 deletions migrations/0001_warning.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# This is here just to print a warning for users who use the old Fabric upgrade script.

if __name__ == '__main__':
warning = "You're using an outdated upgrade script that is running migrations the wrong way. Please upgrade to " \
"newer version of the script before continuning the upgrade process."
print "*" * 20
print warning
print "*" * 20
exit(1)
1 change: 1 addition & 0 deletions migrations/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generic single-database configuration.
44 changes: 44 additions & 0 deletions migrations/alembic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# A generic, single database configuration.

[alembic]
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s

# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false


# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = [%(asctime)s][PID:%(process)d][%(levelname)s][%(name)s] %(message)s
87 changes: 87 additions & 0 deletions migrations/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from __future__ import with_statement
from alembic import context
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig
import logging

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from flask import current_app
config.set_main_option('sqlalchemy.url',
current_app.config.get('SQLALCHEMY_DATABASE_URI'))
target_metadata = current_app.extensions['migrate'].db.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def run_migrations_offline():
"""Run migrations in 'offline' mode.

This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.

Calls to context.execute() here emit the given string to the
script output.

"""
url = config.get_main_option("sqlalchemy.url")
context.configure(url=url)

with context.begin_transaction():
context.run_migrations()


def run_migrations_online():
"""Run migrations in 'online' mode.

In this scenario we need to create an Engine
and associate a connection with the context.

"""

# this callback is used to prevent an auto-migration from being generated
# when there are no changes to the schema
# reference: http://alembic.readthedocs.org/en/latest/cookbook.html
def process_revision_directives(context, revision, directives):
if getattr(config.cmd_opts, 'autogenerate', False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
logger.info('No changes in schema detected.')

engine = engine_from_config(config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
poolclass=pool.NullPool)

connection = engine.connect()
context.configure(connection=connection,
target_metadata=target_metadata,
process_revision_directives=process_revision_directives,
**current_app.extensions['migrate'].configure_args)

try:
with context.begin_transaction():
context.run_migrations()
finally:
connection.close()

if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
24 changes: 24 additions & 0 deletions migrations/script.py.mako
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""${message}

Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}

"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}

# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}


def upgrade():
${upgrades if upgrades else "pass"}


def downgrade():
${downgrades if downgrades else "pass"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Add is_draft status to queries and dashboards

Revision ID: 65fc9ede4746
Revises:
Create Date: 2016-12-07 18:08:13.395586

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
from sqlalchemy.exc import ProgrammingError

revision = '65fc9ede4746'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
try:
op.add_column('queries', sa.Column('is_draft', sa.Boolean, default=True, index=True))
op.add_column('dashboards', sa.Column('is_draft', sa.Boolean, default=True, index=True))
op.execute("UPDATE queries SET is_draft = (name = 'New Query')")
op.execute("UPDATE dashboards SET is_draft = false")
except ProgrammingError as e:
# The columns might exist if you ran the old migrations.
if 'column "is_draft" of relation "queries" already exists' in e.message:
print "Can't run this migration as you already have is_draft columns, please run:"
print "./manage.py db stamp {} # you might need to alter the command to match your environment.".format(revision)
exit()


def downgrade():
op.drop_column('queries', 'is_draft')
op.drop_column('dashboards', 'is_draft')
File renamed without changes.
8 changes: 5 additions & 3 deletions redash/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from flask_mail import Mail
from flask_limiter import Limiter
from flask_limiter.util import get_ipaddr
from flask_migrate import Migrate

from redash import settings
from redash.query_runner import import_query_runners
Expand Down Expand Up @@ -52,6 +53,7 @@ def create_redis_connection():
setup_logging()
redis_connection = create_redis_connection()
mail = Mail()
migrate = Migrate()
mail.init_mail(settings.all_settings())
statsd_client = StatsClient(host=settings.STATSD_HOST, port=settings.STATSD_PORT, prefix=settings.STATSD_PREFIX)
limiter = Limiter(key_func=get_ipaddr, storage_uri=settings.REDIS_URL)
Expand Down Expand Up @@ -105,13 +107,13 @@ def create_app():
logging.getLogger().addHandler(sentry_handler)

# configure our database
settings.DATABASE_CONFIG.update({'threadlocals': True})
app.config['DATABASE'] = settings.DATABASE_CONFIG
app.config['SQLALCHEMY_DATABASE_URI'] = settings.SQLALCHEMY_DATABASE_URI
app.config.update(settings.all_settings())

provision_app(app)
init_admin(app)
db.init_app(app)
migrate.init_app(app, db)
init_admin(app)
mail.init_app(app)
setup_authentication(app)
handlers.init_app(app)
Expand Down
Loading