sinkMeta.go 6.2 KB

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