diff --git a/common.go b/common.go index e898373..d5ab857 100644 --- a/common.go +++ b/common.go @@ -32,6 +32,11 @@ func (c *common) init() { c.mutex = &sync.Mutex{} } +// GetCreators return a map of all the known creators keyed on domain. +func (c *common) GetCreators() map[string]*Creator { + return c.creators +} + // getCreator takes a domain name and returns the associated creator. If a // creator does not exist then nil is returned. func (c *common) getCreator(domain string) (*Creator, error) { diff --git a/creator.go b/creator.go index e1366c1..27b2b2b 100644 --- a/creator.go +++ b/creator.go @@ -17,6 +17,7 @@ package owid import ( + "encoding/json" "fmt" "time" ) @@ -118,6 +119,33 @@ func (c *Creator) SubjectPublicKeyInfo() (string, error) { // Domain associated with the creator. func (c *Creator) Domain() string { return c.domain } +// MarshalJSON marshals a node to JSON without having to expose the fields in +// the node struct. This is achieved by converting a node to a map. +func (c *Creator) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "domain": c.domain, + "privateKey": c.privateKey, + "publicKey": c.publicKey, + "name": c.name}) +} + +// UnmarshalJSON called by json.Unmarshall unmarshals a node from JSON and turns +// it into a new node. As the node is marshalled to JSON by converting it to a +// map, the unmarshalling from JSON needs to handle the type of each field +// correctly. +func (c *Creator) UnmarshalJSON(b []byte) error { + var d map[string]string + err := json.Unmarshal(b, &d) + if err != nil { + return err + } + c.domain = d["domain"] + c.privateKey = d["privateKey"] + c.publicKey = d["publicKey"] + c.name = d["name"] + return nil +} + func newCreator( domain string, privateKey string, diff --git a/handlerOwids.go b/handlerOwids.go new file mode 100644 index 0000000..2fce826 --- /dev/null +++ b/handlerOwids.go @@ -0,0 +1,43 @@ +/* **************************************************************************** + * Copyright 2020 51 Degrees Mobile Experts Limited (51degrees.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * ***************************************************************************/ + +package owid + +import ( + "encoding/json" + "net/http" +) + +// HandlerNodesJSON is a handler that returns a list of all the alive nodes +// which is then used to serialize to JSON. +func HandlerOwidsJSON(s *Services) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + j, err := getJSON(s) + if err != nil { + returnAPIError(s, w, err, http.StatusInternalServerError) + return + } + sendResponse(s, w, "application/json", j) + } +} + +func getJSON(s *Services) ([]byte, error) { + j, err := json.Marshal(s.store.GetCreators()) + if err != nil { + return nil, err + } + return j, nil +} diff --git a/handlers.go b/handlers.go index 0f10263..7f9c5a8 100644 --- a/handlers.go +++ b/handlers.go @@ -31,6 +31,9 @@ func AddHandlers(s *Services) { http.HandleFunc(b+"public-key", HandlerPublicKey(s)) http.HandleFunc(b+"creator", HandlerCreator(s)) http.HandleFunc(b+"verify", HandlerVerify(s)) + if s.config.Debug { + http.HandleFunc(b+"owids", HandlerOwidsJSON(s)) + } } } diff --git a/local.go b/local.go index a47a994..c6fd3de 100644 --- a/local.go +++ b/local.go @@ -33,14 +33,6 @@ type Local struct { common } -// item is the internal JSON representation of a Creator. -type item struct { - Domain string - PrivateKey string - PublicKey string - Name string -} - // NewLocalStore creates a new instance of Local from a given file path. func NewLocalStore(file string) (*Local, error) { var l Local @@ -61,18 +53,7 @@ func (l *Local) setCreator(creator *Creator) error { l.creators[creator.domain] = creator l.mutex.Unlock() - cs := make(map[string]*item) - - for k, v := range l.creators { - cs[k] = &item{ - Domain: v.domain, - PrivateKey: v.privateKey, - PublicKey: v.publicKey, - Name: v.name, - } - } - - data, err := json.MarshalIndent(&cs, "", "\t") + data, err := json.MarshalIndent(&l.creators, "", "\t") if err != nil { return err } @@ -122,22 +103,17 @@ func (l *Local) refresh() error { // fetch creators reads the Creators from the persistent JSON files and // converts them from a map of storage items to a map of Creators. func (l *Local) fetchCreators() (map[string]*Creator, error) { - cis := make(map[string]*item) cs := make(map[string]*Creator) data, err := readLocalStore(l.file) if err != nil { return nil, err } - err = json.Unmarshal(data, &cis) + err = json.Unmarshal(data, &cs) if err != nil && len(data) > 0 { return nil, err } - for k, v := range cis { - cs[k] = newCreator(v.Domain, v.PrivateKey, v.PublicKey, v.Name) - } - return cs, nil } diff --git a/node.go b/node.go index f0c75e2..32bcd33 100644 --- a/node.go +++ b/node.go @@ -114,7 +114,7 @@ func (n *Node) Find(condition func(n *Node) bool) *Node { } // AddChild adds the child to the children of this Node returning the index of -// the child. +// the child. Returns the index of the added child. func (n *Node) AddChild(child *Node) (uint32, error) { if child == nil { return uint32(0), fmt.Errorf("child must for a valid array") diff --git a/store.go b/store.go index 31d0245..414c94b 100644 --- a/store.go +++ b/store.go @@ -18,6 +18,7 @@ package owid import ( "errors" + "fmt" "log" "os" ) @@ -42,6 +43,9 @@ type Store interface { // GetCreator returns the creator information for the domain. GetCreator(domain string) (*Creator, error) + // GetCreators return a map of all the known creators keyed on domain. + GetCreators() map[string]*Creator + // setCreator inserts a new creator. setCreator(c *Creator) error } @@ -65,7 +69,7 @@ func NewStore(owidConfig Configuration) Store { panic(errors.New("Either the AZURE_STORAGE_ACCOUNT or " + "AZURE_STORAGE_ACCESS_KEY environment variable is not set")) } - log.Printf("OWID: Using Azure Table Storage") + log.Printf("OWID:Using Azure Table Storage") owidStore, err = NewAzure( azureAccountName, azureAccountKey) @@ -73,19 +77,19 @@ func NewStore(owidConfig Configuration) Store { panic(err) } } else if len(gcpProject) > 0 && (os == "" || os == "gcp") { - log.Printf("OWID: Using Google Firebase") + log.Printf("OWID:Using Google Firebase") owidStore, err = NewFirebase(gcpProject) if err != nil { panic(err) } } else if len(owidFile) > 0 && (os == "" || os == "local") { - log.Printf("OWID: Using local storage") + log.Printf("OWID:Using local storage") owidStore, err = NewLocalStore(owidFile) if err != nil { panic(err) } } else if len(awsEnabled) > 0 && (os == "" || os == "aws") { - log.Printf("OWID: Using AWS DynamoDB") + log.Printf("OWID:Using AWS DynamoDB") owidStore, err = NewAWS() if err != nil { panic(err) @@ -93,7 +97,21 @@ func NewStore(owidConfig Configuration) Store { } if owidStore == nil { - panic(errors.New("owid store not configured")) + panic(fmt.Errorf("OWID:no store has been configured.\r\n" + + "Provide details for store by specifying one or more sets of " + + "environment variables:\r\n" + + "(1) Azure Storage account details 'AZURE_STORAGE_ACCOUNT' & 'AZURE_STORAGE_ACCESS_KEY'\r\n" + + "(2) GCP project in 'GCP_PROJECT' \r\n" + + "(3) Local storage file paths in 'OWID_FILE'\r\n" + + "(4) AWS Dynamo DB by setting 'AWS_ENABLED' to true\r\n" + + "Refer to https://github.com/SWAN-community/owid-go/blob/main/README.md " + + "for specifics on setting up each storage solution")) + } else if owidConfig.Debug { + + // If in debug more log the nodes at startup. + for _, o := range owidStore.GetCreators() { + log.Println(fmt.Sprintf("OWID:\t%s", o.Domain())) + } } return owidStore