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