github.com/vmware/govmomi@v0.43.0/vapi/tags/tags.go (about) 1 /* 2 Copyright (c) 2018 VMware, Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package tags 18 19 import ( 20 "context" 21 "fmt" 22 "net/http" 23 "strings" 24 25 "github.com/vmware/govmomi/vapi/internal" 26 "github.com/vmware/govmomi/vapi/rest" 27 ) 28 29 // Manager extends rest.Client, adding tag related methods. 30 type Manager struct { 31 *rest.Client 32 } 33 34 // NewManager creates a new Manager instance with the given client. 35 func NewManager(client *rest.Client) *Manager { 36 return &Manager{ 37 Client: client, 38 } 39 } 40 41 // isName returns true if the id is not a urn. 42 func isName(id string) bool { 43 return !strings.HasPrefix(id, "urn:") 44 } 45 46 // Tag provides methods to create, read, update, delete, and enumerate tags. 47 type Tag struct { 48 ID string `json:"id,omitempty"` 49 Description string `json:"description,omitempty"` 50 Name string `json:"name,omitempty"` 51 CategoryID string `json:"category_id,omitempty"` 52 UsedBy []string `json:"used_by,omitempty"` 53 } 54 55 // Patch merges updates from the given src. 56 func (t *Tag) Patch(src *Tag) { 57 if src.Name != "" { 58 t.Name = src.Name 59 } 60 if src.Description != "" { 61 t.Description = src.Description 62 } 63 if src.CategoryID != "" { 64 t.CategoryID = src.CategoryID 65 } 66 } 67 68 // CreateTag creates a new tag with the given Name, Description and CategoryID. 69 func (c *Manager) CreateTag(ctx context.Context, tag *Tag) (string, error) { 70 // create avoids the annoyance of CreateTag requiring a "description" key to be included in the request, 71 // even though the field value can be empty. 72 type create struct { 73 Name string `json:"name"` 74 Description string `json:"description"` 75 CategoryID string `json:"category_id"` 76 } 77 spec := struct { 78 Tag create `json:"create_spec"` 79 }{ 80 Tag: create{ 81 Name: tag.Name, 82 Description: tag.Description, 83 CategoryID: tag.CategoryID, 84 }, 85 } 86 if isName(tag.CategoryID) { 87 cat, err := c.GetCategory(ctx, tag.CategoryID) 88 if err != nil { 89 return "", err 90 } 91 spec.Tag.CategoryID = cat.ID 92 } 93 url := c.Resource(internal.TagPath) 94 var res string 95 return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res) 96 } 97 98 // UpdateTag can update one or both of the tag Description and Name fields. 99 func (c *Manager) UpdateTag(ctx context.Context, tag *Tag) error { 100 spec := struct { 101 Tag Tag `json:"update_spec"` 102 }{ 103 Tag: Tag{ 104 Name: tag.Name, 105 Description: tag.Description, 106 }, 107 } 108 url := c.Resource(internal.TagPath).WithID(tag.ID) 109 return c.Do(ctx, url.Request(http.MethodPatch, spec), nil) 110 } 111 112 // DeleteTag deletes an existing tag. 113 func (c *Manager) DeleteTag(ctx context.Context, tag *Tag) error { 114 url := c.Resource(internal.TagPath).WithID(tag.ID) 115 return c.Do(ctx, url.Request(http.MethodDelete), nil) 116 } 117 118 // GetTag fetches the tag information for the given identifier. 119 // The id parameter can be a Tag ID or Tag Name. 120 func (c *Manager) GetTag(ctx context.Context, id string) (*Tag, error) { 121 if isName(id) { 122 tags, err := c.GetTags(ctx) 123 if err != nil { 124 return nil, err 125 } 126 127 for i := range tags { 128 if tags[i].Name == id { 129 return &tags[i], nil 130 } 131 } 132 } 133 134 url := c.Resource(internal.TagPath).WithID(id) 135 var res Tag 136 return &res, c.Do(ctx, url.Request(http.MethodGet), &res) 137 138 } 139 140 // GetTagForCategory fetches the tag information for the given identifier in the given category. 141 func (c *Manager) GetTagForCategory(ctx context.Context, id, category string) (*Tag, error) { 142 if category == "" { 143 return c.GetTag(ctx, id) 144 } 145 146 ids, err := c.ListTagsForCategory(ctx, category) 147 if err != nil { 148 return nil, err 149 } 150 151 for _, tagid := range ids { 152 tag, err := c.GetTag(ctx, tagid) 153 if err != nil { 154 return nil, fmt.Errorf("get tag for category %s %s: %s", category, tagid, err) 155 } 156 if tag.ID == id || tag.Name == id { 157 return tag, nil 158 } 159 } 160 161 return nil, fmt.Errorf("tag %q not found in category %q", id, category) 162 } 163 164 // ListTags returns all tag IDs in the system. 165 func (c *Manager) ListTags(ctx context.Context) ([]string, error) { 166 url := c.Resource(internal.TagPath) 167 var res []string 168 return res, c.Do(ctx, url.Request(http.MethodGet), &res) 169 } 170 171 // GetTags fetches an array of tag information in the system. 172 func (c *Manager) GetTags(ctx context.Context) ([]Tag, error) { 173 ids, err := c.ListTags(ctx) 174 if err != nil { 175 return nil, fmt.Errorf("get tags failed for: %s", err) 176 } 177 178 var tags []Tag 179 for _, id := range ids { 180 tag, err := c.GetTag(ctx, id) 181 if err != nil { 182 return nil, fmt.Errorf("get category %s failed for %s", id, err) 183 } 184 185 tags = append(tags, *tag) 186 187 } 188 return tags, nil 189 } 190 191 // The id parameter can be a Category ID or Category Name. 192 func (c *Manager) ListTagsForCategory(ctx context.Context, id string) ([]string, error) { 193 if isName(id) { 194 cat, err := c.GetCategory(ctx, id) 195 if err != nil { 196 return nil, err 197 } 198 id = cat.ID 199 } 200 201 body := struct { 202 ID string `json:"category_id"` 203 }{id} 204 url := c.Resource(internal.TagPath).WithID(id).WithAction("list-tags-for-category") 205 var res []string 206 return res, c.Do(ctx, url.Request(http.MethodPost, body), &res) 207 } 208 209 // The id parameter can be a Category ID or Category Name. 210 func (c *Manager) GetTagsForCategory(ctx context.Context, id string) ([]Tag, error) { 211 ids, err := c.ListTagsForCategory(ctx, id) 212 if err != nil { 213 return nil, err 214 } 215 216 var tags []Tag 217 for _, id := range ids { 218 tag, err := c.GetTag(ctx, id) 219 if err != nil { 220 return nil, fmt.Errorf("get tag %s: %s", id, err) 221 } 222 223 tags = append(tags, *tag) 224 } 225 return tags, nil 226 }