mirror of https://github.com/apache/superset.git
Adding links pointing to the new user profile page (#1704)
* Adding links pointing to the new user profile page * Raising coverage * Fixing tests
This commit is contained in:
parent
50da4f8c07
commit
7eef46e941
|
@ -19,6 +19,7 @@ export default class Favorites extends React.PureComponent {
|
||||||
renderSliceTable() {
|
renderSliceTable() {
|
||||||
const mutator = (data) => data.map(slice => ({
|
const mutator = (data) => data.map(slice => ({
|
||||||
slice: <a href={slice.url}>{slice.title}</a>,
|
slice: <a href={slice.url}>{slice.title}</a>,
|
||||||
|
creator: <a href={slice.creator_url}>{slice.creator}</a>,
|
||||||
favorited: moment.utc(slice.dttm).fromNow(),
|
favorited: moment.utc(slice.dttm).fromNow(),
|
||||||
_favorited: slice.dttm,
|
_favorited: slice.dttm,
|
||||||
}));
|
}));
|
||||||
|
@ -26,7 +27,7 @@ export default class Favorites extends React.PureComponent {
|
||||||
<TableLoader
|
<TableLoader
|
||||||
dataEndpoint={`/superset/fave_slices/${this.props.user.userId}/`}
|
dataEndpoint={`/superset/fave_slices/${this.props.user.userId}/`}
|
||||||
className="table table-condensed"
|
className="table table-condensed"
|
||||||
columns={['slice', 'favorited']}
|
columns={['slice', 'creator', 'favorited']}
|
||||||
mutator={mutator}
|
mutator={mutator}
|
||||||
noDataText="No favorite slices yet, go click on stars!"
|
noDataText="No favorite slices yet, go click on stars!"
|
||||||
sortable
|
sortable
|
||||||
|
@ -36,6 +37,7 @@ export default class Favorites extends React.PureComponent {
|
||||||
renderDashboardTable() {
|
renderDashboardTable() {
|
||||||
const mutator = (data) => data.map(dash => ({
|
const mutator = (data) => data.map(dash => ({
|
||||||
dashboard: <a href={dash.url}>{dash.title}</a>,
|
dashboard: <a href={dash.url}>{dash.title}</a>,
|
||||||
|
creator: <a href={dash.creator_url}>{dash.creator}</a>,
|
||||||
favorited: moment.utc(dash.dttm).fromNow(),
|
favorited: moment.utc(dash.dttm).fromNow(),
|
||||||
}));
|
}));
|
||||||
return (
|
return (
|
||||||
|
@ -44,7 +46,7 @@ export default class Favorites extends React.PureComponent {
|
||||||
mutator={mutator}
|
mutator={mutator}
|
||||||
dataEndpoint={`/superset/fave_dashboards/${this.props.user.userId}/`}
|
dataEndpoint={`/superset/fave_dashboards/${this.props.user.userId}/`}
|
||||||
noDataText="No favorite dashboards yet, go click on stars!"
|
noDataText="No favorite dashboards yet, go click on stars!"
|
||||||
columns={['dashboard', 'favorited']}
|
columns={['dashboard', 'creator', 'favorited']}
|
||||||
sortable
|
sortable
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,7 +15,6 @@ const profileViewContainer = document.getElementById('app');
|
||||||
const bootstrap = JSON.parse(profileViewContainer.getAttribute('data-bootstrap'));
|
const bootstrap = JSON.parse(profileViewContainer.getAttribute('data-bootstrap'));
|
||||||
|
|
||||||
const user = bootstrap.user;
|
const user = bootstrap.user;
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<App user={user} />,
|
<App user={user} />,
|
||||||
profileViewContainer
|
profileViewContainer
|
||||||
|
|
|
@ -149,13 +149,19 @@ class AuditMixinNullable(AuditMixin):
|
||||||
Integer, ForeignKey('ab_user.id'),
|
Integer, ForeignKey('ab_user.id'),
|
||||||
default=cls.get_user_id, onupdate=cls.get_user_id, nullable=True)
|
default=cls.get_user_id, onupdate=cls.get_user_id, nullable=True)
|
||||||
|
|
||||||
@renders('created_on')
|
def _user_link(self, user):
|
||||||
|
if not user:
|
||||||
|
return ''
|
||||||
|
url = '/superset/profile/{}/'.format(user.username)
|
||||||
|
return '<a href="{}">{}</a>'.format(url, escape(user) or '')
|
||||||
|
|
||||||
|
@renders('created_by')
|
||||||
def creator(self): # noqa
|
def creator(self): # noqa
|
||||||
return '{}'.format(self.created_by or '')
|
return self._user_link(self.created_by)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def changed_by_(self):
|
def changed_by_(self):
|
||||||
return '{}'.format(self.changed_by or '')
|
return self._user_link(self.changed_by)
|
||||||
|
|
||||||
@renders('changed_on')
|
@renders('changed_on')
|
||||||
def changed_on_(self):
|
def changed_on_(self):
|
||||||
|
|
|
@ -1796,7 +1796,7 @@ class Superset(BaseSupersetView):
|
||||||
)
|
)
|
||||||
.filter(
|
.filter(
|
||||||
sqla.and_(
|
sqla.and_(
|
||||||
M.Log.action != 'queries',
|
~M.Log.action.in_(('queries', 'shortner', 'sql_json')),
|
||||||
M.Log.user_id == user_id,
|
M.Log.user_id == user_id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1845,13 +1845,21 @@ class Superset(BaseSupersetView):
|
||||||
models.FavStar.dttm.desc()
|
models.FavStar.dttm.desc()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
payload = [{
|
payload = []
|
||||||
|
for o in qry.all():
|
||||||
|
d = {
|
||||||
'id': o.Dashboard.id,
|
'id': o.Dashboard.id,
|
||||||
'dashboard': o.Dashboard.dashboard_link(),
|
'dashboard': o.Dashboard.dashboard_link(),
|
||||||
'title': o.Dashboard.dashboard_title,
|
'title': o.Dashboard.dashboard_title,
|
||||||
'url': o.Dashboard.url,
|
'url': o.Dashboard.url,
|
||||||
'dttm': o.dttm,
|
'dttm': o.dttm,
|
||||||
} for o in qry.all()]
|
}
|
||||||
|
if o.Dashboard.created_by:
|
||||||
|
user = o.Dashboard.created_by
|
||||||
|
d['creator'] = str(user)
|
||||||
|
d['creator_url'] = '/superset/profile/{}/'.format(
|
||||||
|
user.username)
|
||||||
|
payload.append(d)
|
||||||
return Response(
|
return Response(
|
||||||
json.dumps(payload, default=utils.json_int_dttm_ser),
|
json.dumps(payload, default=utils.json_int_dttm_ser),
|
||||||
mimetype="application/json")
|
mimetype="application/json")
|
||||||
|
@ -1934,12 +1942,20 @@ class Superset(BaseSupersetView):
|
||||||
models.FavStar.dttm.desc()
|
models.FavStar.dttm.desc()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
payload = [{
|
payload = []
|
||||||
|
for o in qry.all():
|
||||||
|
d = {
|
||||||
'id': o.Slice.id,
|
'id': o.Slice.id,
|
||||||
'title': o.Slice.slice_name,
|
'title': o.Slice.slice_name,
|
||||||
'url': o.Slice.slice_url,
|
'url': o.Slice.slice_url,
|
||||||
'dttm': o.dttm,
|
'dttm': o.dttm,
|
||||||
} for o in qry.all()]
|
}
|
||||||
|
if o.Slice.created_by:
|
||||||
|
user = o.Slice.created_by
|
||||||
|
d['creator'] = str(user)
|
||||||
|
d['creator_url'] = '/superset/profile/{}/'.format(
|
||||||
|
user.username)
|
||||||
|
payload.append(d)
|
||||||
return Response(
|
return Response(
|
||||||
json.dumps(payload, default=utils.json_int_dttm_ser),
|
json.dumps(payload, default=utils.json_int_dttm_ser),
|
||||||
mimetype="application/json")
|
mimetype="application/json")
|
||||||
|
@ -2613,15 +2629,15 @@ class Superset(BaseSupersetView):
|
||||||
)
|
)
|
||||||
roles = {}
|
roles = {}
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
permissions = defaultdict(list)
|
permissions = defaultdict(set)
|
||||||
for role in user.roles:
|
for role in user.roles:
|
||||||
perms = []
|
perms = set()
|
||||||
for perm in role.permissions:
|
for perm in role.permissions:
|
||||||
perms.append(
|
perms.add(
|
||||||
(perm.permission.name, perm.view_menu.name)
|
(perm.permission.name, perm.view_menu.name)
|
||||||
)
|
)
|
||||||
if perm.permission.name in ('datasource_access', 'database_access'):
|
if perm.permission.name in ('datasource_access', 'database_access'):
|
||||||
permissions[perm.permission.name].append(perm.view_menu.name)
|
permissions[perm.permission.name].add(perm.view_menu.name)
|
||||||
roles[role.name] = [
|
roles[role.name] = [
|
||||||
[perm.permission.name, perm.view_menu.name]
|
[perm.permission.name, perm.view_menu.name]
|
||||||
for perm in role.permissions
|
for perm in role.permissions
|
||||||
|
@ -2643,7 +2659,8 @@ class Superset(BaseSupersetView):
|
||||||
'superset/profile.html',
|
'superset/profile.html',
|
||||||
title=user.username + "'s profile",
|
title=user.username + "'s profile",
|
||||||
navbar_container=True,
|
navbar_container=True,
|
||||||
bootstrap_data=json.dumps(payload))
|
bootstrap_data=json.dumps(payload, default=utils.json_iso_dttm_ser)
|
||||||
|
)
|
||||||
|
|
||||||
@has_access
|
@has_access
|
||||||
@expose("/sqllab")
|
@expose("/sqllab")
|
||||||
|
|
|
@ -140,19 +140,23 @@ class SupersetTestCase(unittest.TestCase):
|
||||||
return db.session.query(models.DruidDatasource).filter_by(
|
return db.session.query(models.DruidDatasource).filter_by(
|
||||||
datasource_name=name).first()
|
datasource_name=name).first()
|
||||||
|
|
||||||
def get_resp(self, url, data=None, follow_redirects=True):
|
def get_resp(
|
||||||
|
self, url, data=None, follow_redirects=True, raise_on_error=True):
|
||||||
"""Shortcut to get the parsed results while following redirects"""
|
"""Shortcut to get the parsed results while following redirects"""
|
||||||
if data:
|
if data:
|
||||||
resp = self.client.post(
|
resp = self.client.post(
|
||||||
url, data=data, follow_redirects=follow_redirects)
|
url, data=data, follow_redirects=follow_redirects)
|
||||||
return resp.data.decode('utf-8')
|
|
||||||
else:
|
else:
|
||||||
resp = self.client.get(url, follow_redirects=follow_redirects)
|
resp = self.client.get(url, follow_redirects=follow_redirects)
|
||||||
|
if raise_on_error and resp.status_code > 400:
|
||||||
|
raise Exception(
|
||||||
|
"http request failed with code {}".format(resp.status_code))
|
||||||
return resp.data.decode('utf-8')
|
return resp.data.decode('utf-8')
|
||||||
|
|
||||||
def get_json_resp(self, url, data=None):
|
def get_json_resp(
|
||||||
|
self, url, data=None, follow_redirects=True, raise_on_error=True):
|
||||||
"""Shortcut to get the parsed results while following redirects"""
|
"""Shortcut to get the parsed results while following redirects"""
|
||||||
resp = self.get_resp(url, data=data)
|
resp = self.get_resp(url, data, follow_redirects, raise_on_error)
|
||||||
return json.loads(resp)
|
return json.loads(resp)
|
||||||
|
|
||||||
def get_main_database(self, session):
|
def get_main_database(self, session):
|
||||||
|
@ -200,6 +204,7 @@ class SupersetTestCase(unittest.TestCase):
|
||||||
dbid = self.get_main_database(db.session).id
|
dbid = self.get_main_database(db.session).id
|
||||||
resp = self.get_json_resp(
|
resp = self.get_json_resp(
|
||||||
'/superset/sql_json/',
|
'/superset/sql_json/',
|
||||||
|
raise_on_error=False,
|
||||||
data=dict(database_id=dbid, sql=sql, select_as_create_as=False,
|
data=dict(database_id=dbid, sql=sql, select_as_create_as=False,
|
||||||
client_id=client_id),
|
client_id=client_id),
|
||||||
)
|
)
|
||||||
|
|
|
@ -478,6 +478,23 @@ class CoreTests(SupersetTestCase):
|
||||||
|
|
||||||
def test_user_profile(self):
|
def test_user_profile(self):
|
||||||
self.login(username='admin')
|
self.login(username='admin')
|
||||||
|
slc = self.get_slice("Girls", db.session)
|
||||||
|
|
||||||
|
# Setting some faves
|
||||||
|
url = '/superset/favstar/Slice/{}/select/'.format(slc.id)
|
||||||
|
resp = self.get_json_resp(url)
|
||||||
|
self.assertEqual(resp['count'], 1)
|
||||||
|
|
||||||
|
dash = (
|
||||||
|
db.session
|
||||||
|
.query(models.Dashboard)
|
||||||
|
.filter_by(slug="births")
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
url = '/superset/favstar/Dashboard/{}/select/'.format(dash.id)
|
||||||
|
resp = self.get_json_resp(url)
|
||||||
|
self.assertEqual(resp['count'], 1)
|
||||||
|
|
||||||
userid = appbuilder.sm.find_user('admin').id
|
userid = appbuilder.sm.find_user('admin').id
|
||||||
resp = self.get_resp('/superset/profile/admin/')
|
resp = self.get_resp('/superset/profile/admin/')
|
||||||
self.assertIn('"app"', resp)
|
self.assertIn('"app"', resp)
|
||||||
|
|
|
@ -118,8 +118,7 @@ class SqlLabTests(SupersetTestCase):
|
||||||
self.run_some_queries()
|
self.run_some_queries()
|
||||||
self.login('admin')
|
self.login('admin')
|
||||||
# Test search queries on database Id
|
# Test search queries on database Id
|
||||||
resp = self.get_resp('/superset/search_queries?database_id=1')
|
data = self.get_json_resp('/superset/search_queries?database_id=1')
|
||||||
data = json.loads(resp)
|
|
||||||
self.assertEquals(3, len(data))
|
self.assertEquals(3, len(data))
|
||||||
db_ids = [data[k]['dbId'] for k in data]
|
db_ids = [data[k]['dbId'] for k in data]
|
||||||
self.assertEquals([1, 1, 1], db_ids)
|
self.assertEquals([1, 1, 1], db_ids)
|
||||||
|
@ -164,8 +163,8 @@ class SqlLabTests(SupersetTestCase):
|
||||||
def test_search_query_on_text(self):
|
def test_search_query_on_text(self):
|
||||||
self.run_some_queries()
|
self.run_some_queries()
|
||||||
self.login('admin')
|
self.login('admin')
|
||||||
resp = self.get_resp('/superset/search_queries?search_text=permission')
|
url = '/superset/search_queries?search_text=permission'
|
||||||
data = json.loads(resp)
|
data = self.get_json_resp(url)
|
||||||
self.assertEquals(1, len(data))
|
self.assertEquals(1, len(data))
|
||||||
self.assertIn('permission', list(data.values())[0]['sql'])
|
self.assertIn('permission', list(data.values())[0]['sql'])
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue