ipld-eth-server/vendor/github.com/aristanetworks/goarista/openconfig/json.go

238 lines
6.3 KiB
Go
Raw Normal View History

// Copyright (c) 2016 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.
package openconfig
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"github.com/openconfig/reference/rpc/openconfig"
)
// joinPath builds a string out of an Element
func joinPath(path *openconfig.Path) string {
if path == nil {
return ""
}
return strings.Join(path.Element, "/")
}
func convertUpdate(update *openconfig.Update) (interface{}, error) {
switch update.Value.Type {
case openconfig.Type_JSON:
var value interface{}
decoder := json.NewDecoder(bytes.NewReader(update.Value.Value))
decoder.UseNumber()
if err := decoder.Decode(&value); err != nil {
return nil, fmt.Errorf("Malformed JSON update %q in %s",
update.Value.Value, update)
}
return value, nil
case openconfig.Type_BYTES:
return update.Value.Value, nil
default:
return nil,
fmt.Errorf("Unhandled type of value %v in %s", update.Value.Type, update)
}
}
// NotificationToJSON converts a Notification into a JSON string
func NotificationToJSON(notif *openconfig.Notification) (string, error) {
m := make(map[string]interface{}, 1)
m["timestamp"] = notif.Timestamp
m["path"] = "/" + joinPath(notif.Prefix)
if len(notif.Update) != 0 {
updates := make(map[string]interface{}, len(notif.Update))
var err error
for _, update := range notif.Update {
updates[joinPath(update.Path)], err = convertUpdate(update)
if err != nil {
return "", err
}
}
m["updates"] = updates
}
if len(notif.Delete) != 0 {
deletes := make([]string, len(notif.Delete))
for i, del := range notif.Delete {
deletes[i] = joinPath(del)
}
m["deletes"] = deletes
}
m = map[string]interface{}{"notification": m}
js, err := json.MarshalIndent(m, "", " ")
if err != nil {
return "", err
}
return string(js), nil
}
// SubscribeResponseToJSON converts a SubscribeResponse into a JSON string
func SubscribeResponseToJSON(resp *openconfig.SubscribeResponse) (string, error) {
m := make(map[string]interface{}, 1)
var err error
switch resp := resp.Response.(type) {
case *openconfig.SubscribeResponse_Update:
return NotificationToJSON(resp.Update)
case *openconfig.SubscribeResponse_Heartbeat:
m["heartbeat"] = resp.Heartbeat.Interval
case *openconfig.SubscribeResponse_SyncResponse:
m["syncResponse"] = resp.SyncResponse
default:
return "", fmt.Errorf("Unknown type of response: %T: %s", resp, resp)
}
js, err := json.MarshalIndent(m, "", " ")
if err != nil {
return "", err
}
return string(js), nil
}
// EscapeFunc is the escaping method for attribute names
type EscapeFunc func(k string) string
// escapeValue looks for maps in an interface and escapes their keys
func escapeValue(value interface{}, escape EscapeFunc) interface{} {
valueMap, ok := value.(map[string]interface{})
if !ok {
return value
}
escapedMap := make(map[string]interface{}, len(valueMap))
for k, v := range valueMap {
escapedKey := escape(k)
escapedMap[escapedKey] = escapeValue(v, escape)
}
return escapedMap
}
// addPathToMap creates a map[string]interface{} from a path. It returns the node in
// the map corresponding to the last element in the path
func addPathToMap(root map[string]interface{}, path []string, escape EscapeFunc) (
map[string]interface{}, error) {
parent := root
for _, element := range path {
k := escape(element)
node, found := parent[k]
if !found {
node = map[string]interface{}{}
parent[k] = node
}
var ok bool
parent, ok = node.(map[string]interface{})
if !ok {
return nil, fmt.Errorf(
"Node %s is of type %T (expected map[string]interface traversing %q)",
element, node, path)
}
}
return parent, nil
}
// NotificationToMap maps a Notification into a nested map of entities
func NotificationToMap(addr string, notification *openconfig.Notification,
escape EscapeFunc) (map[string]interface{}, error) {
if escape == nil {
escape = func(name string) string {
return name
}
}
prefix := notification.GetPrefix()
// Convert deletes
var deletes map[string]interface{}
notificationDeletes := notification.GetDelete()
if notificationDeletes != nil {
deletes = make(map[string]interface{})
node := deletes
if prefix != nil {
var err error
node, err = addPathToMap(node, prefix.Element, escape)
if err != nil {
return nil, err
}
}
for _, delete := range notificationDeletes {
_, err := addPathToMap(node, delete.Element, escape)
if err != nil {
return nil, err
}
}
}
// Convert updates
var updates map[string]interface{}
notificationUpdates := notification.GetUpdate()
if notificationUpdates != nil {
updates = make(map[string]interface{})
node := updates
if prefix != nil {
var err error
node, err = addPathToMap(node, prefix.Element, escape)
if err != nil {
return nil, err
}
}
for _, update := range notificationUpdates {
updateNode := node
path := update.GetPath()
elementLen := len(path.Element)
// Convert all elements before the leaf
if elementLen > 1 {
parentElements := path.Element[:elementLen-1]
var err error
updateNode, err = addPathToMap(updateNode, parentElements, escape)
if err != nil {
return nil, err
}
}
// Convert the value in the leaf
value := update.GetValue()
var unmarshaledValue interface{}
switch value.Type {
case openconfig.Type_JSON:
if err := json.Unmarshal(value.Value, &unmarshaledValue); err != nil {
return nil, err
}
case openconfig.Type_BYTES:
unmarshaledValue = update.Value.Value
default:
return nil, fmt.Errorf("Unexpected value type %s for path %v",
value.Type, path)
}
updateNode[escape(path.Element[elementLen-1])] = escapeValue(
unmarshaledValue, escape)
}
}
// Build the complete map to return
root := map[string]interface{}{
"timestamp": notification.Timestamp,
}
if addr != "" {
root["dataset"] = addr
}
if deletes != nil {
root["delete"] = deletes
}
if updates != nil {
root["update"] = updates
}
return root, nil
}
// NotificationToJSONDocument maps a Notification into a single JSON document
func NotificationToJSONDocument(addr string, notification *openconfig.Notification,
escape EscapeFunc) ([]byte, error) {
m, err := NotificationToMap(addr, notification, escape)
if err != nil {
return nil, err
}
return json.Marshal(m)
}