sinkMeta.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. package plugins
  2. import (
  3. "fmt"
  4. "github.com/emqx/kuiper/common"
  5. "io/ioutil"
  6. "path"
  7. "strings"
  8. )
  9. const (
  10. sink = `sink`
  11. source = `source`
  12. )
  13. type (
  14. author struct {
  15. Name string `json:"name"`
  16. Email string `json:"email"`
  17. Company string `json:"company"`
  18. Website string `json:"website"`
  19. }
  20. fileLanguage struct {
  21. English string `json:"en_US"`
  22. Chinese string `json:"zh_CN"`
  23. }
  24. fileField struct {
  25. Name string `json:"name"`
  26. Default interface{} `json:"default"`
  27. Control string `json:"control"`
  28. Optional bool `json:"optional"`
  29. Type string `json:"type"`
  30. Hint *fileLanguage `json:"hint"`
  31. Label *fileLanguage `json:"label"`
  32. Values interface{} `json:"values"`
  33. }
  34. fileAbout struct {
  35. Trial bool `json:"trial"`
  36. Installed bool `json:"installed"`
  37. Author *author `json:"author"`
  38. HelpUrl *fileLanguage `json:"helpUrl"`
  39. Description *fileLanguage `json:"description"`
  40. }
  41. fileSink struct {
  42. About *fileAbout `json:"about"`
  43. Libs []string `json:"libs"`
  44. Fields []*fileField `json:"properties"`
  45. }
  46. language struct {
  47. English string `json:"en"`
  48. Chinese string `json:"zh"`
  49. }
  50. about struct {
  51. Trial bool `json:"trial"`
  52. Installed bool `json:"installed"`
  53. Author *author `json:"author"`
  54. HelpUrl *language `json:"helpUrl"`
  55. Description *language `json:"description"`
  56. }
  57. field struct {
  58. Exist bool `json:"exist"`
  59. Name string `json:"name"`
  60. Default interface{} `json:"default"`
  61. Type string `json:"type"`
  62. Control string `json:"control"`
  63. Optional bool `json:"optional"`
  64. Values interface{} `json:"values"`
  65. Hint *language `json:"hint"`
  66. Label *language `json:"label"`
  67. }
  68. uiSink struct {
  69. About *about `json:"about"`
  70. Libs []string `json:"libs"`
  71. Fields []field `json:"properties"`
  72. }
  73. uiSinks struct {
  74. CustomProperty map[string]*uiSink `json:"customProperty"`
  75. language string
  76. }
  77. )
  78. func isInternalSink(fiName string) bool {
  79. internal := []string{`edgex.json`, `log.json`, `mqtt.json`, `nop.json`, `rest.json`}
  80. for _, v := range internal {
  81. if v == fiName {
  82. return true
  83. }
  84. }
  85. return false
  86. }
  87. func newLanguage(fi *fileLanguage) *language {
  88. if nil == fi {
  89. return nil
  90. }
  91. ui := new(language)
  92. ui.English = fi.English
  93. ui.Chinese = fi.Chinese
  94. return ui
  95. }
  96. func newField(fis []*fileField) (uis []field, err error) {
  97. for _, fi := range fis {
  98. if nil == fi {
  99. continue
  100. }
  101. ui := field{
  102. Name: fi.Name,
  103. Type: fi.Type,
  104. Control: fi.Control,
  105. Optional: fi.Optional,
  106. Values: fi.Values,
  107. Hint: newLanguage(fi.Hint),
  108. Label: newLanguage(fi.Label),
  109. }
  110. uis = append(uis, ui)
  111. switch t := fi.Default.(type) {
  112. case []map[string]interface{}:
  113. var auxFi []*fileField
  114. if err = common.MapToStruct(t, &auxFi); nil != err {
  115. return nil, err
  116. }
  117. if ui.Default, err = newField(auxFi); nil != err {
  118. return nil, err
  119. }
  120. default:
  121. ui.Default = fi.Default
  122. }
  123. }
  124. return uis, err
  125. }
  126. func newAbout(fi *fileAbout) *about {
  127. if nil == fi {
  128. return nil
  129. }
  130. ui := new(about)
  131. ui.Trial = fi.Trial
  132. ui.Installed = fi.Installed
  133. ui.Author = fi.Author
  134. ui.HelpUrl = newLanguage(fi.HelpUrl)
  135. ui.Description = newLanguage(fi.Description)
  136. return ui
  137. }
  138. func newUiSink(fi *fileSink) (*uiSink, error) {
  139. if nil == fi {
  140. return nil, nil
  141. }
  142. var err error
  143. ui := new(uiSink)
  144. ui.Libs = fi.Libs
  145. ui.About = newAbout(fi.About)
  146. ui.Fields, err = newField(fi.Fields)
  147. return ui, err
  148. }
  149. var g_sinkMetadata map[string]*uiSink //immutable
  150. func (m *Manager) readSinkMetaDir() error {
  151. g_sinkMetadata = make(map[string]*uiSink)
  152. confDir, err := common.GetConfLoc()
  153. if nil != err {
  154. return err
  155. }
  156. dir := path.Join(confDir, "sinks")
  157. files, err := ioutil.ReadDir(dir)
  158. if nil != err {
  159. return err
  160. }
  161. for _, file := range files {
  162. fname := file.Name()
  163. if !strings.HasSuffix(fname, ".json") {
  164. continue
  165. }
  166. filePath := path.Join(dir, fname)
  167. if err := m.readSinkMetaFile(filePath); nil != err {
  168. return err
  169. }
  170. }
  171. return nil
  172. }
  173. func (m *Manager) uninstalSink(name string) {
  174. if ui, ok := g_sinkMetadata[name+".json"]; ok {
  175. if nil != ui.About {
  176. ui.About.Installed = false
  177. }
  178. }
  179. }
  180. func (m *Manager) readSinkMetaFile(filePath string) error {
  181. finame := path.Base(filePath)
  182. pluginName := strings.TrimSuffix(finame, `.json`)
  183. metadata := new(fileSink)
  184. err := common.ReadJsonUnmarshal(filePath, metadata)
  185. if nil != err {
  186. return fmt.Errorf("filePath:%s err:%v", filePath, err)
  187. }
  188. if nil == metadata.About {
  189. return fmt.Errorf("not found about of %s", finame)
  190. } else if isInternalSink(finame) {
  191. metadata.About.Installed = true
  192. } else {
  193. _, metadata.About.Installed = m.registry.Get(SINK, pluginName)
  194. }
  195. g_sinkMetadata[finame], err = newUiSink(metadata)
  196. if nil != err {
  197. return err
  198. }
  199. common.Log.Infof("Loading metadata file for sink: %s", finame)
  200. return nil
  201. }
  202. func (us *uiSinks) setCustomProperty(pluginName string) error {
  203. fileName := pluginName + `.json`
  204. sinkMetadata := g_sinkMetadata
  205. data, ok := sinkMetadata[fileName]
  206. if !ok {
  207. return fmt.Errorf(`%s%s`, getMsg(us.language, sink, "not_found_plugin"), pluginName)
  208. }
  209. if 0 == len(us.CustomProperty) {
  210. us.CustomProperty = make(map[string]*uiSink)
  211. }
  212. us.CustomProperty[pluginName] = data
  213. return nil
  214. }
  215. func (us *uiSinks) hintWhenNewSink(pluginName string) (err error) {
  216. return us.setCustomProperty(pluginName)
  217. }
  218. func GetSinkMeta(pluginName, language string) (ptrSinkProperty *uiSinks, err error) {
  219. ptrSinkProperty = new(uiSinks)
  220. ptrSinkProperty.language = language
  221. err = ptrSinkProperty.hintWhenNewSink(pluginName)
  222. return ptrSinkProperty, err
  223. }
  224. type pluginfo struct {
  225. Name string `json:"name"`
  226. About *about `json:"about"`
  227. }
  228. func GetSinks() (sinks []*pluginfo) {
  229. sinkMeta := g_sinkMetadata
  230. for fileName, v := range sinkMeta {
  231. node := new(pluginfo)
  232. node.Name = strings.TrimSuffix(fileName, `.json`)
  233. node.About = v.About
  234. i := 0
  235. for ; i < len(sinks); i++ {
  236. if node.Name <= sinks[i].Name {
  237. sinks = append(sinks, node)
  238. copy(sinks[i+1:], sinks[i:])
  239. sinks[i] = node
  240. break
  241. }
  242. }
  243. if len(sinks) == i {
  244. sinks = append(sinks, node)
  245. }
  246. }
  247. return sinks
  248. }