dashboard.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. package sqlstore
  2. import (
  3. "bytes"
  4. "fmt"
  5. "github.com/go-xorm/xorm"
  6. "github.com/grafana/grafana/pkg/bus"
  7. "github.com/grafana/grafana/pkg/metrics"
  8. m "github.com/grafana/grafana/pkg/models"
  9. "github.com/grafana/grafana/pkg/services/search"
  10. )
  11. func init() {
  12. bus.AddHandler("sql", SaveDashboard)
  13. bus.AddHandler("sql", GetDashboard)
  14. bus.AddHandler("sql", GetDashboards)
  15. bus.AddHandler("sql", DeleteDashboard)
  16. bus.AddHandler("sql", SearchDashboards)
  17. bus.AddHandler("sql", GetDashboardTags)
  18. bus.AddHandler("sql", GetDashboardSlugById)
  19. bus.AddHandler("sql", GetDashboardsByPluginId)
  20. }
  21. func SaveDashboard(cmd *m.SaveDashboardCommand) error {
  22. return inTransaction(func(sess *xorm.Session) error {
  23. dash := cmd.GetDashboardModel()
  24. // try get existing dashboard
  25. var existing, sameTitle m.Dashboard
  26. if dash.Id > 0 {
  27. dashWithIdExists, err := sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existing)
  28. if err != nil {
  29. return err
  30. }
  31. if !dashWithIdExists {
  32. return m.ErrDashboardNotFound
  33. }
  34. // check for is someone else has written in between
  35. if dash.Version != existing.Version {
  36. if cmd.Overwrite {
  37. dash.Version = existing.Version
  38. } else {
  39. return m.ErrDashboardVersionMismatch
  40. }
  41. }
  42. // do not allow plugin dashboard updates without overwrite flag
  43. if existing.PluginId != "" && cmd.Overwrite == false {
  44. return m.UpdatePluginDashboardError{PluginId: existing.PluginId}
  45. }
  46. }
  47. sameTitleExists, err := sess.Where("org_id=? AND slug=?", dash.OrgId, dash.Slug).Get(&sameTitle)
  48. if err != nil {
  49. return err
  50. }
  51. if sameTitleExists {
  52. // another dashboard with same name
  53. if dash.Id != sameTitle.Id {
  54. if cmd.Overwrite {
  55. dash.Id = sameTitle.Id
  56. } else {
  57. return m.ErrDashboardWithSameNameExists
  58. }
  59. }
  60. }
  61. affectedRows := int64(0)
  62. if dash.Id == 0 {
  63. metrics.M_Models_Dashboard_Insert.Inc(1)
  64. affectedRows, err = sess.Insert(dash)
  65. } else {
  66. dash.Version += 1
  67. dash.Data.Set("version", dash.Version)
  68. affectedRows, err = sess.Id(dash.Id).Update(dash)
  69. }
  70. if affectedRows == 0 {
  71. return m.ErrDashboardNotFound
  72. }
  73. // delete existing tabs
  74. _, err = sess.Exec("DELETE FROM dashboard_tag WHERE dashboard_id=?", dash.Id)
  75. if err != nil {
  76. return err
  77. }
  78. // insert new tags
  79. tags := dash.GetTags()
  80. if len(tags) > 0 {
  81. for _, tag := range tags {
  82. if _, err := sess.Insert(&DashboardTag{DashboardId: dash.Id, Term: tag}); err != nil {
  83. return err
  84. }
  85. }
  86. }
  87. cmd.Result = dash
  88. return err
  89. })
  90. }
  91. func GetDashboard(query *m.GetDashboardQuery) error {
  92. dashboard := m.Dashboard{Slug: query.Slug, OrgId: query.OrgId}
  93. has, err := x.Get(&dashboard)
  94. if err != nil {
  95. return err
  96. } else if has == false {
  97. return m.ErrDashboardNotFound
  98. }
  99. dashboard.Data.Set("id", dashboard.Id)
  100. query.Result = &dashboard
  101. return nil
  102. }
  103. type DashboardSearchProjection struct {
  104. Id int64
  105. Title string
  106. Slug string
  107. Term string
  108. }
  109. func SearchDashboards(query *search.FindPersistedDashboardsQuery) error {
  110. var sql bytes.Buffer
  111. params := make([]interface{}, 0)
  112. sql.WriteString(`SELECT
  113. dashboard.id,
  114. dashboard.title,
  115. dashboard.slug,
  116. dashboard_tag.term
  117. FROM dashboard
  118. LEFT OUTER JOIN dashboard_tag on dashboard_tag.dashboard_id = dashboard.id`)
  119. if query.IsStarred {
  120. sql.WriteString(" INNER JOIN star on star.dashboard_id = dashboard.id")
  121. }
  122. sql.WriteString(` WHERE dashboard.org_id=?`)
  123. params = append(params, query.OrgId)
  124. if query.IsStarred {
  125. sql.WriteString(` AND star.user_id=?`)
  126. params = append(params, query.UserId)
  127. }
  128. if len(query.DashboardIds) > 0 {
  129. sql.WriteString(" AND (")
  130. for i, dashboardId := range query.DashboardIds {
  131. if i != 0 {
  132. sql.WriteString(" OR")
  133. }
  134. sql.WriteString(" dashboard.id = ?")
  135. params = append(params, dashboardId)
  136. }
  137. sql.WriteString(")")
  138. }
  139. if len(query.Title) > 0 {
  140. sql.WriteString(" AND dashboard.title " + dialect.LikeStr() + " ?")
  141. params = append(params, "%"+query.Title+"%")
  142. }
  143. sql.WriteString(fmt.Sprintf(" ORDER BY dashboard.title ASC LIMIT 1000"))
  144. var res []DashboardSearchProjection
  145. err := x.Sql(sql.String(), params...).Find(&res)
  146. if err != nil {
  147. return err
  148. }
  149. query.Result = make([]*search.Hit, 0)
  150. hits := make(map[int64]*search.Hit)
  151. for _, item := range res {
  152. hit, exists := hits[item.Id]
  153. if !exists {
  154. hit = &search.Hit{
  155. Id: item.Id,
  156. Title: item.Title,
  157. Uri: "db/" + item.Slug,
  158. Type: search.DashHitDB,
  159. Tags: []string{},
  160. }
  161. query.Result = append(query.Result, hit)
  162. hits[item.Id] = hit
  163. }
  164. if len(item.Term) > 0 {
  165. hit.Tags = append(hit.Tags, item.Term)
  166. }
  167. }
  168. return err
  169. }
  170. func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
  171. sql := `SELECT
  172. COUNT(*) as count,
  173. term
  174. FROM dashboard
  175. INNER JOIN dashboard_tag on dashboard_tag.dashboard_id = dashboard.id
  176. WHERE dashboard.org_id=?
  177. GROUP BY term`
  178. query.Result = make([]*m.DashboardTagCloudItem, 0)
  179. sess := x.Sql(sql, query.OrgId)
  180. err := sess.Find(&query.Result)
  181. return err
  182. }
  183. func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
  184. return inTransaction2(func(sess *session) error {
  185. dashboard := m.Dashboard{Slug: cmd.Slug, OrgId: cmd.OrgId}
  186. has, err := sess.Get(&dashboard)
  187. if err != nil {
  188. return err
  189. } else if has == false {
  190. return m.ErrDashboardNotFound
  191. }
  192. deletes := []string{
  193. "DELETE FROM dashboard_tag WHERE dashboard_id = ? ",
  194. "DELETE FROM star WHERE dashboard_id = ? ",
  195. "DELETE FROM dashboard WHERE id = ?",
  196. "DELETE FROM playlist_item WHERE type = 'dashboard_by_id' AND value = ?",
  197. }
  198. for _, sql := range deletes {
  199. _, err := sess.Exec(sql, dashboard.Id)
  200. if err != nil {
  201. return err
  202. }
  203. }
  204. if err := DeleteAlertDefinition(dashboard.Id, sess.Session); err != nil {
  205. return nil
  206. }
  207. return nil
  208. })
  209. }
  210. func GetDashboards(query *m.GetDashboardsQuery) error {
  211. if len(query.DashboardIds) == 0 {
  212. return m.ErrCommandValidationFailed
  213. }
  214. var dashboards = make([]*m.Dashboard, 0)
  215. err := x.In("id", query.DashboardIds).Find(&dashboards)
  216. query.Result = dashboards
  217. if err != nil {
  218. return err
  219. }
  220. return nil
  221. }
  222. func GetDashboardsByPluginId(query *m.GetDashboardsByPluginIdQuery) error {
  223. var dashboards = make([]*m.Dashboard, 0)
  224. err := x.Where("org_id=? AND plugin_id=?", query.OrgId, query.PluginId).Find(&dashboards)
  225. query.Result = dashboards
  226. if err != nil {
  227. return err
  228. }
  229. return nil
  230. }
  231. type DashboardSlugDTO struct {
  232. Slug string
  233. }
  234. func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error {
  235. var rawSql = `SELECT slug from dashboard WHERE Id=?`
  236. var slug = DashboardSlugDTO{}
  237. exists, err := x.Sql(rawSql, query.Id).Get(&slug)
  238. if err != nil {
  239. return err
  240. } else if exists == false {
  241. return m.ErrDashboardNotFound
  242. }
  243. query.Result = slug.Slug
  244. return nil
  245. }