Improve URLs for Chart and Dashboard ModelViews (#5544)

* Improve URLs for Chart and Dashboard ModelViews

Prior to this, the ModelView for Chart and Dashboard would be
at `/slicemodelview/list/` and `/dashboardmodelview/list/`.

Now we have cleaner URLs at `/chart/list/` and `/dashboard/list/`

* Fix unrelated js lint

* addressing comments
This commit is contained in:
Maxime Beauchemin 2018-08-03 12:46:48 -07:00 committed by GitHub
parent 1e155663a7
commit 51bd17d6f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 43 additions and 186 deletions

View File

@ -9,7 +9,7 @@ import AsyncSelect from '../../../src/components/AsyncSelect';
describe('AsyncSelect', () => {
const mockedProps = {
dataEndpoint: '/slicemodelview/api/read',
dataEndpoint: '/chart/api/read',
onChange: sinon.spy(),
placeholder: 'Select...',
mutator: () => [

View File

@ -26,7 +26,7 @@ export const sliceEntitiesForChart = {
compare_suffix: 'o10Y',
datasource: datasourceId,
},
edit_url: `/slicemodelview/edit/${sliceId}`,
edit_url: `/chart/edit/${sliceId}`,
viz_type: 'pie',
datasource: datasourceId,
description: null,
@ -45,7 +45,7 @@ export const sliceEntitiesForDashboard = {
slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20127%7D',
slice_name: 'Region Filter',
form_data: {},
edit_url: '/slicemodelview/edit/127',
edit_url: '/chart/edit/127',
viz_type: 'filter_box',
datasource: '2__table',
description: null,
@ -58,7 +58,7 @@ export const sliceEntitiesForDashboard = {
slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20128%7D',
slice_name: "World's Population",
form_data: {},
edit_url: '/slicemodelview/edit/128',
edit_url: '/chart/edit/128',
viz_type: 'big_number',
datasource: '2__table',
description: null,
@ -71,7 +71,7 @@ export const sliceEntitiesForDashboard = {
slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20129%7D',
slice_name: 'Most Populated Countries',
form_data: {},
edit_url: '/slicemodelview/edit/129',
edit_url: '/chart/edit/129',
viz_type: 'table',
datasource: '2__table',
description: null,
@ -84,7 +84,7 @@ export const sliceEntitiesForDashboard = {
slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20130%7D',
slice_name: 'Growth Rate',
form_data: {},
edit_url: '/slicemodelview/edit/130',
edit_url: '/chart/edit/130',
viz_type: 'line',
datasource: '2__table',
description: null,
@ -97,7 +97,7 @@ export const sliceEntitiesForDashboard = {
slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20131%7D',
slice_name: '% Rural',
form_data: {},
edit_url: '/slicemodelview/edit/131',
edit_url: '/chart/edit/131',
viz_type: 'world_map',
datasource: '2__table',
description: null,
@ -110,7 +110,7 @@ export const sliceEntitiesForDashboard = {
slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20132%7D',
slice_name: 'Life Expectancy VS Rural %',
form_data: {},
edit_url: '/slicemodelview/edit/132',
edit_url: '/chart/edit/132',
viz_type: 'bubble',
datasource: '2__table',
description: null,
@ -123,7 +123,7 @@ export const sliceEntitiesForDashboard = {
slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20133%7D',
slice_name: 'Rural Breakdown',
form_data: {},
edit_url: '/slicemodelview/edit/133',
edit_url: '/chart/edit/133',
viz_type: 'sunburst',
datasource: '2__table',
description: null,
@ -136,7 +136,7 @@ export const sliceEntitiesForDashboard = {
slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20134%7D',
slice_name: "World's Pop Growth",
form_data: {},
edit_url: '/slicemodelview/edit/134',
edit_url: '/chart/edit/134',
viz_type: 'area',
datasource: '2__table',
description: null,
@ -149,7 +149,7 @@ export const sliceEntitiesForDashboard = {
slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20135%7D',
slice_name: 'Box plot',
form_data: {},
edit_url: '/slicemodelview/edit/135',
edit_url: '/chart/edit/135',
viz_type: 'box_plot',
datasource: '2__table',
description: null,
@ -162,7 +162,7 @@ export const sliceEntitiesForDashboard = {
slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20136%7D',
slice_name: 'Treemap',
form_data: {},
edit_url: '/slicemodelview/edit/136',
edit_url: '/chart/edit/136',
viz_type: 'treemap',
datasource: '2__table',
description: null,

View File

@ -192,7 +192,7 @@ describe('SaveModal', () => {
});
it('calls correct url', () => {
const url = '/dashboardmodelviewasync/api/read?_flt_0_owners=' + userID;
const url = '/dashboardasync/api/read?_flt_0_owners=' + userID;
makeRequest();
expect(ajaxStub.getCall(0).args[0].url).to.be.equal(url);
});

View File

@ -142,10 +142,7 @@ class HeaderActionsDropdown extends React.PureComponent {
triggerNode={<span>{t('Set auto-refresh interval')}</span>}
/>
{editMode && (
<MenuItem
target="_blank"
href={`/dashboardmodelview/edit/${dashboardId}`}
>
<MenuItem target="_blank" href={`/dashboard/edit/${dashboardId}`}>
{t('Edit dashboard metadata')}
</MenuItem>
)}

View File

@ -14,7 +14,7 @@ export function fetchDashboardsFailed(userId) {
export function fetchDashboards(userId) {
return function (dispatch) {
const url = '/dashboardmodelviewasync/api/read?_flt_0_owners=' + userId;
const url = '/dashboardasync/api/read?_flt_0_owners=' + userId;
return $.ajax({
type: 'GET',
url,

View File

@ -101,7 +101,7 @@ class ExploreChartHeader extends React.PureComponent {
>
<a
className="edit-desc-icon"
href={`/slicemodelview/edit/${this.props.slice.slice_id}`}
href={`/chart/edit/${this.props.slice.slice_id}`}
>
<i className="fa fa-edit" />
</a>

View File

@ -22,7 +22,7 @@ export default class DashboardTable extends React.PureComponent {
}
componentDidMount() {
const url = (
'/dashboardmodelviewasync/api/read' +
'/dashboardasync/api/read' +
'?_oc_DashboardModelViewAsync=changed_on' +
'&_od_DashboardModelViewAsync=desc');
$.getJSON(url, (data) => {

View File

@ -240,7 +240,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
@property
def edit_url(self):
return '/slicemodelview/edit/{}'.format(self.id)
return '/chart/edit/{}'.format(self.id)
@property
def slice_link(self):

View File

@ -1,116 +0,0 @@
{% extends "appbuilder/base.html" %}
{% block uncontained %}
<div class="index">
<div id="carousel" class="carousel slide" data-ride="carousel">
<!-- Indicators -->
<ol class="carousel-indicators">
<li data-target="#carousel" data-slide-to="0" class="active"></li>
<li data-target="#carousel" data-slide-to="1"></li>
<li data-target="#carousel" data-slide-to="2"></li>
<li data-target="#carousel" data-slide-to="3"></li>
<li data-target="#carousel" data-slide-to="4"></li>
</ol>
<!-- Wrapper for slides -->
<div class="carousel-inner" role="listbox">
<div class="item active text-center">
<img src="{{ url_for("static", filename="img/dash.png") }}">
<div class="carousel-caption">
<div>
<h1>Superset</h1>
<p>
an open source data visualization platform
</p>
</div>
</div>
</div>
<div class="item">
<img src="{{ url_for("static", filename="img/bubble.png") }}">
<div class="carousel-caption">
<h2>Explore your data
</h2>
<p>
Intuitively navigate your data while slicing, dicing, and
visualizing through a rich set of widgets
</p>
</div>
</div>
<div class="item">
<img src="{{ url_for("static", filename="img/dash.png") }}">
<div class="carousel-caption">
<h2>Create and share dashboards</h2>
<p>Assemble many data visualization "slices" into a rich collection</p>
</div>
</div>
<div class="item">
<img src="{{ url_for("static", filename="img/cloud.png") }}">
<div class="carousel-caption">
<h2>Extend</h2>
<p>Join the community and take part in extending the widget library</p>
</div>
</div>
<div class="item">
<img src="{{ url_for("static", filename="img/servers.jpg") }}">
<div class="carousel-caption">
<h2>Connect</h2>
<p>
Access data from MySql, Presto.db, Postgres, RedShift, Oracle, MsSql,
SQLite, and more through the SqlAlchemy integration. You can also
query realtime data blazingly fast out of Druid.io
</p>
</div>
</div>
</div>
<!-- Controls -->
<div>
<a class="left carousel-control" href="#carousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#carousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
</div>
<hr/>
<div class="container">
<div class="row text-center">
<div class="col-lg-4">
<img class="img-circle" src="{{ url_for('static', filename='img/cardash.jpg') }}" width="140" height="140">
<h2>Dashboards</h2>
<p>Browse the dashboards list</p>
<p><a class="btn btn-default" href="/dashboardmodelview/list/" role="button">
<i class="fa fa-dashboard"></i>
</a></p>
</div><!-- /.col-lg-4 -->
<div class="col-lg-4">
<img class="img-circle" src="{{ url_for('static', filename='img/slice.jpg') }}" width="140" height="140">
<h2>Slices</h2>
<p>"Slices" are individual views into a single dataset</p>
<p><a class="btn btn-default" href="/slicemodelview/list/" role="button">
<i class="fa fa-line-chart"></i>
</a></p>
</div><!-- /.col-lg-4 -->
<div class="col-lg-4">
<img class="img-circle" src="{{ url_for('static', filename='img/gallery.jpg') }}" alt="Generic placeholder image" width="140" height="140">
<h2>Gallery</h2>
<p>Navigate through the growing set of visualizations</p>
<p><a class="btn btn-default" href="#" onclick="alert('Not ready yet!');" role="button">
<i class="fa fa-picture-o"></i>
</a></p>
</div><!-- /.col-lg-4 -->
</div>
</div>
</div>
{% endblock %}
{% block tail_js %}
{{ super() }}
<script>
$(document).ready(function() {
$('#carousel').carousel();
});
</script>
{% endblock %}

View File

@ -1,30 +0,0 @@
{% if slice %}
{% if slice.slice_name %}
<h2>
{{ slice.slice_name }}
<small class="star-edit-icons">
<span class="favstar" class_name="Slice" obj_id="{{ slice.id }}"></span>
<span>
<a
href="/slicemodelview/edit/{{ slice.id }}"
data-toggle="tooltip"
title="Edit Description"
class="edit-slice-description-icon"
>
{% if slice.description %}
<i
class="fa fa-question-circle-o"
data-toggle="tooltip"
data-placement="bottom"
title="{{ slice.description }}">
</i>
{% endif %}
<i class="fa fa-edit"></i>
</a>
</span>
</small>
</h2>
{% endif %}
{% else %}
<h3>[{{ viz.datasource.table_name }}] - untitled</h3>
{% endif %}

View File

@ -12,7 +12,7 @@
<button onclick="window.location += '&action=go';">
{{ _("Request Permissions") }}
</button>
<button onclick="window.location.href = '/slicemodelview/list/';">
<button onclick="window.location.href = '/chart/list/';">
{{ _("Cancel") }}
</button>
</div>

View File

@ -415,6 +415,7 @@ if config.get('ENABLE_ACCESS_REQUEST'):
class SliceModelView(SupersetModelView, DeleteMixin): # noqa
route_base = '/chart'
datamodel = SQLAInterface(models.Slice)
list_title = _('List Charts')
@ -504,6 +505,7 @@ appbuilder.add_view(
class SliceAsync(SliceModelView): # noqa
route_base = '/sliceasync'
list_columns = [
'id', 'slice_link', 'viz_type', 'slice_name',
'creator', 'modified', 'icons']
@ -517,6 +519,7 @@ appbuilder.add_view_no_menu(SliceAsync)
class SliceAddView(SliceModelView): # noqa
route_base = '/sliceaddview'
list_columns = [
'id', 'slice_name', 'slice_url', 'edit_url', 'viz_type', 'params',
'description', 'description_markeddown', 'datasource_id', 'datasource_type',
@ -528,6 +531,7 @@ appbuilder.add_view_no_menu(SliceAddView)
class DashboardModelView(SupersetModelView, DeleteMixin): # noqa
route_base = '/dashboard'
datamodel = SQLAInterface(models.Dashboard)
list_title = _('List Dashboards')
@ -603,7 +607,7 @@ class DashboardModelView(SupersetModelView, DeleteMixin): # noqa
items = [items]
ids = ''.join('&id={}'.format(d.id) for d in items)
return redirect(
'/dashboardmodelview/export_dashboards_form?{}'.format(ids[1:]))
'/dashboard/export_dashboards_form?{}'.format(ids[1:]))
@expose('/export_dashboards_form')
def download_dashboards(self):
@ -615,7 +619,7 @@ class DashboardModelView(SupersetModelView, DeleteMixin): # noqa
mimetype='application/text')
return self.render_template(
'superset/export_dashboards.html',
dashboards_url='/dashboardmodelview/list',
dashboards_url='/dashboard/list',
)
@ -629,6 +633,7 @@ appbuilder.add_view(
class DashboardModelViewAsync(DashboardModelView): # noqa
route_base = '/dashboardasync'
list_columns = [
'id', 'dashboard_link', 'creator', 'modified', 'dashboard_title',
'changed_on', 'url', 'changed_by_name',
@ -645,6 +650,7 @@ appbuilder.add_view_no_menu(DashboardModelViewAsync)
class DashboardAddView(DashboardModelView): # noqa
route_base = '/dashboardaddview'
list_columns = [
'id', 'dashboard_link', 'creator', 'modified', 'dashboard_title',
'changed_on', 'url', 'changed_by_name',
@ -1207,7 +1213,7 @@ class Superset(BaseSupersetView):
models.Dashboard.import_obj(
dashboard, import_time=current_tt)
db.session.commit()
return redirect('/dashboardmodelview/list/')
return redirect('/dashboard/list/')
return self.render_template('superset/import_dashboards.html')
@log_this
@ -1251,7 +1257,7 @@ class Superset(BaseSupersetView):
datasource_id, datasource_type = self.datasource_info(
datasource_id, datasource_type, form_data)
error_redirect = '/slicemodelview/list/'
error_redirect = '/chart/list/'
datasource = ConnectorRegistry.get_datasource(
datasource_type, datasource_id, db.session)
if not datasource:

View File

@ -263,8 +263,8 @@ class CoreTests(SupersetTestCase):
def test_add_slice(self):
self.login(username='admin')
# assert that /slicemodelview/add responds with 200
url = '/slicemodelview/add'
# assert that /chart/add responds with 200
url = '/chart/add'
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
@ -418,8 +418,8 @@ class CoreTests(SupersetTestCase):
def test_gamma(self):
self.login(username='gamma')
assert 'List Charts' in self.get_resp('/slicemodelview/list/')
assert 'List Dashboard' in self.get_resp('/dashboardmodelview/list/')
assert 'List Charts' in self.get_resp('/chart/list/')
assert 'List Dashboard' in self.get_resp('/dashboard/list/')
def test_csv_endpoint(self):
self.login('admin')

View File

@ -279,27 +279,27 @@ class DashboardTests(SupersetTestCase):
self.revoke_public_access_to_table(table)
self.logout()
resp = self.get_resp('/slicemodelview/list/')
resp = self.get_resp('/chart/list/')
self.assertNotIn('birth_names</a>', resp)
resp = self.get_resp('/dashboardmodelview/list/')
resp = self.get_resp('/dashboard/list/')
self.assertNotIn('/superset/dashboard/births/', resp)
self.grant_public_access_to_table(table)
# Try access after adding appropriate permissions.
self.assertIn('birth_names', self.get_resp('/slicemodelview/list/'))
self.assertIn('birth_names', self.get_resp('/chart/list/'))
resp = self.get_resp('/dashboardmodelview/list/')
resp = self.get_resp('/dashboard/list/')
self.assertIn('/superset/dashboard/births/', resp)
self.assertIn('Births', self.get_resp('/superset/dashboard/births/'))
# Confirm that public doesn't have access to other datasets.
resp = self.get_resp('/slicemodelview/list/')
resp = self.get_resp('/chart/list/')
self.assertNotIn('wb_health_population</a>', resp)
resp = self.get_resp('/dashboardmodelview/list/')
resp = self.get_resp('/dashboard/list/')
self.assertNotIn('/superset/dashboard/world_health/', resp)
def test_dashboard_with_created_by_can_be_accessed_by_public_users(self):
@ -370,7 +370,7 @@ class DashboardTests(SupersetTestCase):
gamma_user = security_manager.find_user('gamma')
self.login(gamma_user.username)
resp = self.get_resp('/dashboardmodelview/list/')
resp = self.get_resp('/dashboard/list/')
self.assertNotIn('/superset/dashboard/empty_dashboard/', resp)
dash = (
@ -383,7 +383,7 @@ class DashboardTests(SupersetTestCase):
db.session.merge(dash)
db.session.commit()
resp = self.get_resp('/dashboardmodelview/list/')
resp = self.get_resp('/dashboard/list/')
self.assertIn('/superset/dashboard/empty_dashboard/', resp)

View File

@ -206,7 +206,7 @@ class ImportExportTests(SupersetTestCase):
def test_export_1_dashboard(self):
birth_dash = self.get_dash_by_slug('births')
export_dash_url = (
'/dashboardmodelview/export_dashboards_form?id={}&action=go'
'/dashboard/export_dashboards_form?id={}&action=go'
.format(birth_dash.id)
)
resp = self.client.get(export_dash_url)
@ -235,7 +235,7 @@ class ImportExportTests(SupersetTestCase):
birth_dash = self.get_dash_by_slug('births')
world_health_dash = self.get_dash_by_slug('world_health')
export_dash_url = (
'/dashboardmodelview/export_dashboards_form?id={}&id={}&action=go'
'/dashboard/export_dashboards_form?id={}&id={}&action=go'
.format(birth_dash.id, world_health_dash.id))
resp = self.client.get(export_dash_url)
exported_dashboards = sorted(