regapi.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. //////////////////////////////////////////////////////////////////////////
  2. // DN42 Registry API Server
  3. //////////////////////////////////////////////////////////////////////////
  4. package main
  5. //////////////////////////////////////////////////////////////////////////
  6. import (
  7. // "fmt"
  8. "github.com/gorilla/mux"
  9. log "github.com/sirupsen/logrus"
  10. "net/http"
  11. "strings"
  12. // "time"
  13. )
  14. //////////////////////////////////////////////////////////////////////////
  15. // data structures
  16. type RegMetaReturn struct {
  17. Commit string
  18. }
  19. //////////////////////////////////////////////////////////////////////////
  20. // register the api
  21. func init() {
  22. EventBus.Listen("APIEndpoint", InitRegistryAPI)
  23. }
  24. //////////////////////////////////////////////////////////////////////////
  25. // called from main to initialise the API routing
  26. func InitRegistryAPI(params ...interface{}) {
  27. router := params[0].(*mux.Router)
  28. s := router.
  29. Methods("GET").
  30. PathPrefix("/registry").
  31. Subrouter()
  32. s.HandleFunc("/", regRootHandler)
  33. //s.HandleFunc("/.schema", rTypeListHandler)
  34. //s.HandleFunc("/.meta/", rTypeListHandler)
  35. s.HandleFunc("/.meta", regMetaHandler)
  36. s.HandleFunc("/{type}", regTypeHandler)
  37. s.HandleFunc("/{type}/{object}", regObjectHandler)
  38. s.HandleFunc("/{type}/{object}/{key}", regKeyHandler)
  39. s.HandleFunc("/{type}/{object}/{key}/{attribute}", regAttributeHandler)
  40. log.Info("Registry API installed")
  41. }
  42. //////////////////////////////////////////////////////////////////////////
  43. // return registry metadata
  44. func regMetaHandler(w http.ResponseWriter, r *http.Request) {
  45. rv := RegMetaReturn{
  46. Commit: RegistryData.Commit,
  47. }
  48. ResponseJSON(w, rv)
  49. }
  50. //////////////////////////////////////////////////////////////////////////
  51. // filter functions
  52. // return a list of types that match the filter
  53. func filterTypes(filter string) []*RegType {
  54. var rtypes []*RegType = nil
  55. // check if filter starts with '*'
  56. if filter[0] == '*' {
  57. // try and match the filter against all reg types
  58. filter = strings.ToLower(filter[1:])
  59. // special case, if the filter was '*' return all types
  60. if len(filter) == 0 {
  61. rtypes = make([]*RegType, 0, len(RegistryData.Types))
  62. for _, rtype := range RegistryData.Types {
  63. rtypes = append(rtypes, rtype)
  64. }
  65. } else {
  66. // otherwise substring match the types
  67. for _, rtype := range RegistryData.Types {
  68. lname := strings.ToLower(rtype.Ref)
  69. if strings.Contains(lname, filter) {
  70. // matched, add it to the list
  71. rtypes = append(rtypes, rtype)
  72. }
  73. }
  74. }
  75. } else {
  76. // perform an exact match with one entry
  77. rtype := RegistryData.Types[filter]
  78. if rtype != nil {
  79. // return a single answer
  80. rtypes = []*RegType{rtype}
  81. }
  82. }
  83. return rtypes
  84. }
  85. // return a list of objects from a set of types that match a filter
  86. func filterObjects(rtypes []*RegType, filter string) []*RegObject {
  87. var objects []*RegObject = nil
  88. // check if filter starts with '*'
  89. if filter[0] == '*' {
  90. // try and match objects against the filter
  91. filter = strings.ToLower(filter[1:])
  92. // for each type
  93. for _, rtype := range rtypes {
  94. // special case, if the filter was '*' return all objects
  95. if len(filter) == 0 {
  96. objs := make([]*RegObject, 0, len(rtype.Objects))
  97. for _, object := range rtype.Objects {
  98. objs = append(objs, object)
  99. }
  100. objects = append(objects, objs...)
  101. } else {
  102. // otherwise substring match the object names
  103. for _, object := range rtype.Objects {
  104. lname := strings.ToLower(object.Ref)
  105. if strings.Contains(lname, filter) {
  106. // matched, add it to the list
  107. objects = append(objects, object)
  108. }
  109. }
  110. }
  111. }
  112. } else {
  113. // perform an exact match against one object for each type
  114. for _, rtype := range rtypes {
  115. object := rtype.Objects[filter]
  116. if object != nil {
  117. // add the object
  118. objects = append(objects, object)
  119. }
  120. }
  121. }
  122. return objects
  123. }
  124. // return a list of key indices matching the filter
  125. func filterKeys(rtypes []*RegType, filter string) []*RegKeyIndex {
  126. var ix []*RegKeyIndex = nil
  127. // check if filter starts with '*'
  128. if filter[0] == '*' {
  129. // try and match keys against the filter
  130. filter = strings.ToLower(filter[1:])
  131. // for each type
  132. for _, rtype := range rtypes {
  133. ref := rtype.Ref
  134. schema := RegistryData.Schema[ref]
  135. // special case, if the filter was '*' return all indices
  136. if len(filter) == 0 {
  137. tmp := make([]*RegKeyIndex, 0, len(schema.KeyIndex))
  138. for _, keyix := range schema.KeyIndex {
  139. tmp = append(tmp, keyix)
  140. }
  141. ix = append(ix, tmp...)
  142. } else {
  143. // otherwise substring match the key names
  144. for kname, keyix := range schema.KeyIndex {
  145. kname = strings.ToLower(kname)
  146. if strings.Contains(kname, filter) {
  147. ix = append(ix, keyix)
  148. }
  149. }
  150. }
  151. }
  152. } else {
  153. // perform an exact match, one key for each type
  154. for _, rtype := range rtypes {
  155. ref := rtype.Ref
  156. schema := RegistryData.Schema[ref]
  157. keyix := schema.KeyIndex[filter]
  158. if keyix != nil {
  159. // add the index
  160. ix = append(ix, keyix)
  161. }
  162. }
  163. }
  164. return ix
  165. }
  166. // helper func to determine if an attribute matches a filter
  167. func matchAttribute(attribute *RegAttribute,
  168. filter string, isExact bool) bool {
  169. if isExact {
  170. return filter == attribute.RawValue
  171. } else {
  172. l := strings.ToLower(attribute.RawValue)
  173. return strings.Contains(l, filter)
  174. }
  175. }
  176. // return a map of objects and attribute values that match the filter
  177. func filterAttributes(ix []*RegKeyIndex, objects []*RegObject,
  178. filter string, raw bool) map[string]map[string][]string {
  179. result := make(map[string]map[string][]string)
  180. // pre-calculate the search type
  181. isExact := true
  182. isAll := false
  183. if filter[0] == '*' {
  184. isExact = false
  185. filter = strings.ToLower(filter[1:])
  186. if len(filter) == 0 {
  187. isAll = true
  188. }
  189. }
  190. // for each key index
  191. for _, keyix := range ix {
  192. // for each object
  193. for _, object := range objects {
  194. // attributes in this object that match this key
  195. attributes := keyix.Objects[object]
  196. if attributes != nil {
  197. // this object has at least one relevant key
  198. // match the attributes
  199. for _, attribute := range attributes {
  200. if isAll || matchAttribute(attribute, filter, isExact) {
  201. // match found !
  202. objmap := result[object.Ref]
  203. if objmap == nil {
  204. objmap = make(map[string][]string)
  205. result[object.Ref] = objmap
  206. }
  207. // append the result
  208. var value *string
  209. if raw {
  210. value = &attribute.RawValue
  211. } else {
  212. value = &attribute.Value
  213. }
  214. objmap[keyix.Ref] = append(objmap[keyix.Ref], *value)
  215. }
  216. }
  217. }
  218. }
  219. }
  220. return result
  221. }
  222. //////////////////////////////////////////////////////////////////////////
  223. // root handler, lists all types within the registry
  224. func regRootHandler(w http.ResponseWriter, r *http.Request) {
  225. response := make(map[string]int)
  226. for _, rType := range RegistryData.Types {
  227. response[rType.Ref] = len(rType.Objects)
  228. }
  229. ResponseJSON(w, response)
  230. }
  231. //////////////////////////////////////////////////////////////////////////
  232. // type handler returns list of objects that match the type
  233. func regTypeHandler(w http.ResponseWriter, r *http.Request) {
  234. // request parameters
  235. vars := mux.Vars(r)
  236. tFilter := vars["type"] // type filter
  237. // match registry types against the filter
  238. rtypes := filterTypes(tFilter)
  239. if rtypes == nil {
  240. http.Error(w, "No objects matching '"+tFilter+"' found",
  241. http.StatusNotFound)
  242. return
  243. }
  244. // construct the response
  245. response := make(map[string][]string)
  246. for _, rtype := range rtypes {
  247. objects := make([]string, 0, len(rtype.Objects))
  248. for key := range rtype.Objects {
  249. objects = append(objects, key)
  250. }
  251. response[rtype.Ref] = objects
  252. }
  253. ResponseJSON(w, response)
  254. }
  255. //////////////////////////////////////////////////////////////////////////
  256. // object handler returns object data
  257. // per object response structure
  258. type RegObjectResponse struct {
  259. Attributes [][2]string
  260. Backlinks []string
  261. }
  262. func regObjectHandler(w http.ResponseWriter, r *http.Request) {
  263. // request parameters
  264. vars := mux.Vars(r)
  265. query := r.URL.Query()
  266. tFilter := vars["type"] // type filter
  267. oFilter := vars["object"] // object filter
  268. raw := query["raw"] // raw or decorated results
  269. // select the type(s)
  270. rtypes := filterTypes(tFilter)
  271. if rtypes == nil {
  272. http.Error(w, "No objects matching '"+tFilter+"' found",
  273. http.StatusNotFound)
  274. return
  275. }
  276. // then select the objects
  277. objects := filterObjects(rtypes, oFilter)
  278. if objects == nil {
  279. http.Error(w, "No objects matching '"+tFilter+
  280. "/"+oFilter+"' found", http.StatusNotFound)
  281. return
  282. }
  283. // collate the results in to the response data
  284. if raw == nil {
  285. // provide a decorated response
  286. response := make(map[string]RegObjectResponse)
  287. // for each object in the results
  288. for _, object := range objects {
  289. // copy the raw attributes
  290. attributes := make([][2]string, len(object.Data))
  291. for ix, attribute := range object.Data {
  292. attributes[ix] = [2]string{attribute.Key, attribute.Value}
  293. }
  294. // construct the backlinks
  295. backlinks := make([]string, len(object.Backlinks))
  296. for ix, object := range object.Backlinks {
  297. backlinks[ix] = object.Ref
  298. }
  299. // add to the response
  300. response[object.Ref] = RegObjectResponse{
  301. Attributes: attributes,
  302. Backlinks: backlinks,
  303. }
  304. }
  305. ResponseJSON(w, response)
  306. } else {
  307. // provide a response with just the raw registry data
  308. response := make(map[string][][2]string)
  309. // for each object in the results
  310. for _, object := range objects {
  311. attributes := make([][2]string, len(object.Data))
  312. response[object.Ref] = attributes
  313. // copy the raw attributes
  314. for ix, attribute := range object.Data {
  315. attributes[ix] = [2]string{attribute.Key, attribute.RawValue}
  316. }
  317. }
  318. ResponseJSON(w, response)
  319. }
  320. }
  321. //////////////////////////////////////////////////////////////////////////
  322. // key handler returns attribute data matching the key
  323. func regKeyHandler(w http.ResponseWriter, r *http.Request) {
  324. // request parameters
  325. vars := mux.Vars(r)
  326. query := r.URL.Query()
  327. tFilter := vars["type"] // type filter
  328. oFilter := vars["object"] // object filter
  329. kFilter := vars["key"] // key filter
  330. raw := query["raw"] // raw or decorated results
  331. // select the type(s)
  332. rtypes := filterTypes(tFilter)
  333. if rtypes == nil {
  334. http.Error(w, "No objects matching '"+tFilter+"' found",
  335. http.StatusNotFound)
  336. return
  337. }
  338. // select the key indices
  339. ix := filterKeys(rtypes, kFilter)
  340. if rtypes == nil {
  341. http.Error(w, "No objects matching '"+tFilter+"/*/"+
  342. kFilter+"' found", http.StatusNotFound)
  343. return
  344. }
  345. // select the objects
  346. objects := filterObjects(rtypes, oFilter)
  347. if objects == nil {
  348. http.Error(w, "No objects matching '"+tFilter+
  349. "/"+oFilter+"' found", http.StatusNotFound)
  350. return
  351. }
  352. // select objects that match the keys
  353. amap := filterAttributes(ix, objects, "*", (raw != nil))
  354. if len(amap) == 0 {
  355. http.Error(w, "No attributes matching '"+tFilter+"/"+
  356. oFilter+"/"+kFilter+"' found", http.StatusNotFound)
  357. return
  358. }
  359. ResponseJSON(w, amap)
  360. }
  361. //////////////////////////////////////////////////////////////////////////
  362. // attribute handler returns attribute data matching the attribute
  363. func regAttributeHandler(w http.ResponseWriter, r *http.Request) {
  364. // request parameters
  365. vars := mux.Vars(r)
  366. query := r.URL.Query()
  367. tFilter := vars["type"] // type filter
  368. oFilter := vars["object"] // object filter
  369. kFilter := vars["key"] // key filter
  370. aFilter := vars["attribute"] // attribute filter
  371. raw := query["raw"] // raw or decorated results
  372. // select the type(s)
  373. rtypes := filterTypes(tFilter)
  374. if rtypes == nil {
  375. http.Error(w, "No objects matching '"+tFilter+"' found",
  376. http.StatusNotFound)
  377. return
  378. }
  379. // select the key indices
  380. ix := filterKeys(rtypes, kFilter)
  381. if rtypes == nil {
  382. http.Error(w, "No objects matching '"+tFilter+"/*/"+
  383. kFilter+"' found", http.StatusNotFound)
  384. return
  385. }
  386. // then select the objects
  387. objects := filterObjects(rtypes, oFilter)
  388. if objects == nil {
  389. http.Error(w, "No objects matching '"+tFilter+
  390. "/"+oFilter+"' found", http.StatusNotFound)
  391. return
  392. }
  393. // select objects that match the keys
  394. amap := filterAttributes(ix, objects, aFilter, (raw != nil))
  395. if len(amap) == 0 {
  396. http.Error(w, "No attributes matching '"+tFilter+"/"+
  397. oFilter+"/"+kFilter+"/"+aFilter+"' found", http.StatusNotFound)
  398. return
  399. }
  400. ResponseJSON(w, amap)
  401. }
  402. //////////////////////////////////////////////////////////////////////////
  403. // end of code