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  }