Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Better return messages in SQL Editor #14381

Merged
merged 28 commits into from
May 18, 2021

Conversation

AAfghahi
Copy link
Member

@AAfghahi AAfghahi commented Apr 27, 2021

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:

  • DISPLAY_MAX_ROW causing the results to be limited (admin and non-admin messaging)
  • The query itself limiting the options
  • the dropdown limiting results
  • the default dropdown of 1000 being the cause for the limit

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Before:
image

After:

display max row for admin
Screen Shot 2021-04-27 at 3 58 14 PM
non-admin
Screen Shot 2021-04-27 at 5 10 25 PM

Query Limit
Screen Shot 2021-04-27 at 4 06 05 PM
Dropdown limit:
Screen Shot 2021-04-27 at 4 56 20 PM
Default Dropdown limit of 1000
Screen Shot 2021-04-27 at 4 46 42 PM

TEST PLAN

Visual Testing

ADDITIONAL INFORMATION

  • Has associated issue:
  • [ x] Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@Steejay
Copy link

Steejay commented Apr 27, 2021

@AAfghahi the non admin messsage should also have a header that displays the row returned number.

@AAfghahi
Copy link
Member Author

@AAfghahi the non admin messsage should also have a header that displays the row returned number.

Ah, they do. My picture was of the wrong thing. TY!

@codecov
Copy link

codecov bot commented Apr 27, 2021

Codecov Report

Merging #14381 (af14960) into master (6871ad1) will increase coverage by 0.28%.
The diff coverage is 59.52%.

Impacted file tree graph

@@            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     
Flag Coverage Δ
javascript 72.48% <60.97%> (+0.08%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
...-frontend/src/SqlLab/components/SouthPane/state.ts 100.00% <ø> (ø)
...erset-frontend/src/SqlLab/components/SqlEditor.jsx 54.62% <ø> (ø)
...src/components/FilterableTable/FilterableTable.tsx 82.26% <ø> (ø)
superset/views/core.py 75.48% <0.00%> (-0.04%) ⬇️
...erset-frontend/src/SqlLab/components/ResultSet.tsx 67.31% <57.89%> (-2.69%) ⬇️
...rset-frontend/src/SqlLab/components/QueryTable.jsx 67.85% <100.00%> (+1.19%) ⬆️
...tend/src/SqlLab/components/SouthPane/SouthPane.tsx 79.54% <100.00%> (ø)
...uperset-frontend/src/components/Form/FormLabel.tsx 68.75% <0.00%> (-31.25%) ⬇️
superset-frontend/src/profile/components/App.tsx 85.71% <0.00%> (-14.29%) ⬇️
...ontend/src/common/hooks/apiResources/dashboards.ts 40.00% <0.00%> (-10.00%) ⬇️
... and 85 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6871ad1...af14960. Read the comment docs.

let limitMessage;
const appContainer = document.getElementById('app');
const bootstrapData = JSON.parse(
appContainer?.getAttribute('data-bootstrap') || '{}',
Copy link
Member

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?

Copy link
Member Author

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?

Copy link
Member

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 =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

Copy link
Member Author

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;
Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@AAfghahi AAfghahi force-pushed the ch5675_returnMessages branch 2 times, most recently from 22eca93 to f346554 Compare April 29, 2021 18:10
@github-actions
Copy link
Contributor

⚠️ @AAfghahi Your base branch master has just also updated superset/migrations.

Please consider rebasing your branch to avoid db migration conflicts.

1 similar comment
@github-actions
Copy link
Contributor

github-actions bot commented May 3, 2021

⚠️ @AAfghahi Your base branch master has just also updated superset/migrations.

Please consider rebasing your branch to avoid db migration conflicts.

@AAfghahi AAfghahi force-pushed the ch5675_returnMessages branch 3 times, most recently from 378e720 to 66cf78c Compare May 3, 2021 15:59
@hughhhh
Copy link
Member

hughhhh commented May 3, 2021

/testenv up

@github-actions
Copy link
Contributor

github-actions bot commented May 3, 2021

@hughhhh Ephemeral environment spinning up at http://54.212.228.251:8080. Credentials are admin/admin. Please allow several minutes for bootstrapping and startup.

.shallow();
const mockStore = configureStore([thunk]);
const store = mockStore({});
const spyOnUseSelector = jest.spyOn(redux, 'useSelector');
Copy link
Member

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?

Copy link
Member Author

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">
Copy link
Member

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);
Copy link
Member

@eschutho eschutho May 4, 2021

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>
Copy link
Member

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?

Copy link
Member

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') {
Copy link
Member

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?

@rusackas
Copy link
Member

rusackas commented May 5, 2021

Fixes #14455 as bycatch

@betodealmeida
Copy link
Member

/testenv up

@github-actions
Copy link
Contributor

@betodealmeida Ephemeral environment spinning up at http://54.202.234.102:8080. Credentials are admin/admin. Please allow several minutes for bootstrapping and startup.

}

function defaultDropdown(queryLimit: number, limitingFactor: string) {
return queryLimit === 1000 && limitingFactor === LIMITING_FACTOR.DROPDOWN;
Copy link
Member

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.

Copy link
Member Author

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.

Comment on lines 65 to 67
function limitReached(displayLimitReached: boolean) {
return displayLimitReached;
}
Copy link
Member

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 (
Copy link
Member

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) {
Copy link
Member

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');
Copy link
Member

@eschutho eschutho May 12, 2021

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

Copy link
Member

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

Copy link
Member

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;
Copy link
Member

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.

Copy link
Member Author

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,
            )}

Copy link
Member Author

@AAfghahi AAfghahi May 13, 2021

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

Copy link
Member

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?

Copy link
Member Author

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) {
Copy link
Member

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) && (
Copy link
Member

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) && (
Copy link
Member

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`,
Copy link
Member

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.

Copy link
Member

@eschutho eschutho left a 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!

@AAfghahi AAfghahi force-pushed the ch5675_returnMessages branch 2 times, most recently from 3900eed to b834013 Compare May 14, 2021 21:01
`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 },
Copy link
Member

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.

Copy link
Member Author

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

@eschutho
Copy link
Member

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

@betodealmeida betodealmeida merged commit a7a011c into apache:master May 18, 2021
@github-actions
Copy link
Contributor

Ephemeral environment shutdown and build artifacts deleted.

cccs-RyanS pushed a commit to CybercentreCanada/superset that referenced this pull request Dec 17, 2021
* 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>
QAlexBall pushed a commit to QAlexBall/superset that referenced this pull request Dec 29, 2021
* 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>
cccs-rc pushed a commit to CybercentreCanada/superset that referenced this pull request Mar 6, 2024
* 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>
@mistercrunch mistercrunch added 🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels 🚢 1.3.0 labels Mar 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels size/L 🚢 1.3.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants