dn42regsrv/regapi.go
2019-02-09 21:17:17 +00:00

268 lines
6.2 KiB
Go

//////////////////////////////////////////////////////////////////////////
// DN42 Registry API Server
//////////////////////////////////////////////////////////////////////////
package main
//////////////////////////////////////////////////////////////////////////
import (
"encoding/json"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"net/http"
"strings"
// "time"
)
//////////////////////////////////////////////////////////////////////////
// register the api
func init() {
RegisterAPIEndpoint(InitRegAPI)
}
//////////////////////////////////////////////////////////////////////////
// called from main to initialise the API routing
func InitRegAPI(router *mux.Router) {
s := router.
Methods("GET").
PathPrefix("/registry").
Subrouter()
s.HandleFunc("/", regRootHandler)
//s.HandleFunc("/.schema", rTypeListHandler)
//s.HandleFunc("/.meta/", rTypeListHandler)
s.HandleFunc("/{type}", regTypeHandler)
s.HandleFunc("/{type}/{object}", regObjectHandler)
log.Info("Registry API installed")
}
//////////////////////////////////////////////////////////////////////////
// handler utility funcs
func responseJSON(w http.ResponseWriter, v interface{}) {
// for response time testing
//time.Sleep(time.Second)
// marshal the JSON string
data, err := json.Marshal(v)
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("Failed to marshal JSON")
}
// write back to http handler
w.Header().Set("Content-Type", "application/json")
w.Write(data)
}
//////////////////////////////////////////////////////////////////////////
// root handler, lists all types within the registry
func regRootHandler(w http.ResponseWriter, r *http.Request) {
response := make(map[string]int)
for _, rType := range RegistryData.Types {
response[rType.Ref] = len(rType.Objects)
}
responseJSON(w, response)
}
//////////////////////////////////////////////////////////////////////////
// type handler returns list of objects that match the type
func regTypeHandler(w http.ResponseWriter, r *http.Request) {
// request parameters
vars := mux.Vars(r)
query := r.URL.Query()
typeName := vars["type"] // type name to list
match := query["match"] // single query or match
// special case to return all types
all := false
if typeName == "*" {
match = []string{}
all = true
}
// results will hold the types to return
var results []*RegType
// check match type
if match == nil {
// exact match
// check the type object exists
rType := RegistryData.Types[typeName]
if rType == nil {
http.Error(w, "No types matching '"+typeName+"' found", http.StatusNotFound)
return
}
// return just a single result
results = []*RegType{rType}
} else {
// substring match
// comparisons are lower case
typeName = strings.ToLower(typeName)
// walk through the types and filter to the results list
results = make([]*RegType, 0)
for key, rType := range RegistryData.Types {
if all || strings.Contains(strings.ToLower(key), typeName) {
// match found, add to the list
results = append(results, rType)
}
}
}
// construct the response
response := make(map[string][]string)
for _, rType := range results {
objects := make([]string, 0, len(rType.Objects))
for key := range rType.Objects {
objects = append(objects, key)
}
response[rType.Ref] = objects
}
responseJSON(w, response)
}
//////////////////////////////////////////////////////////////////////////
// object handler returns object data
// per object response structure
type RegObjectResponse struct {
Attributes [][2]string
Backlinks []string
}
func regObjectHandler(w http.ResponseWriter, r *http.Request) {
// request parameters
vars := mux.Vars(r)
query := r.URL.Query()
typeName := vars["type"] // object type
objName := vars["object"] // object name or match
match := query["match"] // single query or match
raw := query["raw"] // raw or decorated results
// special case to return all objects
all := false
if objName == "*" {
match = []string{}
all = true
}
// verify the type exists
rType := RegistryData.Types[typeName]
if rType == nil {
http.Error(w, "No types matching '"+typeName+"' found",
http.StatusNotFound)
return
}
// results will hold the objects to return
var results []*RegObject
// check match type
if match == nil {
// exact match
// check the object exists
object := rType.Objects[objName]
if object == nil {
http.Error(w, "No objects matching '"+objName+"' found",
http.StatusNotFound)
return
}
// then just create a results list with one object
results = []*RegObject{object}
} else {
// substring matching
// comparisons are lower case
objName = strings.ToLower(objName)
// walk through the type objects and filter to the results list
results = make([]*RegObject, 0)
for key, object := range rType.Objects {
if all || strings.Contains(strings.ToLower(key), objName) {
// match found, add to the list
results = append(results, object)
}
}
}
// collate the results in to the response data
if raw == nil {
// provide a decorated response
response := make(map[string]RegObjectResponse)
// for each object in the results
for _, object := range results {
// copy the raw attributes
attributes := make([][2]string, len(object.Data))
for ix, attribute := range object.Data {
attributes[ix] = [2]string{attribute.Key, attribute.Value}
}
// construct the backlinks
backlinks := make([]string, len(object.Backlinks))
for ix, object := range object.Backlinks {
backlinks[ix] = object.Ref
}
// add to the response
response[object.Ref] = RegObjectResponse{
Attributes: attributes,
Backlinks: backlinks,
}
}
responseJSON(w, response)
} else {
// provide a response with just the raw registry data
response := make(map[string][][2]string)
// for each object in the results
for _, object := range results {
attributes := make([][2]string, len(object.Data))
response[object.Ref] = attributes
// copy the raw attributes
for ix, attribute := range object.Data {
attributes[ix] = [2]string{attribute.Key, attribute.RawValue}
}
}
responseJSON(w, response)
}
}
//////////////////////////////////////////////////////////////////////////
// end of code