github.com/ali-iotechsys/cli@v20.10.0+incompatible/cli/context/store/metadatastore.go (about) 1 package store 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "reflect" 10 "sort" 11 12 "github.com/fvbommel/sortorder" 13 ) 14 15 const ( 16 metadataDir = "meta" 17 metaFile = "meta.json" 18 ) 19 20 type metadataStore struct { 21 root string 22 config Config 23 } 24 25 func (s *metadataStore) contextDir(id contextdir) string { 26 return filepath.Join(s.root, string(id)) 27 } 28 29 func (s *metadataStore) createOrUpdate(meta Metadata) error { 30 contextDir := s.contextDir(contextdirOf(meta.Name)) 31 if err := os.MkdirAll(contextDir, 0755); err != nil { 32 return err 33 } 34 bytes, err := json.Marshal(&meta) 35 if err != nil { 36 return err 37 } 38 return ioutil.WriteFile(filepath.Join(contextDir, metaFile), bytes, 0644) 39 } 40 41 func parseTypedOrMap(payload []byte, getter TypeGetter) (interface{}, error) { 42 if len(payload) == 0 || string(payload) == "null" { 43 return nil, nil 44 } 45 if getter == nil { 46 var res map[string]interface{} 47 if err := json.Unmarshal(payload, &res); err != nil { 48 return nil, err 49 } 50 return res, nil 51 } 52 typed := getter() 53 if err := json.Unmarshal(payload, typed); err != nil { 54 return nil, err 55 } 56 return reflect.ValueOf(typed).Elem().Interface(), nil 57 } 58 59 func (s *metadataStore) get(id contextdir) (Metadata, error) { 60 contextDir := s.contextDir(id) 61 bytes, err := ioutil.ReadFile(filepath.Join(contextDir, metaFile)) 62 if err != nil { 63 return Metadata{}, convertContextDoesNotExist(err) 64 } 65 var untyped untypedContextMetadata 66 r := Metadata{ 67 Endpoints: make(map[string]interface{}), 68 } 69 if err := json.Unmarshal(bytes, &untyped); err != nil { 70 return Metadata{}, err 71 } 72 r.Name = untyped.Name 73 if r.Metadata, err = parseTypedOrMap(untyped.Metadata, s.config.contextType); err != nil { 74 return Metadata{}, err 75 } 76 for k, v := range untyped.Endpoints { 77 if r.Endpoints[k], err = parseTypedOrMap(v, s.config.endpointTypes[k]); err != nil { 78 return Metadata{}, err 79 } 80 } 81 return r, err 82 } 83 84 func (s *metadataStore) remove(id contextdir) error { 85 contextDir := s.contextDir(id) 86 return os.RemoveAll(contextDir) 87 } 88 89 func (s *metadataStore) list() ([]Metadata, error) { 90 ctxDirs, err := listRecursivelyMetadataDirs(s.root) 91 if err != nil { 92 if os.IsNotExist(err) { 93 return nil, nil 94 } 95 return nil, err 96 } 97 var res []Metadata 98 for _, dir := range ctxDirs { 99 c, err := s.get(contextdir(dir)) 100 if err != nil { 101 return nil, err 102 } 103 res = append(res, c) 104 } 105 sort.Slice(res, func(i, j int) bool { 106 return sortorder.NaturalLess(res[i].Name, res[j].Name) 107 }) 108 return res, nil 109 } 110 111 func isContextDir(path string) bool { 112 s, err := os.Stat(filepath.Join(path, metaFile)) 113 if err != nil { 114 return false 115 } 116 return !s.IsDir() 117 } 118 119 func listRecursivelyMetadataDirs(root string) ([]string, error) { 120 fis, err := ioutil.ReadDir(root) 121 if err != nil { 122 return nil, err 123 } 124 var result []string 125 for _, fi := range fis { 126 if fi.IsDir() { 127 if isContextDir(filepath.Join(root, fi.Name())) { 128 result = append(result, fi.Name()) 129 } 130 subs, err := listRecursivelyMetadataDirs(filepath.Join(root, fi.Name())) 131 if err != nil { 132 return nil, err 133 } 134 for _, s := range subs { 135 result = append(result, fmt.Sprintf("%s/%s", fi.Name(), s)) 136 } 137 } 138 } 139 return result, nil 140 } 141 142 func convertContextDoesNotExist(err error) error { 143 if os.IsNotExist(err) { 144 return &contextDoesNotExistError{} 145 } 146 return err 147 } 148 149 type untypedContextMetadata struct { 150 Metadata json.RawMessage `json:"metadata,omitempty"` 151 Endpoints map[string]json.RawMessage `json:"endpoints,omitempty"` 152 Name string `json:"name,omitempty"` 153 }