mirror of https://github.com/grafana/grafana.git
				
				
				
			Graphite: Fix date mutation (#107414)
* Fix date mutation - Add unit test - Improve types * Add invalid test and minor cleanup * Review
This commit is contained in:
		
							parent
							
								
									bae8d98a9f
								
							
						
					
					
						commit
						a01b1536f9
					
				|  | @ -3699,8 +3699,7 @@ exports[`better eslint`] = { | ||||||
|       [0, 0, 0, "Unexpected any. Specify a different type.", "15"], |       [0, 0, 0, "Unexpected any. Specify a different type.", "15"], | ||||||
|       [0, 0, 0, "Unexpected any. Specify a different type.", "16"], |       [0, 0, 0, "Unexpected any. Specify a different type.", "16"], | ||||||
|       [0, 0, 0, "Unexpected any. Specify a different type.", "17"], |       [0, 0, 0, "Unexpected any. Specify a different type.", "17"], | ||||||
|       [0, 0, 0, "Unexpected any. Specify a different type.", "18"], |       [0, 0, 0, "Unexpected any. Specify a different type.", "18"] | ||||||
|       [0, 0, 0, "Unexpected any. Specify a different type.", "19"] |  | ||||||
|     ], |     ], | ||||||
|     "public/app/plugins/datasource/graphite/gfunc.ts:5381": [ |     "public/app/plugins/datasource/graphite/gfunc.ts:5381": [ | ||||||
|       [0, 0, 0, "Do not use any type assertions.", "0"], |       [0, 0, 0, "Do not use any type assertions.", "0"], | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| import { isArray } from 'lodash'; | import { isArray } from 'lodash'; | ||||||
|  | import moment from 'moment'; | ||||||
| import { of } from 'rxjs'; | import { of } from 'rxjs'; | ||||||
| import { createFetchResponse } from 'test/helpers/createFetchResponse'; | import { createFetchResponse } from 'test/helpers/createFetchResponse'; | ||||||
| 
 | 
 | ||||||
|  | @ -6,6 +7,7 @@ import { | ||||||
|   AbstractLabelMatcher, |   AbstractLabelMatcher, | ||||||
|   AbstractLabelOperator, |   AbstractLabelOperator, | ||||||
|   DataQueryRequest, |   DataQueryRequest, | ||||||
|  |   dateMath, | ||||||
|   dateTime, |   dateTime, | ||||||
|   getFrameDisplayName, |   getFrameDisplayName, | ||||||
|   MetricFindValue, |   MetricFindValue, | ||||||
|  | @ -956,6 +958,31 @@ describe('graphiteDatasource', () => { | ||||||
|       await assertQueryExport('interpolate(alias(servers.west.001))', []); |       await assertQueryExport('interpolate(alias(servers.west.001))', []); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   describe('translateTime', () => { | ||||||
|  |     it('does not mutate passed in date', async () => { | ||||||
|  |       const date = new Date('2025-06-30T00:00:59.000Z'); | ||||||
|  |       const functionDate = moment(date); | ||||||
|  |       const updatedDate = ctx.ds.translateTime( | ||||||
|  |         dateMath.toDateTime(functionDate.toDate(), { roundUp: undefined, timezone: undefined })!, | ||||||
|  |         true | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       expect(functionDate.toDate()).toEqual(date); | ||||||
|  |       expect(updatedDate).not.toEqual(date.getTime()); | ||||||
|  |     }); | ||||||
|  |     it('does not mutate passed in relative date - string', async () => { | ||||||
|  |       const date = 'now-1m'; | ||||||
|  |       const updatedDate = ctx.ds.translateTime(date, true); | ||||||
|  | 
 | ||||||
|  |       expect(updatedDate).not.toEqual(date); | ||||||
|  |     }); | ||||||
|  |     it('returns the input if the input is invalid', async () => { | ||||||
|  |       const updatedDate = ctx.ds.translateTime('', true); | ||||||
|  | 
 | ||||||
|  |       expect(updatedDate).toBe(''); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| function accessScenario(name: string, url: string, fn: ({ headers }: { headers: Record<string, unknown> }) => void) { | function accessScenario(name: string, url: string, fn: ({ headers }: { headers: Record<string, unknown> }) => void) { | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| import { map as _map, each, indexOf, isArray, isString } from 'lodash'; | import { map as _map, each, indexOf, isArray, isString } from 'lodash'; | ||||||
|  | import moment from 'moment'; | ||||||
| import { lastValueFrom, merge, Observable, of, OperatorFunction, pipe, throwError } from 'rxjs'; | import { lastValueFrom, merge, Observable, of, OperatorFunction, pipe, throwError } from 'rxjs'; | ||||||
| import { catchError, map } from 'rxjs/operators'; | import { catchError, map } from 'rxjs/operators'; | ||||||
| 
 | 
 | ||||||
|  | @ -12,16 +13,17 @@ import { | ||||||
|   DataSourceApi, |   DataSourceApi, | ||||||
|   DataSourceWithQueryExportSupport, |   DataSourceWithQueryExportSupport, | ||||||
|   dateMath, |   dateMath, | ||||||
|  |   DateTime, | ||||||
|   dateTime, |   dateTime, | ||||||
|   getSearchFilterScopedVar, |   getSearchFilterScopedVar, | ||||||
|   MetricFindValue, |   MetricFindValue, | ||||||
|   QueryResultMetaStat, |   QueryResultMetaStat, | ||||||
|   ScopedVars, |   ScopedVars, | ||||||
|   TimeRange, |   TimeRange, | ||||||
|   TimeZone, |  | ||||||
|   toDataFrame, |   toDataFrame, | ||||||
| } from '@grafana/data'; | } from '@grafana/data'; | ||||||
| import { BackendSrvRequest, FetchResponse, getBackendSrv } from '@grafana/runtime'; | import { BackendSrvRequest, FetchResponse, getBackendSrv } from '@grafana/runtime'; | ||||||
|  | import { TimeZone } from '@grafana/schema'; | ||||||
| import { isVersionGtOrEq, SemVersion } from 'app/core/utils/version'; | import { isVersionGtOrEq, SemVersion } from 'app/core/utils/version'; | ||||||
| import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; | import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; | ||||||
| import { getRollupNotice, getRuntimeConsolidationNotice } from 'app/plugins/datasource/graphite/meta'; | import { getRollupNotice, getRuntimeConsolidationNotice } from 'app/plugins/datasource/graphite/meta'; | ||||||
|  | @ -500,17 +502,32 @@ export class GraphiteDatasource | ||||||
|     return this.templateSrv.containsTemplate(target.target ?? ''); |     return this.templateSrv.containsTemplate(target.target ?? ''); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   translateTime(date: any, roundUp?: boolean, timezone?: TimeZone) { |   translateTime(date: DateTime | string, roundUp?: boolean, timezone?: TimeZone) { | ||||||
|     if (isString(date)) { |     const parseDate = () => { | ||||||
|       if (date === 'now') { |       if (isString(date)) { | ||||||
|         return 'now'; |         if (date === 'now') { | ||||||
|       } else if (date.indexOf('now-') >= 0 && date.indexOf('/') === -1) { |           return 'now'; | ||||||
|         date = date.substring(3); |         } else if (date.indexOf('now-') >= 0 && date.indexOf('/') === -1) { | ||||||
|         date = date.replace('m', 'min'); |           return date.substring(3).replace('m', 'min').replace('M', 'mon'); | ||||||
|         date = date.replace('M', 'mon'); |         } | ||||||
|         return date; |         const parsedDate = dateMath.toDateTime(date, { roundUp, timezone }); | ||||||
|  | 
 | ||||||
|  |         // If the date is invalid return the original string
 | ||||||
|  |         // e.g. if an empty string is passed in or if the roundng is invalid e.g. now/2y
 | ||||||
|  |         if (!parsedDate || parsedDate.isValid() === false) { | ||||||
|  |           return date; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return moment(parsedDate.toDate()); | ||||||
|  |       } else { | ||||||
|  |         return moment(date.toDate()); | ||||||
|       } |       } | ||||||
|       date = dateMath.parse(date, roundUp, timezone); |     }; | ||||||
|  | 
 | ||||||
|  |     const parsedDate = parseDate(); | ||||||
|  | 
 | ||||||
|  |     if (typeof parsedDate === 'string') { | ||||||
|  |       return parsedDate; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // graphite' s from filter is exclusive
 |     // graphite' s from filter is exclusive
 | ||||||
|  | @ -518,16 +535,16 @@ export class GraphiteDatasource | ||||||
|     // to guarantee that we get all the data that
 |     // to guarantee that we get all the data that
 | ||||||
|     // exists for the specified range
 |     // exists for the specified range
 | ||||||
|     if (roundUp) { |     if (roundUp) { | ||||||
|       if (date.get('s')) { |       if (parsedDate.get('s')) { | ||||||
|         date.add(1, 's'); |         parsedDate.add(1, 's'); | ||||||
|       } |       } | ||||||
|     } else if (roundUp === false) { |     } else if (roundUp === false) { | ||||||
|       if (date.get('s')) { |       if (parsedDate.get('s')) { | ||||||
|         date.subtract(1, 's'); |         parsedDate.subtract(1, 's'); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return date.unix(); |     return parsedDate.unix(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   metricFindQuery(findQuery: string | GraphiteQuery, optionalOptions?: any): Promise<MetricFindValue[]> { |   metricFindQuery(findQuery: string | GraphiteQuery, optionalOptions?: any): Promise<MetricFindValue[]> { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue