dashboard_importer.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package plugins
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "regexp"
  6. "github.com/grafana/grafana/pkg/bus"
  7. "github.com/grafana/grafana/pkg/components/simplejson"
  8. m "github.com/grafana/grafana/pkg/models"
  9. )
  10. type ImportDashboardCommand struct {
  11. Dashboard *simplejson.Json
  12. Path string
  13. Inputs []ImportDashboardInput
  14. Overwrite bool
  15. OrgId int64
  16. UserId int64
  17. PluginId string
  18. Result *PluginDashboardInfoDTO
  19. }
  20. type ImportDashboardInput struct {
  21. Type string `json:"type"`
  22. PluginId string `json:"pluginId"`
  23. Name string `json:"name"`
  24. Value string `json:"value"`
  25. }
  26. type DashboardInputMissingError struct {
  27. VariableName string
  28. }
  29. func (e DashboardInputMissingError) Error() string {
  30. return fmt.Sprintf("Dashbord input variable: %v missing from import command", e.VariableName)
  31. }
  32. func init() {
  33. bus.AddHandler("plugins", ImportDashboard)
  34. }
  35. func ImportDashboard(cmd *ImportDashboardCommand) error {
  36. var dashboard *m.Dashboard
  37. var err error
  38. if cmd.PluginId != "" {
  39. if dashboard, err = loadPluginDashboard(cmd.PluginId, cmd.Path); err != nil {
  40. return err
  41. }
  42. if err = createDashboardFolderForPlugin(cmd, dashboard); err != nil {
  43. return err
  44. }
  45. } else {
  46. dashboard = m.NewDashboardFromJson(cmd.Dashboard)
  47. }
  48. evaluator := &DashTemplateEvaluator{
  49. template: dashboard.Data,
  50. inputs: cmd.Inputs,
  51. }
  52. generatedDash, err := evaluator.Eval()
  53. if err != nil {
  54. return err
  55. }
  56. saveCmd := m.SaveDashboardCommand{
  57. Dashboard: generatedDash,
  58. OrgId: cmd.OrgId,
  59. UserId: cmd.UserId,
  60. Overwrite: cmd.Overwrite,
  61. PluginId: cmd.PluginId,
  62. FolderId: dashboard.FolderId,
  63. }
  64. if err := bus.Dispatch(&saveCmd); err != nil {
  65. return err
  66. }
  67. cmd.Result = &PluginDashboardInfoDTO{
  68. PluginId: cmd.PluginId,
  69. Title: dashboard.Title,
  70. Path: cmd.Path,
  71. Revision: dashboard.Data.Get("revision").MustInt64(1),
  72. ImportedUri: "db/" + saveCmd.Result.Slug,
  73. ImportedRevision: dashboard.Data.Get("revision").MustInt64(1),
  74. Imported: true,
  75. }
  76. return nil
  77. }
  78. func createDashboardFolderForPlugin(cmd *ImportDashboardCommand, dashboard *m.Dashboard) error {
  79. var err error
  80. var plugin *PluginBase
  81. if plugin, err = getPlugin(cmd.PluginId); err != nil {
  82. return err
  83. }
  84. var pluginType string
  85. if plugin.Type == "datasource" {
  86. pluginType = "Datasource"
  87. } else if plugin.Type == "app" {
  88. pluginType = "App"
  89. }
  90. folderTitle := fmt.Sprint(pluginType, ": ", plugin.Name)
  91. folderDash := simplejson.NewFromAny(map[string]interface{}{
  92. "schemaVersion": 16,
  93. "title": folderTitle,
  94. "editable": true,
  95. "hideControls": true,
  96. })
  97. saveCmd := m.SaveDashboardCommand{
  98. Dashboard: folderDash,
  99. OrgId: cmd.OrgId,
  100. UserId: cmd.UserId,
  101. PluginId: cmd.PluginId,
  102. IsFolder: true,
  103. }
  104. dashModel := saveCmd.GetDashboardModel()
  105. getDashboardQuery := m.GetDashboardQuery{
  106. OrgId: cmd.OrgId,
  107. Slug: dashModel.Slug,
  108. }
  109. if err := bus.Dispatch(&getDashboardQuery); err != nil {
  110. return err
  111. }
  112. if getDashboardQuery.Result != nil {
  113. dashboard.FolderId = getDashboardQuery.Result.Id
  114. return nil
  115. }
  116. if err := bus.Dispatch(&saveCmd); err != nil {
  117. return err
  118. }
  119. dashboard.FolderId = saveCmd.Result.Id
  120. return nil
  121. }
  122. type DashTemplateEvaluator struct {
  123. template *simplejson.Json
  124. inputs []ImportDashboardInput
  125. variables map[string]string
  126. result *simplejson.Json
  127. varRegex *regexp.Regexp
  128. }
  129. func (this *DashTemplateEvaluator) findInput(varName string, varType string) *ImportDashboardInput {
  130. for _, input := range this.inputs {
  131. if varType == input.Type && (input.Name == varName || input.Name == "*") {
  132. return &input
  133. }
  134. }
  135. return nil
  136. }
  137. func (this *DashTemplateEvaluator) Eval() (*simplejson.Json, error) {
  138. this.result = simplejson.New()
  139. this.variables = make(map[string]string)
  140. this.varRegex, _ = regexp.Compile(`(\$\{.+\})`)
  141. // check that we have all inputs we need
  142. for _, inputDef := range this.template.Get("__inputs").MustArray() {
  143. inputDefJson := simplejson.NewFromAny(inputDef)
  144. inputName := inputDefJson.Get("name").MustString()
  145. inputType := inputDefJson.Get("type").MustString()
  146. input := this.findInput(inputName, inputType)
  147. if input == nil {
  148. return nil, &DashboardInputMissingError{VariableName: inputName}
  149. }
  150. this.variables["${"+inputName+"}"] = input.Value
  151. }
  152. return simplejson.NewFromAny(this.evalObject(this.template)), nil
  153. }
  154. func (this *DashTemplateEvaluator) evalValue(source *simplejson.Json) interface{} {
  155. sourceValue := source.Interface()
  156. switch v := sourceValue.(type) {
  157. case string:
  158. interpolated := this.varRegex.ReplaceAllStringFunc(v, func(match string) string {
  159. if replacement, exists := this.variables[match]; exists {
  160. return replacement
  161. } else {
  162. return match
  163. }
  164. })
  165. return interpolated
  166. case bool:
  167. return v
  168. case json.Number:
  169. return v
  170. case map[string]interface{}:
  171. return this.evalObject(source)
  172. case []interface{}:
  173. array := make([]interface{}, 0)
  174. for _, item := range v {
  175. array = append(array, this.evalValue(simplejson.NewFromAny(item)))
  176. }
  177. return array
  178. }
  179. return nil
  180. }
  181. func (this *DashTemplateEvaluator) evalObject(source *simplejson.Json) interface{} {
  182. result := make(map[string]interface{})
  183. for key, value := range source.MustMap() {
  184. if key == "__inputs" {
  185. continue
  186. }
  187. result[key] = this.evalValue(simplejson.NewFromAny(value))
  188. }
  189. return result
  190. }