-
Notifications
You must be signed in to change notification settings - Fork 13.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Better return messages in SQL Editor #14381
feat: Better return messages in SQL Editor #14381
Conversation
5c159bd
to
bceeb54
Compare
@AAfghahi the non admin messsage should also have a header that displays the row returned number. |
bceeb54
to
3e35242
Compare
Ah, they do. My picture was of the wrong thing. TY! |
Codecov Report
@@ Coverage Diff @@
## master #14381 +/- ##
==========================================
+ Coverage 77.09% 77.37% +0.28%
==========================================
Files 959 958 -1
Lines 48309 48562 +253
Branches 5661 5714 +53
==========================================
+ Hits 37243 37577 +334
+ Misses 10866 10783 -83
- Partials 200 202 +2
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
3e35242
to
d455991
Compare
let limitMessage; | ||
const appContainer = document.getElementById('app'); | ||
const bootstrapData = JSON.parse( | ||
appContainer?.getAttribute('data-bootstrap') || '{}', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is in redux already.. can you get it from there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course! I did it this way because this is how user was accessed earlier in the file:
https://github.com/apache/superset/blob/master/superset-frontend/src/SqlLab/components/ResultSet.tsx#L321
should I amend both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, I see, yes that would be great if you could update both.. thanks!
appContainer?.getAttribute('data-bootstrap') || '{}', | ||
); | ||
const isAdmin = bootstrapData.user?.roles?.hasOwnProperty('Admin'); | ||
const defaultDropwdown = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops! embarrassing. TY for the catch.
); | ||
const isAdmin = bootstrapData.user?.roles?.hasOwnProperty('Admin'); | ||
const defaultDropwdown = | ||
limitingFactor === 'DROPDOWN' && queryLimit === 1000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we put 1000 into a const as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
22eca93
to
f346554
Compare
❗ Please consider rebasing your branch to avoid db migration conflicts. |
1 similar comment
❗ Please consider rebasing your branch to avoid db migration conflicts. |
378e720
to
66cf78c
Compare
/testenv up |
@hughhhh Ephemeral environment spinning up at http://54.212.228.251:8080. Credentials are |
.shallow(); | ||
const mockStore = configureStore([thunk]); | ||
const store = mockStore({}); | ||
const spyOnUseSelector = jest.spyOn(redux, 'useSelector'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of using a spy for this, can you push the data that you need into the store when you load it with the provider?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I added it to the store, and added user as a fixture.
actions={actions} | ||
displayLimit={displayLimit} | ||
/> | ||
<div className="scrollable"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest what Evan was demoing the other day and use emotion's css property here instead: https://codesandbox.io/s/emotion-playground-forked-7z07l?file=/src/index.js
That way you can reuse a scrollable style instead of a class.
@@ -64,6 +70,8 @@ const QueryTable = props => { | |||
[props.columns], | |||
); | |||
|
|||
const user = useSelector(({ sqlLab: { user } }) => user); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice destructuring there.
buttonSize="small" | ||
buttonStyle="link" | ||
onClick={() => openQuery(q.queryId)} | ||
> | ||
<i className="fa fa-external-link m-r-3" /> | ||
{t('Edit')} | ||
</Button> | ||
</StyledButton> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it feels a little weird to make all of these buttons static. Can we put them in a wrapper that is static instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized after pulling this down and looking at it that there isn't nothing necessarily significant with the static
style, it just doesn't seem to like the current relative
one. The changes that you made look good.
)} | ||
</span> | ||
); | ||
} else if (limitingFactor === 'QUERY_AND_DROPDOWN') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you put these constants into an enum?
Fixes #14455 as bycatch |
/testenv up |
@betodealmeida Ephemeral environment spinning up at http://54.202.234.102:8080. Credentials are |
} | ||
|
||
function defaultDropdown(queryLimit: number, limitingFactor: string) { | ||
return queryLimit === 1000 && limitingFactor === LIMITING_FACTOR.DROPDOWN; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't remember, is the 1000 default query limit configurable? If so, we should read the configured value. Even if not, we should move this to a constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah! I just found it, and am now piping it into result set for this reason.
function limitReached(displayLimitReached: boolean) { | ||
return displayLimitReached; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function doesn't do anything, we should remove it.
@@ -205,6 +227,17 @@ export default class ResultSet extends React.PureComponent< | |||
) { | |||
this.fetchResults(nextProps.query); | |||
} | |||
if ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any way to close the alert and then have it update props without running a new query?
); | ||
|
||
if (bootstrapData.user && bootstrapData.user.userId) { | ||
if (this.props.user?.userId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since you use this value twice here, do you want to destructure just the userId out?
const limitReached = results?.displayLimitReached; | ||
const { results, rows, queryLimit, limitingFactor } = this.props.query; | ||
let limitMessage; | ||
const isAdmin = this.props.user?.roles.hasOwnProperty('Admin'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why hasOwnProperty
here? are you looking for a boolean? !!this.props.user?roles.Admin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or you could also Object.keys(this.props.user?.roles).includes("Admin")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not that hasOwnProperty
doesn't work, it's just that it was mainly used to make sure that the object isn't inheriting the property from something up the prototype chain, and so I was trying to think about why that was an issue. I think the other methods are more common nowadays.
const isAdmin = this.props.user?.roles.hasOwnProperty('Admin'); | ||
const adminWarning = isAdmin | ||
? ' by the configuration DISPLAY_MAX_ROWS' | ||
: null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you only have one option, it's better to use && `const adminWarning = isAdmin && ' by the....'. it will be falsy otherwise, so setting it to null is redundant, unless you explicitly need null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I explicitly need null here otherwise it returns undefined in the Alert
description={t(
`The number of results displayed is limited to %s%s. Please add
additional limits/filters or download to csv to see more rows up to
the %s limit. %s`,
rows,
adminWarning,
queryLimit,
)}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My original method was two separate Alerts one for admin and one for non-admin but playing around with it, I tried this way and it worked and was less lines. Happy to go back to the other method though if the construction with && is DRY-er
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, ok.. then can you do an empty string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah that works, ty.
return displayLimitReached; | ||
} | ||
|
||
function defaultDropdown(queryLimit: number, limitingFactor: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we make the name more indicative of what this function does? Does it return a boolean, then maybe shouldShowDefaultDropdown
? or shouldUse...
whatever fits better.
</span> | ||
)} | ||
{!limitReached(results?.displayLimitReached) && | ||
defaultDropdown(queryLimit, limitingFactor) && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you want to DRY this up and get the value above and assign it to a const to reuse?
)} | ||
/> | ||
)} | ||
{limitReached(results?.displayLimitReached) && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same thing here.. you can dry up this value
description={t( | ||
`The number of results displayed is limited to %s%s. Please add | ||
additional limits/filters or download to csv to see more rows up to | ||
the %s limit. %s`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do I see four string placeholders above, but three values below? AdminWarning isn't going to get translated either, btw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just minor nits for me.. looks good!
3900eed
to
b834013
Compare
b834013
to
867acf0
Compare
`The number of results displayed is limited to %(rows)d. | ||
Please add additional limits/filters, download to csv, or contact an admin | ||
to see more rows up to the the %(queryLimit)d limit.`, | ||
{ rows, queryLimit }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one other thing I found is that multiline strings don't work well with the translations. I'd recommend breaking this into 3-4 strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh interesting, ok I will
@yousoph do you want to take another pass at a visual review or are you good on this? I think it's ready to go, with one small change left. |
4378548
to
af14960
Compare
Ephemeral environment shutdown and build artifacts deleted. |
* Sqllab limit * Add migration script * Set default values * initial push * revisions * moving migration to separate PR * revisions * Fix apply_limit_to_sql * all but tests * added unit tests * result set * first draft * revisions * made user required prop, added it to all places ResultSet is imported * changed QueryTable test to allow for useSelector * Query Table working * working with heights * fixed scrolling * got rid of animated * fixed tests, revisions * revisions * revisions * heights * fun with heights * alert state * aaron helped me fix this * better alert messages * fixed result set test Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>
* Sqllab limit * Add migration script * Set default values * initial push * revisions * moving migration to separate PR * revisions * Fix apply_limit_to_sql * all but tests * added unit tests * result set * first draft * revisions * made user required prop, added it to all places ResultSet is imported * changed QueryTable test to allow for useSelector * Query Table working * working with heights * fixed scrolling * got rid of animated * fixed tests, revisions * revisions * revisions * heights * fun with heights * alert state * aaron helped me fix this * better alert messages * fixed result set test Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>
* Sqllab limit * Add migration script * Set default values * initial push * revisions * moving migration to separate PR * revisions * Fix apply_limit_to_sql * all but tests * added unit tests * result set * first draft * revisions * made user required prop, added it to all places ResultSet is imported * changed QueryTable test to allow for useSelector * Query Table working * working with heights * fixed scrolling * got rid of animated * fixed tests, revisions * revisions * revisions * heights * fun with heights * alert state * aaron helped me fix this * better alert messages * fixed result set test Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>
SUMMARY
We wanted more robust messaging in Sql Lab that tells user what, if anything, is limiting the results that they get when they run a SQL query. Currently this file will also include a database migration, we are going to wait for this migration to resolve before we continue with this PR.
Because of the varied heights of the alert component, this PR will also fix some of the code in SQL Editor and SouthPane that uses static height.
This has custom messages that are based on:
BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
Before:
After:
display max row for admin
non-admin
Query Limit
Dropdown limit:
Default Dropdown limit of 1000
TEST PLAN
Visual Testing
ADDITIONAL INFORMATION