-
Notifications
You must be signed in to change notification settings - Fork 21
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
[CORE-41] Add new Consolidated Spend Report page #5192
base: dev
Are you sure you want to change the base?
Conversation
<div role='columnheader' aria-sort={ariaSort(sort, 'totalStorage')} style={{ flex: 1 }}> | ||
<HeaderRenderer sort={sort} onSort={onSort} name='totalStorage' /> | ||
</div> | ||
{/* <div role='columnheader' aria-sort={ariaSort(sort, 'otherSpend')} style={{ flex: 1 }}> |
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.
Note: other spend was requested in the query, but left out of the mockup.
src/billing/List/BillingList.tsx
Outdated
type: 'consolidatedSpendReport', | ||
})}`} | ||
aria-current={false} | ||
onClick={void Metrics().captureEvent(Events.billingViewConsolidatedSpendReport)} |
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.
The mixpanel event is captured only when the user clicks on the link, so doesn't count things like logging out and then logging back in still on this page or refreshing. I think that should be correct, since there is currently no other way to get to this page, but if there ever appears another link here then we might want to update the event to happen on loading the page.
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 great, maybe one observation is to make this spinner not to overlay the entire page.
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.
Something is funky with sorting the report:
Screen.Recording.17-12-2024.at.15.41.mp4
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'm not having the same sorting issues that David reported
|
||
// Since the variable names don't match display names for these two columns, we need to map them | ||
const columnTitleToVariableNameMap = { | ||
billingAccount: 'namespace', |
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 should map to billingProject
here and throughout the consolidated report. We tend to reserve "account" for when we're talking about the Google billing account
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.
Meaning just that the words the user should see should be 'Billing Project' instead of 'Billing Account'?
billingProject={{ | ||
cloudPlatform: 'GCP', | ||
billingAccount: workspace.billingAccount, | ||
projectName: workspace.googleProject, |
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.
billingProject.projectName
should be workspace.namespace
. The Google project is a separate entity.
<div role='row' style={{ ...Style.cardList.longCardShadowless, padding: 0, flexDirection: 'column' }}> | ||
<div style={workspaceCardStyles.row}> | ||
<div role='cell' style={workspaceCardStyles.field}> | ||
{billingAccountDisplayName ?? '...'} |
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 might be nice to link to the billing project from the table as well like what we're doing with the workspace dashboard
It's not sorting, but some weird thing with a workspace somehow duplicating itself. I saw that when I was developing and couldn't figure it out; it was always the same workspace so I just deleted the workspace! I think it was in the same |
billingProject={{ | ||
cloudPlatform: 'GCP', | ||
billingAccount: workspace.billingAccount, | ||
billingAccount: workspace.namespace, |
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 was okay as workspace.billingAccount
actually. It's the projectName
below that should be workspace.namespace
. It doesn't really impact this page at all, but the metrics it emits will be wrong.
The billing project is equivalent to the workspace namespace. The billing account refers to the Google billing account that is associated with a given billing project and which gets attached to each workspace's Google project. projectName
here refers to the billing project's name, not the workspace's Google project.
src/libs/feature-previews-config.ts
Outdated
@@ -160,6 +161,16 @@ const featurePreviewsConfig: readonly FeaturePreview[] = [ | |||
)}`, | |||
lastUpdated: '12/12/2024', | |||
}, | |||
{ | |||
id: CONSOLIDATED_SPEND_REPORT, | |||
title: 'Show spend report for all workspaces owned by 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.
Should this be Consolidated Spend Report
so users know what to look for in the UI and the messaging is a little more consistent?
If this has happened to both of us, I fear it could happen to end users as well … I think it's worth tracking down what is causing this! |
So the issue is that the BigQuery query actually returns the same workspace twice; one with the namespace/name of the workspace, and one without (a null value). The two results share the same google project but have different costs (in the same category, so it's not two different spend categories). Since the identifier for the object in the front end that represents the workspace is based on the namespace/name, we end up with two identical keys and react freaks out when that happens. I haven't been able to figure out why BQ is returning these weird results; if it's something insane due to them being dev workspaces we might be able to leave it as is, otherwise we should modify the backend to consolidate BQ results before sending it to the front end. |
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.
A few comments inline; nothing major. Are you intending to release this without pagination, or will that eventually be part of this PR?
UX feedback: I find the link to the consolidated spend report to be just kinda hanging out on the page and unappealing. Dunno if anyone has good suggestions for visuals here:
I anticipate that users will very quickly ask, "how can I download the spend report?"
}); | ||
const [allWorkspaces, setAllWorkspaces] = useState<WorkspaceWrapper[]>(props.workspaces); | ||
|
||
// TODO - how to know how many workspaces there are in total in order to paginate without querying BQ? |
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 see two solutions:
- ask BQ for the exact number. This would probably have to be a brand new
count()
query. - don't attempt to show the exact number; show "results 1-250 of approximately 6500" or the like, and handle errors if a user tries to navigate to a page where there aren't any results. In this approximate mode, you could request 251 rows when you want to display 250; if the backend returns 250 or lower, you know there is no next page; if the backend returns 251, you know there is a next page.
}); | ||
} catch { | ||
// Return default values for each workspace in case of an error | ||
return ownedWorkspaces.map(setDefaultSpendValues); |
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 would be nice to make the try/catch more granular here. Any errors retrieving the spend report could be handled differently than an error adding spend details to a workspace. Ideally, you could have a try/catch within the loop over the spendDataItems
, so a single failure during mapping doesn't cause the entire list to be invalid.
I wouldn't block the PR on this; this could be a future enhancement
src/libs/feature-previews-config.ts
Outdated
'Feedback on Consolidated Spend Report' | ||
)}`, | ||
lastUpdated: '12/20/2024', | ||
}, |
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.
does this feature preview have an articleUrl
?
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 shouldn't be its own feature flag. It should be part of the existing "Improved Spend Reports" feature flag.
There's a mockup in the ticket to have it behave similar to a billing project in the list. It should be its own "card" (although outside of the rest of the list) and have a green indicator once selected. Is that feasible? |
Small sorting visual bug: When sorting by Billing Project or Workspace Name, the arrow does not change direction indicating the sort order. All of the other columns work as expected, only these first two behave incorrectly. |
|
||
const handleSort = (name: string) => { | ||
const variableName = columnTitleToVariableNameMap[name]; | ||
onSort({ field: variableName, direction: sort.direction === 'asc' ? 'desc' : 'asc' }); |
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 think this is backwards:
onSort({ field: variableName, direction: sort.direction === 'asc' ? 'desc' : 'asc' }); | |
onSort({ field: variableName, direction: sort.direction === 'asc' ? 'asc' : 'desc' }); |
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.
whoops
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, no, that's correct; onSort
is the update function so it changes the direction from whatever it was to the other one
Quality Gate passedIssues Measures |
Jira Ticket: https://broadworkbench.atlassian.net/browse/CORE-41
Summary of changes:
What
Adds a new
Consolidated Spend Report
page using the new cross billing spend report API in rawls. This page is hidden behind a feature flag, but a link to it will appear on the billing page if the flag is enabled.Workspaces that the user has owner permission to will appear, initially sorted by total spend. They can subsequently be sorted by any column, but see note. Spend report can be chosen for the last 7, 30, or 90 days. Results can be searched by workspace name, billing project name, or bucket name.
Results of the report are cached (per time period) so that reloading the page will not make another call to big query (until the next day).
Note: The query is set up to be paginated but as currently implemented, this page requests 250 workspaces (this will be the top spending 250 workspaces), shows them all, and does not allow paginating. I had initially included paginating but waffled over how to implement it and so have removed it until settling on something satisfactory. Mostly the issue is not knowing the total number of workspaces out of which we are displaying. (e.g. "1-10 out of 1000" shown at the bottom of the page). This can be approximately calculated because we actually have ALL user workspaces available on the page - we could filter that for owner access level (this will probably be the same, but not necessarily since the workspaces in the query are determined slightly differently). Alternatively, we can forego a paginator and just load more workspaces as the user scrolls. What is a reasonable number of workspaces to have as a default page size? Or a reasonable max number to put in for pagesize in the query (which does require the pagesize parameter)?
Second note: Sonarcloud is complaining about my use of
<role = "row">
and the like instead of<tr>
; I have used the same structure elsewhere on the billing page and axe devtools doesn't give me any complaints. My attempts to change the code as suggested by Sonarcloud resulted in absolute disarray on the page. Does anyone have more knowledge or insight into this issue and how much attention I should pay to Sonarcloud?Why
Testing strategy