Repository navigation
[WIP] feat(preprod): add pre-built app size monitoring dashboard #105365
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
Closed
Closed
Changes from all commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
22254ca
progress
trevor-e fd9b2a0
:hammer_and_wrench: Sync API Urls to TypeScirpt
getsantry[bot] cfdc82d
fix
trevor-e 6bdc133
fix
trevor-e 46caebe
fix
trevor-e 42f0e43
fix
trevor-e c4a9ce4
fix
trevor-e 2362605
Merge branch 'master' into telkins/app-size-endpoint
trevor-e ebdcb81
fix
trevor-e ad368a5
fix
trevor-e f65b9ed
fix
trevor-e f19387f
fix
trevor-e fca4692
fix
trevor-e 6ac79f7
params
trevor-e 67387dc
type
trevor-e 067565c
frontend
trevor-e e0e1576
check null
trevor-e 37112be
progress
trevor-e 16177e9
remove prebuilt dashboard
trevor-e 1dc780c
:hammer_and_wrench: apply pre-commit fixes
getsantry[bot] 7115721
Merge branch 'telkins/app-size-endpoint' into telkins/app-size-dashboard
trevor-e ee44cd3
progress
trevor-e 3b29a6b
fix
trevor-e 9291caf
Merge branch 'master' into telkins/app-size-dashboard
trevor-e 68c3f68
fix
trevor-e 17993e8
create config
trevor-e cec10c7
progress
trevor-e 78d335b
fix
trevor-e e739268
progress
trevor-e 88d5bf7
progress
trevor-e 7c9d13d
fixes
trevor-e e427c62
fix
trevor-e fdc7b71
fixes
trevor-e 77417c6
fix
trevor-e fb4bd05
cleanup
trevor-e d17425f
clean up params
trevor-e d4d949d
fix chart labels
trevor-e 70d841a
revert some of the query changes, needed for graph units
trevor-e 351ca3c
fix
trevor-e File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
292 changes: 292 additions & 0 deletions
292
static/app/views/dashboards/datasetConfig/mobileAppSize.spec.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,292 @@ | ||
| import {OrganizationFixture} from 'sentry-fixture/organization'; | ||
| import {WidgetFixture} from 'sentry-fixture/widget'; | ||
|
|
||
| import {WidgetType} from 'sentry/views/dashboards/types'; | ||
|
|
||
| import type {AppSizeResponse} from './mobileAppSize'; | ||
| import {MobileAppSizeConfig} from './mobileAppSize'; | ||
|
|
||
| describe('MobileAppSizeConfig', () => { | ||
| const organization = OrganizationFixture(); | ||
|
|
||
| describe('transformSeries', () => { | ||
| it('transforms API response to series data', () => { | ||
| const data: AppSizeResponse[] = [ | ||
| { | ||
| data: [ | ||
| [1609459200, [{count: 1000000}]], | ||
| [1609545600, [{count: 1100000}]], | ||
| [1609632000, [{count: 1200000}]], | ||
| ], | ||
| start: 1609459200, | ||
| end: 1609632000, | ||
| meta: {fields: {}}, | ||
| }, | ||
| ]; | ||
|
|
||
| const widgetQuery = { | ||
| conditions: 'app_id=com.example.app', | ||
| aggregates: [], | ||
| fields: [], | ||
| columns: [], | ||
| fieldAliases: [], | ||
| name: '', | ||
| orderby: '', | ||
| }; | ||
|
|
||
| const result = MobileAppSizeConfig.transformSeries!( | ||
| data, | ||
| widgetQuery, | ||
| organization | ||
| ); | ||
|
|
||
| expect(result).toHaveLength(1); | ||
| expect(result[0]!.seriesName).toBe('com.example.app : max(max_install_size)'); | ||
| expect(result[0]!.data).toHaveLength(3); | ||
| expect(result[0]!.data[0]).toEqual({ | ||
| name: 1609459200000, | ||
| value: 1000000, | ||
| }); | ||
| }); | ||
|
|
||
| it('filters out null and undefined values', () => { | ||
| const data: AppSizeResponse[] = [ | ||
| { | ||
| data: [ | ||
| [1609459200, [{count: 1000000}]], | ||
| [1609545600, [{count: null}]], | ||
| [1609632000, []], | ||
| [1609718400, [{count: 1200000}]], | ||
| ], | ||
| start: 1609459200, | ||
| end: 1609718400, | ||
| meta: {fields: {}}, | ||
| }, | ||
| ]; | ||
|
|
||
| const widgetQuery = { | ||
| conditions: '', | ||
| aggregates: [], | ||
| fields: [], | ||
| columns: [], | ||
| fieldAliases: [], | ||
| name: '', | ||
| orderby: '', | ||
| }; | ||
|
|
||
| const result = MobileAppSizeConfig.transformSeries!( | ||
| data, | ||
| widgetQuery, | ||
| organization | ||
| ); | ||
|
|
||
| expect(result[0]!.data).toHaveLength(2); | ||
| expect(result[0]!.data[0]!.value).toBe(1000000); | ||
| expect(result[0]!.data[1]!.value).toBe(1200000); | ||
| }); | ||
|
|
||
| it('includes artifact type in series name', () => { | ||
| const data: AppSizeResponse[] = [ | ||
| { | ||
| data: [[1609459200, [{count: 1000000}]]], | ||
| start: 1609459200, | ||
| end: 1609459200, | ||
| meta: {fields: {}}, | ||
| }, | ||
| ]; | ||
|
|
||
| const widgetQuery = { | ||
| conditions: 'app_id=com.example.app&artifact_type=2', | ||
| aggregates: [], | ||
| fields: [], | ||
| columns: [], | ||
| fieldAliases: [], | ||
| name: '', | ||
| orderby: '', | ||
| }; | ||
|
|
||
| const result = MobileAppSizeConfig.transformSeries!( | ||
| data, | ||
| widgetQuery, | ||
| organization | ||
| ); | ||
|
|
||
| expect(result[0]!.seriesName).toContain('com.example.app'); | ||
| expect(result[0]!.seriesName).toContain('apk'); | ||
| expect(result[0]!.seriesName).toContain('max(max_install_size)'); | ||
| }); | ||
|
|
||
| it('includes branch and build config in series name', () => { | ||
| const data: AppSizeResponse[] = [ | ||
| { | ||
| data: [[1609459200, [{count: 1000000}]]], | ||
| start: 1609459200, | ||
| end: 1609459200, | ||
| meta: {fields: {}}, | ||
| }, | ||
| ]; | ||
|
|
||
| const widgetQuery = { | ||
| conditions: | ||
| 'app_id=com.example.app&git_head_ref=main&build_configuration_name=Release', | ||
| aggregates: [], | ||
| fields: [], | ||
| columns: [], | ||
| fieldAliases: [], | ||
| name: '', | ||
| orderby: '', | ||
| }; | ||
|
|
||
| const result = MobileAppSizeConfig.transformSeries!( | ||
| data, | ||
| widgetQuery, | ||
| organization | ||
| ); | ||
|
|
||
| expect(result[0]!.seriesName).toContain('com.example.app'); | ||
| expect(result[0]!.seriesName).toContain('main'); | ||
| expect(result[0]!.seriesName).toContain('Release'); | ||
| expect(result[0]!.seriesName).toContain('max(max_install_size)'); | ||
| }); | ||
|
|
||
| it('uses correct aggregate for download size', () => { | ||
| const data: AppSizeResponse[] = [ | ||
| { | ||
| data: [[1609459200, [{count: 500000}]]], | ||
| start: 1609459200, | ||
| end: 1609459200, | ||
| meta: {fields: {}}, | ||
| }, | ||
| ]; | ||
|
|
||
| const widgetQuery = { | ||
| conditions: 'app_id=com.example.app&size_type=download', | ||
| aggregates: [], | ||
| fields: [], | ||
| columns: [], | ||
| fieldAliases: [], | ||
| name: '', | ||
| orderby: '', | ||
| }; | ||
|
|
||
| const result = MobileAppSizeConfig.transformSeries!( | ||
| data, | ||
| widgetQuery, | ||
| organization | ||
| ); | ||
|
|
||
| expect(result[0]!.seriesName).toBe('com.example.app : max(max_download_size)'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('getSeriesRequest', () => { | ||
| it('uses install size field by default', async () => { | ||
| const api = new MockApiClient(); | ||
| const widget = WidgetFixture({ | ||
| widgetType: WidgetType.MOBILE_APP_SIZE, | ||
| queries: [ | ||
| { | ||
| conditions: 'app_id=com.example.app', | ||
| aggregates: [], | ||
| fields: [], | ||
| columns: [], | ||
| fieldAliases: [], | ||
| name: '', | ||
| orderby: '', | ||
| }, | ||
| ], | ||
| }); | ||
|
|
||
| const mockRequest = MockApiClient.addMockResponse({ | ||
| url: `/organizations/${organization.slug}/preprod/app-size-stats/`, | ||
| body: { | ||
| data: [[1609459200, [{count: 1000000}]]], | ||
| start: 1609459200, | ||
| end: 1609459200, | ||
| meta: {fields: {}}, | ||
| }, | ||
| }); | ||
|
|
||
| await MobileAppSizeConfig.getSeriesRequest!(api, widget, 0, organization, { | ||
| datetime: {start: null, end: null, period: '14d', utc: false}, | ||
| environments: [], | ||
| projects: [1], | ||
| }); | ||
|
|
||
| expect(mockRequest).toHaveBeenCalledWith( | ||
| `/organizations/${organization.slug}/preprod/app-size-stats/`, | ||
| expect.objectContaining({ | ||
| query: expect.objectContaining({ | ||
| field: 'max(max_install_size)', | ||
| }), | ||
| }) | ||
| ); | ||
| }); | ||
|
|
||
| it('uses download size field when size_type=download', async () => { | ||
| const api = new MockApiClient(); | ||
| const widget = WidgetFixture({ | ||
| widgetType: WidgetType.MOBILE_APP_SIZE, | ||
| queries: [ | ||
| { | ||
| conditions: 'app_id=com.example.app&size_type=download', | ||
| aggregates: [], | ||
| fields: [], | ||
| columns: [], | ||
| fieldAliases: [], | ||
| name: '', | ||
| orderby: '', | ||
| }, | ||
| ], | ||
| }); | ||
|
|
||
| const mockRequest = MockApiClient.addMockResponse({ | ||
| url: `/organizations/${organization.slug}/preprod/app-size-stats/`, | ||
| body: { | ||
| data: [[1609459200, [{count: 500000}]]], | ||
| start: 1609459200, | ||
| end: 1609459200, | ||
| meta: {fields: {}}, | ||
| }, | ||
| }); | ||
|
|
||
| await MobileAppSizeConfig.getSeriesRequest!(api, widget, 0, organization, { | ||
| datetime: {start: null, end: null, period: '14d', utc: false}, | ||
| environments: [], | ||
| projects: [1], | ||
| }); | ||
|
|
||
| expect(mockRequest).toHaveBeenCalledWith( | ||
| `/organizations/${organization.slug}/preprod/app-size-stats/`, | ||
| expect.objectContaining({ | ||
| query: expect.objectContaining({ | ||
| field: 'max(max_download_size)', | ||
| }), | ||
| }) | ||
| ); | ||
| }); | ||
| }); | ||
|
|
||
| describe('getSeriesResultType', () => { | ||
| it('returns size_base10 output type for both install and download aggregates', () => { | ||
| const data: AppSizeResponse[] = []; | ||
| const widgetQuery = { | ||
| conditions: '', | ||
| aggregates: [], | ||
| fields: [], | ||
| columns: [], | ||
| fieldAliases: [], | ||
| name: '', | ||
| orderby: '', | ||
| }; | ||
|
|
||
| const result = MobileAppSizeConfig.getSeriesResultType!(data, widgetQuery); | ||
|
|
||
| // Both aggregates should be registered to handle multi-query widgets | ||
| expect(result).toEqual({ | ||
| 'max(max_install_size)': 'size_base10', | ||
| 'max(max_download_size)': 'size_base10', | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.