github.com/grafana/pyroscope@v1.18.0/public/app/models/app.ts (about) 1 import { parse, brandQuery, Query } from '@pyroscope/models/query'; 2 import { SpyNameSchema, UnitsSchema } from '@pyroscope/legacy/models'; 3 import { z } from 'zod'; 4 5 export const PyroscopeAppLabel = 'pyroscope_app'; 6 export const ServiceNameLabel = 'service_name'; 7 8 const AppWithPyroscopeAppIndex = z.object({ 9 __profile_type__: z.string(), 10 pyroscope_app: z.string(), 11 // Fake a discriminated union 12 __name_id__: z.enum([PyroscopeAppLabel]).default(PyroscopeAppLabel), 13 name: z.string().optional().default(''), 14 }); 15 16 const AppWithServiceNameIndex = z.object({ 17 __profile_type__: z.string(), 18 service_name: z.string(), 19 // Fake a discriminated union 20 __name_id__: z.enum([ServiceNameLabel]).default(ServiceNameLabel), 21 name: z.string().optional().default(''), 22 }); 23 24 // Backwards compatibility, 25 // even though https://github.com/grafana/phlare/pull/710 is merged 26 // we can't guarantee backend is deployed to support that 27 export const BasicAppSchema = AppWithPyroscopeAppIndex.or( 28 AppWithServiceNameIndex 29 ).transform(enhanceWithName); 30 31 const ExtraFields = z.object({ 32 __type__: z.string(), 33 __name__: z.string(), 34 }); 35 36 export const AppSchema = AppWithPyroscopeAppIndex.merge(ExtraFields) 37 .or(AppWithServiceNameIndex.merge(ExtraFields)) 38 .transform(enhanceWithName); 39 40 // Always populate the 'field' name, to make it easier for components that 41 // only need to display a name 42 function enhanceWithName< 43 T extends 44 | { __name_id__: 'pyroscope_app'; pyroscope_app: string; name: string } 45 | { 46 __name_id__: 'service_name'; 47 service_name: string; 48 name: string; 49 } 50 >(a: T) { 51 if (a.__name_id__ === 'pyroscope_app') { 52 a.name = a.pyroscope_app; 53 } 54 if (a.__name_id__ === 'service_name') { 55 a.name = a.service_name; 56 } 57 return a; 58 } 59 60 export type App = z.infer<typeof AppSchema>; 61 62 export type BasicApp = z.infer<typeof BasicAppSchema>; 63 64 // Given a query returns an App 65 export function appFromQuery( 66 query: Query 67 ): z.infer<typeof BasicAppSchema> | undefined { 68 const parsed = parse(query); 69 70 if (!parsed) { 71 return undefined; 72 } 73 74 const app = { 75 __profile_type__: parsed?.profileId, 76 ...parsed?.tags, 77 }; 78 79 const parsedApp = BasicAppSchema.safeParse(app); 80 if (!parsedApp.success) { 81 return undefined; 82 } 83 84 return parsedApp.data; 85 } 86 87 export function appToQuery(app: z.infer<typeof BasicAppSchema>): Query { 88 // Useless check just to satisfy type checking 89 if (app.__name_id__ === 'pyroscope_app') { 90 return brandQuery( 91 `${app.__profile_type__}{${app.__name_id__}="${app[app.__name_id__]}"}` 92 ); 93 } 94 95 return brandQuery( 96 `${app.__profile_type__}{${app.__name_id__}="${app[app.__name_id__]}"}` 97 ); 98 } 99 100 // TODO old App type 101 // 102 // export type App = z.infer<typeof appModel>; 103 104 export const appModel = z.object({ 105 name: z.string(), 106 spyName: SpyNameSchema, 107 units: UnitsSchema, 108 }); 109 110 export const appsModel = z.array(appModel);