sinkMeta.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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 meta
  15. import (
  16. "fmt"
  17. "os"
  18. "path"
  19. "strings"
  20. "github.com/lf-edge/ekuiper/internal/conf"
  21. "github.com/lf-edge/ekuiper/internal/pkg/filex"
  22. "github.com/lf-edge/ekuiper/pkg/cast"
  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. ConnectionRelated bool `json:"connection_related"`
  44. Optional bool `json:"optional"`
  45. Type string `json:"type"`
  46. Hint *fileLanguage `json:"hint"`
  47. Label *fileLanguage `json:"label"`
  48. Values interface{} `json:"values"`
  49. }
  50. fileAbout struct {
  51. Trial bool `json:"trial"`
  52. Installed bool `json:"installed"`
  53. Author *author `json:"author"`
  54. HelpUrl *fileLanguage `json:"helpUrl"`
  55. Description *fileLanguage `json:"description"`
  56. }
  57. //fileNode struct {
  58. // Category string `json:"category"`
  59. // Icon string `json:"iconPath"`
  60. // Label *fileLanguage `json:"label"`
  61. //}
  62. fileSink struct {
  63. About *fileAbout `json:"about"`
  64. Libs []string `json:"libs"`
  65. Fields []*fileField `json:"properties"`
  66. Node interface{} `json:"node"`
  67. }
  68. language struct {
  69. English string `json:"en"`
  70. Chinese string `json:"zh"`
  71. }
  72. about struct {
  73. Trial bool `json:"trial"`
  74. Installed bool `json:"installed"`
  75. Author *author `json:"author"`
  76. HelpUrl *language `json:"helpUrl"`
  77. Description *language `json:"description"`
  78. }
  79. field struct {
  80. Exist bool `json:"exist"`
  81. Name string `json:"name"`
  82. Default interface{} `json:"default"`
  83. Type string `json:"type"`
  84. Control string `json:"control"`
  85. ConnectionRelated bool `json:"connection_related"`
  86. Optional bool `json:"optional"`
  87. Values interface{} `json:"values"`
  88. Hint *language `json:"hint"`
  89. Label *language `json:"label"`
  90. }
  91. node struct {
  92. Category string `json:"category"`
  93. Icon string `json:"iconPath"`
  94. Label *language `json:"label"`
  95. }
  96. uiSink struct {
  97. About *about `json:"about"`
  98. Libs []string `json:"libs"`
  99. Fields []field `json:"properties"`
  100. Node interface{} `json:"node"`
  101. }
  102. )
  103. func newLanguage(fi *fileLanguage) *language {
  104. if nil == fi {
  105. return nil
  106. }
  107. ui := new(language)
  108. ui.English = fi.English
  109. ui.Chinese = fi.Chinese
  110. return ui
  111. }
  112. func newField(fis []*fileField) (uis []field, err error) {
  113. for _, fi := range fis {
  114. if nil == fi {
  115. continue
  116. }
  117. ui := field{
  118. Name: fi.Name,
  119. Type: fi.Type,
  120. Control: fi.Control,
  121. ConnectionRelated: fi.ConnectionRelated,
  122. Optional: fi.Optional,
  123. Values: fi.Values,
  124. Hint: newLanguage(fi.Hint),
  125. Label: newLanguage(fi.Label),
  126. }
  127. switch t := fi.Default.(type) {
  128. case []interface{}:
  129. var auxFi []*fileField
  130. if err = cast.MapToStruct(t, &auxFi); nil != err {
  131. return nil, err
  132. }
  133. if ui.Default, err = newField(auxFi); nil != err {
  134. return nil, err
  135. }
  136. default:
  137. ui.Default = fi.Default
  138. }
  139. uis = append(uis, ui)
  140. }
  141. return uis, err
  142. }
  143. func newAbout(fi *fileAbout) *about {
  144. if nil == fi {
  145. return nil
  146. }
  147. ui := new(about)
  148. ui.Trial = fi.Trial
  149. ui.Installed = fi.Installed
  150. ui.Author = fi.Author
  151. ui.HelpUrl = newLanguage(fi.HelpUrl)
  152. ui.Description = newLanguage(fi.Description)
  153. return ui
  154. }
  155. // func newNode(fi *fileNode) *node {
  156. // if nil == fi {
  157. // return nil
  158. // }
  159. // ui := new(node)
  160. // ui.Category = fi.Category
  161. // ui.Icon = fi.Icon
  162. // ui.Label = newLanguage(fi.Label)
  163. // return ui
  164. // }
  165. func newUiSink(fi *fileSink) (*uiSink, error) {
  166. if nil == fi {
  167. return nil, nil
  168. }
  169. var err error
  170. ui := new(uiSink)
  171. ui.Libs = fi.Libs
  172. ui.Node = fi.Node
  173. ui.About = newAbout(fi.About)
  174. ui.Fields, err = newField(fi.Fields)
  175. return ui, err
  176. }
  177. var gSinkmetadata = make(map[string]*uiSink) //immutable
  178. func ReadSinkMetaDir(checker InstallChecker) error {
  179. confDir, err := conf.GetConfLoc()
  180. if nil != err {
  181. return err
  182. }
  183. dir := path.Join(confDir, "sinks")
  184. files, err := os.ReadDir(dir)
  185. if nil != err {
  186. return err
  187. }
  188. for _, file := range files {
  189. fname := file.Name()
  190. if !strings.HasSuffix(fname, ".json") {
  191. continue
  192. }
  193. filePath := path.Join(dir, fname)
  194. if err := ReadSinkMetaFile(filePath, checker(strings.TrimSuffix(fname, ".json"))); nil != err {
  195. return err
  196. }
  197. }
  198. confDir, err = conf.GetDataLoc()
  199. if nil != err {
  200. return err
  201. }
  202. dir = path.Join(confDir, "sinks")
  203. files, err = os.ReadDir(dir)
  204. if nil != err {
  205. return err
  206. }
  207. for _, file := range files {
  208. fname := file.Name()
  209. if !strings.HasSuffix(fname, ".json") {
  210. continue
  211. }
  212. filePath := path.Join(dir, fname)
  213. if err := ReadSinkMetaFile(filePath, checker(strings.TrimSuffix(fname, ".json"))); nil != err {
  214. return err
  215. }
  216. }
  217. return nil
  218. }
  219. func UninstallSink(name string) {
  220. if ui, ok := gSinkmetadata[name+".json"]; ok {
  221. if nil != ui.About {
  222. ui.About.Installed = false
  223. delete(gSinkmetadata, name+".json")
  224. }
  225. }
  226. delYamlConf(fmt.Sprintf(SinkCfgOperatorKeyTemplate, name))
  227. }
  228. func ReadSinkMetaFile(filePath string, installed bool) error {
  229. finame := path.Base(filePath)
  230. metadata := new(fileSink)
  231. err := filex.ReadJsonUnmarshal(filePath, metadata)
  232. if nil != err {
  233. return fmt.Errorf("filePath:%s err:%v", filePath, err)
  234. }
  235. if nil == metadata.About {
  236. return fmt.Errorf("not found about of %s", finame)
  237. } else {
  238. metadata.About.Installed = installed
  239. }
  240. gSinkmetadata[finame], err = newUiSink(metadata)
  241. if nil != err {
  242. return err
  243. }
  244. loadConfigOperatorForSink(strings.TrimSuffix(finame, `.json`))
  245. conf.Log.Infof("Loading metadata file for sink: %s", finame)
  246. return nil
  247. }
  248. func GetSinkMeta(pluginName, language string) (*uiSink, error) {
  249. fileName := pluginName + `.json`
  250. sinkMetadata := gSinkmetadata
  251. data, ok := sinkMetadata[fileName]
  252. if !ok || data == nil {
  253. return nil, fmt.Errorf(`%s%s`, getMsg(language, sink, "not_found_plugin"), pluginName)
  254. }
  255. return data, nil
  256. }
  257. type pluginfo struct {
  258. Name string `json:"name"`
  259. About *about `json:"about"`
  260. }
  261. func GetSinks() (sinks []*pluginfo) {
  262. sinkMeta := gSinkmetadata
  263. for fileName, v := range sinkMeta {
  264. node := new(pluginfo)
  265. node.Name = strings.TrimSuffix(fileName, `.json`)
  266. node.About = v.About
  267. i := 0
  268. for ; i < len(sinks); i++ {
  269. if node.Name <= sinks[i].Name {
  270. sinks = append(sinks, node)
  271. copy(sinks[i+1:], sinks[i:])
  272. sinks[i] = node
  273. break
  274. }
  275. }
  276. if len(sinks) == i {
  277. sinks = append(sinks, node)
  278. }
  279. }
  280. return sinks
  281. }