2022-09-22 15:04:48 -05:00
package navtree
2022-09-28 01:29:35 -05:00
import (
"encoding/json"
"sort"
)
2022-09-22 15:04:48 -05:00
const (
// These weights may be used by an extension to reliably place
// itself in relation to a particular item in the menu. The weights
// are negative to ensure that the default items are placed above
// any items with default weight.
2022-10-03 09:05:19 -05:00
WeightHome = ( iota - 20 ) * 100
WeightSavedItems
2022-09-22 15:04:48 -05:00
WeightDashboard
WeightExplore
WeightAlerting
2022-10-05 04:46:27 -05:00
WeightAlertsAndIncidents
2023-12-12 04:57:52 -06:00
WeightTestingAndSynthetics
2022-10-05 04:46:27 -05:00
WeightMonitoring
2023-11-30 09:18:05 -06:00
WeightInfrastructure
WeightApplication
WeightFrontend
2024-01-26 09:30:36 -06:00
WeightAsserts
2023-04-14 05:46:18 -05:00
WeightDataConnections
2022-10-05 04:46:27 -05:00
WeightApps
2023-04-17 10:01:32 -05:00
WeightPlugin
WeightConfig
2022-09-22 15:04:48 -05:00
WeightProfile
WeightHelp
)
2022-09-28 01:29:35 -05:00
const (
2023-12-12 04:57:52 -06:00
NavIDRoot = "root"
NavIDDashboards = "dashboards/browse"
NavIDExplore = "explore"
NavIDCfg = "cfg" // NavIDCfg is the id for org configuration navigation node
NavIDAlertsAndIncidents = "alerts-and-incidents"
NavIDTestingAndSynthetics = "testing-and-synthetics"
NavIDAlerting = "alerting"
NavIDMonitoring = "monitoring"
NavIDInfrastructure = "infrastructure"
NavIDFrontend = "frontend"
NavIDReporting = "reports"
NavIDApps = "apps"
NavIDCfgGeneral = "cfg/general"
NavIDCfgPlugins = "cfg/plugins"
NavIDCfgAccess = "cfg/access"
2022-09-28 01:29:35 -05:00
)
2022-09-22 15:04:48 -05:00
type NavLink struct {
2023-04-17 10:01:32 -05:00
Id string ` json:"id,omitempty" `
Text string ` json:"text" `
SubTitle string ` json:"subTitle,omitempty" `
Icon string ` json:"icon,omitempty" ` // Available icons can be browsed in Storybook: https://developers.grafana.com/ui/latest/index.html?path=/story/docs-overview-icon--icons-overview
Img string ` json:"img,omitempty" `
Url string ` json:"url,omitempty" `
Target string ` json:"target,omitempty" `
SortWeight int64 ` json:"sortWeight,omitempty" `
HideFromTabs bool ` json:"hideFromTabs,omitempty" `
RoundIcon bool ` json:"roundIcon,omitempty" `
IsSection bool ` json:"isSection,omitempty" `
Children [ ] * NavLink ` json:"children,omitempty" `
HighlightText string ` json:"highlightText,omitempty" `
HighlightID string ` json:"highlightId,omitempty" `
EmptyMessageId string ` json:"emptyMessageId,omitempty" `
PluginID string ` json:"pluginId,omitempty" ` // (Optional) The ID of the plugin that registered nav link (e.g. as a standalone plugin page)
IsCreateAction bool ` json:"isCreateAction,omitempty" `
2024-01-15 05:30:55 -06:00
Keywords [ ] string ` json:"keywords,omitempty" `
2022-09-22 15:04:48 -05:00
}
2022-09-28 01:29:35 -05:00
func ( node * NavLink ) Sort ( ) {
Sort ( node . Children )
}
type NavTreeRoot struct {
Children [ ] * NavLink
}
func ( root * NavTreeRoot ) AddSection ( node * NavLink ) {
root . Children = append ( root . Children , node )
}
func ( root * NavTreeRoot ) RemoveSection ( node * NavLink ) {
var result [ ] * NavLink
for _ , child := range root . Children {
if child != node {
result = append ( result , child )
}
}
root . Children = result
}
func ( root * NavTreeRoot ) FindById ( id string ) * NavLink {
return FindById ( root . Children , id )
}
2023-10-17 11:15:51 -05:00
func ( root * NavTreeRoot ) FindByURL ( url string ) * NavLink {
return FindByURL ( root . Children , url )
}
2022-09-28 01:29:35 -05:00
func ( root * NavTreeRoot ) Sort ( ) {
Sort ( root . Children )
}
func ( root * NavTreeRoot ) MarshalJSON ( ) ( [ ] byte , error ) {
return json . Marshal ( root . Children )
}
func Sort ( nodes [ ] * NavLink ) {
sort . SliceStable ( nodes , func ( i , j int ) bool {
iw := nodes [ i ] . SortWeight
if iw == 0 {
iw = int64 ( i ) + 1
}
jw := nodes [ j ] . SortWeight
if jw == 0 {
jw = int64 ( j ) + 1
}
2022-09-22 15:04:48 -05:00
2022-09-28 01:29:35 -05:00
return iw < jw
} )
for _ , child := range nodes {
child . Sort ( )
2022-09-22 15:04:48 -05:00
}
2022-09-28 01:29:35 -05:00
}
2024-03-15 11:39:13 -05:00
func ( root * NavTreeRoot ) ApplyHelpVersion ( version string ) {
helpNode := root . FindById ( "help" )
if helpNode != nil {
helpNode . SubTitle = version
}
}
2023-11-17 04:04:34 -06:00
func ( root * NavTreeRoot ) ApplyAdminIA ( ) {
2022-11-18 09:11:59 -06:00
orgAdminNode := root . FindById ( NavIDCfg )
if orgAdminNode != nil {
2023-01-13 00:32:09 -06:00
adminNodeLinks := [ ] * NavLink { }
2022-11-18 09:11:59 -06:00
2023-11-17 04:04:34 -06:00
generalNodeLinks := [ ] * NavLink { }
generalNodeLinks = AppendIfNotNil ( generalNodeLinks , root . FindById ( "upgrading" ) ) // TODO does this even exist
generalNodeLinks = AppendIfNotNil ( generalNodeLinks , root . FindById ( "licensing" ) )
generalNodeLinks = AppendIfNotNil ( generalNodeLinks , root . FindById ( "org-settings" ) )
generalNodeLinks = AppendIfNotNil ( generalNodeLinks , root . FindById ( "server-settings" ) )
generalNodeLinks = AppendIfNotNil ( generalNodeLinks , root . FindById ( "global-orgs" ) )
generalNodeLinks = AppendIfNotNil ( generalNodeLinks , root . FindById ( "feature-toggles" ) )
generalNodeLinks = AppendIfNotNil ( generalNodeLinks , root . FindById ( "storage" ) )
2024-02-21 09:50:13 -06:00
generalNodeLinks = AppendIfNotNil ( generalNodeLinks , root . FindById ( "migrate-to-cloud" ) )
2023-11-17 04:04:34 -06:00
generalNode := & NavLink {
Text : "General" ,
SubTitle : "Manage default preferences and settings across Grafana" ,
Id : NavIDCfgGeneral ,
Url : "/admin/general" ,
Icon : "shield" ,
Children : generalNodeLinks ,
}
2023-10-11 08:37:36 -05:00
2023-11-17 04:04:34 -06:00
pluginsNodeLinks := [ ] * NavLink { }
pluginsNodeLinks = AppendIfNotNil ( pluginsNodeLinks , root . FindById ( "plugins" ) )
pluginsNodeLinks = AppendIfNotNil ( pluginsNodeLinks , root . FindById ( "datasources" ) )
pluginsNodeLinks = AppendIfNotNil ( pluginsNodeLinks , root . FindById ( "recordedQueries" ) )
pluginsNodeLinks = AppendIfNotNil ( pluginsNodeLinks , root . FindById ( "correlations" ) )
pluginsNodeLinks = AppendIfNotNil ( pluginsNodeLinks , root . FindById ( "plugin-page-grafana-cloud-link-app" ) )
pluginsNode := & NavLink {
Text : "Plugins and data" ,
SubTitle : "Install plugins and define the relationships between data" ,
Id : NavIDCfgPlugins ,
Url : "/admin/plugins" ,
Icon : "shield" ,
Children : pluginsNodeLinks ,
}
2023-10-11 08:37:36 -05:00
2023-11-17 04:04:34 -06:00
accessNodeLinks := [ ] * NavLink { }
accessNodeLinks = AppendIfNotNil ( accessNodeLinks , root . FindById ( "global-users" ) )
accessNodeLinks = AppendIfNotNil ( accessNodeLinks , root . FindById ( "teams" ) )
accessNodeLinks = AppendIfNotNil ( accessNodeLinks , root . FindById ( "standalone-plugin-page-/a/grafana-auth-app" ) )
accessNodeLinks = AppendIfNotNil ( accessNodeLinks , root . FindById ( "serviceaccounts" ) )
accessNodeLinks = AppendIfNotNil ( accessNodeLinks , root . FindById ( "apikeys" ) )
usersNode := & NavLink {
Text : "Users and access" ,
SubTitle : "Configure access for individual users, teams, and service accounts" ,
Id : NavIDCfgAccess ,
Url : "/admin/access" ,
Icon : "shield" ,
Children : accessNodeLinks ,
}
2023-10-11 08:37:36 -05:00
2023-11-17 04:04:34 -06:00
if len ( generalNode . Children ) > 0 {
adminNodeLinks = append ( adminNodeLinks , generalNode )
}
2023-10-11 08:37:36 -05:00
2023-11-17 04:04:34 -06:00
if len ( pluginsNode . Children ) > 0 {
adminNodeLinks = append ( adminNodeLinks , pluginsNode )
}
2023-10-11 08:37:36 -05:00
2023-11-17 04:04:34 -06:00
if len ( usersNode . Children ) > 0 {
adminNodeLinks = append ( adminNodeLinks , usersNode )
}
2023-10-11 08:37:36 -05:00
2023-11-17 04:04:34 -06:00
authenticationNode := root . FindById ( "authentication" )
if authenticationNode != nil {
authenticationNode . IsSection = true
adminNodeLinks = append ( adminNodeLinks , authenticationNode )
}
2023-10-17 11:15:51 -05:00
2023-11-17 04:04:34 -06:00
costManagementNode := root . FindById ( "plugin-page-grafana-costmanagementui-app" )
2023-10-17 11:15:51 -05:00
2023-11-17 04:04:34 -06:00
if costManagementNode != nil {
adminNodeLinks = append ( adminNodeLinks , costManagementNode )
}
2023-10-17 11:15:51 -05:00
2023-11-17 04:04:34 -06:00
costManagementMetricsNode := root . FindByURL ( "/a/grafana-costmanagementui-app/metrics" )
adaptiveMetricsNode := root . FindById ( "plugin-page-grafana-adaptive-metrics-app" )
2023-10-17 11:15:51 -05:00
2023-11-17 04:04:34 -06:00
if costManagementMetricsNode != nil && adaptiveMetricsNode != nil {
costManagementMetricsNode . Children = append ( costManagementMetricsNode . Children , adaptiveMetricsNode )
}
2023-10-17 11:15:51 -05:00
2023-11-17 04:04:34 -06:00
costManagementLogsNode := root . FindByURL ( "/a/grafana-costmanagementui-app/logs" )
logVolumeExplorerNode := root . FindById ( "plugin-page-grafana-logvolumeexplorer-app" )
2023-10-17 11:15:51 -05:00
2023-11-17 04:04:34 -06:00
if costManagementLogsNode != nil && logVolumeExplorerNode != nil {
costManagementLogsNode . Children = append ( costManagementLogsNode . Children , logVolumeExplorerNode )
2023-10-11 08:37:36 -05:00
}
2022-12-08 12:57:33 -06:00
2022-11-18 09:11:59 -06:00
if len ( adminNodeLinks ) > 0 {
orgAdminNode . Children = adminNodeLinks
} else {
root . RemoveSection ( orgAdminNode )
}
}
}
func AppendIfNotNil ( children [ ] * NavLink , newChild * NavLink ) [ ] * NavLink {
if newChild != nil {
return append ( children , newChild )
}
return children
}
2022-09-28 01:29:35 -05:00
func FindById ( nodes [ ] * NavLink , id string ) * NavLink {
for _ , child := range nodes {
if child . Id == id {
return child
} else if len ( child . Children ) > 0 {
if found := FindById ( child . Children , id ) ; found != nil {
return found
}
}
2022-09-22 15:04:48 -05:00
}
2022-09-28 01:29:35 -05:00
return nil
2022-09-22 15:04:48 -05:00
}
2023-10-17 11:15:51 -05:00
func FindByURL ( nodes [ ] * NavLink , url string ) * NavLink {
for _ , child := range nodes {
if child . Url == url {
return child
} else if len ( child . Children ) > 0 {
if found := FindByURL ( child . Children , url ) ; found != nil {
return found
}
}
}
return nil
}