sinkMeta.go 5.7 KB

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