Compare commits

...

570 Commits

Author SHA1 Message Date
Arik Fraimovich
7ac60585fa Update v8 branch to v8-beta.2 (#4149)
* Migrate Parameters component to React (#4006)

* Start Parameters Migration

* Add dirtyCount

* Use workaround with setState

* Apply Changes

* Add EditSettingsDialog

* Add Cmd/Ctrl + Enter behavior

* Remove isApplying

* Delete Angular version of parameters

* Update tests

* Remove angular stuff

* Update jest

* Drag placeholder

* Update events

* Use old button styling and move css

* Reviewing code

* Add parameter rearrange test

* Add Parameter Settings title change test

* Update Parameter Settings button styling

* Move parameter url logic back to Parameters

* Disable url update when query is new

* Styling changes (#4019)

* Ran's title width styling

* Update drag test

* Improve sizing for Number inputs

Co-Authored-By: Ran Byron <ranbena@gmail.com>

* Fix issue with dragged parameter wrapping

Co-Authored-By: Ran Byron <ranbena@gmail.com>

* Don't reevaluate dirtyParamCount

* Allow multiple values :)

* Fix parameter alignments

* Fix Select width on search

* Update client/app/components/Parameters.less

Co-Authored-By: Ran Byron <ranbena@gmail.com>

* Humanize param.name

* Make sure angular updates Execute disabled status

* Add more flake8 tests and fail build if any test fails (#4055)

* Add more flake8 tests and fail build if any test fails

Run all flake8 E9xx + F63x + F7xx + F82x tests.

* long = long in Python 2

* Fix: MySQL connections without SSL are failing (#4090)

* Move connection logic into a single method & make sure not to pass ssl value if not used.

* Remove wildcard import and format file.

* Removed redash-newstyle.less (#4017)

* Make sure we always pass a list to _get_column_lists (#4095)

(some data sources might return None as the columns list)

* [Data Sources] Add: Azure Data Explorer (Kusto) query runner (#4091)

* [Data Sources] Add: Azure Data Explorer (Kusto) query runner

* CodeClimate fixes

* Remove TODO

* Fixed configuration properties names for Azure Kusto

* Azure Kusto: get_schema in one query

* azure-kusto-data update to 0.0.32

* Add Kusto to the default query runners list

* [Qubole] - Adding support to process Quantum query types. (#4066)

* [Qubole] - Adding support to process Quantum query types.

Quantum is a serverless interactive service that offers
direct SQL access to user's data lake. Changes are made
to accept `quantum` query type from user which makes
`Cluster Label` as optional.

* -Making quantum as defult query.
-Dictionary safe access to connection parmeters

* keeping pep8 standards

* Maintainig pep8 std

* Use latest version of qds-sdk

* Use qds-sdk v1.13.0

* Use qds-sdk v1.12.0

* Use qds-sdk v1.13.0

* Updating SDK with verified version

* hive as default query type

* qds-sdk : Locking most recent release version

* qds-sdk : Locking recent release version

* falling back to original version of qds-sdk

* Dashboard: when updating parameters, run only relevant queries (#3804)

* refresh only affected queries in dashboard when parameters are changed

* rename pendingParameters to updatedParameters

* select which widgets to update according to their mapping as a dashboard-level parameter

* use lodash's include

* Migrate with SQL statements. (#4105)

* Update badge in README.md to link to CircleCI (#4104)

* Update README.md

* Update README.md

* Update README.md

Co-Authored-By: Ran Byron <ranbena@gmail.com>

* Update README.md

* Fix widget bottom element alignment (#4110)

* Fix: allow users with view only acces to use the queries in Query Results (#4112)

* Fix: allow users with view only acces to access the queries

* Add tests

* Update error message

* Update error message. Take 2

* Fix Dropdown parameter options appearing behind Dialog (#4109)

* Add ability to use Ant's Table loading property when using ItemsTable (#4117)

* Display data source icon in query editor (#4119)

* Move annotation logic into Query Runner (#4113)

* Code formatting

* Move annotation logic into query runner, so it can be overriden in the query runner.

* Add mixin to __all__

* Switch to flag instead of mixin

* Feature (Redshift): option to set query group for adhoc/scheduled queries  (#4114)

* Add scheduled status to query job metadata.

* Add: option to set query group for adhoc/scheduled Redshift queries

* Scheduled might not be set for already enqueued queries.

* Fix number param value normlization (#4116)

* Use ng-src for data source icons (#4123)

* hive_ds: show a user friendly error message when possible (#4121)

* Update botocore, to get pass pip warning (#4122)

* Widget table scroll-x visible (#4101)

* Table viz horizontal scroll made visible

* Added tests

* Fixed snapshot pre-condition

* Perhaps this would trigger percy

* Upgrade Sentry-SDK and enable additional integratoins (#4127)

* Update sentry-sdk version

* Add additional Sentry integrations

* Migrate Counter visualization to React (#4106)

* Migrate Counter to React: Renderer

* Migrate Counter to React: Editor

* Cleanup

* Review and fix rows indexing algorithm

* Counter not properly scaled in editor

* Fix wrong label for/input id pair

* Tests

* Tests

* Fix vendor prefixes

* Remove unnecessary useEffect dependencies

* Update tests

* Fix Percy snapshot names

* Sync botocor eversions across requirements files. (#4128)

* Decrease size of widget pagination (#4120)

* Added tests

* Perhaps this would trigger percy

* Decrease size of widget pagination

* Removed unused attr

* Updated tests

* Allow the user to decide how to handle null values in charts (#4071)

* getredash/redash#2629 Refactor Chart visualization, add option for handling NULL values (keep/convert to 0.0)

* Handle null values in line/area stacking code; some cleanup

* Handle edge case: line/area stacking when last value of one of series is missing

* Mjnor update to line/area stacking code

* Fix line/area normalize to percents feature

* Unit tests

* Refine tests; add tests for prepareLayout function

* Tests for prepareData (heatmap) function

* Tests for prepareData (pie) function

* Tests for prepareData (bar, line, area) function

* Tests for prepareData (scatter, bubble) function

* Tests for prepareData (box) function

* Remove unused file

* Alerts: Add more condition comparison options (#4134)

* getredash/redash#4132 Add more condition comparison options

* Add arguments to fallback lambda

* Remove duplicate messages method (#4131)

* Migrate Chart visualization to React Part 1: Renderer (#4130)

* Migrate Chart visualization: Renderer

* Refine PlotlyChart component; move stylesheets to visualization's folder

* Migrate Custom JS Chart to React

* Cleanup

* Add jsconfig settings with '@' webpack alias (#4135)

* Counter Editor: move components to own files (#4138)

* Allow users to share aggregated usage information with us (#4108)

* Initial commit of BeaconConsent component

* Add comment about being able to change setting

* Use <Text> correctly

* Final version of consent screen

* Show beacon consent message on homepage only if it wasn't enabled already.

* Add consent setting to organization settings screen.

* Add support for custom message in OrgSetting.save.

* Implmenet consent saving.

* If consent given, send extra data

* Add HelpTrigger

* Make CodeClimate happy

* Wrap everything with DynamicComponent

* CHANGELOG for V8-beta. (#4057)

* CHANGELOG for V8-beta.

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Bug fix: Query view doesn't sync parameters when selecting and deleting (#4146)

* Query Snippets: Use onClick instead of link for 'Click here' option (#4144)

* Snippets: Don't change url when not needed

* Revert "Snippets: Don't change url when not needed"

This reverts commit 2f346f3bb4.

* Query Snippets: use onClick instead of link

* Color picker component (#4136)

* Widget filters overlapped by visualization (#4137)

* Fix: widget filters overlapped by visualization

* Fix tests

* Fix tests

* CHANGELOG for v8.0.0-beta.2 (#4145)

* Stop building tarballs.

* Update version reference.

* CHANGELOG for 8.0.0-beta.2
2019-09-18 12:33:45 +03:00
Vladimir Ponarevsky
b426e4fdc4 Fix clickhouse password leak (#4078)
* Fix clickhouse password leak

* Fix after review
2019-08-18 11:05:41 +03:00
Arik Fraimovich
e5e926bac5 Pin kombu version (#4075)
kombu is a dependency of Celery and usually we let them declare the version, but their version is pinned and the most recent release (4.6.4) has a severe regression where workers stop responding to inspect commands.
2019-08-16 19:27:24 +03:00
Arik Fraimovich
24d68008fa Format target value as a number with reasonable default (#4073) 2019-08-15 15:54:32 +03:00
Arik Fraimovich
0e90b89acc ParameterizedQuery: handle the case where a value is null (#4072) 2019-08-15 15:18:40 +03:00
Jannis Leidel
2c2f241671 Require a more up-to-date version of importlib-metadata. (#4069) 2019-08-15 11:50:27 +03:00
Jakdaw
d49514abe9 When we fork a query, make sure we create the new visualizations in the same order as per the source query (#4067) 2019-08-14 11:08:56 +03:00
Arik Fraimovich
934a145ced Switch to mysqlclient from Python-MySQL (#4061) 2019-08-14 10:11:53 +03:00
Omer Lachish
f7c70c2b91 Add parameter dialog doesn't work when query has selected text (#4032)
* debounce updateQuery to prevent pasting parameters over selected texts failing parseQuery (see #4032)

* drop defer
2019-08-14 07:47:34 +03:00
The Alchemist
69ba165565 [Data Sources] Initial commit for adding Dgraph support (#3987)
* Initial commit for adding Dgraph support

* Made suggestions from https://codeclimate.com/github/getredash/redash/pull/3964

* feedback from @arikfr

* added logo for Dgraph from Twitter

* Better conversion of Dgraph JSON to Redash's internal JSON

* made recommendations from @arikfr

* removed unused function
2019-08-13 13:14:37 +03:00
Jannis Leidel
7b5696dc75 Fix loading of periodic tasks and clean up extension loading. (#4064)
* Fix loading of periodic tasks and clean up extension loading.

This does a few things:

- add tests for extension loading
- refactor the extension and periodic task loading
- better handle assertions raised by extensions (e.g. when an extension tries to override an already registered view)
- attach exception traceback to error log during loading for improved debugging

* Use site.addsitedir instead of calling pip.

* Use sys.path instead of site.addsitedir and also the setup.py egg_info command.
2019-08-13 13:11:59 +03:00
Gabriel Dutra
4698408a08 Cypress: Fix cy.clock not freezing time (#4060) 2019-08-13 07:08:59 -03:00
Ievgen Aleinikov
be142d60df Add assume role as a credential source for AWS Athena Query runner (#4028)
* allowing to specify a custom work group for AWS Athena queries

* Fixing title + adding correct position in the UI

* Adding assume role configuration to Athena query runner.

* removing extra blank lines

* fixes based on comments to the PR
2019-08-12 16:45:56 +03:00
Arik Fraimovich
aceea6516f Change the required Docker Compose version to 3.2 (#4059)
With the default Docker installed from sources on Ubuntu 19.04 it failed starting the project when asking for Compose version 3.7, but everything worked fine with 3.2.
2019-08-12 13:26:29 +03:00
Arik Fraimovich
685b53672e Prevent CSP violations by not having script URLs (#4062)
* Fix: remove inline script to avoid CSP violation

Closes #4039.

* Restore eslint rule that prevents javascript href attributes.

* Remove all inline script links.
2019-08-12 13:25:07 +03:00
Arik Fraimovich
7dd62ef948 Add option to control whether to format target value. (#4063) 2019-08-12 13:24:11 +03:00
Evghenii Goncearov
7c2acc34c9 Dont send password reset link to disabled users (#2631)
* Dont send password reset link to disabled users

* Update email subject

* Update blocked email text.

* Update blocked email text (plain text version).

* Remove debug print.
2019-08-11 17:29:26 +03:00
Arik Fraimovich
c5a90876f3 Add Cassandra to the list of default enabled query runners (#4058) 2019-08-11 17:17:57 +03:00
Takuya Arita
8abaf89394 Add tag management commands (#3168) 2019-08-11 16:30:48 +03:00
PengYuan Lai
aa2bd0042e check float if scale > 0 in snowflake query result (#3876) 2019-08-11 16:21:57 +03:00
Yoshiken
a7b14bfb9a Fix according to pycodestyle format (#4011)
* Fix W292 no newline at end of file

* Fix extra whitespace

* Fix E305 expected 2 blank lines after class or function definition

* Fix W391 blank line at end of file

* Fix E231 missing whitespace after

* Fix E303 too many blank lines

* Fix E302 expected 2 blank lines

* Fix E128 continuation line under-indented for visual indent
2019-08-11 16:09:04 +03:00
Oluwafemi Sule
4e5f55a4b7 Align content vertically in restricted widget type (#4056) 2019-08-11 15:28:46 +03:00
Omer Lachish
76fbe858ba refresh_queries requires Request Context (#4045)
* avoid using 'abort' in parameterized query - raise an exception instead

* when facing invalid parameters or detached dropdown queries - continue to refresh the rest of the outdated queries

* test that dropdown queries detached from data source raise an exception when fetch values is attempted

* test that queries with invalid parameters arent refreshed

* test that queries with dropdown query parameters which are detached from the data source are skipped

* fix stale test double name

* newlines. newlines everywhere.

* pass org into dropdown_values

* pass in org in every ParameterizedQuery usage

* Update redash/tasks/queries.py

Co-Authored-By: Arik Fraimovich <arik@arikfr.com>

* reduce refresh_queries log noise

* track failure count for queries that failed to apply parameters, and also notify the failures

* Update redash/tasks/queries.py

Co-Authored-By: Arik Fraimovich <arik@arikfr.com>

* newlines. newlines everywhere.
2019-08-09 15:26:31 +03:00
Omer Lachish
cf7aef1e16 Make sure there is an event for any query execution (#4051)
* move event recording for query executions inside run_query

* include indication of cache hit or miss inside execute_query events
2019-08-09 15:24:17 +03:00
Jannis Leidel
77625b2a13 Remove duplicate base_url function. (#4043) 2019-08-08 10:44:44 +03:00
Omer Lachish
c4dcf01b3c avoid variable shadowing (#4050) 2019-08-07 22:43:04 +03:00
Ran Byron
a167c590b6 Added arrow to multi-select component (#4044) 2019-08-06 16:46:53 +03:00
Gabriel Dutra
8e23f93433 Allow dynamic values dropdown to scroll with the page (#4040) 2019-08-06 08:55:25 -03:00
Levko Kravets
e41d40bbe0 getredash/redash#4036 Visualisation editor crashes out when changing datetype to non-HTML text (#4037) 2019-08-05 12:37:21 +03:00
Gabriel Dutra
6fc4d5b551 Focus DatePicker after selecting dynamic values (#4033) 2019-08-04 22:24:33 -03:00
Gabriel Dutra
f0576a3623 Support multi-select in parameters (#3952)
* Allow multiple values for enum parameter

* Allow multi-select for Query dropdown parameters

* CR + make sure list values are allowed

* Add prefix, suffix and separator

* Rename multipleValues and cast options as strings

* Replicate serialization logic on frontend

* Add Quote Option Select

* Make sure it's enum or query before join

* Add a couple of tests

* Add help to quote option

* Add min-width and normalize empty array

* Improve behavior when changing parameter settings
- Set parameter value again to pass through checks
- Add setValue check for multi values

* Validate enum values on setValue + CodeClimate

* Ran wording suggestions

* Updates after Apply Changes

* Fix failing Cypress tests

* Make sure enumOptions exists before split

* Improve propTypes for QueyBasedParameterInput

Co-Authored-By: Ran Byron <ranbena@gmail.com>

* CR

* Cypress: Test for multi-select Enum

* Fix multi-selection Cypress spec

* Update Refresh Schedule
2019-08-04 15:47:30 +03:00
Levko Kravets
9eabf89771 getredash/redash#4031 Counter visualization: formatting not applied to target value (#4035) 2019-08-04 15:22:53 +03:00
Arik Fraimovich
11cc274c1c Update Snowflake connector version to latest (#4029) 2019-08-04 08:55:02 +03:00
Ran Byron
8ad08a566a Revert "Revoked widget refresh button spinners" (#4027)
This reverts commit ab5494a8fd.
2019-08-01 08:23:17 +03:00
Levko Kravets
ef31d0d768 Fix: don't update dashboard's version when adding a widget (#4026) 2019-07-31 22:28:12 +03:00
Levko Kravets
4640c33387 Bug fix: error when trying to collect dashboard-level filters for a textbox widget (#4024) 2019-07-31 18:03:40 +03:00
Levko Kravets
9b290913a6 Migrate Table visualization to React Part 1: Renderer (#3963) 2019-07-31 17:33:33 +03:00
Ran Byron
db89c4f7bc Turned off max asset size warning (#4023) 2019-07-31 11:34:52 +03:00
Gabriel Dutra
eae1fb7d73 Force readonly inputs click (#4016) 2019-07-30 11:16:34 +03:00
Arik Fraimovich
4f742aeaac Fix: support for unicode in DynamoDB queries (#4015) 2019-07-30 11:14:57 +03:00
Ran Byron
5ddad862be Updated timeago strings (#4012)
* Updated timeago strings

* Moved moment config to app/config
2019-07-29 18:03:59 +03:00
Ran Byron
6f811f163a Added widget header refresh indicator (#3970) 2019-07-29 16:43:44 +03:00
Omer Lachish
7fb33e3ebb Failed Scheduled Queries Report (#3798)
* initial work on e-mail report for failed queries

* send failure report only for scheduled queries and not for adhoc queries

* add setting to determine if to send failure reports

* add setting to determine interval of aggregated e-mail report

* html templating of scheduled query failure report

* break line

* support timeouts for failure reports

* aggregate errors within message and warn if approaching threshold

* handle errors in QueryExecutor.run instead of on_failure

* move failure report to its own module

* indicate that failure count is since last report

* copy changes

* format with <code>

* styling, copy and add a link to the query instead of the query text

* separate reports with <hr>

* switch to UTC

* move <h2> to actual e-mail subject

* add explicit message for SoftTimeLimitExceeded

* fix test to use soft time limits

* default query failure threshold to 100

* use base_url from utils

* newlines. newlines everywhere.

* remove redundant import

* apply new design for failure report

* use jinja to format the failure report

* don't show comment block if no comment is provided

* don't send emails if, for some reason, there are no available errors

* subtract 1 from failure count, because the first one is represented by 'Last failed'

* don't show '+X more failures' if there's only one

* extract subject to variable

* format as text, while we're at it

* allow scrolling for long exception messages

* test that e-mails are scheduled only  when beneath limit

* test for indicating when approaching report limits + refactor

* test that failures are aggregated

* test that report counts per query and reason

* test that the latest failure occurence is reported

* force sending reports for testing purposes

* Update redash/templates/emails/failures.html

Co-Authored-By: Ran Byron <ranbena@gmail.com>

* Update redash/templates/emails/failures.html

Co-Authored-By: Ran Byron <ranbena@gmail.com>

* Update redash/tasks/failure_report.py

* add org setting for email reports

* remove logo from failure report email

* correctly use the organization setting for sending failure reports

* use user id as key for failure reports data structure

* Update redash/tasks/failure_report.py

Co-Authored-By: Arik Fraimovich <arik@arikfr.com>

* build comments while creating context for e-mail templates

* figure out the base url when creating the e-mail

* no need to expire pending failure report keys as they are deleted anyway when sent

* a couple of CodeClimate changes

* refactor key creationg to a single location

* refactor tests to send e-mail from a single function

* use beat to schedule a periodic send_aggregated_errors task instead of using countdown per email

* remove pending key as it is no longer required when a periodic task picks up the reports to send

* a really important blank line. REALLY important.

* Revert "a really important blank line. REALLY important."

This reverts commit c7d8ed8972.

* a really important blank line. REALLY important. It is the best blank line.

* don't send failure emails to disabled users
2019-07-28 12:40:54 +03:00
Omer Lachish
f165168860 recycle gunicorn workers (#4013) 2019-07-28 11:39:14 +03:00
Gabriel Dutra
86b0608fde Fix Apply Changes is lost when query is edited (#4010)
Co-Authored-By: Ran Byron <ranbena@gmail.com>
2019-07-27 19:05:49 -03:00
Gabriel Dutra
cd4daf8823 Add Dynamic Values to Date and Date Range Parameters (#3904)
* Draft for Date Dynamic values

* Use value with prefix instead of specific attr

* Fix not possible to select static value

* Update antd version

* Cleanup and DateRangeParameter

* Dynamic DateTimeRange

* Add Dynamic options to Date Parameters

* UI refinements

* Add getDynamicValue function

* Add 'This' options and prevent text clipping

* Make allowClear available

* Update ScheduleDialog snapshot

* Add some protections and separate Date/DateRange

* Accept null values on date or daterange parameters

* Handle undefined values on Moment propType

* Move export to end of files

* Remove Today/Now option

* Update with Apply Changes

* Show name instead of value for dynamic values

* Add comment about supporting useCurrentDateTime

* Cypress Tests: Date Parameters

* Cypress Tests: Date Range Parameters

* Don't put null params in the url

* Add workaround comments to Cypress tests

Co-Authored-By: Ran Byron <ranbena@gmail.com>

* Fix Dynamic Value as default for global parameters

* Update Back to Static Value

* Add isValid to value on Date and DateRange inputs

* CR suggestions

* Fix Back to Static Value for Dates

* Update Dynamic Value Styling

* Fix failing Date tests

* Fix selectedDynamicValue

* Parameter spec: Remove date range clickThrough

* Add transition

* Fix failing Cypress tests

* Back with 'width: auto'

* Check value is valid on Back to Static value

* CR

* Update Date Range width
2019-07-26 22:40:13 +03:00
Gabriel Dutra
78cae474e0 Cypress: Specify widgets position on sharing spec (#4009) 2019-07-26 13:15:39 -03:00
Naoyuki Kataoka
c518c7a4bc Modified PagerDuty destination to avoid an error for multi-byte characters (#4008) 2019-07-24 09:06:25 +03:00
Gabriel Dutra
8c2f51d09d Percy: Fix shared dashboard inconsistent snapshots (#4002) 2019-07-23 11:55:24 -03:00
Gabriel Dutra
6f6c68bd79 Cypress: Separate dashboard spec (#4003) 2019-07-22 11:09:08 -03:00
Ran Byron
64f274f58e Disable execute when params are dirty (#4001)
* Disable execute when params dirty

* Removed special apply handling for query page

* Updated tests
2019-07-22 12:13:34 +03:00
Omer Lachish
dd89bd885f Add "deprecated" flag to query runners (and alert destinations) (#3972)
* add a deprecated flag to query runners and show only non-deprecated query runners when adding a new data source

* add a deprecated flag to alert destinations and show only non-deprecated alert destinations when adding a new alert destination

* add a deprecated() decorator for a more succint way to deprecate

* deprecate URL query runner and HipChat alert destination

* use class properties instead of class methods for deprecation

* I <3 newlines
2019-07-22 10:36:31 +03:00
Ran Byron
b2295197cf Added publish notification to query rename (#3998) 2019-07-21 15:01:08 +03:00
Omer Lachish
ea0e411053 Return unsafe sharing error from backend (#3990)
* return message explaining unsafe sharing

* use backend-generated message for public dashboards

* use backend-generated message for embeds

* Update redash/handlers/query_results.py

Co-Authored-By: Arik Fraimovich <arik@arikfr.com>

* refactor simple (non-interpolated) query result handler error messages to a single location

* use error_messages to test out unsafe error messages (along with a couple of others)

* Update redash/handlers/query_results.py

Co-Authored-By: Ran Byron <ranbena@gmail.com>

* Update redash/handlers/query_results.py

Co-Authored-By: Arik Fraimovich <arik@arikfr.com>
2019-07-21 09:21:45 +03:00
Arik Fraimovich
9bdb3412a5 Move query runners/destinations import from redash.app to redash. (#3993)
* Move query runners/destinations import from redash.app to redash.

* Add missing argument
2019-07-21 09:05:29 +03:00
Fumiya Karasawa
ad4a760545 Search dropdown parameters (#3796) 2019-07-20 16:07:03 +03:00
Omer Lachish
c1f4147807 Avoid committing it.only (#3995)
* remove it.only, left by mistake

* use no-only-tests

* 'off' should be used instead of 'none'

* Dedup jest/only rule

* always error for .only
2019-07-19 18:30:38 +03:00
Ran Byron
c054ae8be0 Fixed filter style issue (#3996) 2019-07-18 13:49:37 +03:00
Omer Lachish
d1edd3d068 Query Result API response shouldn't include query information for non authenticated users (#3985)
* avoid catching errors on text widgets' load(), as they don't have a visualization and therefore do not return any promise

* throw error when failing to load widgets on public dashboards - in case something needs to be done with it at a later time, and it's the right thing to do anyway

* use Promise.resolve instead of checking for undefined

* call serialize_query_result instead of directly calling to_dict

* filter unneeded query result fields for unauthenticated users

* test for serialization filtering

* lint

* use project instead of list comprehension
2019-07-18 12:12:49 +03:00
Arik Fraimovich
4989bfae60 Remove custom Redis connection code in favor of redis.from_url (#3992) 2019-07-18 12:03:52 +03:00
Gabriel Dutra
f20a020003 Use AceEditor for Query Snippets (#3973)
Co-Authored-By: Ran Byron <ranbena@gmail.com>
2019-07-17 13:47:31 -03:00
Ran Byron
01da8c158a Parameter “Apply Changes” button (#3907) 2019-07-17 17:17:39 +03:00
Omer Lachish
c83e40b047 Celery doesn't auto reload in development (#3898)
* pick up *.py file changes and restart scheduler

* only watch /redash in order to avoid reloading on other file changes (e.g. tests)

* add dev_scheduler entrypoint

* use exec

* Update bin/docker-entrypoint

* rename dev_scheduler to dev_worker

* use same defaults as worker
2019-07-17 10:38:56 +03:00
Ran Byron
c3cc65a21d Viz embed logo alignment (#3956) 2019-07-16 11:37:31 +03:00
Omer Lachish
5929139ab8 A couple of parameters-on-public-dashboards loose ends (#3988)
* avoid catching errors on text widgets' load(), as they don't have a visualization and therefore do not return any promise

* throw error when failing to load widgets on public dashboards - in case something needs to be done with it at a later time, and it's the right thing to do anyway

* use Promise.resolve instead of checking for undefined
2019-07-16 10:48:37 +03:00
Ran Byron
66794acd1f Added loading indicator to public dashboard (#3984) 2019-07-16 10:31:19 +03:00
Arik Fraimovich
bce0832e48 Show error in case of failing to load a dashboard (#3983) 2019-07-15 22:13:46 +03:00
Yuri Grishaev
9f006997a0 mattermost needs whitespace to use h4 heading (#3981)
#### Even Smaller Heading - good
####Even Smaller Heading - bad
2019-07-15 21:13:17 +03:00
Omer Lachish
51d8131db5 Allow Parameters on Public Dashboards (#3659)
* change has_access and require_access signatures to work with the objects that require access, instead of their groups

* use the textless endpoint (/api/queries/:id/results) for pristine
queriest

* Revert "use the textless endpoint (/api/queries/:id/results) for pristine"

This reverts commit cd2cee7738.

* go to textless /api/queries/:id/results by default

* change `run_query`'s signature to accept a ParameterizedQuery instead of
constructing it inside

* raise HTTP 400 when receiving invalid parameter values. Fixes #3394

* enqueue jobs for ApiUsers

* rename `id` to `user_id`

* support executing queries using Query api_keys by instantiating an ApiUser that would be able to execute the specific query

* show deprecation messages for ALLOW_PARAMETERS_IN_EMBEDS. Also, move
other message (email not verified) to use the same mechanism

* add link to forum message regarding embed deprecation

* change API to /api/queries/:id/dropdowns/:dropdown_id

* split to 2 different dropdown endpoints and implement the second

* add test cases for /api/queries/:id/dropdowns/:id

* use new /dropdowns endpoint in frontend

* first e2e test for sharing embeds

* Pleasing the CodeClimate overlords

* All glory to CodeClimate

* remove residues from bad rebase

* add query id and data source id to serialized public dashboards

* add global parameters directive to public dashboards page

* allow access to a query by the api_key of the dashboard which includes
it

* rename `object` to `obj`

* simplify permission tests once `has_access` accepts groups

* support global parameters for public dashboards

* change has_access and require_access signatures to work with the objects that require access, instead of their groups

* rename `object` to `obj`

* simplify permission tests once `has_access` accepts groups

* no need to log `is_api_key`

* send parameters to public dashboard page

* allow access to a query by the api_key of the dashboard which includes it

* disable sharing if dashboard is associated with unsafe queries

* remove cypress test added in the wrong place due to a faulty rebase

* add support for clicking buttons in cy.clickThrough

* Cypress test which verifies that dashboards with safe queries can be shared

* Cypress test which verifies that dashboards with unsafe queries can't be shared

* remove duplicate tests

* use this.enabled and negate when needed

* remove stale comment

* add another Cypress test to verify that unauthenticated users have access to public dashboards with parameters

* obviously, I commit 'only' the first time I use it

* search for query access by query id and not api_key

* no need to fetch latest query data as it is loaded by frontend from the textless endpoint

* test that queries associated with dashboards are accessible when supplying the dashboard api_key

* propagate `isDirty` down to `QueryBasedParameterInput`

* go to /api/:id/dropdown while editing a query, since dropdown queries might still not be associated with the parent. see #3711

* show helpful error message if dropdown values cannot be fetched

* use backticks instead of line concatenation

* remove requirement to have direct access to dropdown query in order validate it. parent query association checks are sufficient

* remove isDirty-based implementation and allow dropdown queries through nested ACL even if they aren't associated yet (given that the user has _direct_ access to the dropdown query)

* fix tests to cover all cases for /api/queries/:id/dropdowns/:id

* fix indentation

* require access to the query, not the data source

* resolve dashboard user by query id

* apply new copy to Cypress tests

* if only something would have prevented me from commiting an 'only' call 🤔

* very important handling of whitespace

* respond to parameter's Apply button

* text widgets are safe for sharing

* remove redundant event

* add a safety check that object has dashboard_api_keys before calling it

* supply a parameter value for text parameters to have it show up

* add parameter values for date and datetime

* use the current year and month to avoid pagination

* use Cypress.moment() instead of preinstalled moment()

* explicitly create parameters

* refresh query data if a  querystring parameter is provided

* avoid sending a data_source_id - it's only relevant to unsaved queries, since a saved query's data_source is available in the backend

* remove empty query text workaround

* provide default value to parameter

* add a few more dashboard sharing specs

* lint

* wait for DynamicTable to appear to reveal that actual results are displaying

* override error message for unsafely shared widgets
2019-07-15 15:09:30 +03:00
Arik Fraimovich
c793b5dd11 Remove explicit kombu dependency (#3978)
We used an explicit kombu dependency to target the correct Redis version, but current version of Celery supposed to use it by default.
2019-07-14 08:47:15 +03:00
Levko Kravets
4e9da3f116 [Bug fix] Plotly legend overlaps plot on small screens (when legend clicked) (#3976) 2019-07-13 18:27:53 +03:00
Arik Fraimovich
15a8eecdde JSON Data Source (#3805)
* WIP: JSON Data Source

* Add JSON data source to default list
2019-07-11 14:23:38 +03:00
k-tomoyasu
a8ff2500be Build custom alert message (#3137)
* build custom alert message

* fit button color tone

* pass existing test

* fix typos

* follow code style

* add webhook alert description and avoid key error

* refactor: create alert template module

* follow code style

* use es6 class, fix template display

* use alerts.options, use mustache

* fix email description

* alert custom subject

* add alert state to template context, sanitized preview

* remove console.log 🙇

* chatwork custom_subject

* add alert custom message. pagerduty, mattermost, hangoutschat

* Pass custom subject in webhook destination

* Add log message when checking alert.

* Add feature flag for extra alert options.
2019-07-11 13:23:06 +03:00
Ran Byron
7bf84e856c Workaround fixes for datepicker display bug in Cypress tests (#3967) 2019-07-10 17:47:46 +03:00
Levko Kravets
5149bf67ca [Bug fix] Archiving a dashboard cause widgets to show reload spinner forever (#3968) 2019-07-10 13:35:15 +03:00
Arik Fraimovich
93449db325 Improvements to Query Result serialization code (#3960)
* Fix: allow serializing empty or bad dates

* Improve date serialization performance

* Remove duplicate assertion.
2019-07-10 10:51:34 +03:00
Arik Fraimovich
df57d22e81 Add explicit route for dashboards to allow embedding in iframes. (#3957)
* Add explicit route for dashboards to allow embedding in iframes.

* Add missing blank lines
2019-07-10 10:25:08 +03:00
Gabriel Dutra
de0a44ee85 Migrate Query Snippets to React (#3627) 2019-07-09 09:27:39 -03:00
Tomoki Sekiyama
261062d491 Support multi-byte search for query names and descriptions (#3908)
* Support multi-byte search for query names and descriptions

* add multi_byte_support_enabled option to organization settings

* add `ilike %...%` to query search conditions when the option is enabled

* Improve description for multi_byte_search_enabled option

Co-Authored-By: Arik Fraimovich <arik@arikfr.com>

* Remove tsvector from search when multi_byte_search_enabled

* Add a multi-byte search test case
2019-07-08 10:01:47 +03:00
Arik Fraimovich
1878e8bf90 Add additional Celery config options (#3961) 2019-07-08 08:32:18 +03:00
Arik Fraimovich
47fc8a942a Add options to hide different parts of embed UI (parameters, title, link to query) (#3955)
* Move closing tag to correct location

* Add options to hide elements in query embed UI

* Fix for headless top padding (#3959)
2019-07-07 14:34:00 +03:00
Arik Fraimovich
addecbdd8f Allow calling query results endpoint without parameters. (#3958)
* Allow calling query results endpoint without parameters.

* Fix: allow serializing empty or bad dates

* Revert "Fix: allow serializing empty or bad dates"

This reverts commit cc49319d9e.
2019-07-07 14:22:08 +03:00
Gabriel Dutra
baec5d56f5 Remove time from Date column in filters (#3953) 2019-07-05 18:13:12 -03:00
Levko Kravets
1f4325ba8d Migrate Box Plot visualization to React (#3948) 2019-07-04 22:25:09 +03:00
Arik Fraimovich
5e5b56ed6a Fix: render date/time values as strings and not epoch time (#3951) 2019-07-04 20:49:59 +03:00
Arik Fraimovich
45a3b72730 Update fsevents to v1.2.9 (#3950) 2019-07-04 15:43:43 +03:00
Levko Kravets
cc48de0d8f Migrate Word Cloud visualization to React (#3930) 2019-07-03 13:29:05 +03:00
Arik Fraimovich
300f3f6780 Fix: waiting tasks are not shown in admin view (#3942)
* Fix: waiting tasks are not shown properly

* Added a comment.
2019-07-03 11:09:42 +03:00
Omer Lachish
2e4a69cba4 Parameter spec fixes (#3932)
* supply a parameter value for text parameters to have it show up

* add parameter values for date and datetime

* use the current year and month to avoid pagination

* use Cypress.moment() instead of preinstalled moment()

* capture time before clicking on Now

* use now from input

* use now from input for another test
2019-07-01 09:56:55 +03:00
Arik Fraimovich
6748e9a15d Add option to hide Pivot Table totals (#3943)
* Add option to hide Pivot Table totals

* Simplify implementation using DEFAULT_OPTIONS.

* Flip hide pivot controls to show pivot controls

* Update client/app/visualizations/pivot/Editor.jsx

Co-Authored-By: Ran Byron <ranbena@gmail.com>
2019-06-30 15:43:18 +03:00
Arik Fraimovich
7ceb68a468 Visualization: details view (#3778)
* Details visualization

* Add PropTypes and guard against no rows
2019-06-30 15:34:02 +03:00
Levko Kravets
3c1d1e3d4e Explicitly mark default visualization (#3944) 2019-06-30 14:09:00 +03:00
Levko Kravets
92391e3cbc [Bug fix] Toggling Use Dashboard Level Filter cause widgets to show reload spinner forever (#3939) 2019-06-28 15:02:07 +03:00
Levko Kravets
17438002d7 [Bug fix] Adding widget from query page is broken (#3921) 2019-06-25 20:06:36 +03:00
Jianchao Yang
a00c5a8857 Dockerfile front end stage copies client side files only (#3924)
So that changing other files will not trigger the
very expensive rebuild process.
2019-06-23 11:33:52 +03:00
John Karahalis
a696fa55f3 Set unique class name for Query Control Dropdown (#3922)
This will help me target the Query Control Drodpwon in my extension.
2019-06-23 10:01:46 +03:00
Arik Fraimovich
27259b5abe Add support for int/float values in guess_type (#3913) 2019-06-20 08:55:31 +03:00
Ran Byron
9ee393ec75 Fix schedule dialog needless confirm saves (#3919) 2019-06-20 08:54:53 +03:00
Ran Byron
cfafa97218 Fixed boolean filter (#3915) 2019-06-19 14:54:27 +03:00
deecay
be580b24a5 Expose celery job timeout setting to env var (#3912)
* Expose celery job timeout to env

* Change variable name
2019-06-19 14:41:12 +03:00
Arik Fraimovich
a6960c5f19 Fix: time format option was wrong (#3916) 2019-06-19 14:37:02 +03:00
Arik Fraimovich
6dd321beeb Rockset: handle query errors (#3910) 2019-06-18 14:10:25 +03:00
Arik Fraimovich
27c64b42ac Add keyboard shortcut for format query (Ctrl/Cmd+Shift+F) (#3911)
* Add keyboard shortcut for format query

* Added to button tooltip
2019-06-18 14:10:05 +03:00
deecay
99bf6d122c Custom Map Markers (#3840) 2019-06-18 09:50:09 +03:00
Ran Byron
d617f57f7d Increase celery job timeout (#3903) 2019-06-17 08:35:02 +03:00
Guy Cohen
21a27ee0b1 Fix OverflowError on celery worker (#3899) 2019-06-16 11:34:29 +03:00
YOSHIDA Katsuhiko
ac293c7f92 Add alert deletion confirmation dialog (#3902) 2019-06-15 14:28:55 +03:00
YOSHIDA Katsuhiko
8e38dcd244 Support regenerating Query API Key (#3764)
* Add regenerate function of query's API Key

* Add regenerate API Key button

* Add regenerate Query API Key tests

* Fix too long line

* Replace  with this

* Return a simple version query

* Update only API Key

* Update API Key via query
2019-06-12 13:09:21 +03:00
Aidarbek Suleimenov
2bab144107 Celery task to clear schedule was added (#3801)
* Celery task to clear schedule was added

* fix formating

* empty_schedules task was put in separate task

* worker interval changed, new tests added

* past artifact deleted

* test queries moved to right class, lambda was used to filter data

* unnecessary changes eliminated

* more unnecessary files deleted

* line shortened

* Line shortened more

* codeclimate changes

* Unused test deleted, logs added
2019-06-12 13:07:15 +03:00
Mike Nason
4e0a251034 Add support ssl connections to redis (#3848)
* Add support ssl connections to redis

* Fix line length

* Update redash/__init__.py w suggestion

Co-Authored-By: Omer Lachish <omer@rauchy.net>

* Cleanup init after suggestion

* Move redis SSL config to settings

* Do not pass celery SSL config unless necessary

* Fix typo
2019-06-12 13:04:34 +03:00
Levko Kravets
7a9f4b07e0 Force a Choose account step for Google OAuth (#3884) 2019-06-12 11:48:25 +03:00
Arik Fraimovich
1630cbb904 Google Sheets: friendlier error message in case of an API error and more reliable test connection (#3883)
* Google Sheets: friendlier error message in case of an APIError and more reliable test connection.

* Pleasing the pep8 gods
2019-06-12 11:46:59 +03:00
Levko Kravets
f8d05dda9f getredash/redash#3879 Plotly legent overlaps plot on small screens (FF only) (#3882) 2019-06-12 11:46:34 +03:00
Omer Lachish
2af8b39d21 Authorize according to API key (if given) over cookies (#3877)
* remove legacy session identifier support

* remove redundant test

* redirect to login to support any invalid session identifiers

* be more specific with caught errors

* use authorization according to api_key (if provided) over session
2019-06-12 11:45:28 +03:00
dependabot[bot]
3faed0fdfe Bump pyopenssl from 16.2.0 to 17.5.0 (#3872)
Bumps [pyopenssl](https://github.com/pyca/pyopenssl) from 16.2.0 to 17.5.0.
- [Release notes](https://github.com/pyca/pyopenssl/releases)
- [Changelog](https://github.com/pyca/pyopenssl/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/pyopenssl/compare/16.2.0...17.5.0)
2019-06-12 11:38:47 +03:00
dependabot[bot]
e45f49b86e Bump cryptography from 2.0.2 to 2.3 (#3870)
Bumps [cryptography](https://github.com/pyca/cryptography) from 2.0.2 to 2.3.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/2.0.2...2.3)
2019-06-12 11:38:38 +03:00
Omer Lachish
e33ad3b164 Query Results: querying a column with a dictionary or array fails (#3887)
* flatten lists and dicts to json to be used with SQLite's json_extract functions

* add test that verifies that lists and dicts are saved

* add test that verifies that lists and dicts are saved
2019-06-11 17:41:15 +03:00
Omer Lachish
6605f62f3a add api_key to embed download urls (#3896) 2019-06-11 14:02:02 +03:00
Omer Lachish
ed2ac407ab Remove schema after deleting data source (#3894)
* remove schema from redis after deleting data sources

* switch to _pause_key to property
2019-06-10 22:39:26 +03:00
Ran Byron
dda75cce24 Drawer menu with recreated close button (#3889)
* Drawer menu with recreated close button

* Added “Open in new window” drawer menu button (#3890)
2019-06-09 12:21:53 +03:00
Omer Lachish
5b780ac460 Refresh Public Dashboards (#3881)
* remove legacy session identifier support

* remove redundant test

* redirect to login to support any invalid session identifiers

* be more specific with caught errors

* fix refresh for public dashboards
2019-06-06 11:07:12 +03:00
koooge
c0e8ef3000 Upgrade gspread 3.1.0 for supporting team drive (#3838)
* Upgrade gspread 3.1.0 for supporting team drive

Signed-off-by: koooge <koooooge@gmail.com>

* Revert "Upgrade gspread 3.1.0 for supporting team drive"

This reverts commit e53e8cb75b.

* Upgrade gspread 3.1.0 for supporting team drive

Signed-off-by: koooge <koooooge@gmail.com>

* Update Sheets query runner name
2019-06-06 11:02:08 +03:00
Gabriel Dutra
a82fd0cabc Cypress: Fix date parameters false positive (#3873) 2019-06-04 12:19:55 -03:00
Ran Byron
0e3e2eaf38 Restrict dynamic-table internal scroll only when pagination appears (#3875) 2019-06-04 09:11:19 -06:00
Omer Lachish
05f6ef0fb6 Remove legacy session identifier support (#3866)
* remove legacy session identifier support

* remove redundant test

* redirect to login to support any invalid session identifiers

* be more specific with caught errors
2019-06-03 22:18:24 +03:00
dependabot[bot]
e433efebc4 Bump flask from 0.11.1 to 0.12.3 (#3871)
* Bump flask from 0.11.1 to 0.12.3

Bumps [flask](https://github.com/pallets/flask) from 0.11.1 to 0.12.3.
- [Release notes](https://github.com/pallets/flask/releases)
- [Changelog](https://github.com/pallets/flask/blob/master/CHANGES.rst)
- [Commits](https://github.com/pallets/flask/compare/0.11.1...0.12.3)

* Bump to Flask 0.12.4 to fix an issue
2019-06-03 18:04:43 +03:00
Arik Fraimovich
a9588eac79 Update version to 8.0.0-beta. (#3869) 2019-06-02 14:37:16 +03:00
Arik Fraimovich
090b570a71 Rearrange README badges. 2019-06-02 14:01:09 +03:00
Arik Fraimovich
60b12e3121 Update PromiseRejectionError to show error message from API response (#3868)
* Update PromiseRejectionError to show error message from API response

* Update version to 8.0.0-beta.

* Revert "Update version to 8.0.0-beta."

This reverts commit c8fa74967f.
2019-06-02 11:47:26 +03:00
Arik Fraimovich
3f8c7333be Use the debian flavor of the redash/base image (#3240) 2019-06-02 11:42:19 +03:00
Arik Fraimovich
be8dec5f04 Add Collapse component (#3867) 2019-06-02 11:12:37 +03:00
Levko Kravets
10b3b50f3d getredash/redash#3862 Widget menu should not be available on public dashboards (#3863) 2019-06-02 11:00:10 +03:00
Levko Kravets
6f290ddfa1 Use more specific CSS for public dashboard page to avoid conflicts with other components (#3864) 2019-05-31 23:41:43 +03:00
Ran Byron
10b62ebe02 Beautify autoheight code a bit (#3865) 2019-05-31 14:09:29 -06:00
Ran Byron
04453409da Fix widget autoHeight related exception (#3858) 2019-05-31 05:57:26 -06:00
Gabriel Dutra
b27df216f4 Cypress tests for query parameters (#3810) 2019-05-30 10:01:44 -03:00
Levko Kravets
a0c76d777b getredash/redash#3851 Disable filter which does not have values (#3852) 2019-05-30 15:53:28 +03:00
Levko Kravets
2e96e2fb98 getredash/redash#3849 Fix initialization of dashboard-level parameters (#3853) 2019-05-30 15:51:58 +03:00
Gabriel Dutra
c2e31f040d Fix table isn't displayed with date filter (#3842) 2019-05-29 09:40:25 -03:00
Arik Fraimovich
816f4d912f Capitalize "elsewhere" (#3844) 2019-05-29 14:47:17 +03:00
Arik Fraimovich
9292ae8d3f CSV: correctly serialize booleans and dates. (#3841)
* CSV: correctly serialize booleans and dates.

Closes #3736, closes #2751.

* pep8 fixes

* Move column iteration to a helper function.

* Use elif, as types are mutually exclusive.

* Refactor parsing implementation.

* Move the csv generation fucntion
2019-05-29 10:45:29 +03:00
Levko Kravets
9480d89e4c [Feature] Migrate CreateDashboardDialog to React (#3826) 2019-05-27 23:12:52 +03:00
Ran Byron
5dff5b929c Unsupported browser redirect script outputted to file (#3832) 2019-05-27 13:18:04 -06:00
Gabriel Dutra
28e9740e2f Update Data Source Lifecycle Events (#3828) 2019-05-27 19:28:16 +03:00
Ran Byron
7679df63ba Fix for Mac browser’s scrollbar obscuring content (#3830) 2019-05-27 18:31:02 +03:00
Jannis Leidel
07c9530984 Decouple extensions from Flask app. (#3569)
* Decouple extensions from Flask app.

This separates the extension registry from the Flask app and also introduces a separate registry for preriodic tasks.

Fix #3466.

* Address review feedback.

* Update redash/extensions.py

Co-Authored-By: jezdez <jannis@leidel.info>

* Minor comment in requirements.

* Refactoring after getting feedback.

* Uncoupled bin/bundle-extensions from Flas app instance.

* Load bundles in bundle script and don’t rely on Flask.

* Upgraded to importlib-metadata 0.9.

* Add missing requirement.

* Fix TypeError.

* Added requirements for bundle_extension script.

* Install bundles requirement file correctly.

* Decouple bundle loading code from Redash.

* Install bundle requirements from requirements.txt.

* Use circleci/node for build-docker-image step, too.
2019-05-26 14:56:02 +03:00
Rueian
aecd0bf37a include bigquery_gce.png in db-logos (#3825) 2019-05-26 13:27:29 +03:00
Omer Lachish
4143bd3f20 when authenticated, the query shouldn't be sent over to the /jobs endpoint (#3831) 2019-05-26 12:19:32 +03:00
Arik Fraimovich
020dc35faf Create SECURITY.md (#3823)
* Create SECURITY.md

* Update SECURITY.md

Co-Authored-By: Gabriel Dutra <nesk.frz@gmail.com>
2019-05-26 09:32:41 +03:00
Gabriel Dutra
d7b03bac02 Add CircleCI env vars to Cypress docker (#3827) 2019-05-23 22:33:23 +03:00
Omer Lachish
29875e66d4 Plug custom Celery tasks via dynamic settings (#3819)
* plug custom celery tasks via dynamic settings

* an extra blank line
2019-05-22 11:37:18 +03:00
taminif
d97ce15837 refactor format semi-colon (#3812) 2019-05-22 11:10:16 +03:00
Gabriel Dutra
b263bb7077 [Bug fix] Fix "Now" in DateTime parameter not working (#3808) 2019-05-16 18:42:48 +03:00
Ran Byron
606cf12e74 Dashboard grid React migration #1 (#3722)
* Dashboard grid React migration

* Updated tests

* Fixes comments

* One col layout

* Tests unskipped

* Test fixes

* Test fix

* AutoHeight feature

* Kebab-cased

* Get rid of lazyInjector

* Replace react-grid-layout with patched fork to fix performance issues

* Fix issue with initial layout when page has a scrollbar

* Decrease polling interval (500ms is too slow)

* Rename file to match it's contents

* Added some notes and very minor fixes

* Fix Remove widget button (should be visible only in editing mode); fix widget actions menu

* Fixed missing grid markings

* Enhanced resize handle

* Updated placeholder color

* Render DashboardGrid only when dashboard is loaded
2019-05-16 15:43:46 +03:00
Levko Kravets
4508975749 [Bug fix] Plotly modebar appears above modals (#3799) 2019-05-15 10:43:34 +03:00
Gabriel Dutra
c76955be28 Refresh query when parameters update (#3737)
* Add touch state to parameters and autoupdate query

* Use values change event instead of $watch

* Remove getQueryResultDebounced

* Add Apply button

* Remove Input Number spinners for Parameters

* Make Apply Button optional

* Update share_embed_spec

* Change debounce to the Parameters component

* Remove unnecessary click on Execute query

* Add apply button to the remaining places

* Update dashboard_spec

* Use onKeyUp for InputNumber

* Simplify onParametersValuesChanged

* Update DateTime onChange function

* Don't apply when modifier key is pressed

* Remove refresh Button from Parameters

* Update apply button styling

* Update apply right distance

* Remove debounce for testing

* Use data-dirty instead of classNames for styling

* Make sure $apply runs before calling onChange
2019-05-15 08:57:06 +03:00
Gabriel Dutra
4f402379e8 Migrate Embed Query Dialog to React (#3783)
* Update Antd

* Migrate Embed Query Dialog to React

* Update jest ScheduleDialog snapshot

* Add Alert for unsafe queries

* Add CodeBlock

* Add inputs to change iframe size

* Undo ant update

* Update share embed spec

* Update styling

* Change border-radius to 2px

* Update margin between Public URL and IFrame Embed
2019-05-15 08:38:40 +03:00
Ran Byron
733b60102d Fixed visual-card alignment (#3795) 2019-05-14 14:10:35 -06:00
Levko Kravets
b9b30a39d2 [Bug fix] Edit parameter mapping: error when trying to change mapping type to Static; cannot change static value (#3800)
* Edit parameter mapping: error when trying to change mapping type to Static

* Parameter mapping editor: cannot change static value
2019-05-14 19:10:02 +03:00
Levko Kravets
c74d469181 resize-event: take into account transformations and transitions (#3794) 2019-05-13 22:51:05 +03:00
Ran Byron
95f11e6686 Loading indicator till app inits (#3788) 2019-05-13 12:11:22 -06:00
Jakdaw
ad6f7109de Fix support for calling MySQL Stored Procedures and allow queries to be cancelled (#3003)
* If MySQL returns multiple resultSets (eg when executing multiple statements, or calling stored procedures) then use the last resultSet that has columns

* Make cancellation of a MySQL query work (the same way the C client does it)

* Address code climate moans
2019-05-13 18:51:51 +03:00
taminif
b09ae46a9f filtered tag remove empty name at edit query (#3784)
* filtered tag remove empty name at edit query

* use filter
2019-05-13 17:57:45 +03:00
Arik Fraimovich
0cda0369f0 [BigQuery] Fix: in some queries there is no mode field (#3786)
Happened with INSERT/UPDATE queries.
2019-05-13 17:08:18 +03:00
Arik Fraimovich
50f11069ce Presto: ignore blank passwords (#3791)
PyHive expects only None as no password.
2019-05-13 12:36:07 +03:00
Arik Fraimovich
6bf764be07 Update query to bring only name to make sure screenshots are consistent. (#3790) 2019-05-12 20:43:32 +03:00
Ran Byron
3159410694 Restrict markdown image dimensions (#3789) 2019-05-12 20:23:10 +03:00
Gabriel Dutra
76bd2e3c50 Migrate Organization Settings to React (#3728)
* Migrate Organization Settings to React

* Fix failing spec and replace default values from inputs

* Add HelpTrigger and handleChange to SAML options

* Undo changes to ant-variables.less

* Add time format to OrganizationSettings
2019-05-12 14:23:22 +03:00
Omer Lachish
50a6f723b1 Fix Ability to Add Query-based Parameters to Existing Queries (#3716)
* propagate `isDirty` down to `QueryBasedParameterInput`

* go to /api/:id/dropdown while editing a query, since dropdown queries might still not be associated with the parent. see #3711

* show helpful error message if dropdown values cannot be fetched

* use backticks instead of line concatenation

* remove requirement to have direct access to dropdown query in order validate it. parent query association checks are sufficient

* remove isDirty-based implementation and allow dropdown queries through nested ACL even if they aren't associated yet (given that the user has _direct_ access to the dropdown query)

* fix tests to cover all cases for /api/queries/:id/dropdowns/:id

* fix indentation

* require access to the query, not the data source

* use require_access instead of has_access
2019-05-12 12:48:01 +03:00
Omer Lachish
0ee20797c8 Fix embeds without parameters (#3775)
* provide queryId when fetching query results in order to allow query-based api-key authentication to work properly

* cypress test to verify that embeds without parameters are shared succesfully

* rename Percy snapshot
2019-05-12 12:28:31 +03:00
taminif
d7515562a4 Fix: Filter empty tags (#3780) 2019-05-10 09:55:53 -06:00
Osmo Salomaa
feafbbe318 Avoid error with duplicate log lines (#3777)
https://docs.angularjs.org/error/ngRepeat/dupes
2019-05-07 22:07:39 +03:00
deecay
b7b345dacd [Feature] Choropleth customize (added new map: Japanese Prefectures) (#3154) 2019-05-07 12:04:17 +03:00
Arik Fraimovich
0b22aa55a1 DynamoDB: safe implementation of schema loading (#3774)
* Safe implementation of describe_all.

* autopep8.
2019-05-06 20:13:45 +03:00
Arik Fraimovich
3eddea6e88 Show non relative timestamp when printing an embed. (#3773)
(Also used for the Slack snapshots)
2019-05-06 20:12:57 +03:00
yoavbls
c85e097f8a [Bug fix] Fix dashboard filters to collect options too (#3759) 2019-05-06 11:40:21 +03:00
YOSHIDA Katsuhiko
81bc4ef58b [Feature] Add direction option in Pie Chart (#3762) 2019-05-06 11:30:48 +03:00
Omer Lachish
9fec3ca9ea Poll for results in parameterized embeds (#3752)
* add an endpoint for fetching job using a query's api_key

* when unauthenticated, use api_key to get job, and fetch the latest query
result (as opposed to fetching the query result by ID)

* add 'refresh dataset' button to parameters directive

* fix scope error introduced by earlier commit

* show timer when refreshing results

* Show input for missing parameters in embedded visualizations (#3741)

* Redirect to default parameter values when parameters are missing in
embedded visualizations

* Revert "Redirect to default parameter values when parameters are missing in"

This reverts commit 43c65500b7.

* load all data after page is loaded

* return no data only when parameters are missing

* data binding no longer required

* show an error on embeds that fail to load

* data binding no longer required

* present full-page error when dealing with unsafe queries

* don't render the execute button for each parameter

* show 'missing parameter value' error

* Don't reload the whole page when parameter value changes.

* Set API key and load config before rendering.

* Add Query#hasParameters method.

* Don't show download controls for parameterized queries (they won't work).

* Use getUrl to construct a correct query link.

* WIP: have a single way to load results

1. This preloads the query before rendering the page, so we can benefit from using default parameters & make the logic in component simpler.
2. Use a single way to load results, to make sure we do polling when try to load the query results for the first time.

* Show persistent errors and finish loading logic.

* Check if query is safe and show message otherwise.

* Fix test for unsafe parameters embed.

* wait for query results to return before taking snapshot
2019-05-06 09:14:56 +03:00
Arik Fraimovich
ee29cf9efc Fix: pie chart not rendering when series doesn't exist in options. (#3756) 2019-05-05 09:04:52 +03:00
Arik Fraimovich
17aba39636 Fix: default value for Presto password should be None (#3757) 2019-05-05 09:04:42 +03:00
Yusuke Goto
2cd1b07a41 Add: organization setting for time format (#3754)
* Support for time format

* Add selects test

* Rename into date_time_format_config
2019-05-05 09:03:27 +03:00
taminif
72d00314a4 [Code style] Add semi-colons (#3755) 2019-05-02 22:30:05 +03:00
Aidarbek Suleimenov
5b077ab083 Support for Presto password (#3619) 2019-05-01 17:25:59 +03:00
Takuya Arita
da2d6bc3a8 Move is_url_key method to function for testability. (#3750) 2019-05-01 13:52:41 +03:00
Takuya Arita
33930a5b9c Remove unused import statements (#3751) 2019-05-01 13:51:56 +03:00
John Karahalis
fbff4f9219 Convert query control dropdown button to React (#3698) 2019-05-01 07:20:54 +03:00
Jannis Leidel
30f725f1e1 Add missing parameter to new BigQuery query runner method. (#3747) 2019-04-30 21:16:58 +02:00
Gabriel Dutra
47cd05b48e Cypress: Fix Stuck E2E test - create_query_spec (#3748) 2019-04-30 14:21:26 -03:00
Levko Kravets
9a4433bf68 Migrate visualizations registry/renderer/editor to React (#3493) 2019-04-30 16:34:00 +03:00
Ran Byron
d0b2151b4d Fix query page height (#3744) 2019-04-29 23:29:17 +03:00
Omer Lachish
21e22a2d0d add get_by_id to Organization (#3712) 2019-04-29 21:58:29 +03:00
Gabriel Dutra
f3a653c57f Fix query based parameter has value null when created (#3707)
* Fix query based parameter value null when created

* Use toString to avoid having 'null' string
2019-04-29 21:50:04 +03:00
guwenqing
c9bf412240 Update npm run to fix hpe_header_overflow (#3732)
Nodejs has set max header size to 8k in http_parser,
need to provide a larger header size to make the proxy work.
2019-04-29 21:23:06 +03:00
Osmo Salomaa
48955b5fa1 Use monospace font in query output log (#3743)
Closes #3739
2019-04-29 21:21:51 +03:00
Ran Byron
24a5748528 Dashboard grid markings (#3656) 2019-04-29 15:49:09 +03:00
Arik Fraimovich
8758279b14 Use REDASH_BASE_PATH everywhere instead of hardcoded path (#3740)
Closes #3727
2019-04-29 14:28:16 +03:00
Jannis Leidel
99bb24d899 Make creating the BigQuery job data pluggable. (#3742)
This would for example allow adding custom job labels (https://cloud.google.com/bigquery/docs/adding-using-labels#job-label) for easier accounting.
2019-04-29 14:18:36 +03:00
Omer Lachish
c93a905c1d Fix Ability to save with Multiple Dropdown Parameters (#3717)
* support multiple associations of the same query-based dropdown parameter

* include several query-based parameters in association tests
2019-04-28 14:25:26 +03:00
AntonZ
a1e75d2f0b feature: add couchbase query runner (#3658)
* feature: add couchbase query runner

* fix style

* fix style

* fix style

* fix naming due to convention

* extracting protocol as parameter
2019-04-24 20:13:59 +03:00
Ran Byron
fb48bc374a Refactored dashboard drag/resize testing (#3726) 2019-04-22 10:07:22 +03:00
Ran Byron
10a6ccbbcd Dashboard save fail indication (#3715) 2019-04-19 21:41:35 +03:00
Gabriel Dutra
fea082ec77 Update Percy network idle timeout (#3724) 2019-04-19 10:58:37 -03:00
Jannis Leidel
aa9d2466cd Split redash/__init__.py to prevent import time side-effects. (#3601)
## What type of PR is this? (check all applicable)
<!-- Please leave only what's applicable -->

- [x] Refactor
- [x] Bug Fix

## Description

This basically makes sure that when import the redash package we don't accidentally trigger import-time side-effects such as requiring Redis.

Refs #3569 and #3466.
2019-04-18 18:39:38 +02:00
Arik Fraimovich
97492d7aa0 Fix: update default CSP policy to allow KB iframe. (#3714)
## What type of PR is this? (check all applicable)

- [x] Bug Fix

## Description

Without this change the Help Drawer couldn't load content anymore.

## Related Tickets & Documents

#3404
2019-04-17 10:13:45 +02:00
Ran Byron
18761cf07b Dashboard auto-saving (#3653) 2019-04-17 10:07:48 +03:00
Arik Fraimovich
9b3dd82ec0 Sync PyAthena/botocore versions with requirements_all_ds.txt. (#3713) 2019-04-17 09:43:33 +03:00
Arik Fraimovich
e485c964c5 Add rate limits to user creation/update (#3709)
* Add rate limits for user resources.

* Disable rate limiting in tests (except for tests that need it).

* Update strings to unicode to avoid SQLA warnings
2019-04-15 13:58:30 +03:00
Omer Lachish
5b30d081d7 Dynamic query time limits (#3702)
* extract time limit decisions to a dynamic settings function

* introduce environment variable for scheduled query time limits

* pass in org_id to query_time_limit

* add an interaction test that verifies that time limits are applied to
jobs

* really important newlines according to CodeClimate
2019-04-15 12:06:37 +03:00
Omer Lachish
b96094b878 add a test to make sure reset password form are displayed correctly (#3678) 2019-04-14 14:59:21 +03:00
Gabriel Dutra
1f43537304 Update CardsList to use visual-card styling (#3679)
* Update CardsList to use old markup

* CR
2019-04-14 13:10:40 +03:00
Aidarbek Suleimenov
01e64db0dc Fix Decimal128 error (#3684) 2019-04-14 13:07:12 +03:00
Arik Fraimovich
3ab46bb39a BigQuery: support for NaN values. (#3701) 2019-04-14 11:23:14 +03:00
Jakdaw
af168c69b9 Fix search ordered by best match (#3706)
* Don't force an order by created date - any usecases that want
this already request it explicitly and it breaks the search
function that wants to order by best match.

* Fix the logic so that we fall back to a default search order when there's
no search term, rather than when there is one and we should use the
best-match ordering.

* Remove accidentially added blank line
2019-04-14 10:44:28 +03:00
Jakdaw
63e052c3a3 Support LDAP servers where one doesn't first have to bind to the LDAP server with a username/password (#3002)
* Support LDAP servers where one doesn't first have to bind to the LDAP server with a username/password

* Address code climate things
2019-04-14 10:36:41 +03:00
Ran Byron
563e34a816 Fixed public dashboard footer (#3703) 2019-04-14 10:19:12 +03:00
Gabriel Dutra
1524d06149 Percy: Introduce hide-in-percy and hide diff problematic elements (#3689) 2019-04-11 11:49:39 -03:00
Ran Byron
e9711a0b9c Bye footer (#3697) 2019-04-10 11:42:36 +03:00
Omer Lachish
9fcf510ffd add package.json after including qs (#3695) 2019-04-10 11:25:56 +03:00
Ran Byron
70227f2e43 Changed viz embed download menu to drop up (#3696) 2019-04-10 11:02:03 +03:00
Ran Byron
1babd01f38 Bolder markdown in textbox (#3686) 2019-04-09 10:25:03 +03:00
Ran Byron
768bfb3525 Cypress Dashboard Service (#3683) 2019-04-09 08:49:10 +03:00
Ran Byron
fc5a624efb Dashboard one column mode test (#3621) 2019-04-08 07:51:18 +03:00
Omer Lachish
47bf91e150 Fix: support date ranges for parameterized embeds (#3681)
* support date ranges for parameterized embeds

* add qs

* remove hideous implementation and use qs

* get rid of apiKey querystring parameter and introduce query-string
module
2019-04-07 20:34:14 +03:00
Ran Byron
8f4288583e Updated Cypress default timeout values (#3685) 2019-04-07 16:40:06 +03:00
Omer Lachish
595af3bce8 avoid erroring when creating embed links for queries that don't have any parameters (#3680) 2019-04-06 13:49:40 +03:00
HirokiTanaka
dba7efe030 refs https://github.com/getredash/redash/issues/3675 (#3676) 2019-04-04 09:27:07 +03:00
Omer Lachish
1b142b33f1 reduce volatility in embed percy snapshots (#3672) 2019-04-02 13:40:28 +03:00
Gabriel Dutra
13814c752d Add max-width to Notification (#3667) 2019-04-02 11:53:44 +03:00
Omer Lachish
dd477d49ec Sharing embeds with safe parameters (#3495)
* change has_access and require_access signatures to work with the objects that require access, instead of their groups

* change has_access and require_access signatures to work with the objects that require access, instead of their groups

* use the textless endpoint (/api/queries/:id/results) for pristine
queriest

* Revert "use the textless endpoint (/api/queries/:id/results) for pristine"

This reverts commit cd2cee7738.

* go to textless /api/queries/:id/results by default

* change `run_query`'s signature to accept a ParameterizedQuery instead of
constructing it inside

* raise HTTP 400 when receiving invalid parameter values. Fixes #3394

* support querystring params

* extract coercing of numbers to function, along with a friendlier
implementation

* wire embeds to textless endpoint

* allow users with view_only permissions to execute queries on the
textless endpoint, as it only allows safe queries to run

* enqueue jobs for ApiUsers

* add parameters component for embeds

* include existing parameters in embed code

* fetch correct values for json requests

* remove previous embed parameter code

* rename `id` to `user_id`

* support executing queries using Query api_keys by instantiating an ApiUser that would be able to execute the specific query

* bring back ALLOW_PARAMETERS_IN_EMBEDS (with link on deprecation coming up)

* show deprecation messages for ALLOW_PARAMETERS_IN_EMBEDS. Also, move
other message (email not verified) to use the same mechanism

* add link to forum message on setting deprecation

* rephrase deprecation message

* add link to forum message regarding embed deprecation

* change API to /api/queries/:id/dropdowns/:dropdown_id

* split to 2 different dropdown endpoints and implement the second

* add test cases for /api/queries/:id/dropdowns/:id

* use new /dropdowns endpoint in frontend

* first e2e test for sharing embeds

* Pleasing the CodeClimate overlords

* All glory to CodeClimate

* change has_access and require_access signatures to work with the objects that require access, instead of their groups

* split has_access between normal users and ApiKey users

* remove residues from bad rebase

* allow access to safe queries via api keys

* rename `object` to `obj`

* support both objects and group dicts in `has_access` and `require_access`

* simplify permission tests once `has_access` accepts groups

* change has_access and require_access signatures to work with the objects that require access, instead of their groups

* rename `object` to `obj`

* support both objects and group dicts in `has_access` and `require_access`

* simplify permission tests once `has_access` accepts groups

* fix bad rebase

* send embed parameters through POST data

* no need to log `is_api_key`

* move query fetching by api_key to within the Query model

* fetch user by adding a get_by_id function on the User model

* pass parameters as POST data (fixes test failure introduced by switching
from query string parameters to POST data)

* test the right thing - queries with safe parameters should be embeddable

* introduce cy.clickThrough

* add another Cypress test to make sure unsafe queries cannot be embedded

* serialize Parameters into query string

* set is_api_key as the last parameter to (hopefully) avoid
backward-dependency problems

* Update redash/models/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* attempt to fix empty percy snapshots

* snap percies after DOM is fully loaded
2019-04-02 11:45:38 +03:00
Ran Byron
5decd2624a Fixed wrong width assertion (#3665) 2019-04-01 13:49:24 +03:00
Justin Clift
6f9aee42a7 Update to modern Redis for the docker images (#3640) 2019-04-01 11:21:06 +03:00
Omer Lachish
1333aae7fb Handle dropdown queries which are detached from data source (#3453)
* handle an edge case where dropdown queries are connected to data sources
that no longer exist

* Rethinking it, an empty result set makes no sense and it's better to
throw an error

* remove redundant import
2019-04-01 11:19:52 +03:00
Omer Lachish
33ad89a381 in case of a parameter type mismatch, show the actual message to the user (#3664) 2019-04-01 11:19:18 +03:00
Ran Byron
02a5852072 Widget size and position test (#3628) 2019-03-29 21:47:26 +03:00
Gabriel Dutra
12782e4daf Fix Percy diff due to Api Key secret (#3654) 2019-03-29 07:50:30 -03:00
Levko Kravets
704b78a003 [Feature, Bug fix] Migrate Timer component to React; update TimeAgo component (#3648) 2019-03-28 20:33:05 +02:00
Omer Lachish
ec4f77c8b7 Change has_access and require_access signatures (#3611)
* change has_access and require_access signatures to work with the objects that require access, instead of their groups

* rename `object` to `obj`

* support both objects and group dicts in `has_access` and `require_access`

* simplify permission tests once `has_access` accepts groups
2019-03-28 15:01:06 +02:00
Ran Byron
1871287a1f Fixed notification alignment (#3645) 2019-03-28 10:08:13 +02:00
Gabriel Dutra
f9cc230227 Migrate Data Sources and Alert Destinations pages to React (#3470)
* Migrate TypePicker to React

* Migrate DataSources and Destinations List

* Fixes to DestinationsList

* Add CreateDataSource (testing with Steps)

* Render the form after type selection

* Add HELP_LINKS to CreateDataSource

* Add Done behavior

* Add scrollToTop to CreateDataSource

* TypePicker styling adjusts

* Add CreateDestination

* Update resouce gets to componentDidMount

* Create EditForm components

* Migrate Edit pages

* Remove angular logic from DynamicForm

* Add actions to EditPages

* TypePicker title style adjustments

* Add Empty and Loading state

* UX improvements

* Review changes

* Styling updates on TypePicker, forms background fix

* Add blank line removed by mistaken

* Reorganize TypePicker

* Hide Search on List Pages

* Fix spacing in Forms

* Update Create Data Source and Destination to be a Dialog

* Remove max-height from the form

* Fix DynamicForm import in CreateUserDialog

* Route /new to open CreateSourceDialog

* Add HelpTrigger + refine styling and Edit Pages

* Remove help links from data source resource

* Update Cypress specs

* TypePicker -> CardsList

* Remove old TypePicker styling and change CardsList styling to less

* Test if Percy shows Dialogs

* Personal review cleanup

* CR

* Remove unnecessary query on dialog success

* Handle resource errors in Edit Pages

* Add CreateDestination policy

* Add placeholder and separator to the Name field

* Use cy.click instead of cy.wait

* Revert "Use cy.click instead of cy.wait" (Didn't work)

This reverts commit 77285d9fa3.

* Align help trigger on the right and rename Steps

* Refine behavior for long names

* Update toastr calls to use notification instead

* Redirect to target after creation

* Remove autoFocus on DynamicForm for Edit Pages

* Add eslint-disable for cy.wait
2019-03-28 10:06:46 +02:00
Ran Byron
fe4a7b65e7 Widget resize tests (#3620) 2019-03-28 05:55:03 +02:00
Allen Short
b3819de878 Treat repeated BigQuery fields as arrays (#3480)
* Treat repeated BigQuery fields as arrays

* handle untransformed field types and None
2019-03-27 22:00:09 +02:00
Gabriel Dutra
2699d24441 Manage user groups in UserEdit (#3450) 2019-03-27 16:29:48 -03:00
Jannis Leidel
1933dee8ca Fix Celery worker --max-tasks-per-child for Celery 4.x. (#3625)
* Fix Celery worker CLI parameter name that was changed in Celery 4.x.

* Set Celery worker --max-memory-per-child to 1/4th of total system memory.

* Review fixes.

* Review fixes.
2019-03-27 21:08:20 +02:00
Gabriel Dutra
375e61f263 Add error message when destination name already exists (#3597)
* Return 400 when destination name already exists

* Remove whitespace

* Unicode 1

Co-Authored-By: gabrieldutra <nesk.frz@gmail.com>

* Unicode 2

Co-Authored-By: gabrieldutra <nesk.frz@gmail.com>
2019-03-27 18:09:56 +02:00
shinsuke-nara
872d0ca5e6 Show accessible tables only in New Query view for PostgreSQL (#3599)
* Show accessible tables only.

* Get table information from information_schema.columns.

* Union old query.
2019-03-27 18:08:38 +02:00
Justin Clift
973ad565cd Update PostgreSQL version to always use latest in the 9.5 series (#3639) 2019-03-27 18:06:40 +02:00
ialeinikov
7a7fdf9c99 allowing to specify a custom work group for AWS Athena queries (#3592)
* allowing to specify a custom work group for AWS Athena queries

* Fixing title + adding correct position in the UI
2019-03-27 17:58:48 +02:00
Omer Lachish
49ffaae3ec Fix email shows as unverified when no email server is configured (#3613)
* check that e-mail server is configured before marking the email address
as not verified and sending out a verification e-mail

* use helper method in `invite_user`

* move email_server_configured helper to settings

* add test to verify that email addresses arent marked as unverified if
there's no e-mail server to verify them

* simplify a couple of tests with patch

* combine conditions into single variable

* Booleans, gotta love 'em
2019-03-27 17:57:51 +02:00
Allen Short
d5494cff08 Fail query task properly even if error message is empty (#3499) 2019-03-27 17:50:39 +02:00
Byunghwa Yun
71afc99ec3 Add phoenix query runner. (#3153)
* Add phoenix query runner.

* Improved error handling.
2019-03-27 17:48:49 +02:00
Ran Byron
b5d97e25b7 Browser support config (#3609)
* Browser support config

* Removed some offending code

* Added unsupported html page and redirect for IE

* Typo in regex

* Made html page static

* Added redirect script to multi_org

* Moved static html page to client/app
2019-03-27 17:47:12 +02:00
Jannis Leidel
6c26aa7a99 Render LDAP and remote auth login links correctly when multi org mode is enabled. (#3530)
* Make LDAP auth handler org scoped.

* Render LDAP and remote auth login links correctly when multi org mode is enabled.
2019-03-27 17:26:00 +02:00
Jannis Leidel
712fc63f93 Use flask-talisman for handling backend response headers (#3404)
* Normalize Flask initialization API use.

* Use Flask-Talisman.

* Enable HSTS when HTTPS is enforced.

* More details about how CSP is formatted and write CSP directives as a string.

* Use CSP frame-ancestors directive and not X-Frame-Options for embedable endpoints.

* Add link to flask-talisman docs.

* set remember_token cookie to be HTTP-Only and Secure

* Reorganize secret key configuration to be forward thinking and backward compatible.
2019-03-27 17:24:15 +02:00
Jannis Leidel
77c53130a4 Fix a few more inconsistencies when loading and dumping JSON. (#3626)
* Fix a few more inconsistencies when loading and dumping JSON.

Refs #2807. Original work in: #2817

These change have been added since c2429e92d2.

* Review fixes.
2019-03-27 17:14:32 +02:00
Levko Kravets
73c8e3096d [Feature] Migrate Admin pages to React (#3568) 2019-03-27 09:48:50 +02:00
Ran Byron
8230098f50 Migrated Textbox edit dialog to React (#3632) 2019-03-26 19:23:00 +02:00
Arik Fraimovich
fd42091f87 Add Lint step to CircleCI (#3642) 2019-03-26 16:40:26 +02:00
Ran Byron
ec4b36b178 Cypress eslint fixes and config (#3636) 2019-03-25 22:14:51 +02:00
Ran Byron
0995dfbf43 Widget drag tests (#3598) 2019-03-25 19:16:41 +02:00
Gabriel Dutra
70d4c724c2 Add env var to skip Flask rate limits (#3622) 2019-03-25 13:15:20 -03:00
Justin Clift
1d7378f84b Update docker compose with the stable Redash v7 tag (#3638) 2019-03-25 14:50:40 +02:00
Gabriel Dutra
b4a4ee212e Replace toastr with Ant Notification (#3610) 2019-03-24 19:08:35 -03:00
Gabriel Dutra
25910e7655 Move cypress to client folder (#3566) 2019-03-24 11:24:59 -03:00
Jannis Leidel
8e5ba804f6 Fix a DeprecationWarning about the Flask.static_path parameter. (#3624)
Code: d1d82ca8ce/flask/app.py (L347-L351)
2019-03-24 15:57:35 +02:00
Arik Fraimovich
173f9ba7e8 Fix: triggers not created for queries.search_vector (#3631) 2019-03-24 15:35:59 +02:00
Arik Fraimovich
e712c19bbe E2E test for query search (#3633)
* Apply prettier to app-header.html.
* Add: E2E test for query search
2019-03-24 15:20:08 +02:00
Ran Byron
aea3c9dbaa Fix for time based mongodb test (#3630) 2019-03-24 11:29:44 +02:00
Ran Byron
2f8aade697 Added a skipped test for issue #3202 (#3616) 2019-03-23 22:02:39 +02:00
Ran Byron
a7b930a422 Widget tests - add, remove, auto height (#3590) 2019-03-23 14:27:43 +02:00
Levko Kravets
4e69b73b0f [Bug fix] Query Results fails to use query which has double quotes in column names (#3618) 2019-03-21 19:39:22 +02:00
Omer Lachish
c47dd05095 Nest query ACL to dropdowns (#3544)
* change API to /api/queries/:id/dropdowns/:dropdown_id

* extract  property

* split to 2 different dropdown endpoints and implement the second

* make access control optional for dropdowns (assuming it is verified at a
different level)

* add test cases for /api/queries/:id/dropdowns/:id

* use new /dropdowns endpoint in frontend

* require access to dropdown queries when creating or updating parent
queries

* rename Query resource dropdown endpoints

* check access to dropdown query associations in one fugly query

* move ParameterizedQuery to models folder

* add dropdown association tests to query creation

* move group by query ids query into models.Query

* use bound parameters for groups query

* format groups query

* use new associatedDropdowns endpoint in dashboards

* pass down parameter and let it return dropdown options. Go Levko!

* change API to /api/queries/:id/dropdowns/:dropdown_id

* split to 2 different dropdown endpoints and implement the second

* use new /dropdowns endpoint in frontend

* pass down parameter and let it return dropdown options. Go Levko!

* fix bad rebase

* add comment to clarify the purpose of checking the queryId
2019-03-20 09:16:10 +02:00
Arik Fraimovich
15c815fb5e Remove node_modules before creating tarball (#3603)
* Update pack

* Remove node_modules before packing
2019-03-18 12:16:31 +02:00
Arik Fraimovich
9de676acee Fix: make sure that only the top level node_modules directory is excluded (#3600)
* Fix: make sure that only the top level node_modules directory is excluded

* Remove old unused packing script
2019-03-18 11:13:42 +02:00
Arik Fraimovich
ff0967f0d8 Update v7 CHANGELOG and version (#3595) 2019-03-17 22:05:55 +02:00
Ran Byron
e5d082b9b3 Textbox testing - add, remove, edit (#3589) 2019-03-17 18:26:01 +02:00
Arik Fraimovich
93aa6b5b80 Fix: accept integer values in dropdowns. (#3596) 2019-03-17 15:56:23 +02:00
Gabriel Dutra
1f74c0bad5 [Bug fix] Update user list when an user is created (#3594) 2019-03-15 20:56:10 +02:00
Feng
a8cb70910e [Codebase improvement] Reuse handlers base require_fields (#3577) 2019-03-15 13:21:48 +02:00
Bernhard Mäser
852636f07c update to latest stable version (#3588) 2019-03-14 18:13:18 +02:00
Arik Fraimovich
cf5c2c5ba2 Revert "Add SAML scheme override env var (#2947)" (#3587)
This reverts commit 4768fd081e.
2019-03-14 13:54:14 +02:00
Levko Kravets
10f4b99cd3 Widget title link not updated when parameter value changes (#3586) 2019-03-14 11:20:28 +02:00
Jannis Leidel
8456bbf762 Revert "Schema Viewer Drawer (#3291)" (#3585)
This reverts commit cb4d81d6ad.
2019-03-14 10:51:30 +02:00
Ran Byron
ab39242cc4 Cypress test - dashboard create/archive (#3565) 2019-03-14 08:08:56 +02:00
Ran Byron
b799ab6f0b Added alt+enter key binding (#3479) 2019-03-14 08:06:39 +02:00
Marina Samuel
cb4d81d6ad Schema Viewer Drawer (#3291)
* Process extra column metadata for a few sql-based data sources.

* Add Table and Column metadata tables.

* Periodically update table and column schema tables in a celery task.

* Fetching schema returns data from table and column metadata tables.

* Add tests for backend changes.

* Front-end shows extra table metadata and uses new schema response.

* Delete datasource schema data when deleting a data source.

* Process and store data source schema when a data source is first created or after a migration.

* Tables should have a unique name per datasource.

* Addressing review comments.

* Update migration file for mixins.

* Appease PEP8

* Upgrade migration file for rebase.

* Cascade delete.

* Adding org_id

* Remove redundant column and table prefixes.

* Non-existing tables and columns should be filtered out on the server side not client side.

* Fetching table samples should be optional and should happen in a separate task per table.

* Allow users to force a schema refresh.

* Use updated_at to help prune old schema metadata periodically.

* Using settings.SCHEMAS_REFRESH_QUEUE
2019-03-13 18:08:00 +01:00
Ran Byron
adf935b1df Fix for parameter mapping bug #3581 (#3582)
* Fix for parameter mapping bug #3581

* Must run original snapshot with original dashboard params
2019-03-13 16:41:03 +02:00
Arik Fraimovich
f1cb0101b9 👋 goodbye, similar-code false positives. (#3578) 2019-03-13 11:04:02 +01:00
Leo Palmer Sunmo
4768fd081e Add SAML scheme override env var (#2947)
* Add SAML scheme override env var

* Make it pretty, please the linter
2019-03-13 11:39:02 +02:00
Levko Kravets
4a8d9a7fb0 Fork query does not fork tables but instead adds default table (#3580)
* getredash/redash#3572 Fork query does not fork tables but instead adds default table

* Fix code style

* CR1
2019-03-13 11:29:54 +02:00
Arik Fraimovich
ba62b46a45 Append "UTC" to timestamp on embeds (in print view) (#3574)
To communicate what timezone the timestamp is.
2019-03-13 08:26:12 +02:00
Levko Kravets
fbf4dae001 Fix webpack config to work with symlinks (#3573) 2019-03-12 21:24:21 +02:00
Omer Lachish
5943bf04d5 change the order of configuration of the Elasticsearch data source to (#3571)
make sense
2019-03-12 09:42:17 +02:00
Ran Byron
93ec19b93f Prevent blank edit-in-place value (#3557) 2019-03-11 23:24:38 +02:00
Omer Lachish
63d3f22c93 Convert all dropdown values to strings to support parameter lookup (#3563)
* convert all dropdown values to strings to support parameter lookup.
solves #3562

* unicode all the way down

* show correct default values in QueryBasedParameterInput by converting
them to strings
2019-03-11 17:54:56 +02:00
Gabriel Dutra
685c7713e4 Update Amazon Elasticsearch Service image (#3567) 2019-03-11 06:43:03 +02:00
Levko Kravets
4cfa26a55e [Bug fix] Handle errors on Group members, Group datasources and User profile pages (#3564) 2019-03-10 19:34:54 +02:00
Ran Byron
5dc74e1ef7 npm audit fixes (#3561) 2019-03-10 13:35:27 +02:00
Arik Fraimovich
b703f7a3c4 Create weekly-digest.yml 2019-03-10 11:39:13 +02:00
Arik Fraimovich
26f0ce0749 New Celery/Queries Execution Status API (#3057)
* Remove QueryTaskTracker

* Remove scheudling of cleanup_tasks

* Add Celery introspection tools

* First iteration of updating the admin API.

* Show more details

* Add option to skip building npm in Dockerfile

* Show started_at

* update the refresh schedule, as it's too fast

* Update Celery monitor to report on all active tasks.

* Update task parsing for new format

* WIP: improved celery status screen

* Fix property name.

* Update counters

* Update tab name

* Update counters names

* Move component to its own file and fix lint issues

* Add migratin to remove Redis keys

* Improve columns layout

* Remove skip_npm_build arg as it's not used anymore.

* Convert query from SQL to Python

* Simplify column definition.

* Show alert on error.
2019-03-10 11:19:31 +02:00
Levko Kravets
12d2a04946 Download Query Result links: use query name for downloaded filename (#3559)
* getredash/redash#3554 Download Query Result links: use query name for downloaded filename

* CR1
2019-03-10 10:32:08 +02:00
koooge
5501f3e61c Upgrade jest & babel (#3405) 2019-03-10 09:29:44 +02:00
Arik Fraimovich
61f143dfd3 Snowflake: add support for regions and enable by default (#3550)
* Bring back Snowflake from its exile.

* Snowflake: add support for regions.
2019-03-07 23:40:24 +02:00
Arik Fraimovich
8737e8032e Add: Docker entrypoint to do Celery healthchecks. (#3548) 2019-03-07 22:32:27 +02:00
Arik Fraimovich
dfa48caf63 Fix: order dashboard favorites (#3552)
## What type of PR is this? (check all applicable)

- [x] Refactor
- [x] Bug Fix

## Description

Move favorites list handlers to their relevant modules (`redash.handlers.queries` and `redash.handlers.dashboards`) and applied `order_results` to dashboards.
2019-03-07 15:30:11 +01:00
Arik Fraimovich
e4c933af55 Update PULL_REQUEST_TEMPLATE.md (#3549) 2019-03-07 14:35:27 +02:00
Levko Kravets
be1bd2863f [Bug fix] Wrong behavior when clicking table rows on list pages (#3540) 2019-03-07 13:59:39 +02:00
Ran Byron
507ea61151 Fix long tag labels breaking table layout (#3545) 2019-03-07 11:53:28 +02:00
Arik Fraimovich
4f79c86c0e Pin pymapd version to 0.7.1 (#3543)
Newer versions dropped support for Python 2. Closes #3542.
2019-03-07 09:02:40 +02:00
Ran Byron
160c3c1048 Param fix (#3528)
* Name help text for date range only

* Autofocus on name input

* Form acts on enter key

* Fixed range check

* Fixed startsWith
2019-03-06 10:31:33 +02:00
Jannis Leidel
8eb751f0c3 Remove docker-compose.production.yml in favor of setup/docker-compose.yml. (#3533)
Fix #3251.
2019-03-06 08:49:35 +02:00
Paul Graff
75bc469708 Remove duplicate column information for late-binding views (#3537)
Since the svv_columns system view supports them now https://docs.aws.amazon.com/redshift/latest/dg/r_SVV_COLUMNS.html
2019-03-06 08:41:48 +02:00
Jannis Leidel
21082fbe0e Make the "celery" queue the default instead of "schema" to stay backward-compatible. (#3534)
Fix #3325.
2019-03-06 08:37:54 +02:00
Jannis Leidel
4e7d16b642 Remove Flask-Admin. (#3532) 2019-03-06 08:36:46 +02:00
pieter-venter
b68051d3c5 Add Hangouts Chat as alert destination (#3525)
* Add support for Google Hangouts Chat as alert destination

* Remove redundant imports

* Remove code used for debugging

* Fix pep8 warnings

* Update redash/destinations/hangoutschat.py

Add friendly name by separating type and description

Co-Authored-By: pieter-venter <pieterventer@geotab.com>

* Fix pep8 warnings. Rename image to match desitnation type.

* Show message for unknown alert state in default color
2019-03-05 22:06:25 +02:00
Levko Kravets
bc22797009 [Refactor] Refine New user modal (#3529) 2019-03-05 14:17:59 +02:00
Ran Byron
7a4fe5055d Added UTC display to scheduler dialog (#3517) 2019-03-05 11:54:33 +02:00
Gabriel Dutra
6a75ac4a57 Migrate User Pages to React (#3506)
* Create React version for the EmailSettingsWarning

* Migrate the Create User Page

* Migrate UserProfile to React

* Add /users/me to the routes (Percy ftw)

* Fix UserShow test spec

* Remove Error Messages component

* Show invitation link if email server not setup (#3519)

* return invite link to client if e-mail server is not set up

* add a couple of tests to make sure invite links are only returned when neccessary

* show invite link when e-mail is not configured

* remove "an e-mail has been sent" when there's no e-mail configured

* return invite_url in re-invites as well. Also refactor to reuse the code.

* Use CreateUserDialog instead of Page

* Render invite link on Resend Invitation click

* Add email validation to DynamicForm

* Fix EmailWarning position + update user list with user creation success

* Fix console error on UserProfile

* Redirect from /users/new  + rename createUser -> showCreateUserDialog

* Use alert instead of toastr for user creation errors

* Remove logic from CreateUserDialog

* CR

* Use Promise.reject instead of throw to avoid console error
2019-03-04 18:26:51 -03:00
Ran Byron
34da15fd6a Migrated AddTextboxDialog to AntD (#3524) 2019-03-04 21:51:48 +02:00
Ran Byron
dd0fab7275 Moved widget and dashboard save logic out of dialog (#3522) 2019-03-04 16:31:31 +02:00
Gabriel Dutra
ade3cc72a7 Fix eslint error on AlertsList (#3518) 2019-03-03 00:28:17 +02:00
Omer Lachish
02e82a7658 Fix verification_email endpoint when in MULTI_ORG mode (#3502)
* append slug to /verification_email endpoint when in MULTI_ORG mode

* Revert "append slug to /verification_email endpoint when in MULTI_ORG mode"

This reverts commit 817fb034c4.

* fix for /email_verification in MULTI_ORG setups
2019-03-02 14:12:41 +02:00
Ran Byron
6e3b9c2977 Fixed navbar responsiveness (#3510)
* Fixed navbar responsiveness

* Adjustments so that logo should never hide
2019-02-28 16:53:42 +02:00
Ran Byron
34e03b01bb Migrated query edit/add param dialog to React/AntD/Hooks (#3488) 2019-02-28 16:31:34 +02:00
Ran Byron
dab35acd2c QuerySelector in Alert page (#3501) 2019-02-28 15:56:12 +02:00
Arik Fraimovich
a93741e64b CircleCI build improvements (#3511)
* Make sure master builds tarball/docker image only when backend and frontend tests pass.

* Build a redash/preview image alongside redash/redash image.

* Fix version variable
2019-02-28 15:23:47 +02:00
Ran Byron
549f878c98 Added <QuerySelector /> component (#3494)
* Updated npm to support react hooks

* Added <QuerySelector />

* Changed selectQuery to also clear, completed 2->3 dots in msg, avoiding setSearching on stale rejection.

* Removed unused highlight lib
2019-02-28 15:07:57 +02:00
Gabriel Dutra
194f45263b [Feature] Migrate Alerts List Page to React (#3505) 2019-02-28 12:33:03 +02:00
G. Tsirkas
83668a6840 LDAP Authentication. Create two envars REDASH_LDAP_USE_SSL and REDASH_LDAP_AUTH_BIND (#2776)
* Add two new envars. REDASH_LDAP_USE_SSL which determines if the connection will use ssl and LDAP_AUTH_BIND which determines if the binding is SIMPLE or ANONYMOUS

* Add use_ssl paremeter

* Rename LDAP_AUTH_BIND to LDAP_AUTH_METHOD and modify LDAP_SSL using parse_boolean

* Fix typo

* import ANONYMOUS constant from ldap3

* Add NTLM authentication

* Add comment to authentication method envar
2019-02-28 10:05:41 +02:00
Toshimitsu Takahashi
c9a4f07a7a Support AWS IAM profile for Amazon Elasticsearch (#3005) 2019-02-28 09:57:33 +02:00
Omer Lachish
e9c88ea176 Verify address when users change their e-mail (#3504)
* re-verify e-mail address on change

* send verification e-mail to the new address
2019-02-27 12:17:20 +02:00
ialeinikov
fbaded4548 adding gevent worker in requirements.txt, adding some gunicorn config… (#3333)
* adding gevent worker in requirements.txt, adding some gunicorn configurable parameters with defaults

* reverting the change as it's going to be set via env variable(s)
2019-02-27 11:15:31 +02:00
Omer Lachish
570e8d9f23 set invitation as not-pending in render_token_login_page only when (#3500)
handling invites (as opposed to password reset links)
2019-02-27 09:06:41 +02:00
Omer Lachish
0d76c036cb Be more permissive when parameters are safe (#3383)
* use the textless endpoint (/api/queries/:id/results) for pristine
queriest

* reverse conditional. not not is making me the headaches.

* add ParameterizedQuery#is_safe with an inital naive implementation which
treats any query with a text parameter as not safe. This will be
remedied later when DB drivers will handle these parameters.

* allow getting new query results even if user has only view permissions
to the data source (given that the query is safe)

* fix lint error - getDerivedStateFromProps should be placed after state

* Revert "use the textless endpoint (/api/queries/:id/results) for pristine"

This reverts commit cd2cee7738.

* move execution preparation to a different function, which will be soon
reused

* go to textless /api/queries/:id/results by default

* let the query view decide if text or textless endpoint is needed

* allow safe queries to be executed in the UI even if the user has no
permission to execute and create new query results

* change `run_query`'s signature to accept a ParameterizedQuery instead of
constructing it inside

* use dict#get instead of a None guard

* use ParameterizedQuery in queries handler as well

* test that /queries/:id/results allows execution of safe queries even if
user has view_only permissions

* lint

* raise HTTP 400 when receiving invalid parameter values. Fixes #3394

* remove unused methods

* avoid cyclic imports by importing only when needed

* verify that a ParameterizedQuery without any parameters is considered
safe

* introduce query.parameter_schema

* encapsulate ParameterizedQuery creation inside Query
2019-02-26 20:55:01 +02:00
Gabriel Dutra
138c55cf54 Fix DynamicForm ignoring default value for checkboxes (#3487) 2019-02-26 15:10:14 +02:00
Gabriel Dutra
60cd8812a9 Update Percy token (#3492) 2019-02-25 21:22:46 +02:00
Arik Fraimovich
5c5bfbdbbe Make sure Flask app created in Celery's worker process (#3465)
* Make sure Flask app created in worker process

* Add reference to GitHub issue
2019-02-25 19:11:03 +02:00
Sergei Beregov
75c34bf18d Add custom JSON encoder for PostgreSQL (#3442)
To handle columns with [range types][1] and display them as a
string custom JSON encoder for PostgreSQL was added.

Merging this PR will fix issue #1764

[1]:https://www.postgresql.org/docs/9.3/rangetypes.html
2019-02-25 17:52:45 +02:00
Arik Fraimovich
b56cc1cd16 Pin SQLAlchemy-Utils version (#3490)
Pin the version of SQLAlchemy-Utils following the discussion in #2970.
2019-02-25 11:22:48 +01:00
Wei
1a357df9b3 Fix prometheus query runner: get_schema and query range (#3471) 2019-02-25 11:40:35 +02:00
Yoshiken
d36e5acaea Fix update logo (#3489) 2019-02-25 10:12:02 +02:00
Arik Fraimovich
f4f34d02fb Create PULL_REQUEST_TEMPLATE.md (#3485) 2019-02-25 10:11:21 +02:00
Allen Short
3fdd3080c1 Use series name as pie chart label (#3484)
It was unconditionally using the column name; this uses the series name instead
if specified in the chart editor.
2019-02-25 10:10:27 +02:00
taminif
5d525b80b6 fix broken link in CONTRIBUTING.md (#3483) 2019-02-22 14:28:13 -03:00
Levko Kravets
5e5b0d69d8 [Feature, Tech debt] Improve list pages layout (#3482) 2019-02-22 17:26:29 +02:00
Levko Kravets
33b8bd27eb [Feature] Migrate Group List and Details pages to React (#3411) 2019-02-22 14:47:48 +02:00
Subhi Al Hasan
8679b8756e Cleaner approach for _is_collection_a_view (#3113)
* Fix collection fields retreival bug when Views are present in MongoDB

* fixing _is_collection_a_view function

* Update redash/query_runner/mongodb.py

Co-Authored-By: jodevsa <jodevsa@gmail.com>

* using options() instead of try/except on collstats  to detect if a collection  is a view
2019-02-21 23:39:36 +02:00
Omer Lachish
2a37cb31d9 Fix max-age issues on textless endpoint (#3477)
* max_age should default to -1

* pass maxAge along to `execute`
2019-02-21 16:02:06 +02:00
Ran Byron
4ad303b358 Fix some flex layout issue (#3476) 2019-02-21 14:21:54 +02:00
MURAOKA Taro
8fe1d33068 monitor "schemas" queue to run refresh_schema (#3459)
* monitor "schemas" queue to run refresh_schema

`refresh_schema` tasks won't run because "schemas" queue isn't consumed
with default settings.
and it cause leaking redis storage, a "schemas" list is growing with time.

this PR fix it, adds "schemas" queue to monitor by celery.

* use scheduled_worker for "schemas" queue

instead of "adhoc_worker"
2019-02-21 12:06:25 +02:00
Arik Fraimovich
4999ab5de7 Provide default value for .pop to avoid KeyError exception (#3474) 2019-02-21 11:17:07 +02:00
Ran Byron
8b19f16430 Help trigger dynamic component (#3472) 2019-02-20 17:55:35 +02:00
Omer Lachish
a17eb14cdf support e-mail verification for multi org setups (#3468) 2019-02-20 13:45:09 +02:00
Omer Lachish
1ad0fa6a9b avoid Flask debug error message about strict slashes (#3469) 2019-02-20 13:19:06 +02:00
Arik Fraimovich
77dcc80522 Fix: ParameterizedQuery: support for upper cases column names. (#3461)
* Fix: ParameterizedQuery: support for upper cases column names.

* Fix test name
2019-02-20 12:08:38 +02:00
Arik Fraimovich
fe10b06928 Fix: JS Map needs a set call rather than assignment (#3464) 2019-02-20 12:07:46 +02:00
Arik Fraimovich
e35f2b8f51 Fix: when max_age is None the handler fails (#3462) 2019-02-19 20:47:29 +02:00
Omer Lachish
0bca2d8920 when working with a schema, fail in the case that there are parameters (#3452)
which are not part of the schema
2019-02-19 20:46:30 +02:00
Arik Fraimovich
f421119f9d Remove options NullPool does not support (#3460) 2019-02-19 16:04:00 +02:00
Arik Fraimovich
ebef0efe06 Fix: timedelta values were not JSON serialized properly (#3463)
.
2019-02-19 14:27:37 +01:00
Ran Byron
8fc2ecf55c [Bug fix] Widget oblivious to updated parameter values (#3445) 2019-02-18 22:55:46 +02:00
Ran Byron
3147a0bd98 Version data - footer -> header menu (#3458) 2019-02-18 22:15:51 +02:00
Gabriel Dutra
2c705712fc Fixes to Percy (#3440)
* Add check for repository url

* Use CIRCLE_REPOSITORY_URL directly

* Change percy waiting to be with resources instead of time

* Add data sources types resource to Edit Data Source + eslint fixes

* Separate Page Screenshots in different spec files
2019-02-18 16:30:48 -03:00
Levko Kravets
d483785098 [Codebase improvement] Refine ItemsList base component (previously LiveItemsList) (#3415) 2019-02-18 20:48:26 +02:00
Ran Byron
298fe6a779 HelpTrigger in header (#3457)
* Moved component from services->components

* Added help trigger to header

* Reverted tooltip-anchor swap

* Moved trigger location in html (to mirror saas)

* Removed white space

* Changed tooltip text to “Help”
2019-02-18 19:52:10 +02:00
Arik Fraimovich
f07e613631 Fix: ScheduleDialog won't render for "30 days" interval with no time value (#3447) 2019-02-17 15:12:07 +02:00
Arik Fraimovich
60472e2fe0 Add support for Amazon ES service with IAM authentication (#3446)
* Add support for Amazon ES service with IAM authentication

* Add required dependency.
2019-02-17 15:11:40 +02:00
Ran Byron
81c950407d HelpTrigger to open in drawer (#3436)
* HelpTrigger to open in drawer

* Fixed “typo”

* Simplified version - removed postMessage

* Changed some wording
2019-02-17 15:11:16 +02:00
Arik Fraimovich
a34269cc7d Change: encrypt data source options. 🔓 (#2970)
* Change: encrypt data source options
* Implement migration
2019-02-17 13:54:19 +02:00
Omer Lachish
a8f74a1078 remove the word "type" from error message, as enum out-of-range errors (#3449)
have nothing to do with types.
2019-02-17 13:42:44 +02:00
Arik Fraimovich
58a53e3470 Fix: remove widgets when deleting a visualization. (#3423)
Closes #3257.
2019-02-17 10:30:23 +02:00
Ran Byron
fba2a35cef [Feature] Sharing disabled if dashboard has query params (#3439) 2019-02-16 17:06:09 +02:00
Gabriel Dutra
b9644b7456 React version of UserEdit (#3354)
* Update DynamicForm export

* Move UserShow to users folder

* Migrate User profile header and create DynamicForm for basic data

* Update UserShow to use UserProfile prop

* Add API Key input

* Add handler to regenerate API Key button

* Handle user profile save

* Add readOnly prop to DynamicForm and begin disabled user behavior

* Add Change Password Modal

* Remove action buttons for disabled users

* Add send password reset behavior

* Add minLength and password comparison to Password Modal

* Resend Invitation button

* Add Convert User Info

* Fix UserShow test

* Some code updates

* Add enable/disable user button

* Add UserPolicy as an idea

* Remove UserPolicy

* Create Edit Profile spec

* Move User profile screenshot to Edit Profile Spec

* Add tests for saving user and changing password errors

* CC is back :) - Fix trailing spaces

* Add test for succesful password update

* A few improvements from code review

* Remove Toggle User button when seeing your own profile

* Create InputWithCopy

* Fix possible errors when network is off and improve Email not sent alert

* Add default response object for $http possible errors

* Changes in UserEdit
- removed onClick from methods name
- regenerate API Key now uses InputWithCopy
- Password title added

* Update UserEdit render behavior and styling
- Password title changed to h5
- change rendering rules for actions
- Password modal is now closed when password is changed
- change DynamicForm readOnly to the fields and add hideSubmitButton

* Create ChangePasswordDialog and update UserEdit

* Fix possible console error

* Remove password match assertion from spec

* Fix typo
2019-02-14 14:08:30 -02:00
swfz
afaedb9062 [Feature] Table visualization: Raise the upper limit of MAX_JSON_SIZE (#3310)
* move constant value to clientConfig
* change name maxJsonSize to tableCellMaxJSONSize
* value from environment. default is 50000
2019-02-13 11:42:40 +02:00
Ran Byron
f2df7170d2 HelpTrigger (#3431)
* Moved to HelpTrigger

* Moved share dialog “Learn more” to HelpTrigger
2019-02-13 10:38:25 +02:00
Arik Fraimovich
d6827e3601 Version update time (#3429)
* Version update time 

* Need more 
2019-02-13 09:51:07 +02:00
Omer Lachish
c028e49bfd send ip and user id to sentry (#3430) 2019-02-13 09:41:41 +02:00
Ran Byron
9b1f277530 [Widget Params] Updated help url and tooltip (#3428) 2019-02-13 09:00:48 +02:00
Ran Byron
901f28a79f Pre-commit hook to run on modified files only (#3410)
* Pre-commit hook to run on  modified files only

* Removed git add, fixed script dup

* Removed pre-push, testing related files only in pre-commit
2019-02-13 08:21:42 +02:00
Levko Kravets
3fed697c37 [Bug fix] Query Parameters: don't save urlPrefix (#3427) 2019-02-12 21:10:53 +02:00
Levko Kravets
d567765a3c [Bug fix] Notifications randomly shown with visible page or not shown with inactive page (#3426) 2019-02-12 20:07:36 +02:00
Ran Byron
4dbc17572a Converted Share modal to Ant (#3424) 2019-02-12 16:11:08 +02:00
Omer Lachish
330c5a85f1 Enable remote debugging with ptvsd (#3419)
* open port 3000 for remote debugging

* add ptvsd

* use port 5678 to avoid changes in VSCode's default config

* attach to ptvsd

* no need to wait for attach

* actually, --debugger seems to be working

* create a new docker entry point for remote debugging

* alternative method to switch to debugging
2019-02-12 09:10:18 +02:00
Levko Kravets
2c1400d323 [Feature] Alternative implementation of dashboard param title editing (#3417) 2019-02-11 17:17:05 +02:00
Arik Fraimovich
cb22764d68 Bug fix] Saving a new query removes reference to last query result (#3421)
* Correctly test if the current query result is for the current query.
* Serialize a new query with its visualizations.
2019-02-11 14:13:01 +02:00
Arik Fraimovich
eee77a1c9b [Bug fix] Show query result footer only when there is a query result. (#3422) 2019-02-11 13:46:18 +02:00
Omer Lachish
71fb1442f1 Upgrade Sentry SDK (#3418)
* replace raven with sentry-sdk

* use sentry-sdk in celery

* use sentry-sdk with flask

* unify Flask and Celery initializations for Sentry

* extract sentry stuff to own module

* it's time for Sentry 0.7.2
2019-02-10 21:56:16 +02:00
Jannis Leidel
23908edc28 Handle InterruptException in Athena query runnner like in the Presto query runner. (#3403) 2019-02-10 13:41:16 +02:00
Ran Byron
df4ca86d35 Added short modal specific styling (#3366) 2019-02-10 13:39:55 +02:00
Omer Lachish
03f040da0e Unify query based dropdown population (#3337)
* stop testing `collect_query_parameters`, it's an implementation detail

* add tests for `missing_query_params`

* rename SQLQuery -> ParameterizedSqlQuery

* rename sql_query.py to parameterized_query.py

* split to parameterized queries and parameterized SQL queries, where
parameterized queries only do templating and parameterized SQL queries
add tree validation on top of it

* move missing parameter detection to ParameterizedQuery

* get rid of some old code

* fix tests

* set syntax to `custom`

* revert the max-age-related refactoring

* 👋 tree validations 😢

* BaseQueryRunner is no longer a factory for ParameterizedQuery, for now

* add an endpoint for running a query by its id and (optional) parameters
without having to provide the query text

* adds parameter schema to ParameterizedQuery

* adds parameter schema validation (currently for strings)

* validate number parameters

* validate date parameters

* validate parameters on POST /api/queries/<id>/results

* validate enum parameters

* validate date range parameters

* validate query-based dropdowns by preprocessing them at the handler
level and converting them to a populated enum

* change _is_date_range to be a tad more succinct

* a single assignment with a `map` is sufficiently explanatory

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/handlers/query_results.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* build error message inside the error

* support all types of numbers as number parameters

* check for permissions when populating query-based dropdowns

* check for access to query before running it

* check for empty rows when populating query-based enums

* don't bother loading query results if user doesn't have access

* 💥 on unexpected parameter types

* parameter schema default is a list, not a dictionary

* fix a totally unrelated typo

* remove redundant null guards

* introduce /dropdown.json endpoint with dummy data

* wire frontend to /dropdown.json

* always return name/value combos from /dropdown.json

* load actual data into /dropdown.json

* pluck correct values for `name` and `value`

* reuse dropdwon plucking logic in QueryResultResource

* simplify _get_dropdown_values

* when doing parameter validation, we only care about the value and not
the display name

* rename dropdown to dropdownOptions

* move dropdown_values to utils/parameterized_query.py

* stop converting queries to enums and encapsulate the work inside
ParameterizedQuery (almost - /dropdown.json would still access the
dropdown_values method)

* re-order arguments by importance

* test query parameter validation

* tests for dropdown_values logic

* remove `.json` suffix to the dropdown endpoint

* allow `BaseResource` to handle JSON stuff

* move _pluck_name_and_value outside its containing method

* case-insensitive lookup when plucking name and value

* separate concerns and simplify test isolation for `dropdown_values`

* pick the default column according to the order specified in the query
result columns attribute

* use `current_org` instead of passing `org`

* test that user has access to the query when calling the /dropdown
endpoint
2019-02-10 13:10:39 +02:00
Levko Kravets
e21bbcc6fe [UI/UX Improvement] Use Ant's Button component on users list page (#3416) 2019-02-08 17:48:48 +02:00
Levko Kravets
23f5dde488 [Codebase Improvement] Refine dialog wrapper and use it for all existing dialogs (#3407) 2019-02-08 10:00:22 +02:00
Ran Byron
29326f3610 [Widget Params] Title edit fixes (#3413) 2019-02-08 08:45:46 +02:00
Ran Byron
593ebde211 Fix: “Add TextBox” dialog not opening (#3414) 2019-02-08 01:01:50 +02:00
Levko Kravets
11507c5e5e Show active and pending users separately (for admins) (#3400) 2019-02-07 20:30:55 +02:00
Jannis Leidel
c49dccf254 Work around a resizing issue. (#3412)
* Work around a resizing issue.

Fix #3353.

* Add comment to remove this when we delete Angular.

Co-Authored-By: jezdez <jannis@leidel.info>
2019-02-07 16:05:39 +02:00
Marina Samuel
029bee18fb Coerce to moment when 'datetime' selected by user. (#3150) 2019-02-07 14:42:30 +01:00
Levko Kravets
ec475e4b7b [Bug fix] Few small bugs on Queries list page (#3402)
* Link to query page
* Sidebar menu item title: `Archive` -> `Archived`
* Whitespaces in empty state block
2019-02-06 00:19:31 +02:00
Levko Kravets
045c171bb4 Refactor TagsControl; fix TagsEditorModal animation (#3399)
* Refactor TagsControl; fix TagsEditorModal animation

* Update tooltip text

Co-Authored-By: kravets-levko <levko.ne@gmail.com>

* Update tooltip text

Co-Authored-By: kravets-levko <levko.ne@gmail.com>

* CR1
2019-02-05 23:29:09 +02:00
Arik Fraimovich
21341132f6 Fix: cohorts get stuck when passing strings instead of numbers. (#3397)
* Fix: cohorts get stuck when passing strings instead of numbers.

Parse the value to get a number.

* Use parseInt for stage

* Remove redundant parseInt
2019-02-05 21:20:17 +02:00
Levko Kravets
ac68fe1a6d Migrate Dashboards/Queries/Users list pages to React (#3381)
* Refine existing implementation of dashboards/queries/users lists and a common base controller

* Migrate common list page controller to React and refactor it's logic

* Migrate Dashboard list page to React

* Migrate Queries list page to React

* Migrate Users list page to React

* Remove react-timeago dependency

* Use composition instead of inheritance

* Refine implementation

* Merge sidebar into single component

* Refine column definitions

* Use simple controller instead of React context

* Refine implementation

* Restore changes from getredash/redash#2888

* Tweak Users list page

* Ability to render dynamically defined components

* Tweak users list page

* User list page for non-admins

* Fix: ItemsTable ignores isAvailable field

* Refine implementation

* Refine implementation

* Implement LiveItemsList as higher order component

* Some fixes

* Move some definitions to a better place

* Some fixes

* Refine components

* Refine UsersList page

* More comments for a god of comments

* Fix wrong tables size on smaller screens

* Tweak tables
2019-02-05 21:13:32 +02:00
Arik Fraimovich
13855934f9 Add YAML support in QueryEditor (#3395) 2019-02-05 20:37:54 +02:00
Ran Byron
5b62883c1c [Widget Params] Switched parameter list to table style (all parts) (#3332)
* [Widget Params] Split title and mapping editing

* [Widget Params] Restyled source editing

* [Widget Params] Switched parameter list to table style

* Displaying different labels and help phrases when changing type
Added link to knowledge base
Fixed issue with existing param default select value
2019-02-05 20:36:15 +02:00
Ran Byron
c9681d55bf Added pre-push hook (#3390) 2019-02-05 16:16:49 +02:00
Arik Fraimovich
7cfea8a6a0 Fix: only login user when it's the current user. (#3396)
Otherwise it would login the admin as the user...
2019-02-05 15:53:51 +02:00
Ran Byron
2011864fdb Fix: Selected item in dropdown unreadable (#3398) 2019-02-05 15:50:31 +02:00
Omer Lachish
8f0cffe424 Use textless endpoint for pristine queries (#3367)
* use the textless endpoint (/api/queries/:id/results) for pristine
queriest

* Revert "use the textless endpoint (/api/queries/:id/results) for pristine"

This reverts commit cd2cee7738.

* move execution preparation to a different function, which will be soon
reused

* go to textless /api/queries/:id/results by default

* let the query view decide if text or textless endpoint is needed

* lint
2019-02-05 12:08:12 +02:00
Ran Byron
3df372434f [Widget Params] Migrated edit params + new widget dialog to Ant Modal (#3387) 2019-02-04 21:00:59 +02:00
Arik Fraimovich
933dd753a8 Make the logic around schedule['until'] easier to read (#3376) 2019-02-03 21:13:27 +02:00
koooge
3992bcda9b Ignore to copy some files onto docker container (#3388)
* Ignore to copy some files onto docker container

Signed-off-by: koooge <koooooge@gmail.com>

* Dockerignore venv/

Signed-off-by: koooge <koooooge@gmail.com>
2019-02-03 20:30:08 +02:00
Jannis Leidel
69e34f048a Add archived queries section to queries list. (#2888)
* Add archived queries section to queries list.

* Refactor route building for list based controllers.

This also fixes the dashboard empty state page.
2019-02-03 14:35:25 +02:00
Omer Lachish
b0a11983fa fix lint error - getDerivedStateFromProps should be placed after state (#3391) 2019-02-03 11:13:04 +02:00
Levko Kravets
807e6aaaa6 Migrate "time ago" components to React (#3385)
* Replace <am-time-ago> (angular-moment) and <rd-timer> with React component

* PropTypes: Moment validation

* Increase polling interval

* Refine component implementation

* Add tooltip with formatted date/time

* Refine component implementation
2019-02-02 18:46:48 +02:00
Levko Kravets
324a1f55cc Merge pull request #3384 from getredash/step-component
EmptyState alternative implementation with a Step component
2019-02-02 18:29:21 +02:00
Levko Kravets
abccff03f7 Fix Add datasource step; EmptyState.icon is optional 2019-02-02 18:12:27 +02:00
Levko Kravets
aa619c453f Merge branch 'master' into step-component 2019-02-02 17:56:11 +02:00
Levko Kravets
10b5c03248 Merge pull request #3389 from getredash/cypress
Fix Cypress E2E failing in CI
2019-02-02 17:14:21 +02:00
Gabriel Dutra
fde52f5d84 Fix Cypress E2E failing in CI 2019-02-02 12:44:33 -02:00
Arik Fraimovich
78df7e7cc9 Last refinements:
* Update Step implementation to be easier to read.
* Set some props to required to remove default value.
2019-02-01 10:16:17 +02:00
Levko Kravets
e314715335 Refine implementation, fix 'shouldShow' condition, fix eslint warnings 2019-02-01 08:37:09 +02:00
Arik Fraimovich
a1cf065ec6 No need to export Step 2019-02-01 00:04:03 +02:00
Arik Fraimovich
f9570556c5 Bring back data sources step for non admins 2019-02-01 00:00:19 +02:00
Arik Fraimovich
9859610e80 Alternative implementation: Step component 2019-01-31 23:55:40 +02:00
Marina Samuel
feab2a040b BigQuery should correctly handle tmp tables that do not have a schema field. (#3382) 2019-01-31 23:20:13 +02:00
Marina Samuel
35c390a2f9 Show disabled unpublished queries for alert and dashboard modals. (#3347) 2019-01-31 19:58:47 +02:00
Ran Byron
ebb96d7ad7 Widget param url prefix p to p_w (#3380) 2019-01-31 17:03:13 +02:00
Levko Kravets
3d58860f6f Merge pull request #3374 from kravets-levko/fix-eslint-errors
Fix eslint errors
2019-01-31 14:33:28 +02:00
Levko Kravets
c223566302 Merge branch 'master' into fix-eslint-errors 2019-01-31 14:04:05 +02:00
Levko Kravets
1439714fa6 Migrate EmptyState component to React (#3373)
* Migrate EmptyState component to React

* CR1

* CR2
2019-01-31 14:02:45 +02:00
Levko Kravets
9c06e9a0bd CR1 2019-01-31 13:50:05 +02:00
Levko Kravets
f98cf7812b Merge branch 'master' into fix-eslint-errors 2019-01-31 11:25:41 +02:00
Levko Kravets
ee0e81e795 Fix eslint errors 2019-01-31 11:21:45 +02:00
Jannis Leidel
bd559b6eeb Fix some incompatible dependencies (#3348)
* Pin requests-oauthlib to work around incompatible deptree.

* Update boto3/botocore to fix incompatible deptree.
2019-01-31 09:50:47 +02:00
Arik Fraimovich
c51191aaf0 Tune CodeClimate's config to make it less annoying (#3370) 2019-01-31 07:34:31 +02:00
Levko Kravets
4a2645d4c6 Merge pull request #3368 from kravets-levko/feature/react-favorites-and-tags-list
React migration: FavoritesControl and TagsList components
2019-01-30 21:09:05 +02:00
Levko Kravets
3ad1709a0a Merge branch 'master' into feature/react-favorites-and-tags-list 2019-01-30 20:29:07 +02:00
Ran Byron
cc135398e2 Fix: static param text value is [object Object] (#3371) 2019-01-30 20:27:59 +02:00
Levko Kravets
067472643d CR3 2019-01-30 20:21:22 +02:00
Levko Kravets
225c98c52a Merge branch 'master' into feature/react-favorites-and-tags-list 2019-01-30 19:23:03 +02:00
Ran Byron
00e991ecfc Fix: Static param value not editable for Text/Number (#3369) 2019-01-30 19:18:57 +02:00
Levko Kravets
a362e97dfe CR2 2019-01-30 18:54:53 +02:00
Levko Kravets
1ea532fe26 CR1 2019-01-30 18:45:06 +02:00
Levko Kravets
9a1c8290e4 Migrate TagsList component to React 2019-01-30 17:40:42 +02:00
Levko Kravets
0d959116d8 Migrate FavoritesControl component to React 2019-01-30 17:22:54 +02:00
Aidarbek Suleimenov
0b9f575dab Fix: make ClickHouse password and username truly optional (#3362)
* clickhouse optional password

* clickhouse URL and user made optional
2019-01-30 11:17:43 +02:00
koooge
13bc910d7c Update CodeClimate configuration format to Version 2 (#3286)
* codeclimate: Update format v2
* codeclimate: Ignore generated files
2019-01-30 10:51:32 +02:00
Ran Byron
9e2f8e2461 Fix: Escape button in tag edit modal (#3363) 2019-01-30 10:43:38 +02:00
Aidarbek Suleimenov
61e7cdaa81 Filename set when /results called directly (#3359)
* filename set when /results called directly

* /results filename changed from query name to id

* Long line shortened
2019-01-29 19:47:42 +02:00
Vladislav Denisov
53aecdc607 yandex_metrica: changed auth from params to headers (#3360) 2019-01-29 18:17:13 +02:00
koooge
2da511021e Frontend lint update (#3253)
* client: Add lint command

Signed-off-by: koooge <koooooge@gmail.com>

* client: Override eslint rule object-curly-newline to keep current style

Signed-off-by: koooge <koooooge@gmail.com>

* client: Override eslint rule no-else-return to keep current style

Signed-off-by: koooge <koooooge@gmail.com>

* client: Fix eslint import/named

Signed-off-by: koooge <koooooge@gmail.com>

* client: eslint-5

Signed-off-by: koooge <koooooge@gmail.com>

* codeclimate: Delete the old setting

Signed-off-by: koooge <koooooge@gmail.com>

* client: Downgrade eslint 5 to 4 in codeclimate

Signed-off-by: koooge <koooooge@gmail.com>

* client: npx install-peerdeps --dev eslint-config-airbnb

Signed-off-by: koooge <koooooge@gmail.com>

* client: Enbale .jsx lint

Signed-off-by: koooge <koooooge@gmail.com>

* client: Set warn

Signed-off-by: koooge <koooooge@gmail.com>

* client: Fix lint indent, implicit-arrow-linebreak, lines-between-class-members

Signed-off-by: koooge <koooooge@gmail.com>

* client: Disable eslint operator-linebreak

Signed-off-by: koooge <koooooge@gmail.com>

* Revert "client: Downgrade eslint 5 to 4 in codeclimate"

This reverts commit f0fb0f0059.

* client: Fix react/button-has-type

Signed-off-by: koooge <koooooge@gmail.com>

* client: Disable an eslint rule react/jsx-one-expression-per-line

Signed-off-by: koooge <koooooge@gmail.com>

* codeclimate: Disable no-multiple-empty-lines

Signed-off-by: koooge <koooooge@gmail.com>

* client: Disable eslint react/destructuring-assignment

Signed-off-by: koooge <koooooge@gmail.com>
2019-01-29 17:25:58 +02:00
Omer Lachish
371b319e92 Server-side parameter validation (#3315)
* stop testing `collect_query_parameters`, it's an implementation detail

* add tests for `missing_query_params`

* rename SQLQuery -> ParameterizedSqlQuery

* rename sql_query.py to parameterized_query.py

* split to parameterized queries and parameterized SQL queries, where
parameterized queries only do templating and parameterized SQL queries
add tree validation on top of it

* move missing parameter detection to ParameterizedQuery

* get rid of some old code

* fix tests

* set syntax to `custom`

* revert the max-age-related refactoring

* 👋 tree validations 😢

* BaseQueryRunner is no longer a factory for ParameterizedQuery, for now

* add an endpoint for running a query by its id and (optional) parameters
without having to provide the query text

* adds parameter schema to ParameterizedQuery

* adds parameter schema validation (currently for strings)

* validate number parameters

* validate date parameters

* validate parameters on POST /api/queries/<id>/results

* validate enum parameters

* validate date range parameters

* validate query-based dropdowns by preprocessing them at the handler
level and converting them to a populated enum

* change _is_date_range to be a tad more succinct

* a single assignment with a `map` is sufficiently explanatory

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/handlers/query_results.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/utils/parameterized_query.py

Co-Authored-By: rauchy <omer@rauchy.net>

* build error message inside the error

* support all types of numbers as number parameters

* check for permissions when populating query-based dropdowns

* check for access to query before running it

* check for empty rows when populating query-based enums

* don't bother loading query results if user doesn't have access

* 💥 on unexpected parameter types

* parameter schema default is a list, not a dictionary

* remove redundant null guards
2019-01-29 09:18:07 +02:00
Arik Fraimovich
ff42ec2cc6 Cypress tests: preset the admin API key to a static value (#3358) 2019-01-28 17:54:24 +02:00
Levko Kravets
7278d4b1fc Refactor Policy and OrganizationStatus services (#3345)
* Refactor Policy and OrganizationStatus services
2019-01-28 16:46:26 +02:00
Levko Kravets
6930106380 getredash/redash#3355 Widget params: Date/Date range value empty in static param input (#3357) 2019-01-28 15:59:49 +02:00
Zsolt Kocsmárszky
c0859642fd WIP: Add dashboard details section for dashboard owner and more (#2934)
Show dashboard creator on dashboard page
2019-01-28 14:20:55 +02:00
Gabriel Dutra
37821ee008 Add Percy Page Screenshots (#3338)
* Add Percy Page Screenshots

* Add missing space
2019-01-28 10:21:42 +02:00
Arik Fraimovich
c31cb01065 Move BigQueryGCE to its own file (#3356)
* Move BigQueryGCE to its own file

* Add missing import
2019-01-28 09:37:47 +02:00
Levko Kravets
1fa58674f3 Refine SettingsMenu service and <settings-screen> component (#3339)
* Refine SettingsMenu service and <settings-screen> component

* Rename services/settingsMenu file to match default export name

* CR1
2019-01-25 17:29:35 +02:00
Jannis Leidel
d204c158a2 Allow query owners to hard-overwrite query content in case of overlap with other user (#2370)
* Hard overwrite on conflict for query owners (re #283)

* Use AlertDialog instead of custom global function.
2019-01-25 11:28:09 +01:00
Levko Kravets
b0b4d5e26a Convert Angular services to CommonJS-style and use them in React components instead of injecting (#3331)
* Refine Auth service: remove dead code and fix race condition
* Export services in CommonJS style
* Refine Users, Events and OfflineListener services
* Refactor Notifications service - rewrite to CommonJS
* Replace Angular service injection with imports in React components
* Fix Footer tests
* Events service -> recordEvent function
* CR1
2019-01-24 16:24:58 +02:00
Levko Kravets
c2c722e12e Migrate PageHeader component to React (#3324)
* Migrate PageHeader component to React

* CR1
2019-01-23 20:10:52 +02:00
Vibhor Kumar
1a61ee3ec0 Add: Uptycs query runner (#3319)
* adding uptycs query_runner in redash

* as per comment from Arik comment fixed the code

* fixed function_name

* fixed some indentation issues

* fixed the indentation issue and taken out customer_id from secret

* fixed the dependency of urllib3

* fixed the indententaton issue

* remved the urllib3 from requirements

* fixed the indentation issues

* added the new square image for Uptycs. Removed unnecessary variable and made ssl as an option

* fixed indentation issue

* Renamed SSL to verify_ssl and also added verify_ssl validate in verify in missing places
2019-01-23 20:07:40 +02:00
Ran Byron
d5afa1815e Filtering out incompatible dashboard params (#3330) 2019-01-23 16:27:49 +02:00
Arik Fraimovich
87667676e6 Remove link to roadmap (#3329)
It's no longer maintained 😢
2019-01-23 13:48:11 +02:00
Arik Fraimovich
bfeb015d71 Add configuration for the Support probot. (#3327) 2019-01-23 13:38:08 +02:00
Omer Lachish
a9c514aaf7 Textless query result endpoint (#3311)
* add an endpoint for running a query by its id and (optional) parameters
without having to provide the query text

* check for access to query before running it
2019-01-23 11:10:04 +02:00
Levko Kravets
7fa6665445 Use Ant's Paginator component; migrate SortIcon to React (#3317) 2019-01-22 17:26:11 +02:00
Miles Maddox
ff6b20b69c support for fetching all JQL results by way of pagination (#3304) 2019-01-22 15:52:13 +02:00
YOSHIDA Katsuhiko
c4bf44677a Fix an error of exporting dict value as Excel (#3323) 2019-01-22 14:43:52 +02:00
Gabriel Dutra
8bdcfb06c5 add wait time before percy data source page snapshot (#3320) 2019-01-22 08:52:23 +02:00
YOSHIDA Katsuhiko
b3643ffbb7 Add regenerate function of user's API key (#3224)
* Add regenerate function of user's API Key

* Update client/app/pages/users/show.js

Co-Authored-By: kyoshidajp <claddvd@gmail.com>

* Remove unused error message

* Refactoring: Inline temp

* Update client/app/pages/users/show.js

Co-Authored-By: kyoshidajp <claddvd@gmail.com>

* Change action event of regenerate user API key
2019-01-20 13:38:20 +02:00
Eric Chang
b91d4bdcaf override default integer/float formatting with environment variables (#3307) 2019-01-20 10:10:36 +02:00
Eric Chang
8bc8e2dadf Allow execution of highlighted subquery (#3288)
* allow execution of selected subquery
* fix query save while highlighted
* don't modify queryText and update UI when running selected
* code style and transition
* Fix query selection execution background color
* make naming consistent
2019-01-20 10:06:17 +02:00
Gabriel Dutra
84d5becf2a Update form text colors (#3296)
* Create ant variables and update form colors

* Remove less extension from imports in ant.less

* Update font-weight for labels

* Add percy snapshot for create data source page

* Remove bold in labels only for checkboxes and radio buttons
2019-01-20 09:41:52 +02:00
Arik Fraimovich
e8120c5f79 Use None as "not scheduled" default value of a query (#3277)
* Use null as the default scheduled value.

* Don't serialize None to json, so we can use SQL is not null predicate.

* Fix warning about unicode in tests

* Handling empty query.schedule in UI (#3283)

* Add migration to convert empty schedules to null and drop the not null contraint.
2019-01-18 11:30:45 +02:00
Levko Kravets
40c6a2621c Merge pull request #3299 from kravets-levko/fix/multifilter-dropdown
Multifilter's dropdown cropped when visualization container is too small
2019-01-17 15:37:26 +02:00
Levko Kravets
06887f6ff1 Multifilter's dropdown cropped when visualization container is too small 2019-01-17 15:14:46 +02:00
Omer Lachish
7847cf7d63 Fix invitation pending for older invitations (#3298)
* explicitly look for a False under details['is_invitation_pending'] and
not any falsey result, to avoid locking out invitations which were
created before the Pending Invitation feature was introduced. Solves https://github.com/getredash/redash/issues/3297

* test that old invites (that do not have any is_invitation_pending flag set in their details object) are still acceptable
2019-01-17 11:56:16 +02:00
Omer Lachish
121a44ef15 Remove tree validations and introduce ParameterizedQuery (#3230) 2019-01-17 10:26:00 +02:00
Gabriel Dutra
823e4ccdd6 Migrate DynamicForm to React (#3209)
* create DynamicForm React component

* Render fields based on target in DynamicForm

* Add missing title property to fields

* Fix style properties in DynamicForm

* Render File fields in DynamicForm

* Use React for middle component instead of Angular

* Functional save button

* Update label style

* Render functional actions

* Handle file inputs

* Update render methods to fix code climate issues

* Fix ant input number showing duplicate arrows

* Update DynamicForm style to be vertical

* Separate imports from antd in DynamicForm

* Add Feedback Icons to DynamicForm

* Change Action props on DynamicForm
- use type and pullRight instead of class prop
- update data sources and destinations pages accordingly

* Remove setDefaults method from DynamicForm fields

* Update antd version

* Remove unnecessary class selectors

* Remove another unnecessary class selector
2019-01-15 14:23:33 +02:00
Levko Kravets
0c45d69662 Dashboard Parameters (#2756)
* getredash/redash#2641 Step 1: split Add Widget/Add Textbox buttons

* Convert Add widget/textbox dialogs to React components

* getredash/redash#2641 Step 2: Implement new dashboard parameters logic

* Resolve conflicts and fix build errors

* getredash/redash#2641 Refactoring and improve code quality

* Add Edit parameter mappings dialog to the widget

* getredash/redash#2641 Changes after code review

* Use Ant's Select component instead on <select> tags

* Fix Antd imports

* Fix Antd imports

* Fix Cannot read property 'getParametersDefs' of undefined

* Fix widgets static params bugs (don't show input, don't init from URL)

* Minor UI/UX fixes
2019-01-15 13:14:54 +02:00
Gabriel Dutra
df23842c57 Separate Bootstrap/Ant styling (#3279)
* Separate Ant less dependency tree

* Change order between variables and ant imports

* Remove inc/variables from ant.less

* Update input-height-base for antd

* Set same input-color for Ant and Bootstrap
2019-01-15 11:40:38 +02:00
Arik Fraimovich
d68a4dce5f Pin version of pyparsing (#3282) 2019-01-14 17:17:23 +02:00
Levko Kravets
db86e394cf Update Antd to latest version + fix tests (#3281) 2019-01-14 16:25:57 +02:00
koooge
4c9326a9da handlers: Fix post users (#3273) 2019-01-13 15:52:56 +02:00
Ran Byron
200fea952a Pleasant alert-warning colors (#3276)
* Pleasant alert-warning colors

* Fix for json viewer primitive color
2019-01-13 15:27:26 +02:00
Ran Byron
90a0a7d39b Scheduler tests (jest/enzyme) (#3269)
* Setup enzyme and initial ScheduleDialog test

* Added tests for each schedule setting

* Added refreshOptions tests

* Added count out-of-range tests

* Added modal confirm/cancel tests

* Fixed tests failing due to server timezone difference

* Rebased to master
2019-01-13 15:26:44 +02:00
Omer Lachish
26252be75a Verify admin email address (#3267)
* add an  bit

* prompt on homepage when user's email hasn't been verified

* set e-mail as verified for new setups and invited users

* 👋 copy & paste invite links, it's time for verified e-mails!

* default `is_invitation_pending` to false and actively set it to true
when inviting users, so that existing users won't show "Invitation
Pending"

* fix tests that broke due to default is_invitation_pending value

* treat admin's e-mail address as verified

* add verification endpoint

* send verification e-mail

* Update client/app/components/empty-state/empty-state.html

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/authentication/account.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/handlers/authentication.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/templates/emails/verify.html

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/authentication/account.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/templates/verify.html

Co-Authored-By: rauchy <omer@rauchy.net>

* Update redash/templates/emails/verify.txt

Co-Authored-By: rauchy <omer@rauchy.net>

* add link in case redirects are disabled

* POSTing to /email_verification makes more sense than GETting /send_verification

* avoid sending invitations when no_invite is passed along

* Update client/app/pages/users/new.html

Co-Authored-By: rauchy <omer@rauchy.net>

* move e-mail verification prompt to home-page

* get rid of redundant $scope

* return JSON

* flip is_email_verified's default value so that existing users do not
show as not-verified

* e-mail verification propmt isn't dangerous, it just wants to warn you
2019-01-13 13:57:12 +02:00
Ran Byron
6c6366e6f0 Fix: Refresh schedule durations not pluralized sufficiently (#3268)
* Fix: Refresh schedule durations not pluralized sufficiently

* Allows omitting single value number in durationHumanize (#3274)
2019-01-13 13:31:41 +02:00
Ran Byron
de04a403d7 Fix: Refresh schedule interval count doesn't adhere to permission rules (#3265)
* Fix: Refresh schedule interval count doesn't adhere to permission rules

* Fix “4.28 weeks” to “30 days”

* Merge interval type and count into one <Select>
2019-01-13 11:39:57 +02:00
Ilya Ruzakov
0b6f1fc21b [Data Sources] Implement Apache Drill (#3188)
* Added support for Apache Drill datasource

* Improvements in `Drill` query runner and minor refactoring

1. Drill query runner now inherits from `BaseHTTPQueryRunner`, because they both have a lot of common code.
2. `BaseHTTPQueryRunner.get_response` method now accepts `http_method` argument (original implementation was only capable of sending `GET` HTTP requests).
3. Added `order` to `BaseHTTPRequestRunner` configuration schema to fix order of UI elements based on the schema.
4. Eliminated duplicate method `_guess_type` in `GoogleSpreadsheet`, `Results` and `Drill` query runners, moved `guess_type` to `redash.query_runner`.
5. Removed tests for `_guess_type` in `GoogleSpreadsheet`, `Results` and `Drill` query runners, merged them into single test case and moved to `tests.query_runner.test_utils`.
6. Various minor changes (code style, imports, etc).
2019-01-10 09:12:35 +02:00
Omer Lachish
445f8e5c36 Fix invitation pending for existing users (#3261)
* default `is_invitation_pending` to false and actively set it to true
when inviting users, so that existing users won't show "Invitation
Pending"

* fix tests that broke due to default is_invitation_pending value

* update Flask-OAuthLib
2019-01-09 13:48:47 +02:00
Omer Lachish
a29136037c update Flask-OAuthLib (#3262) 2019-01-09 13:48:21 +02:00
Arik Fraimovich
08953cc919 Redis based implementation of user active_at timestamp update (#3256)
* Switch to simpler implementation
* Fix active_at update code
* Fix sync test
2019-01-08 14:03:49 +02:00
koooge
22f835d3cb client: Remove estraverse (#3254)
Signed-off-by: koooge <koooooge@gmail.com>
2019-01-08 12:29:24 +02:00
Omer Lachish
823f172a9f Invitation Pending changes (#3229)
* determine invitation_pending according to empty password. This commit will be reverted, I'm just deferring the implementation

* show '(Invitation Pending)' to users who haven't accepted their invitation yet

* allow resending invitations

* allow deletion of pending users from user list

* set invitation as not pending when following invite link

* prevent deleting activated users

* test that users who follow invitation links are set as non-pending invitations

* prevent re-using invitations

* invitees who use SSO will now also be marked as "non-pending"

* lint
2019-01-08 08:52:48 +02:00
Jannis Leidel
44dff83046 Add "Active at" column to user list. (#3026)
* add last_active_at to users page

* Use our JSON encoder as the SQLAlchemy JSON serializer.

* Fixed some inconsistencies in the user query class methods.

* Minor cosmetic fixes.

* Add some make tasks for easier development.

* Add user detail sync system based on Redis backend.

There is a periodic Celery task that updates a new “details” JSONB column in the “user” table with the data from Redis.

Currently this is only used for tracking the date of last activity of a user but can be extended with other user information later.

Updates a few dependencies.

* Normalize a few Flask extension API names.

* Reduce implementation complexity of JSONEncoder.

* Use request_started signal to make sure we have a request context.

Otherwise loading the user based on the request won’t work.

* Fix test that checks if disabled users can login.

This correctly uses a URL path that includes the current organization and checks for the error message.

The previous test seems to have been a red herring.

* Minor cosmetic fixes.

* Remove needs_sync in favor of just deleting things.

* Misc review fixes.

* Ignore line length.

* Split redash.models import several modules.

* Move walrus UTC DateTimeField into redash.models.types.

* Restore distinctly loading dashboards.

* Simplify default values for user details.

* Define __repr__ methods generically.

* Consistently have underscore methods at the top of model methods.

* Fix tests.

* Split redash.models import several modules.

* Update to latest walrus and redis-py.

* Update kombu to 4.2.2 for redis-py 3.x compatibility.

* Remove redis-cli container after running Make task.

* Move buffer condition after datetime/time conditions.

* Update walrus to 0.7.1.

* Refactor some query APIs.

This uses the flask-sqlalchemy helpers consistently and makes more use of mixins.

* Post rebase fixes.

* Use correct kombu version

* Fix migration down revision
2019-01-07 10:30:42 +02:00
Ran Byron
569430e5cd Fix: Refresh schedule phrase overlaps title (#3250) 2019-01-06 12:46:55 +02:00
Ran Byron
07a1c23df5 Fix: Able to set out-of-range refresh interval 2019-01-06 12:27:50 +02:00
Ran Byron
b97b8477ad Feature: Refresh schedule - styling (#3247) 2019-01-06 12:14:00 +02:00
Ran Byron
9b72dfe076 Feature: Refresh schedule - save/cancel actions 2019-01-06 11:33:35 +02:00
Ran Byron
3ee83a4c4a Feature: Refresh schedule - code optimizations 2019-01-06 11:17:43 +02:00
Marina Samuel
cdd2259d08 Closes #2396: Add finer-grained scheduling. (#2426)
* Closes #187: Add finer-grained scheduling - backend.

* Closes #2396 - Add finer-grained scheduling - frontend.

* Fix linting issues

* Rename ScheduleDialgo to .jsx
2019-01-06 10:59:50 +02:00
Vincentoo
fc368ee425 Support overriding the default Celery schedule database file via SCHEDULE_DB environment variable. (#3056)
By default Celery will use a file celerybeat-schedule in the current directory.
This is an issue in a Kubernetes/Openshift environment as the file may be lost or even impossible to write.
2019-01-03 20:51:18 +02:00
YOSHIDA Katsuhiko
7a2e08c3eb Upgrade requests package (#3245) 2019-01-03 18:59:58 +02:00
Levko Kravets
ba0d069283 getredash/redash#3213 Scatter charts can have category Y axis (similar to Bubble) (#3243) 2019-01-03 17:15:51 +02:00
Omer Lachish
670d86eb5f Simple user view (#3244)
* show a simple user details page when viewing a user who isn't you (or you arent the admin)

* add a snapshot test

* lint
2019-01-03 15:23:40 +02:00
Ran Byron
63f38b7acd Fix: Query editor duplicates keystrokes [#2972] (#3239) 2019-01-01 17:40:48 +02:00
Arik Fraimovich
8b5ffc6f84 Handle the case when a QueryTracker is None and change order. (#3238) 2019-01-01 15:57:58 +02:00
Arik Fraimovich
cce2052e79 request.view_args might be None and add org_id to ApiUser (#3237) 2019-01-01 15:44:34 +02:00
Arik Fraimovich
8ea6283430 Send argsrepr value with execute_query task (#3235) 2019-01-01 15:32:02 +02:00
Arik Fraimovich
08b86c1c6d Fix: forked query wasn't opening in MULTI_ORG env (#3236) 2019-01-01 15:31:41 +02:00
koooge
0449a3ff31 Delete an unused global (#3231)
Signed-off-by: koooge <koooooge@gmail.com>
2019-01-01 08:58:31 +02:00
Arik Fraimovich
4ea46f197e It's 2019 now ! 🎉 2019-01-01 08:40:51 +02:00
Omer Lachish
d7edaa3ba2 Tests for find_missing_params (#3225)
* stop testing `collect_query_parameters`, it's an implementation detail

* add tests for `missing_query_params`
2018-12-31 12:34:57 +02:00
YOSHIDA Katsuhiko
632fc5aace Merge pull request #3232 from koooge/fix_warn_tapable
Update webpack-build-notifier to dismiss warn
2018-12-31 10:09:23 +09:00
koooge
b9abb36799 Update webpack-build-notifier to dismiss warn
Signed-off-by: koooge <koooooge@gmail.com>
2018-12-30 17:37:42 +00:00
Levko Kravets
d7e7b99a76 Merge pull request #3228 from kyoshidajp/fix_trailing_spaces_error
Fix front-end compile error
2018-12-28 20:43:10 +02:00
Katsuhiko YOSHIDA
db87c8740e Fix front-end compile error 2018-12-29 01:36:16 +09:00
Arik Fraimovich
5f2b54a320 Root folder cleanup (#3220)
* Remove old_migrations folder

* Move Dockerfile.cypress into .circleci
2018-12-26 18:09:41 +02:00
Arik Fraimovich
f62d0e1300 Use lower cased names for groups: (#3221)
Otherwise sorting depends on the Postgres collation and causes tests
to fail on different envoirnments.
2018-12-26 17:16:13 +02:00
Omer Lachish
9e156c1c30 Fix: missing param fails for object values (like date range) (#3218)
* fix: range params not recognized

* Handle parameters with unicode

* Remove debug prints

* DRY up missing_params
2018-12-26 17:06:30 +02:00
Gabriel Dutra
26965b4948 Add dot behavior to autocomplete (#3092)
* Add dot behavior to autocomplete
* Transform the Keyword Builder in an external js
* Remove methods from QueryEditor constructor
2018-12-26 17:04:11 +02:00
Omer Lachish
7a03928f48 we've noticed users with integer legacy session identifiers, so it doesn't hurt to convert to string anyway (#3219) 2018-12-26 16:24:59 +02:00
Omer Lachish
8f14efdaff Avoid exploding sql parse (#3216)
* fail silently when sql_parse explodes

* Update upstream/redash/handlers/query_results.py

Co-Authored-By: rauchy <omer@rauchy.net>

* Update upstream/redash/handlers/query_results.py

Co-Authored-By: rauchy <omer@rauchy.net>
2018-12-26 10:43:00 +02:00
deecay
03a7e24204 Show Sort Y only when type is Heatmap (#3206) 2018-12-24 17:10:16 +02:00
ialeinikov
2f7cb1bc8a [Athena] Fix: missing databases (apply pagination) (#2503)
* adding paginator on databases in glue

* reduce number of nested loops

* fixing indentation

* double for-loop instead of generator

* removing unused generator
2018-12-24 14:02:08 +02:00
koooge
64783b7f06 Switch to MULTI FROM Dockerfile and use Node 10 for builds (#3199) 2018-12-23 15:17:54 +02:00
Takuya Arita
8ed872756c Add test case for redash.utils.generate_token (#3211) 2018-12-23 15:13:00 +02:00
Arik Fraimovich
83ea472d37 Merge pull request #3093 from getredash/backend-parameter-templating
Remove Mustache templating from frontend
2018-12-20 22:24:11 +02:00
Arik Fraimovich
0505163ff9 Merge pull request #3174 from getredash/invalidate-sessions-after-email-or-password-change
Invalidate sessions after email or password change
2018-12-19 15:49:54 +02:00
Arik Fraimovich
3d38bb478f Merge pull request #3205 from kravets-levko/bug/wrong-query-link-in-widget
Widget: fix url parameters in query link
2018-12-19 15:47:30 +02:00
Levko Kravets
654e906f6b getredash/redash#3203 Widget: fix url parameters in query link 2018-12-19 15:13:58 +02:00
Omer Lachish
54da6c69ab Merge branch 'invalidate-sessions-after-email-or-password-change' of github.com:getredash/redash into invalidate-sessions-after-email-or-password-change 2018-12-19 14:36:08 +02:00
Omer Lachish
8284e829fb explain why we call on identity changes 2018-12-19 14:35:42 +02:00
Omer Lachish
bcfff469de Merge branch 'master' into invalidate-sessions-after-email-or-password-change 2018-12-19 14:23:35 +02:00
Omer Lachish
b5ceb268ef Merge branch 'master' into invalidate-sessions-after-email-or-password-change 2018-12-17 12:52:17 +02:00
Arik Fraimovich
8583eaa8ad Merge pull request #3173 from kyoshidajp/golineup
Make it possible to move up one line by Ctrl+P on macOS
2018-12-17 12:08:04 +02:00
Omer Lachish
1b4e0f5de7 rename safely_apply_parameters to apply_parameters 2018-12-16 10:58:20 +02:00
Omer Lachish
479247b60c Merge branch 'master' into backend-parameter-templating 2018-12-16 10:22:29 +02:00
Katsuhiko YOSHIDA
dc842e9201 Fix can't open new parameter dialog by Ctrl+P on Windows and Linux 2018-12-14 18:58:33 +09:00
Omer Lachish
35357afb90 transform legacy session identifiers into new session identifiers 2018-12-13 13:02:11 +02:00
Omer Lachish
419877b364 explain the motivation behind support for legacy session identifiers 2018-12-13 13:02:03 +02:00
Omer Lachish
143d515bfc use login_user instead of manually updating user_id in the session 2018-12-13 12:30:21 +02:00
Omer Lachish
94905a287a tests for legacy session user identifiers 2018-12-12 13:03:50 +02:00
Katsuhiko YOSHIDA
34af780264 Golineup only macOS 2018-12-12 19:00:13 +09:00
Omer Lachish
3c8a3caa1d backward compatibility so users who have the old session identifier don't get logged out 2018-12-12 10:10:13 +02:00
Omer Lachish
9d566ef302 Merge branch 'master' into invalidate-sessions-after-email-or-password-change 2018-12-12 09:49:11 +02:00
Omer Lachish
2312db46f2 test that other sessions are invalidated when changing an e-mail. I had
to resort to comments in code in order to explain this. I'm a failure
today. 😭
2018-12-11 15:14:43 +02:00
Omer Lachish
b3495b8c00 test that user does not get logged out when changing email or password 2018-12-11 12:25:51 +02:00
Omer Lachish
dec790a9f3 Merge branch 'master' into invalidate-sessions-after-email-or-password-change 2018-12-11 10:41:27 +02:00
Omer Lachish
944bee6101 update identity only after succesfully updating user information 2018-12-10 14:25:04 +02:00
Omer Lachish
c426c826f7 fix tests that rely on sessions 2018-12-10 12:27:39 +02:00
Omer Lachish
4b1275ae56 don't sign out the current session when changing email or password 2018-12-09 11:07:42 +02:00
Katsuhiko YOSHIDA
b3c3134a86 Make it possible to move up one line by Ctrl+P 2018-12-09 15:26:01 +09:00
Omer Lachish
c311c12bcf invalidate session when email or password changes - currently not
backwards compatible
2018-12-05 23:49:55 +02:00
Omer Lachish
d769afab6f Merge branch 'master' into backend-parameter-templating 2018-12-03 13:22:44 +02:00
Omer Lachish
cf86509a0b remove leftovers of SQLQuery from utils 2018-12-03 13:17:40 +02:00
Omer Lachish
54894c3a26 track sql injections using an event to detect false positives 2018-12-03 13:12:34 +02:00
Omer Lachish
1519849219 use SQLQuery's text property instead of method 2018-12-03 10:03:45 +02:00
Omer Lachish
202b93d8be use SQLQuery in run_query 2018-12-03 09:53:17 +02:00
Omer Lachish
0a62bee3a1 Merge branch 'master' into backend-parameter-templating 2018-12-03 09:44:52 +02:00
Omer Lachish
ef8839aafa add tests for comment attacks 2018-11-21 13:52:08 +02:00
Omer Lachish
14860f6a8b split .apply calls to newline 2018-11-21 13:51:19 +02:00
Omer Lachish
a52c783857 add test for union query injections 2018-11-21 13:45:52 +02:00
Omer Lachish
5e7c785891 add SQLQuery class with tests for safe queries and non-safe tautology attacks 2018-11-21 13:45:29 +02:00
Omer Lachish
b242cefaa0 Merge branch 'master' into backend-parameter-templating 2018-11-21 10:51:44 +02:00
Omer Lachish
1b3bbb6c3b swap parameters so their order makes more sense 2018-11-19 22:56:51 +02:00
Omer Lachish
7bee07c9da include parameters (and query_id) in the recorded event 2018-11-19 22:54:22 +02:00
Omer Lachish
74ab7a5a42 Merge branch 'master' into backend-parameter-templating 2018-11-19 21:59:14 +02:00
Omer Lachish
bc65b62776 remove Mustache templating from frontend and send all parameters to the
API (in the POST body)
2018-11-19 10:50:00 +02:00
798 changed files with 47136 additions and 23174 deletions

View File

@@ -3,9 +3,10 @@ FROM cypress/browsers:chrome67
ENV APP /usr/src/app
WORKDIR $APP
RUN npm install --no-save cypress @percy/cypress > /dev/null
COPY package.json $APP/package.json
RUN npm run cypress:install > /dev/null
COPY cypress $APP/cypress
COPY client/cypress $APP/client/cypress
COPY cypress.json $APP/cypress.json
RUN ./node_modules/.bin/cypress verify

View File

@@ -45,20 +45,34 @@ jobs:
path: /tmp/test-results
- store_artifacts:
path: coverage.xml
frontend-lint:
docker:
- image: circleci/node:8
steps:
- checkout
- run: mkdir -p /tmp/test-results/eslint
- run: npm install
- run: npm run lint:ci
- store_test_results:
path: /tmp/test-results
frontend-unit-tests:
docker:
- image: circleci/node:8
steps:
- checkout
- run: sudo apt install python-pip
- run: sudo pip install -r requirements_bundles.txt
- run: npm install
- run: npm run bundle
- run: npm test
- run: npm run lint
frontend-e2e-tests:
environment:
COMPOSE_FILE: .circleci/docker-compose.cypress.yml
COMPOSE_PROJECT_NAME: cypress
PERCY_TOKEN_ENCODED: MWM3OGUzNzk4ZWQ2NTE4YTBhMDAwZDNiNWE1Nzc4ZjEzZjYyMzY1MjE0NjY0NDRiOGE5ODc5ZGYzYTU4ZmE4NQ==
PERCY_TOKEN_ENCODED: ZGRiY2ZmZDQ0OTdjMzM5ZWE0ZGQzNTZiOWNkMDRjOTk4Zjg0ZjMxMWRmMDZiM2RjOTYxNDZhOGExMjI4ZDE3MA==
CYPRESS_PROJECT_ID_ENCODED: OTI0Y2th
CYPRESS_RECORD_KEY_ENCODED: YzA1OTIxMTUtYTA1Yy00NzQ2LWEyMDMtZmZjMDgwZGI2ODgx
docker:
- image: circleci/node:8
steps:
@@ -72,33 +86,21 @@ jobs:
name: Setup Redash server
command: |
npm run cypress start
docker-compose run cypress node ./cypress/cypress.js db-seed
docker-compose run cypress npm run cypress db-seed
- run:
name: Execute Cypress tests
command: npm run cypress run-ci
build-tarball:
build-docker-image:
docker:
- image: circleci/node:8
steps:
- checkout
- run: sudo apt install python-pip
- run: npm install
- run: .circleci/update_version
- run: npm run bundle
- run: npm run build
- run: .circleci/pack
- store_artifacts:
path: /tmp/artifacts/
build-docker-image:
docker:
- image: circleci/buildpack-deps:xenial
steps:
- setup_remote_docker
- checkout
- run: sudo apt install python-pip
- run: sudo pip install -r requirements_bundles.txt
- run: .circleci/update_version
- run: docker login -u $DOCKER_USER -p $DOCKER_PASS
- run: docker build -t redash/redash:$(.circleci/docker_tag) .
- run: docker push redash/redash:$(.circleci/docker_tag)
- run: npm run bundle
- run: .circleci/docker_build
workflows:
version: 2
build:
@@ -106,24 +108,21 @@ workflows:
- python-flake8-tests
- legacy-python-flake8-tests
- backend-unit-tests
- frontend-unit-tests
- frontend-e2e-tests
- build-tarball:
- frontend-lint
- frontend-unit-tests:
requires:
- backend-unit-tests
filters:
tags:
only: /v[0-9]+(\.[0-9\-a-z]+)*/
branches:
only:
- master
- /release\/.*/
- frontend-lint
- frontend-e2e-tests:
requires:
- frontend-lint
- build-docker-image:
requires:
- backend-unit-tests
- frontend-unit-tests
- frontend-e2e-tests
filters:
branches:
only:
- master
- preview-build
- preview-image
- /release\/.*/

View File

@@ -13,6 +13,7 @@ services:
REDASH_LOG_LEVEL: "INFO"
REDASH_REDIS_URL: "redis://redis:6379/0"
REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
REDASH_RATELIMIT_ENABLED: "false"
worker:
build: ../
command: scheduler
@@ -23,12 +24,12 @@ services:
REDASH_LOG_LEVEL: "INFO"
REDASH_REDIS_URL: "redis://redis:6379/0"
REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
QUEUES: "queries,scheduled_queries,celery"
QUEUES: "queries,scheduled_queries,celery,schemas"
WORKERS_COUNT: 2
cypress:
build:
context: ../
dockerfile: Dockerfile.cypress
dockerfile: .circleci/Dockerfile.cypress
depends_on:
- server
- worker
@@ -38,6 +39,12 @@ services:
PERCY_BRANCH: ${CIRCLE_BRANCH}
PERCY_COMMIT: ${CIRCLE_SHA1}
PERCY_PULL_REQUEST: ${CIRCLE_PR_NUMBER}
COMMIT_INFO_BRANCH: ${CIRCLE_BRANCH}
COMMIT_INFO_AUTHOR: ${CIRCLE_USERNAME}
COMMIT_INFO_SHA: ${CIRCLE_SHA1}
COMMIT_INFO_REMOTE: ${CIRCLE_REPOSITORY_URL}
CYPRESS_PROJECT_ID: ${CYPRESS_PROJECT_ID}
CYPRESS_RECORD_KEY: ${CYPRESS_RECORD_KEY}
redis:
image: redis:3.0-alpine
restart: unless-stopped

17
.circleci/docker_build Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
VERSION=$(jq -r .version package.json)
VERSION_TAG=$VERSION.b$CIRCLE_BUILD_NUM
docker login -u $DOCKER_USER -p $DOCKER_PASS
if [ $CIRCLE_BRANCH = master ] || [ $CIRCLE_BRANCH = preview-image ]
then
docker build -t redash/redash:preview -t redash/preview:$VERSION_TAG .
docker push redash/redash:preview
docker push redash/preview:$VERSION_TAG
else
docker build -t redash/redash:$VERSION_TAG .
docker push redash/redash:$VERSION_TAG
fi
echo "Built: $VERSION_TAG"

View File

@@ -1,10 +0,0 @@
#!/bin/bash
if [ $CIRCLE_BRANCH = master ] || [ $CIRCLE_BRANCH = preview-build ]
then
FULL_VERSION='preview'
else
VERSION=$(jq -r .version package.json)
FULL_VERSION=$VERSION.b$CIRCLE_BUILD_NUM
fi
echo $FULL_VERSION

View File

@@ -6,4 +6,4 @@ FILENAME=$NAME.$FULL_VERSION.tar.gz
mkdir -p /tmp/artifacts/
tar -zcv -f /tmp/artifacts/$FILENAME --exclude="optipng*" --exclude=".git*" --exclude="*.pyc" --exclude="*.pyo" --exclude="venv" --exclude="node_modules" *
tar -zcv -f /tmp/artifacts/$FILENAME --exclude=".git" --exclude="optipng*" --exclude="cypress" --exclude="*.pyc" --exclude="*.pyo" --exclude="venv" *

View File

@@ -1,22 +1,32 @@
engines:
version: "2"
checks:
complex-logic:
enabled: false
file-lines:
enabled: false
method-complexity:
enabled: false
method-count:
enabled: false
method-lines:
config:
threshold: 100
nested-control-flow:
enabled: false
identical-code:
enabled: false
similar-code:
enabled: false
plugins:
pep8:
enabled: true
eslint:
enabled: true
channel: "eslint-3"
config:
config: client/.eslintrc.js
checks:
import/no-unresolved:
enabled: false
ratings:
paths:
- "redash/**/*.py"
- "client/**/*.js"
exclude_paths:
- tests/**/*.py
- migrations/**/*.py
- old_migrations/**/*.py
- setup/**/*
- bin/**/*
exclude_patterns:
- "tests/**/*.py"
- "migrations/**/*.py"
- "setup/**/*"
- "bin/**/*"
- "**/node_modules/"
- "client/dist/"
- "**/*.pyc"

View File

@@ -3,4 +3,12 @@ client/dist/
node_modules/
.tmp/
.venv/
venv/
.git/
/.codeclimate.yml
/.coverage
/coverage.xml
/.circleci/
/.github/
/netlify.toml
/setup/

View File

@@ -9,6 +9,6 @@ trim_trailing_whitespace = true
indent_style = space
indent_size = 4
[*.{js,css,html}]
[*.{js,jsx,css,less,html}]
indent_style = space
indent_size = 2

15
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,15 @@
## What type of PR is this? (check all applicable)
<!-- Please leave only what's applicable -->
- [ ] Refactor
- [ ] Feature
- [ ] Bug Fix
- [ ] New Query Runner (Data Source)
- [ ] New Alert Destination
- [ ] Other
## Description
## Related Tickets & Documents
## Mobile & Desktop Screenshots/Recordings (if there are UI changes)

23
.github/support.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
# Configuration for Support Requests - https://github.com/dessant/support-requests
# Label used to mark issues as support requests
supportLabel: Support Question
# Comment to post on issues marked as support requests, `{issue-author}` is an
# optional placeholder. Set to `false` to disable
supportComment: >
:wave: @{issue-author}, we use the issue tracker exclusively for bug reports
and planned work. However, this issue appears to be a support request.
Please use [our forum](https://discuss.redash.io) to get help.
# Close issues marked as support requests
close: true
# Lock issues marked as support requests
lock: false
# Assign `off-topic` as the reason for locking. Set to `false` to disable
setLockReason: true
# Repository to extend settings from
# _extends: repo

7
.github/weekly-digest.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
# Configuration for weekly-digest - https://github.com/apps/weekly-digest
publishDay: mon
canPublishIssues: true
canPublishPullRequests: true
canPublishContributors: true
canPublishStargazers: true
canPublishCommits: true

4
.gitignore vendored
View File

@@ -24,5 +24,5 @@ node_modules
.sass-cache
npm-debug.log
cypress/screenshots
cypress/videos
client/cypress/screenshots
client/cypress/videos

View File

@@ -1,5 +1,196 @@
# Change Log
## v8.0.0-beta.2 - 2019-09-16
This is an update to the previous beta release, which includes:
* Add options for users to share anonymous usage information with us (see [docs](https://redash.io/help/open-source/admin-guide/usage-data) for details).
* Visualizations:
- Allow the user to decide how to handle null values in charts.
* Upgrade Sentry-SDK to latest version.
* Make horizontal table scroll visible in dashboard widgets without scrolling.
* Data Sources:
* Add support for Azure Data Explorer (Kusto).
* MySQL: fix connections without SSL configuration failing.
* Amazon Redshift: option to set query group for adhoc/scheduled queries.
* Hive: make error message more friendly.
* Qubole: add support to run Quantum queries.
* Display data source icon in query editor.
* Fix: allow users with view only acces to use the queries in Query Results
* Dashboard: when updating parameters refersh only widgets that use those parameters.
This release had contributions from 12 people: @arikfr, @cclauss, @gabrieldutra, @justinclift, @kravets-levko, @ranbena, @rauchy, @sandeepV2, @shinsuke-nara, @spacentropy, @sphenlee, @swfz.
## v8.0.0-beta - 2019-08-18
After months of being heads down with hard work, it's finally time to wrap up the V8 release 🤩 This release includes many long awaited improvements to parameters, UX improvements, further React migration and other changes, fixes and improvements.
While this version is already running on the hosted platform to make sure it's stable, we're excited to put this in the hands of our Open Source users.
Starting from this release we will no longer build a tarball distribution of the codebase and recommend everyone to switch over to using our Docker images. We're planning on dropping Python 2 support towards its EOL this year and switching over to the Docker image will make this transition much simpler.
This release was made possible by contributions from over 40 people: @aidarbek, @AntonZarutsky, @ariarijp, @arikfr, @combineads, @deecay, @fmy, @gabrieldutra, @guwenqing, @guyco33, @ialeinikov, @Jakdaw, @jezdez, @justinclift, @k-tomoyasu, @katty0324, @koooge, @kravets-levko, @ktmud, @KumanoTanaka, @kyoshidajp, @nason, @oldPadavan, @openjck, @osule, @otsaloma, @ranbena, @rauchy, @rueian, @sekiyama58, @shinsuke-nara, @taminif, @The-Alchemist, @vv-p, @washort, @wudi-ayuan, @ygrishaev, @yoavbls, @yoshiken, @yusukegoto and the support of over 500 organizations who subscribed to our hosted version and by that sponsor the team's work.
### Parameters
- Parameter UI improvements:
- Support for multi-select in dropdown (and query dropdown) parameters.
- Support for dynamic values in date and date-range parameters.
- Search dropdown parameter values.
- New UX for applying parameter changes in queries and dashboards.
- Allow using Safe Parameters in visualization embeds and public dashboards. Safe Parameters are any parameter type except for the a text parameter (dropdowns are safe).
### Data Sources
- New Data Sources: Couchbase, Phoenix and Dgraph.
- New JSON data source (and deprecated old URL data source).
- Snowflake: update connector to latest version.
- PostgreSQL: show only accessible tables in schema.
- BigQuery:
- Correctly handle NaN values.
- Treat repeated fields as rrays.
- [BigQuery] Fix: in some queries there is no mode field
- DynamoDB:
- Support for Unicode in queries.
- Safe loading of schema.
- Rockset: better handling of query errors.
- Google Sheets:
- Support for Team Drive.
- Friendlier error message in case of an API error and more reliable test connection.
- MySQL:
- Support for calling Stored Procedures and better handling of query cancellation.
- Switch to using `mysqlclient` (a maintained fork of `Python-MySQL`).
- MongoDB: Support serializing Decimal128 values.
- Presto: support for passwords in connection settings.
- Amazon Athena: allow to specify custom work group.
- Query Results: querying a column with a dictionary or array fails
- Clickhouse: make sure we don't show password in error messages.
- Enable Cassandra support by default.
### Visualizations
- Charts:
- Fix: legend overlapping chart on small screens.
- Fix: Pie chart not rendering when series doesn't exist in options.
- Pie Chart: add option to set direction of slices.
- WordCloud: rewritten to support new options (provide frequency in query, limits), scale when resizing, handle long words and more.
- Pivot Table: support hiding totals.
- Counters: apply formatting to target value.
- Maps:
- Ability to customize marker icon and color.
- Customization options for Choropleth maps.
- New Visualization: Details View.
### **UX**
- Replace blank screen with a loading indicator when the application is doing its first load.
- Multiple improvements to dashboards editing: auto-save, grid markings and better refresh indicator.
- Admin can now edit user's groups from the user page.
- Add keyboard shortcut (Ctrl/Cmd+Shift+F) to trigger query formatting.
### API
- Query Result API response minimized to only required fields when called with a non user API key.
- Prefer API key over cookies in authentication.
- User can now regenerate Query API Key.
### Other Changes
- Sends CSP headers to prevent various kinds of security attacks via the browser. Might break unusual usages and embeds of Redash.
- New Failed Scheduled Queries email report (can be enabled from organization settings screen).
- Deprecated HipChat Alert Destination.
- Add options to hide different parts of a Visualization embed UI (parameters, title, link to query).
- Support multi-byte search for query names and descriptions (needs to be enabled in Organization settings screen).
- CSV query results download: correctly serialize booleans and date values.
- Dashboard filters now collect values from all widgets with the same filter.
- Support for custom message and description in alert notifications (currently disabled behind a feature flag until we improve the alert UX).
### Bug Fixes
- Fix: adding widget to dashboard from a query page is broken.
- Fix: default time format option was wrong.
- Fix: when too many errors of a scheduled queries occur it causes an OverflowError.
- Fix: when forking a query maintain the same visualizations order.
## v7.0.0 - 2019-03-17
We're trying a new format for the CHANGELOG in this release. Focusing on the bigger changes, but for whoever interested, you can see all the changes [here](https://github.com/getredash/redash/compare/v6.0.0...master).
Besides all the features, bug fixes and improvements listed below we managed to convert a large portion of Redash's frontend code from Angular.js to React. You can see status in [#3071](https://github.com/getredash/redash/issues/3071).
This release was made possible with the help of 34 contributors. 🙇‍♂️
### Data Sources
- **All data source options are now encrypted in the database.** By default the encryption uses the `REDASH_COOKIE_SECRET` value (`redash.settings.COOKIE_SECRET`), but you can specify a different value by setting the `REDASH_SECRET_KEY` environment variable value. Note that you need to set this _before_ doing the upgrade.
- New Data Sources: Uptycs and Apache Drill.
- Snowplow: is now enabled by default & supports region setting.
- Elasticsearch: add support for Amazon Elasticsearch IAM authentication (with IAM profile or key/secret pair).
- PostgreSQL: add support for serializing range values.
- Redshift: remove duplicate column information for late-binding views.
- Athena: load all databases (using pagination).
- BigQuery: correctly handle temp tables with no schema field.
- Jira (JQL): support for fetching all records with pagination.
- Prometheus: fix schema loading and add support for query range.
### In-app Help
You can now open the [Knowledge Base](https://redash.io/help) inside the application. We also added a few "help triggers" in the app, that will open the Knowledge Base in context of what you're currently doing.
### Parameters
- **Dashboard Parameters**: We improved the flow of adding queries with parameters to dashboards and now give you full control over how parameters are mapped. You no longer have to make sure all parameters have the same name or use the `Global` checkbox. We also added new options, like keeping the parameter local to the widget or setting a static value. [Read more in our Knowledge Base →](https://redash.io/help/user-guide/querying/query-parameters#Parameter-Mapping-on-Dashboards)
- We added server side validation of parameter values for all parameter types, except for parameters of `text` type. All validated parameter types are considered safe. When a query is using safe parameters (or no parameters at all), View Only users can refresh it.
- Refreshing safe queries is done using the new results API endpoint, which takes only a query ID (and optionally parameter values) and does not need the query text.
### Query Editor Improvements
- Run only the highlighted query text: hit Execute after highlighting a portion of your query and only the selected portion will be sent to the database. This is useful for testing sub-SELECT statements and CTE's.
- Improved auto complete: add a dot . after a table name in the query editor and auto complete will only suggest columns on that table.
- Autosave parameter configuration changes.
- YAML syntax support (for data sources like Yandex Metrica).
### Improved Query Scheduler
The Query Scheduler got a face lift and some new options: you can pick a day for a weekly schedule to run on and also set an end date after which the query will no longer execute on schedule.
### Data Sources
We added Apache Drill, Uptycs and a new JSON data source. Also fixed a few bugs in Athena's query runner and others.
### User Management
The users page got revamped with a new look and feel and few new features:
- An indication when a user was last active.
- Show if an invited user hasn't finished the setup process yet (Pending Invitations section).
- You can now generate a new API key for users, if there's a concern it was compromised.
### Admin
- New Celery queues status screens, replacing the old Queries Status and better reflecting the status of running queries.
- Make the queue name for schema refresh job configurable. The default used to be hard coded `schemas`, which is not available on all setups. Now it's `celery`.
- The `gevent` library is installed by default, and you can now setup gunicorn to use `gevent` based workers.
- New Docker entrypoint command to do a health check for a worker process.
- Flask-Admin is no longer setup or supported.
### Other Changes
- New Alert destination: Google Hangouts Chat.
- When downloading results from the results API it will set a user friendly filename for the downloaded file.
- Archived Queries section added to the queries list.
### Bug Fixes
- Fixed: fork query does not fork tables but instead adds default table.
- Fixed: when deleting a visualization, any widget using it was left empty on the dashboard.
- Fixed: issues with Query Editor resizing on new versions of Chrome.
- Fixed: issues with exporting dictionaries to Excel.
- Fixed: Cohort visualization gets stuck when passing string values.
- Fixed: use series name for Pie chart label.
- Make sure Flask app created in Celery's worker process (could cause some query runners to get stuck while running queries).
## v6.0.0 - 2018-12-16
v6.0.0 release version. Mainly includes fixes for regressions from the beta version.
@@ -8,48 +199,47 @@ This release had contributions from 5 people: @rauchy, @denisov-vlad, @arikfr, @
### Changed
* #3183 Make refresh_queries less noisey in logs. @arikfr
- #3183 Make refresh_queries less noisey in logs. @arikfr
### Fixed
* #3163 Include correct version in production builds. @rauchy
* #3161 Clickhouse: fix int() conversion error. @denisov-vlad
* #3166 Directly using record_event task requires timestamp. @arikfr
* #3167 Alert.evaluate failing when the column is missing. @arikfr
* ##3162 Remove API permissions for users who have been disabled. @rauchy
* #3171 Reject empty query name. @ariarijp
* #3175, #3186 Fix disable error message. @rauchy, @gabrieldutra
* #3182 [Redshift] support for schema names with dots. @arikfr
* #3187 Safely create_app in Celery code (try to fetch current_app first). @arikfr
- #3163 Include correct version in production builds. @rauchy
- #3161 Clickhouse: fix int() conversion error. @denisov-vlad
- #3166 Directly using record_event task requires timestamp. @arikfr
- #3167 Alert.evaluate failing when the column is missing. @arikfr
- ##3162 Remove API permissions for users who have been disabled. @rauchy
- #3171 Reject empty query name. @ariarijp
- #3175, #3186 Fix disable error message. @rauchy, @gabrieldutra
- #3182 [Redshift] support for schema names with dots. @arikfr
- #3187 Safely create_app in Celery code (try to fetch current_app first). @arikfr
### Other
* #3155 Add DB Seed to Cypress and setup Percy. @gabrieldutra
* #3180 Remove coverage from pytest terminal output. @rauchy
- #3155 Add DB Seed to Cypress and setup Percy. @gabrieldutra
- #3180 Remove coverage from pytest terminal output. @rauchy
## v6.0.0-beta - 2018-12-03
This release was 2 months in the making and it is full with good stuff!
* We have 5 new data sources: Databricks, IBM DB2, Kylin, Druid and Rockset. ⌗
* There are fixes and improvements to 11 existing data sources (MySQL, Redshift, Postgres, MongoDB, Google BigQuery, Vertica, TreasureData, Presto, ClickHouse, Google Sheets and Google Analytics).
* The Query Results data source can now load cached results, just use the `cached_query_` prefix instead of `query_`.
* On the visualizations front we added a Heatmap visualization and did updated the table and counter visualizations.
* Alerts got some fixes and a new destination: PagerDuty.
* If the live autocomplete in the code editor annoys you, you can disable it now (although we're working to make it better, see #3092).
* Fast queries will now load faster. 🏃‍♂️
* We improved the layout of visualizations and content on smaller screen sizes. 📱
* For those of you who like sharing, you can now enable the ability to share ownership of queries and dashboards and let others to edit them. Check the Settings page to enable this feature.
- We have 5 new data sources: Databricks, IBM DB2, Kylin, Druid and Rockset. ⌗
- There are fixes and improvements to 11 existing data sources (MySQL, Redshift, Postgres, MongoDB, Google BigQuery, Vertica, TreasureData, Presto, ClickHouse, Google Sheets and Google Analytics).
- The Query Results data source can now load cached results, just use the `cached_query_` prefix instead of `query_`.
- On the visualizations front we added a Heatmap visualization and did updated the table and counter visualizations.
- Alerts got some fixes and a new destination: PagerDuty.
- If the live autocomplete in the code editor annoys you, you can disable it now (although we're working to make it better, see #3092).
- Fast queries will now load faster. 🏃‍♂️
- We improved the layout of visualizations and content on smaller screen sizes. 📱
- For those of you who like sharing, you can now enable the ability to share ownership of queries and dashboards and let others to edit them. Check the Settings page to enable this feature.
There were also important changes to the code and infrastructure:
* More components moved to React.
* We switched to Webpack 4 with the help of @dmonego.
* We upgraded to Celery 4 with the help of @emtwo, @jezdez, @mashrikt and @atharvai.
* We started moving towards Python 3 for our backend. The first step was to make sure our code pass basic sanity tests with Flake 8, which was implemented by @cclauss.
* We improved our testing on the frontend by adding setup for Jest tests and E2E testing using Cypress (@gabrieldutra).
* Each pull request now gets a deploy preview using Netlify to easily test frontend changes.
- More components moved to React.
- We switched to Webpack 4 with the help of @dmonego.
- We upgraded to Celery 4 with the help of @emtwo, @jezdez, @mashrikt and @atharvai.
- We started moving towards Python 3 for our backend. The first step was to make sure our code pass basic sanity tests with Flake 8, which was implemented by @cclauss.
- We improved our testing on the frontend by adding setup for Jest tests and E2E testing using Cypress (@gabrieldutra).
- Each pull request now gets a deploy preview using Netlify to easily test frontend changes.
This is just a summary, you're welcome to review the full list below. ⬇
@@ -57,226 +247,220 @@ This release had contributions from 38 people: @arikfr, @kravets-levko, @jezdez,
### Added
* #2747, #3143 Add a new Databricks query runner. @alison985, @jezdez, @arikfr
* #2767 Add ability to add viz to dashboard from query edit page. @alison985, @jezdez
* #2780 Add a query autocomplete toggle. @alison985, @jezdez, @arikfr
* #2768 Add authentication via JWT providers. @SakuradaJun
* #2790 Add the ability to sort favorited queries, paginate the dashboard list and improve UI inconsistencies. @jezdez
* #2681 Add ability to search table column names in schema browser. @alison985
* #2855 Add option to query cached results. @yoavbls
* #2740 Add ability for extensions to add periodic tasks. @emtwo
* #2924 Google Spreadsheets: Add support for opening by URL. @alexanderlz
* #2903 Add PagerDuty as an Alert Destination. @alexanderlz
* #2824 Add support for expanding dashboard visualizations. @sjakthol
* #2900 Add ability to specify a counter label. @ralphilius
* #2565 Add frontend extension capabilities. @emtwo
* #2848 Add IBM Db2 as a data source using the ibm-db Python package. @nicof38
* #2959 Add option to auto reload widget data in shared dashboards. @arikfr
* #2993 Add page size settings. @kyoshidajp
* #2080 New Heatmap chart visualization with Plotly. @deecay
* #2991 Show users in CLI group list. @GitSumito
* #2342 New SQLPARSE_FORMAT_OPTIONS setting to configure query formatter. @ariarijp
* #3031 Add some tests for Query Results. @ariarijp
* #2936 Add Kylin data source. @Trigl
* #3047 Add Druid data source. @rauchy
* #3077 New user interface for the feature flag of the share edit permissions feature. @arikfr
* #3007 Add permissions to the result of "manage.py groups list" command. @Udomomo
* #3088 Add get_current_user() fuction for the Python query runner. @kyoshidajp
* #3114 Add event tracking to autocomplete toggle. @arikfr
* #3068 Add Rockset query runner. @igorcanadi, @arikfr
* #3105 Display frontend version. @rauchy
- #2747, #3143 Add a new Databricks query runner. @alison985, @jezdez, @arikfr
- #2767 Add ability to add viz to dashboard from query edit page. @alison985, @jezdez
- #2780 Add a query autocomplete toggle. @alison985, @jezdez, @arikfr
- #2768 Add authentication via JWT providers. @SakuradaJun
- #2790 Add the ability to sort favorited queries, paginate the dashboard list and improve UI inconsistencies. @jezdez
- #2681 Add ability to search table column names in schema browser. @alison985
- #2855 Add option to query cached results. @yoavbls
- #2740 Add ability for extensions to add periodic tasks. @emtwo
- #2924 Google Spreadsheets: Add support for opening by URL. @alexanderlz
- #2903 Add PagerDuty as an Alert Destination. @alexanderlz
- #2824 Add support for expanding dashboard visualizations. @sjakthol
- #2900 Add ability to specify a counter label. @ralphilius
- #2565 Add frontend extension capabilities. @emtwo
- #2848 Add IBM Db2 as a data source using the ibm-db Python package. @nicof38
- #2959 Add option to auto reload widget data in shared dashboards. @arikfr
- #2993 Add page size settings. @kyoshidajp
- #2080 New Heatmap chart visualization with Plotly. @deecay
- #2991 Show users in CLI group list. @GitSumito
- #2342 New SQLPARSE_FORMAT_OPTIONS setting to configure query formatter. @ariarijp
- #3031 Add some tests for Query Results. @ariarijp
- #2936 Add Kylin data source. @Trigl
- #3047 Add Druid data source. @rauchy
- #3077 New user interface for the feature flag of the share edit permissions feature. @arikfr
- #3007 Add permissions to the result of "manage.py groups list" command. @Udomomo
- #3088 Add get_current_user() fuction for the Python query runner. @kyoshidajp
- #3114 Add event tracking to autocomplete toggle. @arikfr
- #3068 Add Rockset query runner. @igorcanadi, @arikfr
- #3105 Display frontend version. @rauchy
### Changed
* #2636 Rewrite query editor with React. @washort, @arikfr
* #2637 Convert edit-in-place component to React. @washort, @arikfr
* #2766 Suitable events are now being recorded server side instead of in the frontend. @alison985, @jezdez
* #2796 Change placement (right/bottom) of chart legend depending on chart width. @kravets-levko
* #2833 Uses server side sort order for tag list and show count of tagged items. @jezdez
* #2318 Support authentication for the URL data source. @jezdez
* #2884 Rename Yandex Metrika to Metrica. @jezdez
* #2909 MySQL: hide sys tables. @arikfr
* #2817 Consistently use simplejson for loading and dumping JSON. @jezdez
* #2872 Use Plotly's function to clean y-values (x may be category or date/time). @kravets-levko
* #2938 Auto focus tag input. @kyoshidajp
* #2927 Design refinements for queries pages. @kocsmy
* #2950 Show activity status in CLI user list. @GitSumito
* #2968 Presto data source: setting protocol (http/https), safe loading of error messages. @arikfr
* #2967 Show groups in CLI user list. @GitSumito
* #2603 MongoDB: Update requirements to support srv. @arikfr
* #2961 MongoDB: Skip system collections when loading schema. @arikfr
* #2960 Add timeout to various HTTP requests. @arikfr
* #2983 Databricks: New logo, updated name and enabled by default. @arikfr
* #2982 Table visualization: change default size to 25 and add more size options. @arikfr
* #2866 Redshift: Hide tables the configured user cannot access. @sjakthol
* #3058 Mustache: don't html-escape query parameters values. @kravets-levko
* #3079 Always use basic autocomplete, as well as the live autocomplete. @arikfr
* #3084 Support tel://, sms://, mailto:// links in query results. @zhujunsan
* #3083 Clickhouse: Add WITH TOTALS option support. @denisov-vlad
* #3063 Allow setting colors for bubble charts. @toph
* #3085 BigQuery: Switch to Standard SQL as the default. @kyoshidajp
* #3094 Tags autocomplete: Show note when creating a new label. @kravets-levko
* #2984 Autocomplete toggle improvements. @arikfr
* #3089 Open new tab when forking a query. @kyoshidajp
* #3126 MongoDB: add support for sorting columns. @arikfr
* #3128 Improve backoff algorithm of query results polling to speed it up. @arikfr
* #3125 Vertica: update driver & add support for connection timeout. @arikfr
* #3124 Support unicode in Postgres/Redshift schema. @arikfr
* #3138 Migrate all tags components to React. @kravets-levko
* #3139 Better manage permissions modal. @kocsmy
* #3149 Improve tag link colors and fix group tags on Users page. @kocsmy
* #3146 Update, replace and fix new alert destination logos so it fits better. @kocsmy
* #3147 Add and improve recent db logos that didn't fit in size properly. @kocsmy
* #3148 Fix label positioning on no found screen. @kocsmy
* #3156 json_dumps: add support for serializing buffer objects. @arikfr
- #2636 Rewrite query editor with React. @washort, @arikfr
- #2637 Convert edit-in-place component to React. @washort, @arikfr
- #2766 Suitable events are now being recorded server side instead of in the frontend. @alison985, @jezdez
- #2796 Change placement (right/bottom) of chart legend depending on chart width. @kravets-levko
- #2833 Uses server side sort order for tag list and show count of tagged items. @jezdez
- #2318 Support authentication for the URL data source. @jezdez
- #2884 Rename Yandex Metrika to Metrica. @jezdez
- #2909 MySQL: hide sys tables. @arikfr
- #2817 Consistently use simplejson for loading and dumping JSON. @jezdez
- #2872 Use Plotly's function to clean y-values (x may be category or date/time). @kravets-levko
- #2938 Auto focus tag input. @kyoshidajp
- #2927 Design refinements for queries pages. @kocsmy
- #2950 Show activity status in CLI user list. @GitSumito
- #2968 Presto data source: setting protocol (http/https), safe loading of error messages. @arikfr
- #2967 Show groups in CLI user list. @GitSumito
- #2603 MongoDB: Update requirements to support srv. @arikfr
- #2961 MongoDB: Skip system collections when loading schema. @arikfr
- #2960 Add timeout to various HTTP requests. @arikfr
- #2983 Databricks: New logo, updated name and enabled by default. @arikfr
- #2982 Table visualization: change default size to 25 and add more size options. @arikfr
- #2866 Redshift: Hide tables the configured user cannot access. @sjakthol
- #3058 Mustache: don't html-escape query parameters values. @kravets-levko
- #3079 Always use basic autocomplete, as well as the live autocomplete. @arikfr
- #3084 Support tel://, sms://, mailto:// links in query results. @zhujunsan
- #3083 Clickhouse: Add WITH TOTALS option support. @denisov-vlad
- #3063 Allow setting colors for bubble charts. @toph
- #3085 BigQuery: Switch to Standard SQL as the default. @kyoshidajp
- #3094 Tags autocomplete: Show note when creating a new label. @kravets-levko
- #2984 Autocomplete toggle improvements. @arikfr
- #3089 Open new tab when forking a query. @kyoshidajp
- #3126 MongoDB: add support for sorting columns. @arikfr
- #3128 Improve backoff algorithm of query results polling to speed it up. @arikfr
- #3125 Vertica: update driver & add support for connection timeout. @arikfr
- #3124 Support unicode in Postgres/Redshift schema. @arikfr
- #3138 Migrate all tags components to React. @kravets-levko
- #3139 Better manage permissions modal. @kocsmy
- #3149 Improve tag link colors and fix group tags on Users page. @kocsmy
- #3146 Update, replace and fix new alert destination logos so it fits better. @kocsmy
- #3147 Add and improve recent db logos that didn't fit in size properly. @kocsmy
- #3148 Fix label positioning on no found screen. @kocsmy
- #3156 json_dumps: add support for serializing buffer objects. @arikfr
### Fixed
* #2849 Fix invalid reference to alert.to_dict() in webhook. @wankdanker
* #2840 Improve counter visualization text scaling. @kravets-levko
* #2854 Widget titles are no longer rendered wrong on public dashboards. @kravets-levko
* #2318 Removed redundant exception handling in data sources since that's handled in the query backend. @jezdez
* #2886 Fix Javascript build that broke because registerAll tried to run EditInPlace component. @arikfr
* #2911 Dont show “Add to dashboard” in dropdown to unsaved queries. @jezdez
* #2916 Fix export query results output file name. @gabrieldutra
* #2917 Fix output file name not changing after rename query. @gabrieldutra
* #2868 Address edge case when retrieving Glue schemas for Athena data source. @kadrach
* #2929 Fix: date value in a filter is duplicated. @combineads
* #2875 Unbreak charts with long legend break in horizontal mode. Update plotly.js. @kravets-levko
* #2937 Fix event recording in admin API backend. @kyoshidajp
* #2953 Minor fixes for the Clickhouse data source. @denisov-vlad
* #2941 Bring back fix to Box plot hover. @arikfr
* #2957 Apply missing CSS classes to EditInPlace component. @arikfr
* #2897 Show "Add description" only after saving the query. @arikfr
* #2922 Query page layout improvements for small screens. @kravets-levko
* #2956 Clickhouse: move timeout to params. @denisov-vlad
* #2964 Fix no tags shown when having empty set. @gabrieldutra
* #2757 Use full text search ranking when searching in list views. @jezdez
* #2969 Query Results data source: improved errors, quoted column names. @arikfr
* #2906 Preventing open redirection in loging process. @kyoshidajp
* #2867 TreasureData: Deduplicate column names. @zaimy
* #2994 Fix scheme of various URLs from http to https. @kyoshidajp
* #2992 Fix an invalid prop type warning in new version notifier. @kyoshidajp
* #3022 Fix Toolbox covering part of a chart. @kravets-levko
* #2998 Fix charts losing responsive features after refreshing the dashboard. @kravets-levko
* #3034 Postgres: handle NaN/Infinity values. @kravets-levko
* #2745 Sort columns with undefined values. @Yossi-a
* #3041 Sort CLI output of lists. @GitSumito
* #2803, #3006 Address various tag display issues on query list page. @kocsmy, @alison985
* #3049 Fix edit-in-place component which ignored isEditable flag and didn't work on Groups page. @kravets-levko
* #2965 Google Analytics: Fix crash when no results are returned. @alexanderlz
* #3061 Fix table visualization so that the horizontal scrollbar is not be always visible. @kravets-levko
* #3076 Add white-space padding to separators in the footer. @burnash
* #2919 Fix URL data source to not require a URL. @arikfr
* #3098 Force AngularJS to update query editor properly. @washort
* #3100 Delete redundant regex segment in query result frontend. @zhujunsan
* #2978 Prevent the query update timestamp from changing when it is linked to new query results. @rauchy
* #3046 Fix query page header. @kravets-levko
* #3097 Mongo: Fix collection fields retreival bug when Views are present. @jodevsa
* #3107 Keep query text in local state for now. @washort
* #3111 Fix mobile padding issues on Query results. @kocsmy
* #3122 Show menu divider only if query is archived. @jezdez
* #3120 Fix tag counts for dashboards and queries. @jezdez
* #3141 Fix schema refresh to work on MySQL 8. @hoangphuoc25
* #3142 Fix: editing dashboard title results in the visualizations being replaced by the loading markers. @kravets-levko
- #2849 Fix invalid reference to alert.to_dict() in webhook. @wankdanker
- #2840 Improve counter visualization text scaling. @kravets-levko
- #2854 Widget titles are no longer rendered wrong on public dashboards. @kravets-levko
- #2318 Removed redundant exception handling in data sources since that's handled in the query backend. @jezdez
- #2886 Fix Javascript build that broke because registerAll tried to run EditInPlace component. @arikfr
- #2911 Dont show “Add to dashboard” in dropdown to unsaved queries. @jezdez
- #2916 Fix export query results output file name. @gabrieldutra
- #2917 Fix output file name not changing after rename query. @gabrieldutra
- #2868 Address edge case when retrieving Glue schemas for Athena data source. @kadrach
- #2929 Fix: date value in a filter is duplicated. @combineads
- #2875 Unbreak charts with long legend break in horizontal mode. Update plotly.js. @kravets-levko
- #2937 Fix event recording in admin API backend. @kyoshidajp
- #2953 Minor fixes for the Clickhouse data source. @denisov-vlad
- #2941 Bring back fix to Box plot hover. @arikfr
- #2957 Apply missing CSS classes to EditInPlace component. @arikfr
- #2897 Show "Add description" only after saving the query. @arikfr
- #2922 Query page layout improvements for small screens. @kravets-levko
- #2956 Clickhouse: move timeout to params. @denisov-vlad
- #2964 Fix no tags shown when having empty set. @gabrieldutra
- #2757 Use full text search ranking when searching in list views. @jezdez
- #2969 Query Results data source: improved errors, quoted column names. @arikfr
- #2906 Preventing open redirection in loging process. @kyoshidajp
- #2867 TreasureData: Deduplicate column names. @zaimy
- #2994 Fix scheme of various URLs from http to https. @kyoshidajp
- #2992 Fix an invalid prop type warning in new version notifier. @kyoshidajp
- #3022 Fix Toolbox covering part of a chart. @kravets-levko
- #2998 Fix charts losing responsive features after refreshing the dashboard. @kravets-levko
- #3034 Postgres: handle NaN/Infinity values. @kravets-levko
- #2745 Sort columns with undefined values. @Yossi-a
- #3041 Sort CLI output of lists. @GitSumito
- #2803, #3006 Address various tag display issues on query list page. @kocsmy, @alison985
- #3049 Fix edit-in-place component which ignored isEditable flag and didn't work on Groups page. @kravets-levko
- #2965 Google Analytics: Fix crash when no results are returned. @alexanderlz
- #3061 Fix table visualization so that the horizontal scrollbar is not be always visible. @kravets-levko
- #3076 Add white-space padding to separators in the footer. @burnash
- #2919 Fix URL data source to not require a URL. @arikfr
- #3098 Force AngularJS to update query editor properly. @washort
- #3100 Delete redundant regex segment in query result frontend. @zhujunsan
- #2978 Prevent the query update timestamp from changing when it is linked to new query results. @rauchy
- #3046 Fix query page header. @kravets-levko
- #3097 Mongo: Fix collection fields retreival bug when Views are present. @jodevsa
- #3107 Keep query text in local state for now. @washort
- #3111 Fix mobile padding issues on Query results. @kocsmy
- #3122 Show menu divider only if query is archived. @jezdez
- #3120 Fix tag counts for dashboards and queries. @jezdez
- #3141 Fix schema refresh to work on MySQL 8. @hoangphuoc25
- #3142 Fix: editing dashboard title results in the visualizations being replaced by the loading markers. @kravets-levko
### Other
* #2850 The setup scripts are now based on Ubuntu 18.04 LTS and Docker. @pashaxp, @arikfr
* #2985 Add Jest based tests to our stack. @arikfr
* #2999 Add netlify configuration. @arikfr
* #3000 Initial Cypress based E2E test infrastructure. @gabrieldutra
* #2898 Move Ant styles into a central location. @arikfr
* #2910 Fix webpack build error about BigMessage. @jezdez
* #2928 Speed up builds by skipping installing requirements_all_ds.txt in CI unit tests. @arikfr
* #2963 Fix tarball build failure. @emtwo
* #2996 Fix setup.sh failures when run as root. @arikfr
* #2989 Rearrange make targets. @koooge
* #3036 Update Flask-Admin to 1.5.2. @yoavbls
* #2901 Fix documentation links. @kravets-levko
* #3073 Remove only Redash containers in clean Make task. @ariarijp
* #3048 Remove pytest-watch dependency to workaround an issue with watchdog. @rauchy
* #2905 Update development docker-compose.yml file to use latest Redis and Postgres servers and specify working volume explictly. @Rovel
* #3032 Makefile: Add make targets for test. @koooge
* #2933 Switch to Webpack 4. @dmonego
* #2908 Update setup files. @arikfr
* #2946 Update snowflake_connector_python version. @arikfr
* #2773 Upgrade to Celery 4.2.1. @emtwo, @jezdez
* #2881 CircleCI: Make flake8 tests pass on Legacy Python and Python 3. @cclauss
* #2907 Remove unused dependencies (honcho, wsgiref). @arikfr
* #3039 Build docker image on master branch. @arikfr
* #3106 Fix registerAll failures after minification. @arikfr
- #2850 The setup scripts are now based on Ubuntu 18.04 LTS and Docker. @pashaxp, @arikfr
- #2985 Add Jest based tests to our stack. @arikfr
- #2999 Add netlify configuration. @arikfr
- #3000 Initial Cypress based E2E test infrastructure. @gabrieldutra
- #2898 Move Ant styles into a central location. @arikfr
- #2910 Fix webpack build error about BigMessage. @jezdez
- #2928 Speed up builds by skipping installing requirements_all_ds.txt in CI unit tests. @arikfr
- #2963 Fix tarball build failure. @emtwo
- #2996 Fix setup.sh failures when run as root. @arikfr
- #2989 Rearrange make targets. @koooge
- #3036 Update Flask-Admin to 1.5.2. @yoavbls
- #2901 Fix documentation links. @kravets-levko
- #3073 Remove only Redash containers in clean Make task. @ariarijp
- #3048 Remove pytest-watch dependency to workaround an issue with watchdog. @rauchy
- #2905 Update development docker-compose.yml file to use latest Redis and Postgres servers and specify working volume explictly. @Rovel
- #3032 Makefile: Add make targets for test. @koooge
- #2933 Switch to Webpack 4. @dmonego
- #2908 Update setup files. @arikfr
- #2946 Update snowflake_connector_python version. @arikfr
- #2773 Upgrade to Celery 4.2.1. @emtwo, @jezdez
- #2881 CircleCI: Make flake8 tests pass on Legacy Python and Python 3. @cclauss
- #2907 Remove unused dependencies (honcho, wsgiref). @arikfr
- #3039 Build docker image on master branch. @arikfr
- #3106 Fix registerAll failures after minification. @arikfr
## v5.0.2 - 2018-10-18
### Security
* Fix: prevent Open Redirect vulnerability.
- Fix: prevent Open Redirect vulnerability.
## v5.0.1 - 2018-09-27
### Added
* Added support for JWT authentication (for services like Cloudflare Access or Google IAP).
- Added support for JWT authentication (for services like Cloudflare Access or Google IAP).
### Changed
* Upgraded Celery version to 3.1.26 to make upgrade to Celery 4 easier.
- Upgraded Celery version to 3.1.26 to make upgrade to Celery 4 easier.
## v5.0.0 - 2018-09-21
Final release for V5. Most of the changes were already in the beta release of V5, but this includes several fixes along
with UI improvements.
🙏 Thanks to @arikfr, @jezdez, @kravets-levko, @alison985, @kocsmy, @yossi-a, @tdsmith, @nasmithan, @jrbenny35, @sjakthol, @ariarijp and @combineads who contributed to this release.
### Security
* Fix: don't expose Google OAuth client secret. @arikfr
- Fix: don't expose Google OAuth client secret. @arikfr
### Changed
* Improve mobile rendering of dashboards and queries. @kocsmy
* UI improvements for favorites and empty state. @arikfr
* Remove unnecessary X at the end of the query search. @kocsmy
* Add server-side sorting to dashboard list. @jezdez
* Sort queries in descending order. @jezdez
* Throw error when non-owner tries to add a user to dashboard permissions. @alison985
* Propagate query execution errors from Celery tasks properly. @alison985
* Reload the route when using the app header search input. @jezdez
- Improve mobile rendering of dashboards and queries. @kocsmy
- UI improvements for favorites and empty state. @arikfr
- Remove unnecessary X at the end of the query search. @kocsmy
- Add server-side sorting to dashboard list. @jezdez
- Sort queries in descending order. @jezdez
- Throw error when non-owner tries to add a user to dashboard permissions. @alison985
- Propagate query execution errors from Celery tasks properly. @alison985
- Reload the route when using the app header search input. @jezdez
### Fixed
* Fix: BigQuery default location is null and not US. @arikfr
* Fix: query embeds are broken. @arikfr
* Fix: typo in Celery log foramt. @ariarijp
* Use QuerySerializer in outdated queries list. @jezdez
* Fix: sometimes widgets are getting zero height. @kravets-levko
* Athena: Switch to simple_json to serialize NaN/Infinity values as nulls. @kravets-levko, @jezdez
* Fix: queries with parameters with no value breaking the scheduler. @arikfr
* Fix: MongoDB query results parser didn't support unicode keys. @arikfr
* Fix: Google Analytics schema wasn't loading in some cases. @arikfr
* Fix: date/time parameters not working as global param @kravets-levko.
* Fix: Widgets crumble when trying to move / resize a widget. @kravets-levko
* Fix: handling rows with "length" field with forOwn method. @yossi-a
* Fix: query selection not working on alert page. @sjakthol
* Fix: query_results for Embedded Parameters (removed deprecated to_dict function). @nasmithan
* Fix: unicode not supported in dashboard search. @combineads
* Fix: unicode not supported in users search. @arikfr
- Fix: BigQuery default location is null and not US. @arikfr
- Fix: query embeds are broken. @arikfr
- Fix: typo in Celery log foramt. @ariarijp
- Use QuerySerializer in outdated queries list. @jezdez
- Fix: sometimes widgets are getting zero height. @kravets-levko
- Athena: Switch to simple_json to serialize NaN/Infinity values as nulls. @kravets-levko, @jezdez
- Fix: queries with parameters with no value breaking the scheduler. @arikfr
- Fix: MongoDB query results parser didn't support unicode keys. @arikfr
- Fix: Google Analytics schema wasn't loading in some cases. @arikfr
- Fix: date/time parameters not working as global param @kravets-levko.
- Fix: Widgets crumble when trying to move / resize a widget. @kravets-levko
- Fix: handling rows with "length" field with forOwn method. @yossi-a
- Fix: query selection not working on alert page. @sjakthol
- Fix: query_results for Embedded Parameters (removed deprecated to_dict function). @nasmithan
- Fix: unicode not supported in dashboard search. @combineads
- Fix: unicode not supported in users search. @arikfr
### Other
* Add test for using saved parameters in scheduled queries. @alison985
* Minor code smell cleanup. @jezdez
* Update QueryResultListResource docstring. @tdsmith
* Switch to CirlceCI 2.0 @jrbenny35, @arikfr
* Remove unnecessary init methods. @jezdez
- Add test for using saved parameters in scheduled queries. @alison985
- Minor code smell cleanup. @jezdez
- Update QueryResultListResource docstring. @tdsmith
- Switch to CirlceCI 2.0 @jrbenny35, @arikfr
- Remove unnecessary init methods. @jezdez
## v5.0.0-Beta - 2018-08-06
@@ -284,19 +468,19 @@ This is the first beta of the V5 release (and hopefully the last one). This vers
Some notable changes:
* Extensive work on parameters UI:
* New Date Range parameter type.
* UI for creating new parameters.
* Support for Now/Today as default value of date/time parameter.
* Tagging and favorites ⭐️ support for queries and dashboards.
* Users list page was improved (search, additional information) and you can now disable users.
* Query editor improvements: additional keyboard shortcuts and support for searching in query text.
* Visualizations improvements: option to select colors of pie chart sectors, X Axis type auto detect and option to format values, labels and tooltips.
* Data Sources:
* Support for Yandex Metrika and AppMetrika.
* BigQuery: location property support and schema will load all tables now.
* Elasticsearch: stop sending source_content_type parameter which wasn't supported in older versions.
* Started migrating the frontend codebase to React.
- Extensive work on parameters UI:
- New Date Range parameter type.
- UI for creating new parameters.
- Support for Now/Today as default value of date/time parameter.
- Tagging and favorites ⭐️ support for queries and dashboards.
- Users list page was improved (search, additional information) and you can now disable users.
- Query editor improvements: additional keyboard shortcuts and support for searching in query text.
- Visualizations improvements: option to select colors of pie chart sectors, X Axis type auto detect and option to format values, labels and tooltips.
- Data Sources:
- Support for Yandex Metrika and AppMetrika.
- BigQuery: location property support and schema will load all tables now.
- Elasticsearch: stop sending source_content_type parameter which wasn't supported in older versions.
- Started migrating the frontend codebase to React.
And much more!
@@ -304,82 +488,82 @@ And much more!
### Added
* #2712: Date/Time Range parameter type (@kravets-levko)
* #2482: Add support for ChatWork Alert Destination. (@matsumo)
* #2678: Explicit "Add Parameter" Button in Query Editor. (@kravets-levko)
* #2513: Add location property to BigQuery data source settings. (@kyoshidajp)
* #2616: Pie chart: support setting pie chart sector colors. (@kravets-levko)
* #2697: Date/Time parameters: support for "Now" as default value. (@kravets-levko)
* #2693: Enable search function in Query Editor. (@arikfr)
* #2573: Tagging and favorites for Queries and Dashboards (@arikfr)
* #2640: Keyboard shortcut to collapse query editor/schema browser (@kravets-levko)
* #2674: Add support for the Chrome Logger extension (@arikfr)
* #2653: Add redash db size to status page (@alison985)
* #2669: Store Athena query id with result metadata (@tdawber)
* #2546: Configuration for incorporating React components (@washort)
* #2533: New datasource: Yandex Metrika & AppMetrika (@denisov-vlad)
* #2536: Chart: formats for values, labels and tooltips (@kravets-levko)
* #2560: Introduce Policy object (@arikfr)
* #2380: Admin should be able to disable a user (@kravets-levko)
* #2509: Show custom date format on settings page (@kyoshidajp)
- #2712: Date/Time Range parameter type (@kravets-levko)
- #2482: Add support for ChatWork Alert Destination. (@matsumo)
- #2678: Explicit "Add Parameter" Button in Query Editor. (@kravets-levko)
- #2513: Add location property to BigQuery data source settings. (@kyoshidajp)
- #2616: Pie chart: support setting pie chart sector colors. (@kravets-levko)
- #2697: Date/Time parameters: support for "Now" as default value. (@kravets-levko)
- #2693: Enable search function in Query Editor. (@arikfr)
- #2573: Tagging and favorites for Queries and Dashboards (@arikfr)
- #2640: Keyboard shortcut to collapse query editor/schema browser (@kravets-levko)
- #2674: Add support for the Chrome Logger extension (@arikfr)
- #2653: Add redash db size to status page (@alison985)
- #2669: Store Athena query id with result metadata (@tdawber)
- #2546: Configuration for incorporating React components (@washort)
- #2533: New datasource: Yandex Metrika & AppMetrika (@denisov-vlad)
- #2536: Chart: formats for values, labels and tooltips (@kravets-levko)
- #2560: Introduce Policy object (@arikfr)
- #2380: Admin should be able to disable a user (@kravets-levko)
- #2509: Show custom date format on settings page (@kyoshidajp)
### Changed
* #2715: Improve users list page (@arikfr)
* #2710: Update Ant variables to fit Redash's style (@kocsmy)
* #2709: Move format button next Add New Param button. (@arikfr)
* #2664: Dashboard shows a spinner when query failed to load (@kravets-levko)
* #2626: Show real status when loading cached query result (@kravets-levko)
* #2663: Set column name implicitly when column name is blank (@ariarijp)
* #2695: Improve Date/DateTime type parameters (@kravets-levko)
* #2694: Block users with disposable email addresses (@arikfr)
* #2687: YAML: changed load to safe_load (@denisov-vlad)
* #2514: Update value parsing for google spreadsheets source (@atharvai)
* #2570: fixes query pagination alignment (@alison985)
* #2584: keep query result pagination out of scroll (@alison985)
* #2647: Improve Script Query Runner (@ariarijp)
* #2583: Query header improvements on widgets (@kocsmy)
* #2671: Save some space (@kocsmy)
* #2658: delaying schema filtering to improve responsiveness (@alison985)
* #2648: Update datasource documentation links (@Pablohn26)
* #2613: Improve Script Query Runner (@ariarijp)
* #2619: data source sort case insensitive (@alison985)
* #2604: Improve Google Spreadsheets Query Runner (@ariarijp)
* #2542: Closes #2541: x-axis improvements. (@emtwo)
* #2590: Remove redundant variables (@ariarijp)
* #2585: Show data only mode: allow to add and delete visualizations (@kravets-levko)
* #2549: Allow get_tables to see views and v10-style partitioned tables (@coreyhuinker)
* #2568: sort datasources alphabetically (@alison985)
* #2444: feat: show error if saml response cannot be parsed (@sjakthol)
* #2554: Display name to be delete (@kyoshidajp)
* #2510: Display confirmation dialog when deleting a item (@kyoshidajp)
* #2518: Design improvements (@kocsmy)
* #2520: Filter data sources in a data source input area (@kyoshidajp)
- #2715: Improve users list page (@arikfr)
- #2710: Update Ant variables to fit Redash's style (@kocsmy)
- #2709: Move format button next Add New Param button. (@arikfr)
- #2664: Dashboard shows a spinner when query failed to load (@kravets-levko)
- #2626: Show real status when loading cached query result (@kravets-levko)
- #2663: Set column name implicitly when column name is blank (@ariarijp)
- #2695: Improve Date/DateTime type parameters (@kravets-levko)
- #2694: Block users with disposable email addresses (@arikfr)
- #2687: YAML: changed load to safe_load (@denisov-vlad)
- #2514: Update value parsing for google spreadsheets source (@atharvai)
- #2570: fixes query pagination alignment (@alison985)
- #2584: keep query result pagination out of scroll (@alison985)
- #2647: Improve Script Query Runner (@ariarijp)
- #2583: Query header improvements on widgets (@kocsmy)
- #2671: Save some space (@kocsmy)
- #2658: delaying schema filtering to improve responsiveness (@alison985)
- #2648: Update datasource documentation links (@Pablohn26)
- #2613: Improve Script Query Runner (@ariarijp)
- #2619: data source sort case insensitive (@alison985)
- #2604: Improve Google Spreadsheets Query Runner (@ariarijp)
- #2542: Closes #2541: x-axis improvements. (@emtwo)
- #2590: Remove redundant variables (@ariarijp)
- #2585: Show data only mode: allow to add and delete visualizations (@kravets-levko)
- #2549: Allow get_tables to see views and v10-style partitioned tables (@coreyhuinker)
- #2568: sort datasources alphabetically (@alison985)
- #2444: feat: show error if saml response cannot be parsed (@sjakthol)
- #2554: Display name to be delete (@kyoshidajp)
- #2510: Display confirmation dialog when deleting a item (@kyoshidajp)
- #2518: Design improvements (@kocsmy)
- #2520: Filter data sources in a data source input area (@kyoshidajp)
### Fixed
* #2722: Elasticsearch: Don't send source_content_type parameter. (@arikfr)
* #2719: Remove closing input tags (@maxv)
* #2458: Get all tables in the BigQuery (@kyoshidajp)
* #2698: Make sure we return distinct data source values (@arikfr)
* #2315: Fix: pyHive type matches (@yuua)
* #2638: Dashboard stops rendering when adding widget with empty query (@kravets-levko)
* #2610: Fix export query results output file name (@gabrieldutra)
* #2574: commit query result to db before evaluating alerts (@mtrbean)
* #2580: add break-word wrap to add/edit text box on dashboard (@alison985)
* #2578: Fix connection error when you run "create_tables" (@ariarijp)
* #2572: remove extra menu line if query is archived (@alison985)
* #2526: Fix pivot hide control in dashboards (@deecay)
* #2511: Fixing signed_out.html template (@kocsmy)
* #2523: Frontend: fix boolean field with null value display as null. (@innovia)
- #2722: Elasticsearch: Don't send source_content_type parameter. (@arikfr)
- #2719: Remove closing input tags (@maxv)
- #2458: Get all tables in the BigQuery (@kyoshidajp)
- #2698: Make sure we return distinct data source values (@arikfr)
- #2315: Fix: pyHive type matches (@yuua)
- #2638: Dashboard stops rendering when adding widget with empty query (@kravets-levko)
- #2610: Fix export query results output file name (@gabrieldutra)
- #2574: commit query result to db before evaluating alerts (@mtrbean)
- #2580: add break-word wrap to add/edit text box on dashboard (@alison985)
- #2578: Fix connection error when you run "create_tables" (@ariarijp)
- #2572: remove extra menu line if query is archived (@alison985)
- #2526: Fix pivot hide control in dashboards (@deecay)
- #2511: Fixing signed_out.html template (@kocsmy)
- #2523: Frontend: fix boolean field with null value display as null. (@innovia)
### Other
* #2682: Add Zeit's now support to have preview builds for every PR (@arikfr)
* #2668: Upgrade bootstrap script to Redash 4.0.1 (@ariarijp)
* #2639: Add tests for SpreadSheets (@ariarijp)
* #2635: Add tests for Query Results (@ariarijp)
* #2537: Remove trailing semicolon (@sieben)
- #2682: Add Zeit's now support to have preview builds for every PR (@arikfr)
- #2668: Upgrade bootstrap script to Redash 4.0.1 (@ariarijp)
- #2639: Add tests for SpreadSheets (@ariarijp)
- #2635: Add tests for Query Results (@ariarijp)
- #2537: Remove trailing semicolon (@sieben)
## v4.0.1 - 2018-05-02
@@ -571,7 +755,6 @@ And much more!
- Handling whitespace characters in Query Results data source. @ariarijp
- [MySQL] Close cursor when cancellig the query. @jasonsmithj
## v3.0.0 - 2017-11-13
### Added
@@ -617,7 +800,7 @@ And much more!
- Salesforce: improve error messages we receive from the API. @akiray03
- Custom JS code visualization improvements. @deecay
- DQL: Update version to 0.5.24. @aterreno
- Cassandra: get_schema support for both C* 2.x and 3.x, support for SortedSet type serialization. (@mfouilleul))
- Cassandra: get_schema support for both C\* 2.x and 3.x, support for SortedSet type serialization. (@mfouilleul))
- Replace deprecated ng-annotate with babel plugin. @44px
- Update Python dependencies to recent versions. @alison985
- Bootstrap script: create /opt/redash directory only if it doesn't exist. @isomura
@@ -653,7 +836,6 @@ And much more!
This is a patch release, that adds support for Redshift ACM certificates (see #2044 for details).
## v2.0.0 - 2017-08-08
### Added
@@ -663,7 +845,7 @@ This is a patch release, that adds support for Redshift ACM certificates (see #2
- Add the propertyOrder field to specify order of data source settings. @rmakulov
- Add Plotly based Boxplot visualization. @deecay
- [Presto] Add: query cancellation support. @fbertsch
- [MongoDB] add $oids JSON extension.
- [MongoDB] add \$oids JSON extension.
- [PostgreSQL] support for loading materialized views in schema.
- [MySQL] Add option to hide SSL settings.
- [MySQL] support for RDS MySQL and SSL.
@@ -733,7 +915,7 @@ This is a patch release, that adds support for Redshift ACM certificates (see #2
- [Google Spreadsheets] handle distant future dates.
- [SQLite] better handle utf-8 error messages.
- Fix: don't remove locks for queries with task status of PENDING.
- Only split columns with __/:: that end with filter/MultiFilter.
- Only split columns with \_\_/:: that end with filter/MultiFilter.
- Alert notifications fail (sometime) with a SQLAlchemy error.
- Safeguard against empty query results when checking alert status. @danielerapati
- Delete data source doesn't work when query results referenced by queries.
@@ -752,7 +934,6 @@ This is a patch release, that adds support for Redshift ACM certificates (see #2
- PostgreSQL passwords with spaces were not supported. (#1056)
- PivotTable wasn't updating after first save.
## v1.0.3 - 2017-04-18
### Fixed
@@ -799,7 +980,7 @@ This is a patch release, that adds support for Redshift ACM certificates (see #2
- Fix: query embed dialog close button wasn't working @r0fls
- Fix: make errors from Presto runner JSON-serializable @washort
- Fix: race condition in query task status reporting @washort
- Fix: remove $$hashKey from Pivot table
- Fix: remove \$\$hashKey from Pivot table
- Fix: map visualization had severe performance issue.
- Fix: pemrission dialog wasn't rendering.
- Fix: word cloud visualization didn't show column names.
@@ -816,7 +997,7 @@ This is a patch release, that adds support for Redshift ACM certificates (see #2
### Changed
- [#1563](https://github.com/getredash/redash/pull/1563) Send events to webhook as JSON with a schema.
- [#1601] [Presto] friendlier error messages. (@aslotnick)
- [#1601][presto] friendlier error messages. (@aslotnick)
- Move the query runner unavailable log message to be DEBUG level instead of WARNING, as it was mainly confusing people.
- Remove "Send to Cloud" button from Plotly based visualizations.
- Change Plotly's default hover mode to "Compare".
@@ -825,7 +1006,7 @@ This is a patch release, that adds support for Redshift ACM certificates (see #2
### Fixed
- [#1564] Fix: map visualization column picker wasn't populated. (@janusd)
- [#1597] [SQL Server] Fix: schema wasn't loading on case sensitive servers. (@deecay)
- [#1597][sql server] Fix: schema wasn't loading on case sensitive servers. (@deecay)
- Fix: dashbonard owner couldn't edit his dashboard.
- Fix: toggle_publish event wasn't logged properly.
- Fix: events with API keys were not logged.
@@ -840,7 +1021,7 @@ This is a patch release, that adds support for Redshift ACM certificates (see #2
- Fix: extra whitespace created by the filters component.
- Fix: query results cleanup task was trying to delete query objects.
- Fix: alert subscriptions were not triggered.
- [DynamoDB] Fix: count(*) queries were broken. (@kopanitsa))
- [DynamoDB] Fix: count(\*) queries were broken. (@kopanitsa))
- Fix: Redash is using too many database connections.
- Fix: download links were not working in dashboards.
- Fix: the first selection in multi filters was broken in dashboards.
@@ -857,9 +1038,9 @@ This is a patch release, that adds support for Redshift ACM certificates (see #2
This version has two big changes behind the scenes:
* Refactor the frontend to use latest (at the time) Angular version (1.5) along with better frontend pipeline based on)
- Refactor the frontend to use latest (at the time) Angular version (1.5) along with better frontend pipeline based on)
WebPack.
* Refactor the backend code to use SQLAlchemy and Alembic, for easier migrations/upgrades.)
- Refactor the backend code to use SQLAlchemy and Alembic, for easier migrations/upgrades.)
Along with that we have many fixes, additions, new data sources (Google Analytics, ClickHouse, Amazon Athena, Snowflake)
and fixes to the existing ones (mainly ElasticSearch and Cassandra).
@@ -942,7 +1123,7 @@ We're releasing a new upgrade script -- see [here](https://redash.io/help-onprem
### Added
- 61fe16e #1374: Add: allow '*' in REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN (Allen Short)
- 61fe16e #1374: Add: allow '\*' in REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN (Allen Short)
- 2f09043 #1113: Add: share modify/access permissions for queries and dashboard (whummer)
- 3db0eea #1341: Add: support for specifying SAML nameid-format (zoetrope)
- b0ecd0e #1343: Add: support for local SAML metadata file (zoetrope)
@@ -1006,7 +1187,6 @@ We're releasing a new upgrade script -- see [here](https://redash.io/help-onprem
- 5d43cbe #1198: Change: add support for Standard SQL in BigQuery query runner (mystelynx)
- 84d0c22 #1193: Change: modify the argument order of moment.add function call (Kenya Yamaguchi)
### Fixed
- d6febb0 #1375: Fix: Download Dataset does not work when not logged in (Joshua Dechant)
@@ -1121,7 +1301,7 @@ A big thank you goes to all who contributed code and documentation in this relea
- e10ecd2 #1058: Bring back filters if dashboard filters are enabled (@AntoineAugusti)
- 701035f #1059: Fix: DynamoDB having issues when setting host (@arikfr)
- 2924d4f #1040: Small fixes to visualizations view (@arikfr)
- fec0d5f #1037: Fix: multi filter wasn't working with __ syntax (@dheerajrav)
- fec0d5f #1037: Fix: multi filter wasn't working with \_\_ syntax (@dheerajrav)
- b066ce4 #1033: Fix: only ask for notification permissions if wasn't denied (@arikfr)
- 960c416 #1032: Fix: make sure we return dashboards only for current org only (@arikfr)
- b3844d3 #1029: Hive: close connection only if it exists (@arikfr)

View File

@@ -6,7 +6,6 @@ The following is a set of guidelines for contributing to Redash. These are guide
## Quick Links:
- [Feature Roadmap](https://trello.com/b/b2LUHU7A/redash-roadmap)
- [Feature Requests](https://discuss.redash.io/c/feature-requests)
- [Documentation](https://redash.io/help/)
- [Blog](https://blog.redash.io/)
@@ -61,7 +60,7 @@ If you would like to suggest an enhancement or ask for a new feature:
### Documentation
The project's documentation can be found at [https://redash.io/help/](https://redash.io/help/). The [documentation sources](https://github.com/getredash/website/tree/master/website/_kb) are hosted on GitHub. To contribute edits / new pages, you can use GitHub's interface. Click the "Edit on GitHub" link on the documentation page to quickly open the edit interface.
The project's documentation can be found at [https://redash.io/help/](https://redash.io/help/). The [documentation sources](https://github.com/getredash/website/tree/master/src/pages/kb) are hosted on GitHub. To contribute edits / new pages, you can use GitHub's interface. Click the "Edit on GitHub" link on the documentation page to quickly open the edit interface.
## Additional Notes

View File

@@ -1,16 +1,26 @@
FROM redash/base:latest
FROM node:10 as frontend-builder
WORKDIR /frontend
COPY package.json package-lock.json /frontend/
RUN npm install
COPY client /frontend/client
COPY webpack.config.js /frontend/
RUN npm run build
FROM redash/base:debian
# Controls whether to install extra dependencies needed for all data sources.
ARG skip_ds_deps
# We first copy only the requirements file, to avoid rebuilding on every file
# change.
COPY requirements.txt requirements_dev.txt requirements_all_ds.txt ./
COPY requirements.txt requirements_bundles.txt requirements_dev.txt requirements_all_ds.txt ./
RUN pip install -r requirements.txt -r requirements_dev.txt
RUN if [ "x$skip_ds_deps" = "x" ] ; then pip install -r requirements_all_ds.txt ; else echo "Skipping pip install -r requirements_all_ds.txt" ; fi
COPY . ./
RUN npm install && npm run bundle && npm run build && rm -rf node_modules
COPY . /app
COPY --from=frontend-builder /frontend/client/dist /app/client/dist
RUN chown -R redash /app
USER redash

View File

@@ -1,4 +1,4 @@
Copyright (c) 2013-2018, Arik Fraimovich.
Copyright (c) 2013-2019, Arik Fraimovich.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,

View File

@@ -1,4 +1,4 @@
.PHONY: compose_build up test_db create_database clean down bundle tests lint backend-unit-tests frontend-unit-tests test build watch start
.PHONY: compose_build up test_db create_database clean down bundle tests lint backend-unit-tests frontend-unit-tests test build watch start redis-cli bash
compose_build:
docker-compose build
@@ -49,3 +49,9 @@ watch: bundle
start: bundle
npm run start
redis-cli:
docker-compose run --rm redis redis-cli -h redis
bash:
docker-compose run --rm server bash

View File

@@ -1,11 +1,10 @@
<p align="center">
<img title="Redash" src='https://redash.io/assets/images/logo.png' width="200px"/>
</p>
<p align="center">
<img title="Build Status" src='https://circleci.com/gh/getredash/redash.png?circle-token=8a695aa5ec2cbfa89b48c275aea298318016f040'/>
</p>
[![Documentation](https://img.shields.io/badge/docs-redash.io/help-brightgreen.svg)](https://redash.io/help/)
[![Datree](https://s3.amazonaws.com/catalog.static.datree.io/datree-badge-20px.svg)](https://datree.io/?src=badge)
[![Build Status](https://circleci.com/gh/getredash/redash.png?style=shield&circle-token=8a695aa5ec2cbfa89b48c275aea298318016f040)](https://circleci.com/gh/getredash/redash/tree/master)
**_Redash_** is our take on freeing the data within our company in a way that will better fit our culture and usage patterns.

5
SECURITY.md Normal file
View File

@@ -0,0 +1,5 @@
# Security Policy
## Reporting a Vulnerability
Please email security@redash.io to report any security vulnerabilities. We will acknowledge receipt of your vulnerability and strive to send you regular updates about our progress. If you're curious about the status of your disclosure please feel free to email us again. If you want to encrypt your disclosure email, you can use [this PGP key](https://keybase.io/arikfr/key.asc).

View File

@@ -1,39 +1,118 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Copy bundle extension files to the client/app/extension directory"""
import logging
import os
from subprocess import call
from distutils.dir_util import copy_tree
from pathlib2 import Path
from shutil import copy
from collections import OrderedDict as odict
from pkg_resources import iter_entry_points, resource_filename, resource_isdir
from importlib_metadata import entry_points
from importlib_resources import contents, is_resource, path
# Name of the subdirectory
BUNDLE_DIRECTORY = "bundle"
logger = logging.getLogger(__name__)
# Make a directory for extensions and set it as an environment variable
# to be picked up by webpack.
EXTENSIONS_RELATIVE_PATH = os.path.join('client', 'app', 'extensions')
EXTENSIONS_DIRECTORY = os.path.join(
os.path.dirname(os.path.dirname(__file__)),
EXTENSIONS_RELATIVE_PATH)
extensions_relative_path = Path('client', 'app', 'extensions')
extensions_directory = Path(__file__).parent.parent / extensions_relative_path
if not os.path.exists(EXTENSIONS_DIRECTORY):
os.makedirs(EXTENSIONS_DIRECTORY)
os.environ["EXTENSIONS_DIRECTORY"] = EXTENSIONS_RELATIVE_PATH
if not extensions_directory.exists():
extensions_directory.mkdir()
os.environ["EXTENSIONS_DIRECTORY"] = str(extensions_relative_path)
for entry_point in iter_entry_points('redash.extensions'):
# This is where the frontend code for an extension lives
# inside of its package.
content_folder_relative = os.path.join(
entry_point.name, 'bundle')
(root_module, _) = os.path.splitext(entry_point.module_name)
if not resource_isdir(root_module, content_folder_relative):
def resource_isdir(module, resource):
"""Whether a given resource is a directory in the given module
https://importlib-resources.readthedocs.io/en/latest/migration.html#pkg-resources-resource-isdir
"""
try:
return resource in contents(module) and not is_resource(module, resource)
except (ImportError, TypeError):
# module isn't a package, so can't have a subdirectory/-package
return False
def entry_point_module(entry_point):
"""Returns the dotted module path for the given entry point"""
return entry_point.pattern.match(entry_point.value).group("module")
def load_bundles():
""""Load bundles as defined in Redash extensions.
The bundle entry point can be defined as a dotted path to a module
or a callable, but it won't be called but just used as a means
to find the files under its file system path.
The name of the directory it looks for files in is "bundle".
So a Python package with an extension bundle could look like this::
my_extensions/
├── __init__.py
└── wide_footer
├── __init__.py
└── bundle
├── extension.js
└── styles.css
and would then need to register the bundle with an entry point
under the "redash.bundles" group, e.g. in your setup.py::
setup(
# ...
entry_points={
"redash.bundles": [
"wide_footer = my_extensions.wide_footer",
]
# ...
},
# ...
)
"""
bundles = odict()
for entry_point in entry_points().get("redash.bundles", []):
logger.info('Loading Redash bundle "%s".', entry_point.name)
module = entry_point_module(entry_point)
# Try to get a list of bundle files
if not resource_isdir(module, BUNDLE_DIRECTORY):
logger.error(
'Redash bundle directory "%s" could not be found.', entry_point.name
)
continue
with path(module, BUNDLE_DIRECTORY) as bundle_dir:
bundles[entry_point.name] = list(bundle_dir.rglob("*"))
return bundles
bundles = load_bundles().items()
if bundles:
print('Number of extension bundles found: {}'.format(len(bundles)))
else:
print('No extension bundles found.')
for bundle_name, paths in bundles:
# Shortcut in case not paths were found for the bundle
if not paths:
print('No paths found for bundle "{}".'.format(bundle_name))
continue
content_folder = resource_filename(root_module, content_folder_relative)
# The destination for the bundle files with the entry point name as the subdirectory
destination = Path(extensions_directory, bundle_name)
if not destination.exists():
destination.mkdir()
# This is where we place our extensions folder.
destination = os.path.join(
EXTENSIONS_DIRECTORY,
entry_point.name)
copy_tree(content_folder, destination)
# Copy the bundle directory from the module to its destination.
print('Copying "{}" bundle to {}:'.format(bundle_name, destination.resolve()))
for src_path in paths:
dest_path = destination / src_path.name
print(" - {} -> {}".format(src_path, dest_path))
copy(str(src_path), str(dest_path))

View File

@@ -3,29 +3,48 @@ set -e
worker() {
WORKERS_COUNT=${WORKERS_COUNT:-2}
QUEUES=${QUEUES:-queries,scheduled_queries,celery}
QUEUES=${QUEUES:-queries,scheduled_queries,celery,schemas}
WORKER_EXTRA_OPTIONS=${WORKER_EXTRA_OPTIONS:-}
echo "Starting $WORKERS_COUNT workers for queues: $QUEUES..."
exec /usr/local/bin/celery worker --app=redash.worker -c$WORKERS_COUNT -Q$QUEUES -linfo --maxtasksperchild=10 -Ofair
exec /usr/local/bin/celery worker --app=redash.worker -c$WORKERS_COUNT -Q$QUEUES -linfo --max-tasks-per-child=10 -Ofair $WORKER_EXTRA_OPTIONS
}
scheduler() {
WORKERS_COUNT=${WORKERS_COUNT:-1}
QUEUES=${QUEUES:-celery}
SCHEDULE_DB=${SCHEDULE_DB:-celerybeat-schedule}
echo "Starting scheduler and $WORKERS_COUNT workers for queues: $QUEUES..."
exec /usr/local/bin/celery worker --app=redash.worker --beat -c$WORKERS_COUNT -Q$QUEUES -linfo --maxtasksperchild=10 -Ofair
exec /usr/local/bin/celery worker --app=redash.worker --beat -s$SCHEDULE_DB -c$WORKERS_COUNT -Q$QUEUES -linfo --max-tasks-per-child=10 -Ofair
}
dev_worker() {
WORKERS_COUNT=${WORKERS_COUNT:-2}
QUEUES=${QUEUES:-queries,scheduled_queries,celery,schemas}
SCHEDULE_DB=${SCHEDULE_DB:-celerybeat-schedule}
echo "Starting dev scheduler and $WORKERS_COUNT workers for queues: $QUEUES..."
exec watchmedo auto-restart --directory=./redash/ --pattern=*.py --recursive -- /usr/local/bin/celery worker --app=redash.worker --beat -s$SCHEDULE_DB -c$WORKERS_COUNT -Q$QUEUES -linfo --max-tasks-per-child=10 -Ofair
}
server() {
exec /usr/local/bin/gunicorn -b 0.0.0.0:5000 --name redash -w${REDASH_WEB_WORKERS:-4} redash.wsgi:app
# Recycle gunicorn workers every n-th request. See http://docs.gunicorn.org/en/stable/settings.html#max-requests for more details.
MAX_REQUESTS=${MAX_REQUESTS:-1000}
MAX_REQUESTS_JITTER=${MAX_REQUESTS_JITTER:-100}
exec /usr/local/bin/gunicorn -b 0.0.0.0:5000 --name redash -w${REDASH_WEB_WORKERS:-4} redash.wsgi:app --max-requests $MAX_REQUESTS --max-requests-jitter $MAX_REQUESTS_JITTER
}
create_db() {
exec /app/manage.py database create_tables
}
celery_healthcheck() {
exec /usr/local/bin/celery inspect ping --app=redash.worker -d celery@$HOSTNAME
}
help() {
echo "Redash Docker."
echo ""
@@ -35,9 +54,12 @@ help() {
echo "server -- start Redash server (with gunicorn)"
echo "worker -- start Celery worker"
echo "scheduler -- start Celery worker with a beat (scheduler) process"
echo "dev_worker -- start Celery worker with a beat (scheduler) process which picks up code changes and reloads"
echo "celery_healthcheck -- runs a Celery healthcheck. Useful for Docker's HEALTHCHECK mechanism."
echo ""
echo "shell -- open shell"
echo "dev_server -- start Flask development server with debugger and auto reload"
echo "debug -- start Flask development server with remote debugger via ptvsd"
echo "create_db -- create database tables"
echo "manage -- CLI to manage redash"
echo "tests -- run tests"
@@ -67,10 +89,19 @@ case "$1" in
shift
scheduler
;;
dev_worker)
shift
dev_worker
;;
dev_server)
export FLASK_DEBUG=1
exec /app/manage.py runserver --debugger --reload -h 0.0.0.0
;;
debug)
export FLASK_DEBUG=1
export REMOTE_DEBUG=1
exec /app/manage.py runserver --debugger --no-reload -h 0.0.0.0
;;
shell)
exec /app/manage.py shell
;;

View File

@@ -1,7 +1,9 @@
#!/bin/sh
set -o errexit # fail the build if any task fails
flake8 --version ; pip --version
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

View File

@@ -1,8 +0,0 @@
#!/bin/bash
NAME=redash
VERSION=$(python ./manage.py version)
FULL_VERSION=$VERSION+b$CIRCLE_BUILD_NUM
FILENAME=$NAME.$FULL_VERSION.tar.gz
sed -ri "s/^__version__ = '([A-Za-z0-9.-]*)'/__version__ = '$FULL_VERSION'/" redash/__init__.py
tar -zcv -f $FILENAME --exclude="optipng*" --exclude=".git*" --exclude="*.pyc" --exclude="*.pyo" --exclude="venv" --exclude="node_modules" *

View File

@@ -1,8 +1,18 @@
{
"presets": ["env", "react", "stage-2"],
"presets": [
["@babel/preset-env", {
"exclude": [
"@babel/plugin-transform-async-to-generator",
"@babel/plugin-transform-arrow-functions"
],
"useBuiltIns": "usage"
}],
"@babel/preset-react"
],
"plugins": [
"angularjs-annotate",
"transform-object-assign",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-object-assign",
["babel-plugin-transform-builtin-extend", {
"globals": ["Error"]
}]

View File

@@ -1,3 +1,3 @@
build/*.js
config/*.js
node_modules
client/dist

View File

@@ -1,23 +1,22 @@
module.exports = {
root: true,
extends: ["airbnb", "plugin:jest/recommended"],
plugins: ["jest", "cypress"],
extends: ["airbnb", "plugin:compat/recommended"],
plugins: ["jest", "compat", "no-only-tests"],
settings: {
"import/resolver": "webpack"
},
parser: "babel-eslint",
env: {
"jest/globals": true,
"cypress/globals": true,
"browser": true,
"node": true
browser: true,
node: true
},
rules: {
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'no-param-reassign': 0,
'no-mixed-operators': 0,
'no-underscore-dangle': 0,
"no-debugger": process.env.NODE_ENV === "production" ? 2 : 0,
"no-param-reassign": 0,
"no-mixed-operators": 0,
"no-underscore-dangle": 0,
"no-use-before-define": ["error", "nofunc"],
"prefer-destructuring": "off",
"prefer-template": "off",
"no-restricted-properties": "off",
@@ -26,19 +25,42 @@ module.exports = {
"no-lonely-if": "off",
"consistent-return": "off",
"no-control-regex": "off",
"no-multiple-empty-lines": "warn",
"no-only-tests/no-only-tests": "error",
"operator-linebreak": "off",
"react/destructuring-assignment": "off",
"react/jsx-filename-extension": "off",
"react/jsx-one-expression-per-line": "off",
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error",
"react/jsx-wrap-multilines": "warn",
"react/no-access-state-in-setstate": "warn",
"react/prefer-stateless-function": "warn",
"react/forbid-prop-types": "warn",
"react/prop-types": "warn",
"jsx-a11y/anchor-is-valid": "off",
"max-len": ['error', 120, 2, {
"jsx-a11y/click-events-have-key-events": "off",
"jsx-a11y/label-has-associated-control": [
"warn",
{
controlComponents: true
}
],
"jsx-a11y/label-has-for": "off",
"jsx-a11y/no-static-element-interactions": "off",
"max-len": [
"error",
120,
2,
{
ignoreUrls: true,
ignoreComments: false,
ignoreRegExpLiterals: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
}]
ignoreTemplateLiterals: true
}
],
"no-else-return": ["error", { allowElseIf: true }],
"object-curly-newline": ["error", { consistent: true }]
}
};

10
client/app/.eslintrc.js Normal file
View File

@@ -0,0 +1,10 @@
module.exports = {
extends: ["plugin:jest/recommended"],
plugins: ["jest"],
env: {
"jest/globals": true,
},
rules: {
"jest/no-focused-tests": "off",
},
};

View File

@@ -0,0 +1,4 @@
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });

View File

@@ -0,0 +1,5 @@
import MockDate from 'mockdate';
const date = new Date('2000-01-01T02:00:00.000');
MockDate.set(date);

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,365 @@
@import '~antd/lib/style/core/iconfont';
@import '~antd/lib/style/core/motion';
@import '~antd/lib/alert/style/index';
@import '~antd/lib/input/style/index';
@import '~antd/lib/input-number/style/index';
@import '~antd/lib/date-picker/style/index';
@import '~antd/lib/modal/style/index';
@import '~antd/lib/tooltip/style/index';
@import '~antd/lib/select/style/index';
@import '~antd/lib/checkbox/style/index';
@import '~antd/lib/upload/style/index';
@import '~antd/lib/form/style/index';
@import '~antd/lib/button/style/index';
@import '~antd/lib/radio/style/index';
@import '~antd/lib/time-picker/style/index';
@import '~antd/lib/pagination/style/index';
@import '~antd/lib/table/style/index';
@import '~antd/lib/popover/style/index';
@import '~antd/lib/icon/style/index';
@import '~antd/lib/tag/style/index';
@import '~antd/lib/grid/style/index';
@import '~antd/lib/switch/style/index';
@import '~antd/lib/empty/style/index';
@import '~antd/lib/drawer/style/index';
@import '~antd/lib/card/style/index';
@import '~antd/lib/steps/style/index';
@import '~antd/lib/divider/style/index';
@import '~antd/lib/dropdown/style/index';
@import '~antd/lib/menu/style/index';
@import '~antd/lib/list/style/index';
@import "~antd/lib/badge/style/index";
@import "~antd/lib/card/style/index";
@import "~antd/lib/spin/style/index";
@import "~antd/lib/tabs/style/index";
@import "~antd/lib/notification/style/index";
@import "~antd/lib/collapse/style/index";
@import "~antd/lib/progress/style/index";
@import "~antd/lib/typography/style/index";
@import 'inc/ant-variables';
// Increase z-indexes to avoid conflicts with some other libraries (e.g. Plotly)
@zindex-modal: 2000;
@zindex-modal-mask: 2000;
@zindex-message: 2010;
@zindex-notification: 2010;
@zindex-popover: 2030;
@zindex-dropdown: 2050;
@zindex-picker: 2050;
@zindex-tooltip: 2060;
.@{drawer-prefix-cls} {
&.help-drawer {
z-index: @zindex-tooltip; // help drawer should be topmost
}
}
// Remove bold in labels for Ant checkboxes and radio buttons
.ant-checkbox-wrapper,
.ant-radio-wrapper {
font-weight: normal;
}
// Fix for disabled button styles inside Tooltip component.
// Tooltip wraps disabled buttons with `<span>` and moves all styles
// and classes to that `<span>`. This resets all button styles and
// turns it into simple inline element (because now it's wrapper is a button)
.btn {
button[disabled] {
-moz-appearance: none !important;
-webkit-appearance: none !important;
appearance: none !important;
border: 0 !important;
outline: none !important;
background: transparent !important;
margin: 0 !important;
padding: 0 !important;
}
}
// Button overrides
.@{btn-prefix-cls} {
transition-duration: 150ms;
}
// Fix ant input number showing duplicate arrows
.ant-input-number-input::-webkit-outer-spin-button,
.ant-input-number-input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
// Pagination overrides (based on existing Bootstrap overrides)
.@{pagination-prefix-cls} {
display: inline-block;
margin-top: 18px;
margin-bottom: 18px;
vertical-align: top;
&-item {
background-color: @pagination-bg;
border-color: transparent;
color: @pagination-color;
font-size: 14px;
margin-right: 5px;
a {
color: inherit;
}
&:focus,
&:hover {
background-color: @pagination-hover-bg;
border-color: transparent;
color: @pagination-hover-color;
a {
color: inherit;
}
}
&-active {
&,
&:hover,
&:focus {
background-color: @pagination-active-bg;
color: @pagination-active-color;
border-color: transparent;
pointer-events: none;
cursor: default;
a {
color: inherit;
}
}
}
}
&-disabled {
&,
&:hover,
&:focus {
opacity: 0.5;
pointer-events: none;
}
}
&-prev,
&-next {
.@{pagination-prefix-cls}-item-link {
background-color: @pagination-bg;
border-color: transparent;
color: @pagination-color;
line-height: @pagination-item-size - 2px;
.@{pagination-prefix-cls}.mini & {
line-height: @pagination-item-size-sm - 2px;
}
}
&:focus .@{pagination-prefix-cls}-item-link,
&:hover .@{pagination-prefix-cls}-item-link {
background-color: @pagination-hover-bg;
border-color: transparent;
color: @pagination-hover-color;
}
}
&-prev,
&-jump-prev,
&-jump-next {
margin-right: 5px;
}
&-jump-prev,
&-jump-next {
.@{pagination-prefix-cls}-item-container {
.@{pagination-prefix-cls}-item-link-icon {
color: @pagination-color;
}
}
}
}
// Table
.@{table-prefix-cls} {
color: inherit;
tr,
th,
td {
transition: none !important;
}
&-thead > tr > th {
padding: @table-padding-vertical * 2 @table-padding-horizontal;
}
.@{table-prefix-cls}-column-sorters {
&:before,
&:hover:before {
content: none;
}
}
&-thead > tr > th {
.@{table-prefix-cls}-column-sorter {
&-up,
&-down {
&.on {
color: @table-header-icon-active-color;
}
}
}
}
// Custom styles
&-headerless &-tbody > tr:first-child > td {
border-top: @border-width-base @border-style-base @border-color-split;
}
}
// List
.@{list-prefix-cls} {
&-item {
// custom rule
&.selected {
background-color: #F6F8F9;
}
&.disabled {
background-color: fade(#F6F8F9, 40%);
& > * {
opacity: 0.4;
}
}
}
}
.@{dialog-prefix-cls} {
// styling for short modals (no lines)
&.shortModal {
.@{dialog-prefix-cls} {
&-header,
&-footer {
border: none;
padding: 16px;
}
&-body {
padding: 10px 16px;
}
&-close-x {
width: 46px;
height: 46px;
line-height: 46px;
}
}
}
// fullscreen modals
&-fullscreen {
.@{dialog-prefix-cls} {
position: absolute;
left: 15px;
top: 15px;
right: 15px;
bottom: 15px;
width: auto !important;
height: auto !important;
max-width: none;
max-height: none;
margin: 0;
padding: 0;
.@{dialog-prefix-cls}-content {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: auto;
height: auto;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
}
.@{dialog-prefix-cls}-body {
flex: 1 1 auto;
overflow: auto;
}
}
}
}
// description in modal header
.modal-header-desc {
font-size: @font-size-base;
color: @text-color-secondary;
font-weight: normal;
margin-top: 4px;
}
// Notification overrides
.@{notification-prefix-cls} {
// vertical centering
&-notice-close {
top: 20px;
right: 20px;
}
&-notice-description {
max-width: 484px;
}
}
.@{btn-prefix-cls} .@{iconfont-css-prefix}-ellipsis {
margin: 0 -7px;
}
// Collapse
.@{collapse-prefix-cls} {
&&-headerless {
border: 0;
background: none;
.@{collapse-prefix-cls}-header {
display: none;
}
.@{collapse-prefix-cls}-item,
.@{collapse-prefix-cls}-content {
border: 0;
}
.@{collapse-prefix-cls}-content-box {
padding: 0;
}
}
}
// overrides for tall form components such as ace editor
.@{form-prefix-cls}-item {
&-children {
display: block; // so feeback icon positions correctly
}
// no change for short components, sticks to body for tall ones
&-children-icon {
top: auto !important;
bottom: 8px;
// makes the icon white instead of see-through
& svg {
background: white;
border-radius: 50%;
}
}
}

View File

@@ -1,6 +1,5 @@
.alert {
padding-left: 30px;
padding-right: 30px;
padding: 15px;
span {
cursor: pointer;

View File

@@ -0,0 +1,86 @@
/* --------------------------------------------------------
Colors
-----------------------------------------------------------*/
@lightblue: #03A9F4;
@primary-color: #2196F3;
@redash-gray: rgba(102, 136, 153, 1);
@redash-orange: rgba(255, 120, 100, 1);
@redash-black: rgba(0, 0, 0, 1);
@redash-yellow: rgba(252, 252, 161, 0.75);
/* --------------------------------------------------------
Font
-----------------------------------------------------------*/
@redash-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
@font-family-no-number: @redash-font;
@font-family: @redash-font;
@code-family: @redash-font;
@font-size-base: 13px;
/* --------------------------------------------------------
Borders
-----------------------------------------------------------*/
@border-color-split: #f0f0f0;
/* --------------------------------------------------------
Typograpgy
-----------------------------------------------------------*/
@text-color: #595959;
/* --------------------------------------------------------
Form
-----------------------------------------------------------*/
@input-height-base: 35px;
@input-color: #595959;
@border-radius-base: 2px;
@border-color-base: #E8E8E8;
/* --------------------------------------------------------
Button
-----------------------------------------------------------*/
@btn-danger-bg: fade(@redash-gray, 10%);
@btn-danger-border: fade(@redash-gray, 15%);
/* --------------------------------------------------------
Pagination
-----------------------------------------------------------*/
@pagination-item-size: 33px;
@pagination-font-family: @redash-font;
@pagination-font-weight-active: normal;
@pagination-bg: fade(@redash-gray, 15%);
@pagination-color: #7E7E7E;
@pagination-active-bg: @lightblue;
@pagination-active-color: #FFF;
@pagination-disabled-bg: fade(@redash-gray, 15%);
@pagination-hover-color: #333;
@pagination-hover-bg: fade(@redash-gray, 25%);
/* --------------------------------------------------------
Table
-----------------------------------------------------------*/
@table-border-radius-base: 0;
@table-header-color: #333;
@table-header-bg: fade(@redash-gray, 3%);
@table-header-icon-color: fade(@text-color, 20%);
@table-header-icon-active-color: @text-color;
@table-header-sort-bg: @table-header-bg;
@table-header-sort-active-bg: @table-header-bg;
@table-header-filter-active-bg: @table-header-bg;
@table-body-sort-bg: transparent;
@table-row-hover-bg: fade(@redash-gray, 5%);
@table-padding-vertical: 7px;
@table-padding-horizontal: 10px;
/* --------------------------------------------------------
Notification
-----------------------------------------------------------*/
@notification-padding: @notification-padding-vertical 48px @notification-padding-vertical 17px;
@notification-width: auto;

View File

@@ -19,21 +19,30 @@ html, body {
}
body {
padding-top: @header-height;
position: relative;
padding-bottom: @footer-height;
&.headless {
padding-top: 0;
padding-bottom: 0;
.nav.app-header {
display: none;
}
div#footer {
background: #F6F8F9;
font-family: @redash-font;
position: relative;
&.headless {
padding-top: 10px;
.nav.app-header, .navbar {
display: none;
}
}
}
app-view {
min-height: 100vh;
}
app-view, #app-content {
display: flex;
flex-direction: column;
flex-grow: 1;
}
strong {
font-weight: 500;
}
@@ -67,10 +76,38 @@ strong {
}
}
// Fixed width layout for specific pages
@media (min-width: 768px) {
settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
.container {
width: 750px;
}
}
}
@media (min-width: 992px) {
settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
.container {
width: 970px;
}
}
}
@media (min-width: 1200px) {
settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
.container {
width: 1170px;
}
}
}
.scrollbox {
overflow: auto;
position: relative;
}
.clickable {
cursor: pointer;
}
.resize-vertical {
@@ -86,3 +123,150 @@ strong {
resize: both !important;
transition: height 0s, width 0s !important;
}
// Ace Editor
.ace_editor {
border: 1px solid fade(@redash-gray, 15%) !important;
}
.ace-tm {
.ace_gutter {
background: #fff !important;
}
.ace_gutter-active-line {
background-color: fade(@redash-gray, 20%) !important;
}
.ace_marker-layer .ace_active-line {
background: fade(@redash-gray, 9%) !important;
}
}
.bg-ace {
background-color: fade(@redash-gray, 12%) !important;
}
// resizeable
.rg-top span, .rg-bottom span {
height: 3px;
border-color: #b1c1ce; // TODO: variable
}
.rg-bottom {
bottom: 15px;
span {
margin: 1.5px 0 0 -10px;
}
}
// Plotly
text.slicetext {
text-shadow: 1px 1px 5px #333;
}
// markdown
.markdown strong {
font-weight: bold;
}
.markdown img {
max-width: 100%;
}
.dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus {
background-color: fade(@redash-gray, 15%);
color: #111;
}
.profile__image--navbar {
border-radius: 100%;
margin-right: 3px;
margin-top: -2px;
}
.profile__image--settings {
border-radius: 100%;
}
.profile__image_thumb {
border-radius: 100%;
margin-right: 3px;
margin-top: -2px;
width: 20px;
height: 20px;
}
// Error state
.error-state {
display: flex;
flex-direction: column;
justify-content: flex-start;
text-align: center;
margin-top: 25vh;
padding: 35px;
font-size: 14px;
line-height: 21px;
.error-state__icon {
.zmdi {
font-size: 64px;
color: @redash-gray;
}
}
@media (max-width: 767px) {
margin-top: 10vh;
}
}
// page
.page-header--new .btn-favourite, .page-header--new .btn-archive {
font-size: 19px;
}
.page-title {
display: flex;
align-items: center;
h3 {
margin-right: 5px !important;
}
.label {
margin-top: 3px;
display: inline-block;
}
favorites-control {
margin-right: 5px;
}
@media (max-width: 767px) {
display: block;
favorites-control {
float: left;
}
h3 {
width: 100%;
margin-bottom: 5px !important;
display: block !important;
}
}
}
.page-header-wrapper, .page-header--new {
h3 {
margin: 0.2em 0;
line-height: 1.3;
font-weight: 500;
}
}
.select-option-divider {
margin: 10px 0 !important;
}

View File

@@ -31,7 +31,7 @@
.collapsing,
.collapse.in {
padding: 5px 10px;
padding: 0;
transition: all 0.35s ease;
}

View File

@@ -122,3 +122,21 @@
top: 1px;
position: relative;
}
.btn-default {
background-color: fade(@redash-gray, 15%);
}
.btn-transparent {
background-color: transparent !important;
}
.btn-default:hover, .btn-default:focus, .btn-default.focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default {
background-color: fade(@redash-gray, 25%);
}
.btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus {
color: #333;
background-color: fade(@redash-gray, 45%);
}

View File

@@ -1,48 +0,0 @@
#footer {
position: absolute;
bottom: 0;
text-align: center;
width: 100%;
height: @footer-height;
color: #a2a2a2;
padding-top: 10px;
padding-bottom: 15px;
.f-menu {
display: block;
width: 100%;
.list-inline();
margin-top: 8px;
& > li > a {
color: #a2a2a2;
&:hover {
color: #777;
}
}
}
@media (min-width: (@screen-lg-min + 80px)) {
padding-left: (@sidebar-left-width + @grid-gutter-width);
}
@media (min-width: @screen-sm-min) and (max-width: (@screen-md-max + 80px)) {
padding-left: (@sidebar-left-mid-width + @grid-gutter-width);
}
@media (max-width: (@screen-sm-min)) {
padding-left: @grid-gutter-width/2;
}
}
.footer {
color: #818d9f;
padding-bottom: 30px;
a {
color: #818d9f;
margin-left: 20px;
}
}

View File

@@ -55,14 +55,17 @@ textarea.v-resizable {
.transition-duration(300ms);
resize: none;
box-shadow: 0 0 0 40px rgba(0, 0, 0, 0) !important;
border-radius: 0;
border-radius: @redash-input-radius;
&:focus {
box-shadow: 0 0 1px -2px rgba(121,194,255,0.5) !important;
box-shadow: none !important;
border-color: @blue;
}
&:hover {
border-color: @blue;
}
}
/* --------------------------------------------------------
Custom Checkbox + Radio
-----------------------------------------------------------*/

View File

@@ -146,9 +146,17 @@
Width
-----------------------------------------------------------*/
.w-100 { width: 100% !important; }
.w-50 { width: 50% !important; }
.w-25 { width: 25% !important; }
/* --------------------------------------------------------
Border Radius
-----------------------------------------------------------*/
.brd-2 { border-radius: 2px; }
/* --------------------------------------------------------
Alignment
-----------------------------------------------------------*/
.va-top { vertical-align: top; }

View File

@@ -1,14 +1,37 @@
.label {
border-radius: 1px;
padding: 4px 5px 3px;
}
h1, h2, h3, h4, h5, h6 {
.label {
border-radius: 2px;
}
padding: 3px 6px 4px;
font-weight: 500;
font-size: 11px;
}
.badge {
border-radius: 1px;
}
.label-default {
background: fade(@redash-gray, 85%);
}
.label-tag-unpublished {
background: fade(@redash-gray, 85%);
}
.label-tag-archived {
.label-warning();
}
.label-tag {
background: fade(@redash-gray, 10%);
color: fade(@redash-gray, 75%);
}
.label-tag-unpublished,
.label-tag-archived,
.label-tag {
margin-right: 3px;
display: inline;
margin-top: 2px;
max-width: 24ch;
.text-overflow();
}

View File

@@ -31,6 +31,17 @@ tags-list {
line-height: 1.3;
}
.tags-list {
.badge-light {
background: fade(@redash-gray, 10%);
color: fade(@redash-gray, 75%);
}
a:hover {
cursor: pointer;
}
}
.max-character {
.text-overflow();
}
@@ -45,6 +56,11 @@ tags-list {
line-height: 100%;
margin-top: 2px;
}
&.active, &.active:hover, &.active:focus {
background-color: #fff;
box-shadow: inset 3px 0px 0px @brand-primary;
}
}
.list-group-item-heading {
@@ -76,3 +92,18 @@ tags-list {
height: 38px;
border-radius: 2px;
}
.ui-select-choices-row.disabled > span {
background-color: inherit !important;
}
.list-group-item.inactive,
.ui-select-choices-row.disabled {
background-color: #eee !important;
border-color: transparent;
opacity: 0.5;
box-shadow: none;
color: #333;
pointer-events: none;
cursor: not-allowed;
}

View File

@@ -226,3 +226,12 @@
border-radius: 2px;
width: 37px;
}
/* --------------------------------------------------------
Percy
-----------------------------------------------------------*/
@media only percy {
.hide-in-percy, .pace {
visibility: hidden;
}
}

View File

@@ -1,6 +1,5 @@
a.navbar-brand {
padding: 5px 5px 0px 0px;
margin-left: 0px !important;
}
.navbar .fa {
@@ -31,3 +30,266 @@ a.navbar-brand img {
left: -9px;
bottom: -11px;
}
.caret--nav {
border-top: none;
}
.caret--nav:after {
content: "";
position: absolute;
right: 5px;
top: 9px;
width: 13px;
height: 13px;
display: block;
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='11px' height='6px' viewBox='0 0 11 6' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3C!-- Generator: Sketch 42 %2836781%29 - http://www.bohemiancoding.com/sketch --%3E%3Ctitle%3EShape%3C/title%3E%3Cdesc%3ECreated with Sketch.%3C/desc%3E%3Cdefs%3E%3C/defs%3E%3Cg id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cpath d='M5.296,4.288 L9.382,0.2 C9.66086822,-0.0716916976 10.1065187,-0.068122925 10.381,0.208 C10.661,0.488 10.661,0.932 10.388,1.206 L5.792,5.803 C5.6602899,5.93388911 5.48167943,6.00662966 5.296,6.005 C5.10997499,6.00689786 4.93095449,5.93413702 4.799,5.803 L0.204,1.207 C0.072163111,1.07394937 -0.00121750401,0.893846387 9.62313189e-05,0.706545264 C0.00140996665,0.519244142 0.0773097323,0.340188219 0.211,0.209 C0.485365732,-0.0664648737 0.930253538,-0.0700311086 1.209,0.201 L5.296,4.288 L5.296,4.288 Z' id='Shape' fill='%23000000'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
background-size: 100% 100%;
transition: transform .2s cubic-bezier(.75,0,.25,1);
}
.navbar .caret--nav:after {
top: 19px;
}
.dropdown--profile .caret--nav:after {
right: 8px;
}
.btn--create {
padding-right: 20px;
.caret--nav:after {
top: 10px;
right: 10px;
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='11px' height='6px' viewBox='0 0 11 6' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3C!-- Generator: Sketch 42 %2836781%29 - http://www.bohemiancoding.com/sketch --%3E%3Ctitle%3EShape%3C/title%3E%3Cdesc%3ECreated with Sketch.%3C/desc%3E%3Cdefs%3E%3C/defs%3E%3Cg id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cpath d='M5.29592111,4.28945339 L9.38192111,0.201453387 C9.66078932,-0.0702383105 10.1064398,-0.0666695379 10.3809211,0.209453387 C10.6609211,0.489453387 10.6609211,0.933453387 10.3879211,1.20745339 L5.79192111,5.80445339 C5.66021101,5.9353425 5.48160054,6.00808305 5.29592111,6.00645339 C5.1098961,6.00835125 4.9308756,5.9355904 4.79892111,5.80445339 L0.203921109,1.20845339 C0.0720842204,1.07540275 -0.00129639464,0.895299774 1.73406884e-05,0.707998651 C0.00133107602,0.520697529 0.0772308417,0.341641606 0.210921109,0.210453387 C0.485286842,-0.0650114866 0.930174648,-0.0685777215 1.20892111,0.202453387 L5.29592111,4.28945339 L5.29592111,4.28945339 Z' id='Shape' fill='%23FCFCFC'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
}
}
.dropdown.open .caret--nav:after {
transform: rotate(180deg);
}
.navbar {
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
.navbar-collapse {
padding-left: 0;
}
a.dropdown--profile {
padding-top: 10px;
padding-bottom: 10px;
line-height: 2.35;
}
.navbar-inverse {
background-color: @redash-gray;
border: none;
}
}
.navbar-btn {
margin-top: 10px;
margin-bottom: 9px;
}
.navbar-brand {
position: absolute;
left: 50%;
margin-left: -25px !important; // center
display: block;
zoom: 0.9;
}
.menu-search {
margin-top: 2px;
}
.dropdown-menu--profile {
li {
width: 200px;
}
}
.navbar .collapse.in {
background: #fff;
position: relative;
z-index: 999;
padding: 0 10px 0 10px;
}
.navbar {
min-height: initial;
height: 50px;
border: 1px solid #fff;
border-top: none;
border-radius: 0;
background: #fff;
margin-bottom: 10px;
.btn-group.open .dropdown-toggle {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-group .btn:active {
box-shadow: none;
}
}
.navbar-link-ANGULAR_REMOVE_ME {
line-height: 18px;
padding: 10px 15px;
display: block;
@media (min-width: 768px) {
padding-top: 16px;
padding-bottom: 16px;
}
}
.navbar-link-ANGULAR_REMOVE_ME,
.navbar-default .navbar-nav > li > a {
color: #000;
font-weight: 500;
&:active, &:hover, &:focus {
color: #000;
}
}
.navbar-default .btn__new button {
font-weight: 500;
}
.btn__new {
margin-left: 15px;
}
.navbar-default .navbar-nav > li > a:hover {
//background-color: fade(@redash-gray, 10%);
//text-decoration: underline;
//border-radius: 0;
}
.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus {
background-color: fade(@redash-gray, 15%);
color: #111;
}
// Responsive fixes
@media (max-width: 767px) {
.navbar-brand {
left: 2%;
margin-left: 0 !important;
}
//Fix navbar collapse
.navbar .collapse.in {
border: none;
.dropdown-menu--profile {
li {
width: auto;
}
}
.dropdown--profile {
.caret--nav:after {
right: initial !important;
}
}
.dropdown--profile__username {
display: inline-block;
}
.nav__main li a {
padding: 10px 15px;
display: block;
text-align: left;
float: none !important;
}
.navbar-form {
margin-bottom: 0;
margin-top: 0;
}
.navbar-right {
margin-bottom: 0;
}
}
}
@media (min-width: 768px) {
@media (max-width: 880px) {
.navbar-link-ANGULAR_REMOVE_ME,
.navbar-default .navbar-nav > li > a,
.navbar-form {
padding-left: 10px !important;
padding-right: 10px !important;
}
a.navbar-brand {
margin-left: -15px !important;
}
}
@media (max-width: 810px) {
.menu-search {
width: 175px;
}
a.navbar-brand {
margin-left: 13px !important;
}
}
}
@media (max-width: 1084px) {
.dropdown--profile__username {
display: none;
}
}
// Cross-browser fixes
// Firefox
@-moz-document url-prefix() {
.caret--nav::after {
height: 7px;
}
.navbar .caret--nav::after {
top: 22px;
}
.navbar .btn--create .caret--nav::after {
top: 12px;
}
}
// IE10+
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.caret--nav::after {
height: 7px;
}
.navbar .caret--nav::after {
top: 22px;
}
.navbar .btn--create .caret--nav::after {
top: 12px;
}
}
.navbar li a .btn-favourite .fa, .navbar li a .btn-archive .fa {
font-size: 100%;
}

View File

@@ -1,54 +0,0 @@
.pagination {
border-radius: 0;
& > li {
margin: 0 2px;
display: inline-block;
vertical-align: top;
& > a,
& > span {
border-radius: 50% !important;
padding: 0;
width: 40px;
height: 40px;
line-height: 38px;
text-align: center;
font-size: 14px;
z-index: 1;
position: relative;
& > .zmdi {
font-size: 22px;
line-height: 39px;
}
}
&.disabled {
.opacity(0.5);
}
}
}
/* --------------------------------------------------------
Listview Pagination
-----------------------------------------------------------*/
.lv-pagination {
width: 100%;
text-align: center;
padding: 40px 0;
border-top: 1px solid #F0F0F0;
margin-top: 0;
margin-bottom: 0;
}
/* --------------------------------------------------------
Pager
-----------------------------------------------------------*/
.pager li > a, .pager li > span {
padding: 5px 10px 6px;
color: @pagination-color;
}

View File

@@ -1,5 +1,5 @@
.popover {
box-shadow: 0 2px 30px rgba(0, 0, 0, 0.2);
box-shadow: fade(@redash-gray, 25%) 0px 0px 15px 0px;
}
.popover-title {

View File

@@ -12,7 +12,6 @@
#header,
#footer,
#sidebar,
#chat,
.growl-animated,

View File

@@ -132,9 +132,15 @@
}
.tab-nav {
margin-bottom: 0px;
> li.rd-tab-btn {
float: right;
padding-right: 10px;
padding-top: 10px;
}
> li > a {
text-transform: capitalize;
}
}

View File

@@ -97,3 +97,53 @@
background-color: #f4f4f4;
}
.table-data {
tbody > tr > td {
padding-top: 5px !important;
}
.btn-favourite, .btn-archive {
font-size: 15px;
}
}
.table-main-title {
font-weight: 500;
line-height: 1.7 !important;
}
.btn-favourite {
color: #d4d4d4;
transition: all .25s ease-in-out;
&:hover, &:focus {
color: @yellow-darker;
cursor: pointer;
}
.fa-star {
color: @yellow-darker;
}
}
.btn-archive {
color: #d4d4d4;
transition: all .25s ease-in-out;
&:hover, &:focus {
color: @gray-light;
}
.fa-archive {
color: @gray-light;
}
}
.table > thead > tr > th {
text-transform: none;
}
.table-data .label-tag {
display: inline-block;
max-width: 135px;
}

View File

@@ -2,7 +2,8 @@
background-color: #fff;
margin-bottom: @grid-gutter-width;
position: relative;
box-shadow: @tile-shadow;
border-radius: 3px;
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
&[class*="bg-"] {
color: #fff;
@@ -12,6 +13,10 @@
margin-bottom: @grid-gutter-width/2;
}
}
.tiled {
border-radius: 3px;
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
}
.t-header {
.th-title {
@@ -74,6 +79,15 @@
}
}
.t-header:not(.th-alt) {
padding: 15px;
ul {
margin-bottom: 0;
line-height: 2.2;
}
}
.tb-padding {
padding: 20px 23px 30px;
}

View File

@@ -17,13 +17,14 @@
Template Variables
-----------------------------------------------------------*/
@header-height: 60px;
@footer-height: 95px;
@sidebar-left-width: 240px;
@sidebar-left-mid-width: 64px;
@logo-width: @sidebar-left-width;
@logo-height: @header-height;
@boxed-width: 1170px;
@body-bg: #edecec;
@spacing: 15px;
@redash-radius: 3px;
/* --------------------------------------------------------
@@ -40,6 +41,7 @@
-----------------------------------------------------------*/
@font-icon: 'Material-Design-Iconic-Font';
@font-family-sans-serif: 'Roboto', sans-serif;
@redash-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
@font-size-base: 13px;
@@ -55,10 +57,12 @@
/* --------------------------------------------------------
Form
-----------------------------------------------------------*/
@input-color: #595959;
@input-color-placeholder: #b4b4b4;
@input-border: #e8e8e8;
@input-border-radius: 0;
@input-border-radius-large: 0px;
@redash-input-radius: 2px;
@input-height-large: 40px;
@input-height-base: 35px;
@input-height-small: 30px;
@@ -94,11 +98,15 @@
@gray-light: #828282;
@ace: #f8f8f8;
@redash-gray: rgba(102, 136, 153, 1);
@redash-orange: rgba(255, 120, 100, 1);
@redash-black: rgba(0, 0, 0, 1);
@redash-yellow: rgba(252, 252, 161, 0.75);
/** Form States **/
@state-success-text: @green;
@state-info-text: @blue;
@state-danger-text: lighten(@red, 5%);
@state-warning-text: @orange;
/* --------------------------------------------------------
@@ -106,19 +114,16 @@
-----------------------------------------------------------*/
@alert-success-border: transparent;
@alert-info-border: transparent;
@alert-warning-border: transparent;
@alert-danger-border: transparent;
@alert-inverse-border: transparent;
@alert-success-bg: fade(@green, 70%);
@alert-info-bg: fade(@blue, 70%);
@alert-warning-bg: fade(@amber, 70%);
@alert-danger-bg: fade(@red, 70%);
@alert-inverse-bg: #333;
@alert-success-text: #fff;
@alert-info-text: #fff;
@alert-warning-text: #fff;
@alert-danger-text: #fff;
@alert-inverse-text: #fff;
@@ -196,7 +201,6 @@
@pagination-hover-color: #333;
@pagination-hover-bg: #d7d7d7;
@pagination-hover-border: @pagination-border;
@pager-border-radius: 5px;
/* --------------------------------------------------------

View File

@@ -1,45 +0,0 @@
counter-renderer {
display: block;
text-align: center;
padding: 15px 10px;
overflow: hidden;
counter {
margin: 0;
padding: 0;
font-size: 80px;
line-height: normal;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
value,
counter-target {
font-size: 1em;
display: block;
}
counter-name {
font-size: 0.5em;
display: block;
}
&.positive value {
color: #5cb85c;
}
&.negative value {
color: #d9534f;
}
}
counter-target {
color: #ccc;
}
counter-name {
font-size: 0.5em;
display: block;
}
}

View File

@@ -1,3 +1,8 @@
visualization-renderer .pagination {
visualization-renderer {
display: block;
.pagination,
.ant-pagination {
margin: 0;
}
}

View File

@@ -1,3 +1,4 @@
pivot-table-renderer > table, grid-renderer > div, visualization-renderer > div {
.pivot-table-renderer > table,
visualization-renderer > .visualization-renderer-wrapper {
overflow: auto;
}

View File

@@ -1,5 +1,3 @@
@import 'redash/ant';
/** LESS Plugins **/
@import 'inc/less-plugins/for';
@@ -9,7 +7,6 @@
/** Load Vendors Dependencies **/
@import '~font-awesome/less/font-awesome';
@import '~ui-select/dist/select.css';
@import '~angular-toastr/src/toastr';
@import '~angular-resizable/src/angular-resizable.css';
@import '~material-design-iconic-font/dist/css/material-design-iconic-font.css';
@import '~pace-progress/themes/blue/pace-theme-minimal.css';
@@ -35,7 +32,6 @@
@import 'inc/progress-bar';
@import 'inc/widgets';
@import 'inc/table';
@import 'inc/pagination';
@import 'inc/alert';
@import 'inc/media';
@import 'inc/modal';
@@ -47,7 +43,6 @@
@import 'inc/jumbotron';
@import 'inc/profile';
@import 'inc/404';
@import 'inc/footer';
@import 'inc/ie-warning';
@import 'inc/navbar';
@import 'inc/edit-in-place';
@@ -58,11 +53,9 @@
@import 'inc/schema-browser';
@import 'inc/toast';
@import 'inc/visualizations/box';
@import 'inc/visualizations/counter-render';
@import 'inc/visualizations/sankey';
@import 'inc/visualizations/pivot-table';
@import 'inc/visualizations/map';
@import 'inc/visualizations/chart';
@import 'inc/visualizations/sunburst';
@import 'inc/visualizations/cohort';
@import 'inc/visualizations/misc';
@@ -75,10 +68,11 @@
@import 'inc/vendor-overrides/ui-select';
/** REDASH STYLING **/
@import 'redash/redash-newstyle';
@import 'redash/redash-table';
@import 'redash/query';
@import 'redash/tags-control';
@import 'redash/css-logo';
@import 'redash/loading-indicator';

View File

@@ -1,38 +0,0 @@
@import '~antd/lib/style/core/iconfont.less';
@import '~antd/lib/style/core/motion.less';
@import '~antd/lib/input/style/index.less';
@import '~antd/lib/date-picker/style/index.less';
@import '~antd/lib/tooltip/style/index.less';
@import '~antd/lib/select/style/index.less';
// Overwritting Ant Design defaults to fit into Redash current style
@font-family-no-number : @redash-font;
@font-family : @redash-font;
@code-family : @redash-font;
@border-radius-base : @redash-input-radius;
@border-color-base : #e8e8e8;
@primary-color : @blue;
// Fix for disabled button styles inside Tooltip component.
// Tooltip wraps disabled buttons with `<span>` and moves all styles
// and classes to that `<span>`. This resets all button styles and
// turns it into simple inline element (because now it's wrapper is a button)
.btn {
button[disabled] {
-moz-appearance: none !important;
-webkit-appearance: none !important;
appearance: none !important;
border: 0 !important;
outline: none !important;
background: transparent !important;
margin: 0 !important;
padding: 0 !important;
}
}
// Fix for Ant dropdowns when they are used in Boootstrap modals
.ant-dropdown-in-bootstrap-modal {
z-index: 1050;
}

View File

@@ -0,0 +1,88 @@
// based on https://github.com/outbrain/tech-companies-logos-in-css/pull/28
@primary: #ff7964;
@shadow: #ef6c58;
@bar: white;
#css-logo {
width: 100px;
height: 100px;
position: relative;
#circle {
width: 79px;
height: 79px;
background-color: @shadow;
border-radius: 50%;
margin: auto;
overflow: hidden;
position: relative;
& > div {
width: 79px;
height: 73px;
background-color: @primary;
border-radius: 50%;
position: absolute;
top: 0;
}
}
#bars {
position: absolute;
left: 0;
top: 24px;
right: 0;
height: 33px;
display: flex;
padding: 0 22px 0;
.bar {
background: @bar;
box-shadow: 0px 2px 0 0 @shadow;
display: inline-block;
border-radius: 1px;
align-self: flex-end;
flex: 1;
margin: 0 2px;
border-radius: 3px;
&:nth-child(1) {
height: 32%;
}
&:nth-child(2) {
height: 71%;
}
&:nth-child(3) {
height: 50%;
}
&:nth-child(4) {
height: 100%;
}
}
}
#point,
#point > div {
position: absolute;
width: 0;
height: 0;
border: 17px solid @shadow;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
bottom: 0;
left: 48px;
transform: scaleX(0.87);
transform-origin: left;
}
#point > div {
bottom: -12px;
border-color: @primary;
transform: scaleX(1.04);
left: -17px;
}
}

View File

@@ -0,0 +1,51 @@
.loading-indicator {
position: fixed;
top: 50%;
left: 50%;
margin: -50px 0 0 -50px; // center
width: 100px;
height: 100px;
transition-duration: 150ms;
transition-timing-function: linear;
transition-property: opacity, transform;
#css-logo {
animation: hover 2s infinite;
}
#shadow {
width: 33px;
height: 12px;
border-radius: 50%;
background-color: black;
opacity: 0.25;
display: block;
position: absolute;
left: 34px;
top: 115px;
animation: shadow 2s infinite;
}
@keyframes hover {
50% {
transform: translateY(-5px);
}
}
@keyframes shadow {
50% {
transform: scaleX(0.9);
opacity: 0.2;
}
}
}
// hide indicator when app-view has content
app-view:not(:empty) ~ .loading-indicator {
opacity: 0;
transform: scale(0.9);
pointer-events: none;
* {
animation: none !important;
}
}

View File

@@ -92,7 +92,7 @@ edit-in-place p.editable:hover {
}
.filter-container {
margin-bottom: 10px;
margin-bottom: 5px;
}
.ace_editor.ace_autocomplete .ace_completion-highlight {
@@ -172,6 +172,12 @@ edit-in-place p.editable:hover {
}
}
.query-log-line {
font-family: monospace;
white-space: pre;
margin: 0;
}
.paginator-container {
text-align: center;
}
@@ -202,18 +208,21 @@ edit-in-place p.editable:hover {
}
}
.visualization-renderer .pagination {
margin-top: 10px;
}
.embed__vis {
display: flex;
flex-flow: column;
}
.embed-heading {
h3 {
line-height: 1.75;
margin: 0;
}
}
.widget-wrapper {
.body-container {
filters {
.filters-wrapper {
display: block;
padding-left: 15px;
}
@@ -255,6 +264,7 @@ a.label-tag {
.query-page-wrapper {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.query-fullscreen {
@@ -333,7 +343,8 @@ a.label-tag {
border-bottom: 1px solid #efefef;
}
pivot-table-renderer > table, grid-renderer > div, visualization-renderer > div {
.pivot-table-renderer > table,
visualization-renderer > .visualization-renderer-wrapper {
overflow: visible;
}
@@ -548,6 +559,10 @@ nav .rg-bottom {
text-transform: capitalize;
}
.edit-visualization {
margin-right: 5px;
}
// Smaller screens
@media (max-width: 880px) {
@@ -649,10 +664,6 @@ nav .rg-bottom {
}
}
a.navbar-brand {
display: none;
}
.datasource-small {
visibility: visible;
}
@@ -663,15 +674,20 @@ nav .rg-bottom {
display: none;
}
a.navbar-brand {
display: block;
}
.filter-container {
padding-right: 0;
}
}
.btn-edit-visualisation {
// Responsive fixes
@media (max-width: 767px) {
.query-page-wrapper {
h3 {
font-size: 18px;
}
favorites-control {
margin-top: -3px;
}
}
}

View File

@@ -1,935 +0,0 @@
@import (reference, less) '~bootstrap/less/labels.less';
// Variables
@redash-gray: rgba(102, 136, 153, 1);
@redash-orange: rgba(255, 120, 100, 1);
@redash-black: rgba(0, 0, 0, 1);
@redash-yellow: rgba(252, 252, 161, 0.75);
@redash-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
@spacing: 15px;
//Default spacing (between tiles)
@redash-space: 10px;
@redash-radius: 3px;
@redash-input-radius: 2px;
// General
body {
padding-top: 0;
background: #F6F8F9;
font-family: @redash-font;
&.headless {
padding-top: 10px;
padding-bottom: 15px;
.navbar {
display: none !important;
}
div#footer {
display: none;
}
}
}
.clearboth {
clear: both;
}
.callout {
padding: 20px;
border: 1px solid #eee;
border-left-width: 5px;
border-radius: 3px;
}
.callout-warning {
border-left-color: #aa6708;
}
.callout-info {
border-left-color: #1b809e;
}
.list-content {
@media (min-width: 992px) {
padding-right: 0;
}
}
.list-control-r-b {
@media (max-width: 992px) {
display: none;
}
}
.list-control-t {
@media (min-width: 992px) {
display: none;
}
}
// Fixed width layout for specific pages
@media (min-width: 768px) {
settings-screen, home-page, page-dashboard-list, page-queries-list, alerts-list-page, alert-page, queries-search-results-page, .fixed-container {
.container {
width: 750px;
}
}
}
@media (min-width: 992px) {
settings-screen, home-page, page-dashboard-list, page-queries-list, alerts-list-page, alert-page, queries-search-results-page, .fixed-container {
.container {
width: 970px;
}
}
}
@media (min-width: 1200px) {
settings-screen, home-page, page-dashboard-list, page-queries-list, alerts-list-page, alert-page, queries-search-results-page, .fixed-container {
.container {
width: 1170px;
}
}
}
.creation-container {
h5 {
color: #a7a7a7;
}
h3 {
margin: 0px;
margin-bottom: 15px;
}
}
.add-widget-container {
background: #fff;
border-radius: @redash-radius;
padding: 15px;
position: fixed;
left: 15px;
bottom: 20px;
width: calc(~'100% - 30px');
z-index: 99;
box-shadow: fade(@redash-gray, 50%) 0px 7px 29px -3px;
display: flex;
justify-content: space-between;
h2 {
margin: 0;
font-size: 14px;
line-height: 2.1;
font-weight: 400;
.zmdi {
margin: 0;
margin-right: 5px;
font-size: 24px;
position: absolute;
bottom: 18px;
}
span {
padding-left: 30px;
}
}
.btn {
align-self: center;
}
}
body {
.ace-tm .ace_gutter {
background: #fff;
}
.ace_editor {
border: 1px solid fade(@redash-gray, 15%);
}
.ace-tm .ace_gutter-active-line {
background-color: fade(@redash-gray, 20%);
}
.ace-tm .ace_marker-layer .ace_active-line {
background: fade(@redash-gray, 9%);
}
}
.list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus {
background-color: #fff;
box-shadow: inset 3px 0px 0px @brand-primary;
}
.table.table-data {
> tbody > tr > td {
padding-top: 5px !important;
}
tr:hover {
cursor: pointer;
}
.btn-favourite {
font-size: 15px;
}
}
.table-main-title {
font-weight: 500;
line-height: 1.7 !important;
a {
//font-size: 15px;
}
}
.btn-favourite {
color: #d4d4d4;
transition: all .25s ease-in-out;
&:hover, &:focus {
color: @yellow-darker;
}
.fa-star {
color: @yellow-darker;
}
}
.page-header--new .btn-favourite {
font-size: 19px;
}
.page-title {
display: flex;
align-items: center;
h3 {
margin-right: 5px !important;
}
.label {
margin-top: 3px;
display: inline-block;
}
favorites-control {
margin-right: 5px;
}
@media (max-width: 767px) {
display: block;
favorites-control {
float: left;
}
h3 {
width: 100%;
margin-bottom: 5px !important;
display: block !important;
}
}
}
.navbar li a .btn-favourite .fa {
font-size: 100%;
}
.float-right {
float: right;
}
.database-source {
display: inline-flex;
flex-wrap: wrap;
justify-content: center;
}
.visual-card {
background: #FFFFFF;
border: 1px solid fade(@redash-gray, 15%);
border-radius: 3px;
margin: 5px;
width: 212px;
padding: 15px 5px;
cursor: pointer;
box-shadow: none;
transition: transform 0.12s ease-out;
transition-duration: 0.3s;
transition-property: box-shadow;
display: flex;
//flex-direction: row;
align-items: center;
&:hover {
box-shadow: rgba(102, 136, 153, 0.15) 0px 4px 9px -3px;
}
img {
width: 64px !important;
height: 64px !important;
margin-right: 5px;
}
h3 {
font-size: 13px;
color: #323232;
margin: 0 !important;
text-overflow: ellipsis;
overflow: hidden;
}
}
.visual-card--selected {
background: fade(@redash-gray, 3%);
border: 1px solid fade(@redash-gray, 15%);
border-radius: 3px;
padding: 0 15px;
box-shadow: none;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
margin-bottom: 15px;
width: 100%;
img {
width: 64px;
height: 64px;
}
a {
cursor: pointer;
}
}
@media (max-width: 1200px) {
.visual-card {
width: 217px;
}
}
@media (max-width: 755px) {
.visual-card {
width: 47%;
}
}
@media (max-width: 515px) {
.visual-card {
width: 47%;
img {
width: 48px;
height: 48px;
}
}
}
@media (max-width: 408px) {
.visual-card {
width: 100%;
padding: 5px;
margin: 5px 0;
img {
width: 48px;
height: 48px;
}
}
}
.t-header:not(.th-alt) {
padding: 15px;
ul {
margin-bottom: 0;
line-height: 2.2;
}
}
#footer {
height: auto;
line-height: 3;
padding: 20px;
}
page-header, .page-header--new {
h3 {
margin: 0.2em 0;
line-height: 1.3;
font-weight: 500;
}
}
.alert {
padding: 15px;
}
.dynamic-table__pagination {
margin-top: 10px;
}
.rg-top span, .rg-bottom span {
height: 3px;
border-color: #b1c1ce; // TODO: variable
}
.rg-bottom {
bottom: 15px;
span {
margin: 1.5px 0 0 -10px;
}
}
.popover {
box-shadow: fade(@redash-gray, 25%) 0px 0px 15px 0px;
}
.tile__bottom-control a {
color: fade(@redash-black, 65%);
&:hover {
color: fade(@redash-black, 95%);
}
}
.pagination {
.disabled a {
background-color: fade(@redash-gray, 14%);
}
li {
a {
background-color: fade(@redash-gray, 15%);
&:hover {
background-color: fade(@redash-gray, 25%);
}
}
}
}
.btn-default {
background-color: fade(@redash-gray, 15%);
}
.btn-transparent {
background-color: transparent !important;
}
.btn-default:hover, .btn-default:focus, .btn-default.focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default {
background-color: fade(@redash-gray, 25%);
}
.btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus {
color: #333;
background-color: fade(@redash-gray, 45%);
}
.label {
border-radius: 2px;
padding: 3px 6px 4px;
font-weight: 500;
font-size: 11px;
}
.label-default {
background: fade(@redash-gray, 85%);
}
.label-tag-unpublished {
background: fade(@redash-gray, 85%);
}
.label-tag-archived {
.label-warning();
}
.label-tag {
background: fade(@redash-gray, 10%);
color: fade(@redash-gray, 75%);
}
.label-tag-unpublished,
.label-tag-archived,
.label-tag {
margin-right: 3px;
display: inline-block;
margin-top: 2px;
max-width: 24ch;
.text-overflow();
}
.tab-nav > li > a {
text-transform: capitalize;
}
.table > thead > tr > th {
text-transform: none;
}
.dashboard-header {
position: -webkit-sticky; // required for Safari
position: sticky;
background: #f6f7f9;
z-index: 99;
width: 100%;
top: 0;
}
.dashboard__control {
margin: 8px 0;
}
.editing-mode {
a.query-link {
pointer-events: none;
cursor: move;
}
.th-title {
cursor: move;
}
}
.dashboard-header {
position: -webkit-sticky; // required for Safari
position: sticky;
background: #f6f7f9;
z-index: 99;
width: 100%;
top: 0;
}
.widget-wrapper {
.parameter-container {
padding: 0 15px;
}
}
.bg-ace {
background-color: fade(@redash-gray, 12%) !important;
}
.tiled {
border-radius: 3px;
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
}
.tile {
border-radius: 3px;
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
.widget-menu-regular, .btn__refresh {
opacity: 0 !important;
transition: opacity 0.35s ease-in-out;
}
.t-header {
.th-title {
a {
color: fade(@redash-black, 80%);
font-size: 15px;
font-weight: 500;
}
}
.query--description {
font-size: 14px;
line-height: 1.5;
font-style: italic;
p {
margin-bottom: 0;
}
}
}
.t-header.widget {
padding: 15px;
}
&:hover {
.widget-menu-regular, .btn__refresh {
opacity: 1 !important;
transition: opacity 0.35s ease-in-out;
}
}
.tile__bottom-control {
padding: 10px 15px;
line-height: 2;
}
}
.embed-heading {
h3 {
line-height: 1.75;
margin: 0;
}
}
.filter-container {
margin-bottom: 5px;
}
// Navigation
.caret--nav {
border-top: none;
}
.caret--nav:after {
content: "";
position: absolute;
right: 5px;
top: 9px;
width: 13px;
height: 13px;
display: block;
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='11px' height='6px' viewBox='0 0 11 6' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3C!-- Generator: Sketch 42 %2836781%29 - http://www.bohemiancoding.com/sketch --%3E%3Ctitle%3EShape%3C/title%3E%3Cdesc%3ECreated with Sketch.%3C/desc%3E%3Cdefs%3E%3C/defs%3E%3Cg id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cpath d='M5.296,4.288 L9.382,0.2 C9.66086822,-0.0716916976 10.1065187,-0.068122925 10.381,0.208 C10.661,0.488 10.661,0.932 10.388,1.206 L5.792,5.803 C5.6602899,5.93388911 5.48167943,6.00662966 5.296,6.005 C5.10997499,6.00689786 4.93095449,5.93413702 4.799,5.803 L0.204,1.207 C0.072163111,1.07394937 -0.00121750401,0.893846387 9.62313189e-05,0.706545264 C0.00140996665,0.519244142 0.0773097323,0.340188219 0.211,0.209 C0.485365732,-0.0664648737 0.930253538,-0.0700311086 1.209,0.201 L5.296,4.288 L5.296,4.288 Z' id='Shape' fill='%23000000'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
background-size: 100% 100%;
transition: transform .2s cubic-bezier(.75,0,.25,1);
}
.navbar .caret--nav:after {
top: 19px;
}
.dropdown--profile .caret--nav:after {
right: 8px;
}
.btn--create {
padding-right: 20px;
.caret--nav:after {
top: 10px;
right: 10px;
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='11px' height='6px' viewBox='0 0 11 6' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3C!-- Generator: Sketch 42 %2836781%29 - http://www.bohemiancoding.com/sketch --%3E%3Ctitle%3EShape%3C/title%3E%3Cdesc%3ECreated with Sketch.%3C/desc%3E%3Cdefs%3E%3C/defs%3E%3Cg id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cpath d='M5.29592111,4.28945339 L9.38192111,0.201453387 C9.66078932,-0.0702383105 10.1064398,-0.0666695379 10.3809211,0.209453387 C10.6609211,0.489453387 10.6609211,0.933453387 10.3879211,1.20745339 L5.79192111,5.80445339 C5.66021101,5.9353425 5.48160054,6.00808305 5.29592111,6.00645339 C5.1098961,6.00835125 4.9308756,5.9355904 4.79892111,5.80445339 L0.203921109,1.20845339 C0.0720842204,1.07540275 -0.00129639464,0.895299774 1.73406884e-05,0.707998651 C0.00133107602,0.520697529 0.0772308417,0.341641606 0.210921109,0.210453387 C0.485286842,-0.0650114866 0.930174648,-0.0685777215 1.20892111,0.202453387 L5.29592111,4.28945339 L5.29592111,4.28945339 Z' id='Shape' fill='%23FCFCFC'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
}
}
.dropdown.open .caret--nav:after {
transform: rotate(180deg);
}
.collapsing, .collapse.in {
padding: 0;
}
.navbar {
min-height: initial;
height: 50px;
border: 1px solid #fff;
border-top: none;
border-radius: 0;
background: #fff;
margin-bottom: 10px;
a.navbar-brand {
padding: 4px 0px 0px 0px;
margin-left: -3px !important;
}
.btn-group.open .dropdown-toggle {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-group .btn:active {
box-shadow: none;
}
}
.navbar-default .navbar-nav > li > a {
color: #000;
font-weight: 500;
&:active, &:hover, &:focus {
color: #000;
}
}
.navbar-default .btn__new button {
font-weight: 500;
}
.navbar-default .navbar-nav > li > a:hover {
//background-color: fade(@redash-gray, 10%);
//text-decoration: underline;
//border-radius: 0;
}
.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus {
background-color: fade(@redash-gray, 15%);
color: #111;
}
.dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus {
background-color: fade(@redash-gray, 15%);
color: #111;
}
.tab-nav {
margin-bottom: 0px;
}
.profile__image--navbar {
border-radius: 100%;
margin-right: 3px;
margin-top: -2px;
}
.profile__image--settings {
border-radius: 100%;
}
.profile__image_thumb {
border-radius: 100%;
margin-right: 3px;
margin-top: -2px;
width: 20px;
height: 20px;
}
.btn__new {
margin-left: 15px;
}
.navbar-btn {
margin-top: 10px;
margin-bottom: 9px;
}
.navbar-brand {
position: absolute;
left: 49%;
margin-left: -50px !important;
display: block;
zoom: 0.9;
margin-top: 3px;
}
.va-top {
vertical-align: top;
}
.navbar {
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
.navbar-collapse {
padding-left: 0;
}
a.dropdown--profile {
padding-top: 10px;
padding-bottom: 10px;
line-height: 2.35;
}
.navbar-inverse {
background-color: @redash-gray;
border: none;
}
}
.menu-search {
margin-top: 2px;
}
.tags-list {
.badge-light {
background: fade(@redash-gray, 10%);
color: fade(@redash-gray, 75%);
}
}
.dropdown-menu--profile {
li {
width: 200px;
}
}
.navbar .collapse.in {
background: #fff;
position: relative;
z-index: 999;
padding: 0 10px 0 10px;
}
// Pagination
.pagination > li > a, .pagination > li > span {
border-radius: 3px !important;
width: 33px;
height: 33px;
line-height: 31px;
}
// Error state
.error-state {
display: flex;
flex-direction: column;
justify-content: flex-start;
text-align: center;
margin-top: 25vh;
padding: 35px;
font-size: 14px;
line-height: 21px;
.error-state__icon {
.zmdi {
font-size: 64px;
color: @redash-gray;
}
}
@media (max-width: 767px) {
margin-top: 10vh;
}
}
// Forms
.form-control {
border-radius: @redash-input-radius;
color: #9E9E9E;
&:focus {
box-shadow: none !important;
color: #111;
border-color: @blue;
}
&:hover {
border-color: @blue;
}
}
// Plotly
text.slicetext {
text-shadow: 1px 1px 5px #333;
}
// Responsive fixes
@media (max-width: 767px) {
.text-center-xs {
text-align: center !important;
}
.query-page-wrapper {
h3 {
font-size: 18px;
}
favorites-control {
margin-top: -3px;
}
}
.navbar-brand {
left: 2%;
}
//Fix navbar collapse
.navbar .collapse.in {
border: none;
.dropdown-menu--profile {
li {
width: auto;
}
}
.dropdown--profile {
.caret--nav:after {
right: initial !important;
}
}
.dropdown--profile__username {
display: inline-block;
}
.nav__main li a {
padding: 10px 15px;
display: block;
text-align: left;
float: none !important;
}
.navbar-form {
margin-bottom: 0;
margin-top: 0;
}
.navbar-right {
margin-bottom: 0;
}
}
}
@media (min-width: 768px) and (max-width: 850px) {
.menu-search {
width: 175px;
}
a.navbar-brand {
display: none !important;
}
}
@media (max-width: 1084px) {
.dropdown--profile__username {
display: none;
}
}
// Cross-browser fixes
// Firefox
@-moz-document url-prefix() {
.caret--nav::after {
height: 7px;
}
.navbar .caret--nav::after {
top: 22px;
}
.navbar .btn--create .caret--nav::after {
top: 12px;
}
}
// IE10+
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.caret--nav::after {
height: 7px;
}
.navbar .caret--nav::after {
top: 22px;
}
.navbar .btn--create .caret--nav::after {
top: 12px;
}
}

View File

@@ -8,6 +8,15 @@
&.inline-tags-control {
display: inline-block;
vertical-align: middle;
}
&.disabled {
opacity: 0.4;
}
}
// This is for using .inline-tags-control in Angular which renders
// a little differently than React (e.g. in Alert.html)
.inline-tags-control .tags-control {
display: inline-block;
}

View File

@@ -19,8 +19,6 @@
@import 'inc/ie-warning';
@import 'inc/flex';
@import 'redash/redash-newstyle';
html, body {
height: 100%;
margin: 0;

View File

@@ -0,0 +1,22 @@
import React, { forwardRef } from 'react';
import AceEditor from 'react-ace';
import './AceEditorInput.less';
function AceEditorInput(props, ref) {
return (
<div className="ace-editor-input">
<AceEditor
ref={ref}
mode="sql"
theme="textmate"
height="100px"
editorProps={{ $blockScrolling: Infinity }}
showPrintMargin={false}
{...props}
/>
</div>
);
}
export default forwardRef(AceEditorInput);

View File

@@ -0,0 +1,11 @@
.ace-editor-input {
// hide ghost cursor when not focused
.ace_hidden-cursors {
opacity: 0;
}
// allow Ant Form feedback icon to hover scrollbar
.ace_scrollbar {
z-index: auto;
}
}

View File

@@ -2,7 +2,7 @@ import React from 'react';
import Tooltip from 'antd/lib/tooltip';
import PropTypes from 'prop-types';
import '@/redash-font/style.less';
import recordEvent from '@/lib/recordEvent';
import recordEvent from '@/services/recordEvent';
export default function AutocompleteToggle({ state, disabled, onToggle }) {
let tooltipMessage = 'Live Autocomplete Enabled';

View File

@@ -0,0 +1,83 @@
import React, { useState } from 'react';
import { react2angular } from 'react2angular';
import Card from 'antd/lib/card';
import Button from 'antd/lib/button';
import Typography from 'antd/lib/typography';
import { clientConfig } from '@/services/auth';
import { HelpTrigger } from '@/components/HelpTrigger';
import DynamicComponent from '@/components/DynamicComponent';
import OrgSettings from '@/services/organizationSettings';
const Text = Typography.Text;
export function BeaconConsent() {
const [hide, setHide] = useState(false);
if (!clientConfig.showBeaconConsentMessage || hide) {
return null;
}
const hideConsentCard = () => {
clientConfig.showBeaconConsentMessage = false;
setHide(true);
};
const confirmConsent = (confirm) => {
let message = '🙏 Thank you.';
if (!confirm) {
message = 'Settings Saved.';
}
OrgSettings.save({ beacon_consent: confirm }, message)
// .then(() => {
// // const settings = get(response, 'settings');
// // this.setState({ settings, formValues: { ...settings } });
// })
.finally(hideConsentCard);
};
return (
<DynamicComponent name="BeaconConsent">
<div className="m-t-10 tiled">
<Card
title={(
<>
Would you be ok with sharing anonymous usage data with the Redash team?{' '}
<HelpTrigger type="USAGE_DATA_SHARING" />
</>
)}
bordered={false}
>
<Text>Help Redash improve by automatically sending anonymous usage data:</Text>
<div className="m-t-5">
<ul>
<li> Number of users, queries, dashboards, alerts, widgets and visualizations.</li>
<li> Types of data sources, alert destinations and visualizations.</li>
</ul>
</div>
<Text>All data is aggregated and will never include any sensitive or private data.</Text>
<div className="m-t-5">
<Button type="primary" className="m-r-5" onClick={() => confirmConsent(true)}>
Yes
</Button>
<Button type="default" onClick={() => confirmConsent(false)}>
No
</Button>
</div>
<div className="m-t-15">
<Text type="secondary">
You can change this setting anytime from the <a href="settings/organization">Organization Settings</a> page.
</Text>
</div>
</Card>
</div>
</DynamicComponent>
);
}
export default function init(ngModule) {
ngModule.component('beaconConsent', react2angular(BeaconConsent));
}
init.init = true;

View File

@@ -2,9 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
export function BigMessage({ message, icon, children }) {
export function BigMessage({ message, icon, children, className }) {
return (
<div className="tiled bg-white p-15 text-center">
<div className={'p-15 text-center ' + className}>
<h3 className="m-t-0 m-b-0">
<i className={'fa ' + icon} />
</h3>
@@ -19,11 +19,13 @@ BigMessage.propTypes = {
message: PropTypes.string,
icon: PropTypes.string.isRequired,
children: PropTypes.node,
className: PropTypes.string,
};
BigMessage.defaultProps = {
message: '',
children: null,
className: 'tiled bg-white',
};
export default function init(ngModule) {

View File

@@ -0,0 +1,80 @@
import React from 'react';
import PropTypes from 'prop-types';
import Button from 'antd/lib/button';
import Tooltip from 'antd/lib/tooltip';
import './CodeBlock.less';
export default class CodeBlock extends React.Component {
static propTypes = {
copyable: PropTypes.bool,
children: PropTypes.node,
};
static defaultProps = {
copyable: false,
children: null,
};
state = { copied: null };
constructor(props) {
super(props);
this.ref = React.createRef();
this.copyFeatureEnabled = props.copyable && document.queryCommandSupported('copy');
this.resetCopyState = null;
}
componentWillUnmount() {
if (this.resetCopyState) {
clearTimeout(this.resetCopyState);
}
}
copy = () => {
// select text
window.getSelection().selectAllChildren(this.ref.current);
// copy
try {
const success = document.execCommand('copy');
if (!success) {
throw new Error();
}
this.setState({ copied: 'Copied!' });
} catch (err) {
this.setState({
copied: 'Copy failed',
});
}
// reset selection
window.getSelection().removeAllRanges();
// reset tooltip
this.resetCopyState = setTimeout(() => this.setState({ copied: null }), 2000);
};
render() {
const { copyable, children, ...props } = this.props;
const copyButton = (
<Tooltip title={this.state.copied || 'Copy'}>
<Button
icon="copy"
type="dashed"
size="small"
onClick={this.copy}
/>
</Tooltip>
);
return (
<div className="code-block">
<code {...props} ref={this.ref}>
{children}
</code>
{this.copyFeatureEnabled && copyButton}
</div>
);
}
}

View File

@@ -0,0 +1,23 @@
@import '~antd/lib/button/style/index';
.code-block {
background: rgba(0, 0, 0, 0.06);
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 2px;
padding: 3px 27px 3px 3px;
position: relative;
min-height: 32px;
code {
padding: 0;
font-size: 85%;
}
.@{btn-prefix-cls} {
position: absolute;
right: 3px;
bottom: 3px;
padding-left: 3px !important;
padding-right: 3px !important;
}
}

View File

@@ -0,0 +1,24 @@
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import AntCollapse from 'antd/lib/collapse';
export default function Collapse({ collapsed, children, className, ...props }) {
return (
<AntCollapse {...props} activeKey={collapsed ? null : 'content'} className={cx(className, 'ant-collapse-headerless')}>
<AntCollapse.Panel key="content" header="">{children}</AntCollapse.Panel>
</AntCollapse>
);
}
Collapse.propTypes = {
collapsed: PropTypes.bool,
children: PropTypes.node,
className: PropTypes.string,
};
Collapse.defaultProps = {
collapsed: true,
children: null,
className: '',
};

View File

@@ -0,0 +1,12 @@
// ANGULAR_REMOVE_ME
import { react2angular } from 'react2angular';
import ColorPicker from '@/components/ColorPicker';
import './color-box.less';
export default function init(ngModule) {
ngModule.component('colorBox', react2angular(ColorPicker.Swatch));
}
init.init = true;

View File

@@ -0,0 +1,93 @@
import { isNil, isArray, chunk, map, filter, toPairs } from 'lodash';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import tinycolor from 'tinycolor2';
import TextInput from 'antd/lib/input';
import Typography from 'antd/lib/typography';
import Swatch from './Swatch';
import './input.less';
function preparePresets(presetColors, presetColumns) {
presetColors = isArray(presetColors) ? map(presetColors, v => [null, v]) : toPairs(presetColors);
presetColors = map(presetColors, ([title, value]) => {
if (isNil(value)) {
return [title, null];
}
value = tinycolor(value);
if (value.isValid()) {
return [title, '#' + value.toHex().toUpperCase()];
}
return null;
});
return chunk(filter(presetColors), presetColumns);
}
function validateColor(value, callback, prefix = '#') {
if (isNil(value)) {
callback(null);
}
value = tinycolor(value);
if (value.isValid()) {
callback(prefix + value.toHex().toUpperCase());
}
}
export default function Input({ color, presetColors, presetColumns, onChange, onPressEnter }) {
const [inputValue, setInputValue] = useState('');
const [isInputFocused, setIsInputFocused] = useState(false);
const presets = preparePresets(presetColors, presetColumns);
function handleInputChange(value) {
setInputValue(value);
validateColor(value, onChange);
}
useEffect(() => {
if (!isInputFocused) {
validateColor(color, setInputValue, '');
}
}, [color, isInputFocused]);
return (
<React.Fragment>
{map(presets, (group, index) => (
<div className="color-picker-input-swatches" key={`preset-row-${index}`}>
{map(group, ([title, value]) => (
<Swatch key={value} color={value} title={title} size={30} onClick={() => validateColor(value, onChange)} />
))}
</div>
))}
<div className="color-picker-input">
<TextInput
addonBefore={<Typography.Text type="secondary">#</Typography.Text>}
value={inputValue}
onChange={e => handleInputChange(e.target.value)}
onFocus={() => setIsInputFocused(true)}
onBlur={() => setIsInputFocused(false)}
onPressEnter={onPressEnter}
/>
</div>
</React.Fragment>
);
}
Input.propTypes = {
color: PropTypes.string,
presetColors: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.string), // array of colors (no tooltips)
PropTypes.objectOf(PropTypes.string), // color name => color value
]),
presetColumns: PropTypes.number,
onChange: PropTypes.func,
onPressEnter: PropTypes.func,
};
Input.defaultProps = {
color: '#FFFFFF',
presetColors: null,
presetColumns: 8,
onChange: () => {},
onPressEnter: () => {},
};

View File

@@ -0,0 +1,37 @@
import { isString } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import Tooltip from 'antd/lib/tooltip';
import './swatch.less';
export default function Swatch({ className, color, title, size, ...props }) {
const result = (
<span
className={`color-swatch ${className}`}
style={{ backgroundColor: color, width: size }}
{...props}
/>
);
if (isString(title) && (title !== '')) {
return (
<Tooltip title={title} mouseEnterDelay={0} mouseLeaveDelay={0}>{result}</Tooltip>
);
}
return result;
}
Swatch.propTypes = {
className: PropTypes.string,
title: PropTypes.string,
color: PropTypes.string,
size: PropTypes.number,
};
Swatch.defaultProps = {
className: '',
title: null,
color: 'transparent',
size: 12,
};

View File

@@ -0,0 +1,128 @@
import { toString } from 'lodash';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import tinycolor from 'tinycolor2';
import Popover from 'antd/lib/popover';
import Card from 'antd/lib/card';
import Tooltip from 'antd/lib/tooltip';
import Icon from 'antd/lib/icon';
import ColorInput from './Input';
import Swatch from './Swatch';
import './index.less';
function validateColor(value, fallback = null) {
value = tinycolor(value);
return value.isValid() ? '#' + value.toHex().toUpperCase() : fallback;
}
export default function ColorPicker({
color, placement, presetColors, presetColumns, triggerSize, interactive, children, onChange,
}) {
const [visible, setVisible] = useState(false);
const [currentColor, setCurrentColor] = useState('');
function handleApply() {
setVisible(false);
if (!interactive) {
onChange(currentColor);
}
}
function handleCancel() {
setVisible(false);
}
const actions = [];
if (!interactive) {
actions.push((
<Tooltip key="cancel" title="Cancel">
<Icon type="close" onClick={handleCancel} />
</Tooltip>
));
actions.push((
<Tooltip key="apply" title="Apply">
<Icon type="check" onClick={handleApply} />
</Tooltip>
));
}
function handleInputChange(newColor) {
setCurrentColor(newColor);
if (interactive) {
onChange(newColor);
}
}
useEffect(() => {
if (visible) {
setCurrentColor(validateColor(color));
}
}, [color, visible]);
return (
<Popover
overlayClassName={`color-picker ${interactive ? 'color-picker-interactive' : 'color-picker-with-actions'}`}
overlayStyle={{ '--color-picker-selected-color': currentColor }}
content={(
<Card
className="color-picker-panel"
bordered={false}
title={toString(currentColor).toUpperCase()}
headStyle={{
backgroundColor: currentColor,
color: tinycolor(currentColor).isLight() ? '#000000' : '#ffffff',
}}
actions={actions}
>
<ColorInput
color={currentColor}
presetColors={presetColors}
presetColumns={presetColumns}
onChange={handleInputChange}
onPressEnter={handleApply}
/>
</Card>
)}
trigger="click"
placement={placement}
visible={visible}
onVisibleChange={setVisible}
>
{children || (<Swatch className="color-picker-trigger" color={validateColor(color)} size={triggerSize} />)}
</Popover>
);
}
ColorPicker.propTypes = {
color: PropTypes.string,
placement: PropTypes.oneOf([
'top', 'left', 'right', 'bottom',
'topLeft', 'topRight', 'bottomLeft', 'bottomRight',
'leftTop', 'leftBottom', 'rightTop', 'rightBottom',
]),
presetColors: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.string), // array of colors (no tooltips)
PropTypes.objectOf(PropTypes.string), // color name => color value
]),
presetColumns: PropTypes.number,
triggerSize: PropTypes.number,
interactive: PropTypes.bool,
children: PropTypes.node,
onChange: PropTypes.func,
};
ColorPicker.defaultProps = {
color: '#FFFFFF',
placement: 'top',
presetColors: null,
presetColumns: 8,
triggerSize: 30,
interactive: false,
children: null,
onChange: () => {},
};
ColorPicker.Input = ColorInput;
ColorPicker.Swatch = Swatch;

View File

@@ -0,0 +1,40 @@
.color-picker {
&.color-picker-with-actions {
&.ant-popover-placement-top,
&.ant-popover-placement-topLeft,
&.ant-popover-placement-topRight,
&.ant-popover-placement-leftBottom,
&.ant-popover-placement-rightBottom {
> .ant-popover-content > .ant-popover-arrow {
border-color: #fafafa; // same as card actions
}
}
}
&.ant-popover-placement-bottom,
&.ant-popover-placement-bottomLeft,
&.ant-popover-placement-bottomRight,
&.ant-popover-placement-leftTop,
&.ant-popover-placement-rightTop {
> .ant-popover-content > .ant-popover-arrow {
border-color: var(--color-picker-selected-color);
}
}
.ant-popover-inner-content {
padding: 0;
}
.ant-card-head {
text-align: center;
border-bottom-color: rgba(0, 0, 0, 0.1);
}
.ant-card-body {
padding: 10px;
}
}
.color-picker-trigger {
cursor: pointer;
}

View File

@@ -0,0 +1,19 @@
.color-picker-input-swatches {
margin: 0 0 10px 0;
text-align: left;
white-space: nowrap;
.color-swatch {
cursor: pointer;
margin: 0 10px 0 0;
&:last-child {
margin-right: 0;
}
}
}
.color-picker-input {
text-align: left;
white-space: nowrap;
}

View File

@@ -0,0 +1,30 @@
.color-swatch {
display: inline-block;
box-sizing: border-box;
vertical-align: middle;
border-radius: 2px;
overflow: hidden;
width: 12px;
@cell-size: 12px;
@cell-color: rgba(0, 0, 0, 0.1);
background-color: transparent;
background-image:
linear-gradient(45deg, @cell-color 25%, transparent 25%),
linear-gradient(-45deg, @cell-color 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, @cell-color 75%),
linear-gradient(-45deg, transparent 75%, @cell-color 75%);
background-size: @cell-size @cell-size;
background-position: 0 0, 0 @cell-size/2, @cell-size/2 -@cell-size/2, -@cell-size/2 0px;
&:before {
content: "";
display: block;
padding-top: ~"calc(100% - 2px)";
background-color: inherit;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 2px;
overflow: hidden;
}
}

View File

@@ -0,0 +1,191 @@
import React from 'react';
import PropTypes from 'prop-types';
import { isEmpty, toUpper, includes } from 'lodash';
import Button from 'antd/lib/button';
import List from 'antd/lib/list';
import Modal from 'antd/lib/modal';
import Input from 'antd/lib/input';
import Steps from 'antd/lib/steps';
import { wrap as wrapDialog, DialogPropType } from '@/components/DialogWrapper';
import { PreviewCard } from '@/components/PreviewCard';
import EmptyState from '@/components/items-list/components/EmptyState';
import DynamicForm from '@/components/dynamic-form/DynamicForm';
import helper from '@/components/dynamic-form/dynamicFormHelper';
import { HelpTrigger, TYPES as HELP_TRIGGER_TYPES } from '@/components/HelpTrigger';
const { Step } = Steps;
const { Search } = Input;
const StepEnum = {
SELECT_TYPE: 0,
CONFIGURE_IT: 1,
DONE: 2,
};
class CreateSourceDialog extends React.Component {
static propTypes = {
dialog: DialogPropType.isRequired,
types: PropTypes.arrayOf(PropTypes.object),
sourceType: PropTypes.string.isRequired,
imageFolder: PropTypes.string.isRequired,
helpTriggerPrefix: PropTypes.string,
onCreate: PropTypes.func.isRequired,
};
static defaultProps = {
types: [],
helpTriggerPrefix: null,
};
state = {
searchText: '',
selectedType: null,
savingSource: false,
currentStep: StepEnum.SELECT_TYPE,
};
selectType = (selectedType) => {
this.setState({ selectedType, currentStep: StepEnum.CONFIGURE_IT });
};
resetType = () => {
if (this.state.currentStep === StepEnum.CONFIGURE_IT) {
this.setState({ searchText: '', selectedType: null, currentStep: StepEnum.SELECT_TYPE });
}
};
createSource = (values, successCallback, errorCallback) => {
const { selectedType, savingSource } = this.state;
if (!savingSource) {
this.setState({ savingSource: true, currentStep: StepEnum.DONE });
this.props.onCreate(selectedType, values).then((data) => {
successCallback('Saved.');
this.props.dialog.close({ success: true, data });
}).catch((error) => {
this.setState({ savingSource: false, currentStep: StepEnum.CONFIGURE_IT });
errorCallback(error.message);
});
}
};
renderTypeSelector() {
const { types } = this.props;
const { searchText } = this.state;
const filteredTypes = types.filter(type => isEmpty(searchText) ||
includes(type.name.toLowerCase(), searchText.toLowerCase()));
return (
<div className="m-t-10">
<Search
placeholder="Search..."
onChange={e => this.setState({ searchText: e.target.value })}
autoFocus
data-test="SearchSource"
/>
<div className="scrollbox p-5 m-t-10" style={{ minHeight: '30vh', maxHeight: '40vh' }}>
{isEmpty(filteredTypes) ? (<EmptyState className="" />) : (
<List
size="small"
dataSource={filteredTypes}
renderItem={item => this.renderItem(item)}
/>
)}
</div>
</div>
);
}
renderForm() {
const { imageFolder, helpTriggerPrefix } = this.props;
const { selectedType } = this.state;
const fields = helper.getFields(selectedType);
const helpTriggerType = `${helpTriggerPrefix}${toUpper(selectedType.type)}`;
return (
<div className="p-5">
<div className="d-flex justify-content-center align-items-center">
<img
className="p-5"
src={`${imageFolder}/${selectedType.type}.png`}
alt={selectedType.name}
width="48"
/>
<h4 className="m-0">{selectedType.name}</h4>
</div>
<div className="text-right">
{HELP_TRIGGER_TYPES[helpTriggerType] && (
<HelpTrigger className="f-13" type={helpTriggerType}>
Setup Instructions <i className="fa fa-question-circle" />
</HelpTrigger>
)}
</div>
<DynamicForm
id="sourceForm"
fields={fields}
onSubmit={this.createSource}
feedbackIcons
hideSubmitButton
/>
</div>
);
}
renderItem(item) {
const { imageFolder } = this.props;
return (
<List.Item
className="p-l-10 p-r-10 clickable"
onClick={() => this.selectType(item)}
>
<PreviewCard title={item.name} imageUrl={`${imageFolder}/${item.type}.png`} roundedImage={false}>
<i className="fa fa-angle-double-right" />
</PreviewCard>
</List.Item>
);
}
render() {
const { currentStep, savingSource } = this.state;
const { dialog, sourceType } = this.props;
return (
<Modal
{...dialog.props}
title={`Create a New ${sourceType}`}
footer={(currentStep === StepEnum.SELECT_TYPE) ? [
(<Button key="cancel" onClick={() => dialog.dismiss()}>Cancel</Button>),
(<Button key="submit" type="primary" disabled>Create</Button>),
] : [
(<Button key="previous" onClick={this.resetType}>Previous</Button>),
(
<Button
key="submit"
htmlType="submit"
form="sourceForm"
type="primary"
loading={savingSource}
data-test="CreateSourceButton"
>
Create
</Button>
),
]}
>
<div data-test="CreateSourceDialog">
<Steps className="hidden-xs m-b-10" size="small" current={currentStep} progressDot>
{currentStep === StepEnum.CONFIGURE_IT ? (
<Step
title={<a>Type Selection</a>}
className="clickable"
onClick={this.resetType}
/>
) : (<Step title="Type Selection" />)}
<Step title="Configuration" />
<Step title="Done" />
</Steps>
{currentStep === StepEnum.SELECT_TYPE && this.renderTypeSelector()}
{currentStep !== StepEnum.SELECT_TYPE && this.renderForm()}
</div>
</Modal>
);
}
}
export default wrapDialog(CreateSourceDialog);

View File

@@ -1,48 +1,49 @@
import moment from 'moment';
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import DatePicker from 'antd/lib/date-picker';
import { clientConfig } from '@/services/auth';
import { Moment } from '@/components/proptypes';
function DateInput({
const DateInput = React.forwardRef(({
defaultValue,
value,
onSelect,
// eslint-disable-next-line react/prop-types
clientConfig,
}) {
className,
...props
}, ref) => {
const format = clientConfig.dateFormat || 'YYYY-MM-DD';
const additionalAttributes = {};
if (value && value.isValid()) {
additionalAttributes.defaultValue = value;
if (defaultValue && defaultValue.isValid()) {
additionalAttributes.defaultValue = defaultValue;
}
if (value === null || (value && value.isValid())) {
additionalAttributes.value = value;
}
return (
<DatePicker
ref={ref}
className={className}
{...additionalAttributes}
format={format}
placeholder="Select Date"
onChange={onSelect}
{...props}
/>
);
}
});
DateInput.propTypes = {
value: (props, propName, componentName) => {
const value = props[propName];
if ((value !== null) && !moment.isMoment(value)) {
return new Error('Prop `' + propName + '` supplied to `' + componentName +
'` should be a Moment.js instance.');
}
},
defaultValue: Moment,
value: Moment,
onSelect: PropTypes.func,
className: PropTypes.string,
};
DateInput.defaultProps = {
value: null,
defaultValue: null,
value: undefined,
onSelect: () => {},
className: '',
};
export default function init(ngModule) {
ngModule.component('dateInput', react2angular(DateInput, null, ['clientConfig']));
}
init.init = true;
export default DateInput;

View File

@@ -1,53 +1,51 @@
import moment from 'moment';
import { isArray } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import { RangePicker } from 'antd/lib/date-picker';
import DatePicker from 'antd/lib/date-picker';
import { clientConfig } from '@/services/auth';
import { Moment } from '@/components/proptypes';
function DateRangeInput({
const { RangePicker } = DatePicker;
const DateRangeInput = React.forwardRef(({
defaultValue,
value,
onSelect,
// eslint-disable-next-line react/prop-types
clientConfig,
}) {
className,
...props
}, ref) => {
const format = clientConfig.dateFormat || 'YYYY-MM-DD';
const additionalAttributes = {};
if (isArray(value) && value[0].isValid() && value[1].isValid()) {
additionalAttributes.defaultValue = value;
if (isArray(defaultValue) && defaultValue[0].isValid() && defaultValue[1].isValid()) {
additionalAttributes.defaultValue = defaultValue;
}
if (value === null || (isArray(value) && value[0].isValid() && value[1].isValid())) {
additionalAttributes.value = value;
}
return (
<RangePicker
ref={ref}
className={className}
{...additionalAttributes}
format={format}
onChange={onSelect}
{...props}
/>
);
}
});
DateRangeInput.propTypes = {
value: (props, propName, componentName) => {
const value = props[propName];
if (
(value !== null) && !(
isArray(value) && (value.length === 2) &&
moment.isMoment(value[0]) && moment.isMoment(value[1])
)
) {
return new Error('Prop `' + propName + '` supplied to `' + componentName +
'` should be an array of two Moment.js instances.');
}
},
defaultValue: PropTypes.arrayOf(Moment),
value: PropTypes.arrayOf(Moment),
onSelect: PropTypes.func,
className: PropTypes.string,
};
DateRangeInput.defaultProps = {
value: null,
defaultValue: null,
value: undefined,
onSelect: () => {},
className: '',
};
export default function init(ngModule) {
ngModule.component('dateRangeInput', react2angular(DateRangeInput, null, ['clientConfig']));
}
init.init = true;
export default DateRangeInput;

View File

@@ -1,53 +1,54 @@
import moment from 'moment';
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import DatePicker from 'antd/lib/date-picker';
import { clientConfig } from '@/services/auth';
import { Moment } from '@/components/proptypes';
function DateTimeInput({
const DateTimeInput = React.forwardRef(({
defaultValue,
value,
withSeconds,
onSelect,
// eslint-disable-next-line react/prop-types
clientConfig,
}) {
className,
...props
}, ref) => {
const format = (clientConfig.dateFormat || 'YYYY-MM-DD') +
(withSeconds ? ' HH:mm:ss' : ' HH:mm');
const additionalAttributes = {};
if (value && value.isValid()) {
additionalAttributes.defaultValue = value;
if (defaultValue && defaultValue.isValid()) {
additionalAttributes.defaultValue = defaultValue;
}
if (value === null || (value && value.isValid())) {
additionalAttributes.value = value;
}
return (
<DatePicker
ref={ref}
className={className}
showTime
{...additionalAttributes}
format={format}
placeholder="Select Date and Time"
onChange={onSelect}
{...props}
/>
);
}
});
DateTimeInput.propTypes = {
value: (props, propName, componentName) => {
const value = props[propName];
if ((value !== null) && !moment.isMoment(value)) {
return new Error('Prop `' + propName + '` supplied to `' + componentName +
'` should be a Moment.js instance.');
}
},
defaultValue: Moment,
value: Moment,
withSeconds: PropTypes.bool,
onSelect: PropTypes.func,
className: PropTypes.string,
};
DateTimeInput.defaultProps = {
value: null,
defaultValue: null,
value: undefined,
withSeconds: false,
onSelect: () => {},
className: '',
};
export default function init(ngModule) {
ngModule.component('dateTimeInput', react2angular(DateTimeInput, null, ['clientConfig']));
}
init.init = true;
export default DateTimeInput;

View File

@@ -1,59 +1,56 @@
import moment from 'moment';
import { isArray } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import { RangePicker } from 'antd/lib/date-picker';
import DatePicker from 'antd/lib/date-picker';
import { clientConfig } from '@/services/auth';
import { Moment } from '@/components/proptypes';
function DateTimeRangeInput({
const { RangePicker } = DatePicker;
const DateTimeRangeInput = React.forwardRef(({
defaultValue,
value,
withSeconds,
onSelect,
// eslint-disable-next-line react/prop-types
clientConfig,
}) {
className,
...props
}, ref) => {
const format = (clientConfig.dateFormat || 'YYYY-MM-DD') +
(withSeconds ? ' HH:mm:ss' : ' HH:mm');
const additionalAttributes = {};
if (isArray(value) && value[0].isValid() && value[1].isValid()) {
additionalAttributes.defaultValue = value;
if (isArray(defaultValue) && defaultValue[0].isValid() && defaultValue[1].isValid()) {
additionalAttributes.defaultValue = defaultValue;
}
if (value === null || (isArray(value) && value[0].isValid() && value[1].isValid())) {
additionalAttributes.value = value;
}
return (
<RangePicker
ref={ref}
className={className}
showTime
{...additionalAttributes}
format={format}
onChange={onSelect}
{...props}
/>
);
}
});
DateTimeRangeInput.propTypes = {
value: (props, propName, componentName) => {
const value = props[propName];
if (
(value !== null) && !(
isArray(value) && (value.length === 2) &&
moment.isMoment(value[0]) && moment.isMoment(value[1])
)
) {
return new Error('Prop `' + propName + '` supplied to `' + componentName +
'` should be an array of two Moment.js instances.');
}
},
defaultValue: PropTypes.arrayOf(Moment),
value: PropTypes.arrayOf(Moment),
withSeconds: PropTypes.bool,
onSelect: PropTypes.func,
className: PropTypes.string,
};
DateTimeRangeInput.defaultProps = {
value: null,
defaultValue: null,
value: undefined,
withSeconds: false,
onSelect: () => {},
className: '',
};
export default function init(ngModule) {
ngModule.component('dateTimeRangeInput', react2angular(DateTimeRangeInput, null, ['clientConfig']));
}
init.init = true;
export default DateTimeRangeInput;

View File

@@ -0,0 +1,209 @@
import { isFunction } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
/**
Wrapper for dialogs based on Ant's <Modal> component.
Using wrapped dialogs
=====================
Wrapped component is an object with two fields:
{
showModal: (dialogProps) => object({
result: Promise,
close: (result) => void,
dismiss: (reason) => void,
}),
Component: React.Component, // wrapped dialog component
}
To open dialog, use `showModal` method; optionally you can pass additional properties that
will be expanded on wrapped component:
const dialog = SomeWrappedDialog.showModal()
const dialog = SomeWrappedDialog.showModal({ greeting: 'Hello' })
To get result of modal, use `result` property:
dialog.result
.then(...) // pressed OK button or used `close` method; resolved value is a result of dialog
.catch(...) // pressed Cancel button or used `dismiss` method; optional argument is a rejection reason.
Also, dialog has `close` and `dismiss` methods that allows to close dialog by caller. Passed arguments
will be used to resolve/reject `dialog.result` promise. `update` methods allows to pass new properties
to dialog.
Creating a dialog
================
1. Add imports:
import { wrap as wrapDialog, DialogPropType } from 'path/to/DialogWrapper';
2. define a `dialog` property on your component:
propTypes = {
dialog: DialogPropType.isRequired,
};
`dialog` property is an object:
{
props: object, // properties for <Modal> component;
close: (result) => void, // method to confirm dialog; `result` will be returned to caller
dismiss: (reason) => void, // method to reject dialog; `reason` will be returned to caller
}
3. expand additional properties on <Modal> component:
render() {
const { dialog } = this.props;
return (
<Modal {...dialog.props}>
);
}
4. wrap your component and export it:
export default wrapDialog(YourComponent).
Your component is ready to use. Wrapper will manage <Modal>'s visibility and events.
If you want to override behavior of `onOk`/`onCancel` - don't forget to close dialog:
customOkHandler() {
this.saveData().then(() => {
this.props.dialog.close({ success: true }); // or dismiss();
});
}
render() {
const { dialog } = this.props;
return (
<Modal {...dialog.props} onOk={() => this.customOkHandler()}>
);
}
Settings
========
You can setup this wrapper to use custom `Promise` library (for example, Bluebird):
import DialogWrapper from 'path/to/DialogWrapper';
import Promise from 'bluebird';
DialogWrapper.Promise = Promise;
It could be useful to avoid `unhandledrejection` exception that would fire with native Promises,
or when some custom Promise library is used in application.
*/
export const DialogPropType = PropTypes.shape({
props: PropTypes.shape({
visible: PropTypes.bool,
onOk: PropTypes.func,
onCancel: PropTypes.func,
afterClose: PropTypes.func,
}).isRequired,
close: PropTypes.func.isRequired,
dismiss: PropTypes.func.isRequired,
});
// default export of module
const DialogWrapper = {
Promise,
DialogPropType,
wrap() {},
};
function openDialog(DialogComponent, props) {
const dialog = {
props: {
visible: true,
onOk: () => {},
onCancel: () => {},
afterClose: () => {},
},
close: () => {},
dismiss: () => {},
};
const dialogResult = {
resolve: () => {},
reject: () => {},
};
const container = document.createElement('div');
document.body.appendChild(container);
function render() {
ReactDOM.render(<DialogComponent {...props} dialog={dialog} />, container);
}
function destroyDialog() {
// Allow calling chain to roll up, and then destroy component
setTimeout(() => {
ReactDOM.unmountComponentAtNode(container);
document.body.removeChild(container);
}, 10);
}
function closeDialog(result) {
dialogResult.resolve(result);
dialog.props.visible = false;
render();
}
function dismissDialog(reason) {
dialogResult.reject(reason);
dialog.props.visible = false;
render();
}
dialog.props.onOk = closeDialog;
dialog.props.onCancel = dismissDialog;
dialog.props.afterClose = destroyDialog;
dialog.close = closeDialog;
dialog.dismiss = dismissDialog;
const result = {
close: closeDialog,
dismiss: dismissDialog,
update: (newProps) => {
props = { ...props, ...newProps };
render();
},
result: new DialogWrapper.Promise((resolve, reject) => {
dialogResult.resolve = resolve;
dialogResult.reject = reject;
}),
};
render(); // show it only when all structures initialized to avoid unnecessary re-rendering
// Some known libraries support
// Bluebird: http://bluebirdjs.com/docs/api/suppressunhandledrejections.html
if (isFunction(result.result.suppressUnhandledRejections)) {
result.result.suppressUnhandledRejections();
}
return result;
}
export function wrap(DialogComponent) {
return {
Component: DialogComponent,
showModal: props => openDialog(DialogComponent, props),
};
}
DialogWrapper.wrap = wrap;
export default DialogWrapper;

View File

@@ -0,0 +1,50 @@
import { isFunction, isString } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
const componentsRegistry = new Map();
const activeInstances = new Set();
export function registerComponent(name, component) {
if (isString(name) && name !== '') {
componentsRegistry.set(name, isFunction(component) ? component : null);
// Refresh active DynamicComponent instances which use this component
activeInstances.forEach((dynamicComponent) => {
if (dynamicComponent.props.name === name) {
dynamicComponent.forceUpdate();
}
});
}
}
export function unregisterComponent(name) {
registerComponent(name, null);
}
export default class DynamicComponent extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
children: PropTypes.node,
};
static defaultProps = {
children: null,
};
componentDidMount() {
activeInstances.add(this);
}
componentWillUnmount() {
activeInstances.delete(this);
}
render() {
const { name, children, ...props } = this.props;
const RealComponent = componentsRegistry.get(name);
if (!RealComponent) {
return children;
}
return <RealComponent {...props}>{children}</RealComponent>;
}
}

View File

@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import { trim } from 'lodash';
export class EditInPlace extends React.Component {
static propTypes = {
@@ -18,6 +19,7 @@ export class EditInPlace extends React.Component {
placeholder: '',
value: '',
};
constructor(props) {
super(props);
this.state = {
@@ -39,7 +41,7 @@ export class EditInPlace extends React.Component {
};
stopEditing = () => {
const newValue = this.inputRef.current.value;
const newValue = trim(this.inputRef.current.value);
const ignorableBlank = this.props.ignoreBlanks && newValue === '';
if (!ignorableBlank && newValue !== this.props.value) {
this.props.onDone(newValue);
@@ -67,8 +69,7 @@ export class EditInPlace extends React.Component {
</span>
);
renderEdit = () =>
React.createElement(this.props.editor, {
renderEdit = () => React.createElement(this.props.editor, {
ref: this.inputRef,
className: 'rd-form-control',
defaultValue: this.props.value,

View File

@@ -0,0 +1,254 @@
import { includes, words, capitalize, clone, isNull } from 'lodash';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import Checkbox from 'antd/lib/checkbox';
import Modal from 'antd/lib/modal';
import Form from 'antd/lib/form';
import Button from 'antd/lib/button';
import Select from 'antd/lib/select';
import Input from 'antd/lib/input';
import Divider from 'antd/lib/divider';
import { wrap as wrapDialog, DialogPropType } from '@/components/DialogWrapper';
import { QuerySelector } from '@/components/QuerySelector';
import { Query } from '@/services/query';
const { Option } = Select;
const formItemProps = { labelCol: { span: 6 }, wrapperCol: { span: 16 } };
function getDefaultTitle(text) {
return capitalize(words(text).join(' ')); // humanize
}
function isTypeDateRange(type) {
return /-range/.test(type);
}
function joinExampleList(multiValuesOptions) {
const { prefix, suffix } = multiValuesOptions;
return ['value1', 'value2', 'value3']
.map(value => `${prefix}${value}${suffix}`)
.join(',');
}
function NameInput({ name, type, onChange, existingNames, setValidation }) {
let helpText = '';
let validateStatus = '';
if (!name) {
helpText = 'Choose a keyword for this parameter';
setValidation(false);
} else if (includes(existingNames, name)) {
helpText = 'Parameter with this name already exists';
setValidation(false);
validateStatus = 'error';
} else {
if (isTypeDateRange(type)) {
helpText = (
<React.Fragment>
Appears in query as {' '}
<code style={{ display: 'inline-block', color: 'inherit' }}>
{`{{${name}.start}} {{${name}.end}}`}
</code>
</React.Fragment>
);
}
setValidation(true);
}
return (
<Form.Item
required
label="Keyword"
help={helpText}
validateStatus={validateStatus}
{...formItemProps}
>
<Input onChange={e => onChange(e.target.value)} autoFocus />
</Form.Item>
);
}
NameInput.propTypes = {
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
existingNames: PropTypes.arrayOf(PropTypes.string).isRequired,
setValidation: PropTypes.func.isRequired,
type: PropTypes.string.isRequired,
};
function EditParameterSettingsDialog(props) {
const [param, setParam] = useState(clone(props.parameter));
const [isNameValid, setIsNameValid] = useState(true);
const [initialQuery, setInitialQuery] = useState();
const isNew = !props.parameter.name;
// fetch query by id
useEffect(() => {
const { queryId } = props.parameter;
if (queryId) {
Query.get({ id: queryId }, (query) => {
setInitialQuery(query);
});
}
}, []);
function isFulfilled() {
// name
if (!isNameValid) {
return false;
}
// title
if (param.title === '') {
return false;
}
// query
if (param.type === 'query' && !param.queryId) {
return false;
}
return true;
}
function onConfirm(e) {
// update title to default
if (!param.title) {
// forced to do this cause param won't update in time for save
param.title = getDefaultTitle(param.name);
setParam(param);
}
props.dialog.close(param);
e.preventDefault(); // stops form redirect
}
return (
<Modal
{...props.dialog.props}
title={isNew ? 'Add Parameter' : param.name}
width={600}
footer={[(
<Button key="cancel" onClick={props.dialog.dismiss}>Cancel</Button>
), (
<Button key="submit" htmlType="submit" disabled={!isFulfilled()} type="primary" form="paramForm" data-test="SaveParameterSettings">
{isNew ? 'Add Parameter' : 'OK'}
</Button>
)]}
>
<Form layout="horizontal" onSubmit={onConfirm} id="paramForm">
{isNew && (
<NameInput
name={param.name}
onChange={name => setParam({ ...param, name })}
setValidation={setIsNameValid}
existingNames={props.existingParams}
type={param.type}
/>
)}
<Form.Item label="Title" {...formItemProps}>
<Input
value={isNull(param.title) ? getDefaultTitle(param.name) : param.title}
onChange={e => setParam({ ...param, title: e.target.value })}
data-test="ParameterTitleInput"
/>
</Form.Item>
<Form.Item label="Type" {...formItemProps}>
<Select value={param.type} onChange={type => setParam({ ...param, type })} data-test="ParameterTypeSelect">
<Option value="text" data-test="TextParameterTypeOption">Text</Option>
<Option value="number" data-test="NumberParameterTypeOption">Number</Option>
<Option value="enum">Dropdown List</Option>
<Option value="query">Query Based Dropdown List</Option>
<Option disabled key="dv1">
<Divider className="select-option-divider" />
</Option>
<Option value="date" data-test="DateParameterTypeOption">Date</Option>
<Option value="datetime-local" data-test="DateTimeParameterTypeOption">Date and Time</Option>
<Option value="datetime-with-seconds">Date and Time (with seconds)</Option>
<Option disabled key="dv2">
<Divider className="select-option-divider" />
</Option>
<Option value="date-range" data-test="DateRangeParameterTypeOption">Date Range</Option>
<Option value="datetime-range">Date and Time Range</Option>
<Option value="datetime-range-with-seconds">Date and Time Range (with seconds)</Option>
</Select>
</Form.Item>
{param.type === 'enum' && (
<Form.Item label="Values" help="Dropdown list values (newline delimited)" {...formItemProps}>
<Input.TextArea
rows={3}
value={param.enumOptions}
onChange={e => setParam({ ...param, enumOptions: e.target.value })}
/>
</Form.Item>
)}
{param.type === 'query' && (
<Form.Item label="Query" help="Select query to load dropdown values from" {...formItemProps}>
<QuerySelector
selectedQuery={initialQuery}
onChange={q => setParam({ ...param, queryId: q && q.id })}
type="select"
/>
</Form.Item>
)}
{(param.type === 'enum' || param.type === 'query') && (
<Form.Item className="m-b-0" label=" " colon={false} {...formItemProps}>
<Checkbox
defaultChecked={!!param.multiValuesOptions}
onChange={e => setParam({ ...param,
multiValuesOptions: e.target.checked ? {
prefix: '',
suffix: '',
separator: ',',
} : null })}
data-test="AllowMultipleValuesCheckbox"
>
Allow multiple values
</Checkbox>
</Form.Item>
)}
{(param.type === 'enum' || param.type === 'query') && param.multiValuesOptions && (
<Form.Item
label="Quotation"
help={(
<React.Fragment>
Placed in query as: <code>{joinExampleList(param.multiValuesOptions)}</code>
</React.Fragment>
)}
{...formItemProps}
>
<Select
value={param.multiValuesOptions.prefix}
onChange={quoteOption => setParam({ ...param,
multiValuesOptions: {
...param.multiValuesOptions,
prefix: quoteOption,
suffix: quoteOption,
} })}
data-test="QuotationSelect"
>
<Option value="">None (default)</Option>
<Option value="'">Single Quotation Mark</Option>
<Option value={'"'} data-test="DoubleQuotationMarkOption">Double Quotation Mark</Option>
</Select>
</Form.Item>
)}
</Form>
</Modal>
);
}
EditParameterSettingsDialog.propTypes = {
parameter: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
dialog: DialogPropType.isRequired,
existingParams: PropTypes.arrayOf(PropTypes.string),
};
EditParameterSettingsDialog.defaultProps = {
existingParams: [],
};
export default wrapDialog(EditParameterSettingsDialog);

View File

@@ -0,0 +1,93 @@
import React from 'react';
import PropTypes from 'prop-types';
import Dropdown from 'antd/lib/dropdown';
import Menu from 'antd/lib/menu';
import Button from 'antd/lib/button';
import Icon from 'antd/lib/icon';
import { react2angular } from 'react2angular';
import QueryResultsLink from './QueryResultsLink';
export function QueryControlDropdown(props) {
const menu = (
<Menu>
{!props.query.isNew() && (!props.query.is_draft || !props.query.is_archived) && (
<Menu.Item>
<a target="_self" onClick={() => props.openAddToDashboardForm(props.selectedTab)}>
<Icon type="plus-circle" theme="filled" /> Add to Dashboard
</a>
</Menu.Item>
)}
{!props.query.isNew() && (
<Menu.Item>
<a onClick={() => props.showEmbedDialog(props.query, props.selectedTab)} data-test="ShowEmbedDialogButton">
<Icon type="share-alt" /> Embed Elsewhere
</a>
</Menu.Item>
)}
<Menu.Item>
<QueryResultsLink
disabled={props.queryExecuting || !props.queryResult.getData || !props.queryResult.getData()}
query={props.query}
queryResult={props.queryResult}
embed={props.embed}
apiKey={props.apiKey}
>
<Icon type="file" /> Download as CSV File
</QueryResultsLink>
</Menu.Item>
<Menu.Item>
<QueryResultsLink
fileType="xlsx"
disabled={props.queryExecuting || !props.queryResult.getData || !props.queryResult.getData()}
query={props.query}
queryResult={props.queryResult}
embed={props.embed}
apiKey={props.apiKey}
>
<Icon type="file-excel" /> Download as Excel File
</QueryResultsLink>
</Menu.Item>
</Menu>
);
return (
<Dropdown
trigger={['click']}
overlay={menu}
overlayClassName="query-control-dropdown-overlay"
>
<Button data-test="QueryControlDropdownButton">
<Icon type="ellipsis" rotate={90} />
</Button>
</Dropdown>
);
}
QueryControlDropdown.propTypes = {
query: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
queryResult: PropTypes.object, // eslint-disable-line react/forbid-prop-types
queryExecuting: PropTypes.bool.isRequired,
showEmbedDialog: PropTypes.func.isRequired,
embed: PropTypes.bool,
apiKey: PropTypes.string,
selectedTab: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
openAddToDashboardForm: PropTypes.func.isRequired,
};
QueryControlDropdown.defaultProps = {
queryResult: {},
embed: false,
apiKey: '',
selectedTab: '',
};
export default function init(ngModule) {
ngModule.component('queryControlDropdown', react2angular(QueryControlDropdown));
}
init.init = true;

View File

@@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
export default function QueryResultsLink(props) {
let href = '';
const { query, queryResult, fileType } = props;
const resultId = queryResult.getId && queryResult.getId();
const resultData = queryResult.getData && queryResult.getData();
if (resultId && resultData && query.name) {
if (query.id) {
href = `api/queries/${query.id}/results/${resultId}.${fileType}${
props.embed ? `?api_key=${props.apiKey}` : ''
}`;
} else {
href = `api/query_results/${resultId}.${fileType}`;
}
}
return (
<a target="_self" disabled={props.disabled} href={href}>
{props.children}
</a>
);
}
QueryResultsLink.propTypes = {
query: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
queryResult: PropTypes.object, // eslint-disable-line react/forbid-prop-types
fileType: PropTypes.string,
disabled: PropTypes.bool.isRequired,
embed: PropTypes.bool,
apiKey: PropTypes.string,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]).isRequired,
};
QueryResultsLink.defaultProps = {
queryResult: {},
fileType: 'csv',
embed: false,
apiKey: '',
};

View File

@@ -0,0 +1,39 @@
import React from 'react';
import PropTypes from 'prop-types';
import Button from 'antd/lib/button';
import Icon from 'antd/lib/icon';
import { react2angular } from 'react2angular';
export function EditVisualizationButton(props) {
return (
<Button
data-test="EditVisualization"
className="edit-visualization"
onClick={() => props.openVisualizationEditor(props.selectedTab)}
>
<Icon type="form" />
<span className="hidden-xs hidden-s hidden-m">
Edit Visualization
</span>
</Button>
);
}
EditVisualizationButton.propTypes = {
openVisualizationEditor: PropTypes.func.isRequired,
selectedTab: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
};
EditVisualizationButton.defaultProps = {
selectedTab: '',
};
export default function init(ngModule) {
ngModule.component('editVisualizationButton', react2angular(EditVisualizationButton));
}
init.init = true;

View File

@@ -0,0 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import { currentUser, clientConfig } from '@/services/auth';
export function EmailSettingsWarning({ featureName }) {
return (clientConfig.mailSettingsMissing && currentUser.isAdmin) ? (
<p className="alert alert-danger">
{`It looks like your mail server isn't configured. Make sure to configure it for the ${featureName} to work.`}
</p>
) : null;
}
EmailSettingsWarning.propTypes = {
featureName: PropTypes.string.isRequired,
};
export default function init(ngModule) {
ngModule.component('emailSettingsWarning', react2angular(EmailSettingsWarning));
}
init.init = true;

Some files were not shown because too many files have changed in this diff Show More