Skip to content

Commit

Permalink
Support list-modify-delete project tags api calls
Browse files Browse the repository at this point in the history
  • Loading branch information
georgeb committed May 2, 2024
1 parent 8b1eebe commit b18f271
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 0 deletions.
29 changes: 29 additions & 0 deletions internal/acceptance/openstack/identity/v3/projects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,32 @@ func TestProjectsTags(t *testing.T) {
tools.PrintResource(t, updatedProject)
th.AssertEquals(t, len(updatedProject.Tags), 0)
}

func TestProjectsTagsCRUD(t *testing.T) {
clients.RequireAdmin(t)

client, err := clients.NewIdentityV3Client()
th.AssertNoErr(t, err)

createOpts := projects.CreateOpts{
Tags: []string{"Tag1", "Tag2"},
}

projectMain, err := CreateProject(t, client, &createOpts)
th.AssertNoErr(t, err)
defer DeleteProject(t, client, projectMain.ID)

projectTagsList, err := projects.ListTags(context.TODO(), client, projectMain.ID).Extract()
tools.PrintResource(t, projectTagsList)
th.AssertNoErr(t, err)

modifyOpts := projects.ModifyTagsOpts{
Tags: []string{"foo", "bar"},
}
projectTags, err := projects.ModifyTags(context.TODO(), client, projectMain.ID, modifyOpts).Extract()
tools.PrintResource(t, projectTags)
th.AssertNoErr(t, err)

err = projects.DeleteTags(context.TODO(), client, projectMain.ID).ExtractErr()
th.AssertNoErr(t, err)
}
25 changes: 25 additions & 0 deletions openstack/identity/v3/projects/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,30 @@ Example to Delete a Project
if err != nil {
panic(err)
}
Example to List all tags of a Project
projectID := "966b3c7d36a24facaf20b7e458bf2192"
err := projects.ListTags(context.TODO(), identityClient, projectID).Extract()
if err != nil {
panic(err)
}
Example to modify all tags of a Project
projectID := "966b3c7d36a24facaf20b7e458bf2192"
tags := ["foo", "bar"]
projects, err := projects.ModifyTags(context.TODO(), identityClient, projectID, tags).Extract()
if err != nil {
panic(err)
}
Example to Delete all tags of a Project
projectID := "966b3c7d36a24facaf20b7e458bf2192"
err := projects.DeleteTags(context.TODO(), identityClient, projectID).ExtractErr()
if err != nil {
panic(err)
}
*/
package projects
55 changes: 55 additions & 0 deletions openstack/identity/v3/projects/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,58 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}

// CheckTags lists tags for a project.
func ListTags(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r ListTagsResult) {
resp, err := client.Get(ctx, listTagsURL(client, projectID), &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}

// Tags represents a list of Tags object.
type ModifyTagsOpts struct {
// Tags is the list of tags associated with the project.
Tags []string `json:"tags,omitempty"`
}

// ModifyTagsOptsBuilder allows extensions to add additional parameters to
// the Modify request.
type ModifyTagsOptsBuilder interface {
ToModifyTagsCreateMap() (map[string]interface{}, error)
}

// ToModifyTagsCreateMap formats a ModifyTagsOpts into a Modify tags request.
func (opts ModifyTagsOpts) ToModifyTagsCreateMap() (map[string]interface{}, error) {
b, err := gophercloud.BuildRequestBody(opts, "")

if err != nil {
return nil, err
}
return b, nil
}

// ModifyTags deletes all tags of a project and adds new ones.
func ModifyTags(ctx context.Context, client *gophercloud.ServiceClient, projectID string, opts ModifyTagsOpts) (r ModifyTagsResult) {

b, err := opts.ToModifyTagsCreateMap()
if err != nil {
r.Err = err
return
}
resp, err := client.Put(ctx, modifyTagsURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}

// DeleteTag deletes a tag from a project.
func DeleteTags(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r DeleteTagsResult) {
resp, err := client.Delete(ctx, deleteTagsURL(client, projectID), &gophercloud.RequestOpts{
OkCodes: []int{204},
})
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
46 changes: 46 additions & 0 deletions openstack/identity/v3/projects/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,49 @@ func (r projectResult) Extract() (*Project, error) {
err := r.ExtractInto(&s)
return s.Project, err
}

// Tags represents a list of Tags object.
type Tags struct {
// Tags is the list of tags associated with the project.
Tags []string `json:"tags,omitempty"`
}

// ListTagsResult is the result of a List Tags request. Call its Extract method to
// interpret it as a list of tags.
type ListTagsResult struct {
gophercloud.Result
}

// Extract interprets any ListTagsResult as a Tags Object.
func (r ListTagsResult) Extract() (*Tags, error) {
var s = &Tags{}
err := r.ExtractInto(&s)
return s, err
}

// ProjectTags represents a list of Tags object.
type ProjectTags struct {
// Tags is the list of tags associated with the project.
Projects []Project `json:"projects,omitempty"`
// Links contains referencing links to the implied_role.
Links map[string]interface{} `json:"links"`
}

// ModifyTagsResLinksult is the result of a Tags request. Call its Extract method to
// interpret it as a project of tags.
type ModifyTagsResult struct {
gophercloud.Result
}

// Extract interprets any ModifyTags as a Tags Object.
func (r ModifyTagsResult) Extract() (*ProjectTags, error) {
var s = &ProjectTags{}
err := r.ExtractInto(&s)
return s, err
}

// DeleteTagsResult is the result of a Delete Tags request. Call its ExtractErr method to
// determine if the request succeeded or failed.
type DeleteTagsResult struct {
gophercloud.ErrResult
}
92 changes: 92 additions & 0 deletions openstack/identity/v3/projects/testing/fixtures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,44 @@ const UpdateOutput = `
}
`

// ListTagsOutput provides the output to a ListTags request.
const ListTagsOutput = `
{
"tags": ["foo", "bar"]
}
`

// ModifyProjectTagsRequest provides the input to a ModifyTags request.
const ModifyProjectTagsRequest = `
{
"tags": ["foo", "bar"]
}
`

// ModifyProjectTagsOutput provides the output to a ModifyTags request.
const ModifyProjectTagsOutput = `
{
"links": {
"next": null,
"previous": null,
"self": "http://identity:5000/v3/projects"
},
"projects": [
{
"description": "Test Project",
"domain_id": "default",
"enabled": true,
"id": "3d4c2c82bd5948f0bcab0cf3a7c9b48c",
"links": {
"self": "http://identity:5000/v3/projects/3d4c2c82bd5948f0bcab0cf3a7c9b48c"
},
"name": "demo",
"tags": ["foo", "bar"]
}
]
}
`

// FirstProject is a Project fixture.
var FirstProject = projects.Project{
Description: "my first project",
Expand Down Expand Up @@ -212,6 +250,31 @@ var ExpectedAvailableProjectsSlice = []projects.Project{FirstProject, SecondProj
// ExpectedProjectSlice is the slice of projects expected to be returned from ListOutput.
var ExpectedProjectSlice = []projects.Project{RedTeam, BlueTeam}

var ExpectedTags = projects.Tags{
Tags: []string{"foo", "bar"},
}

var ExpectedProjects = projects.ProjectTags{
Projects: []projects.Project{
{
Description: "Test Project",
DomainID: "default",
Enabled: true,
ID: "3d4c2c82bd5948f0bcab0cf3a7c9b48c",
Extra: map[string]interface{}{"links": map[string]interface{}{
"self": "http://identity:5000/v3/projects/3d4c2c82bd5948f0bcab0cf3a7c9b48c",
}},
Name: "demo",
Tags: []string{"foo", "bar"},
},
},
Links: map[string]interface{}{
"next": nil,
"previous": nil,
"self": "http://identity:5000/v3/projects",
},
}

// HandleListAvailableProjectsSuccessfully creates an HTTP handler at `/auth/projects`
// on the test handler mux that responds with a list of two tenants.
func HandleListAvailableProjectsSuccessfully(t *testing.T) {
Expand Down Expand Up @@ -290,3 +353,32 @@ func HandleUpdateProjectSuccessfully(t *testing.T) {
fmt.Fprintf(w, UpdateOutput)
})
}

func HandleListProjectTagsSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/projects/966b3c7d36a24facaf20b7e458bf2192/tags", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)

w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, ListTagsOutput)
})
}

func HandleModifyProjectTagsSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/projects/966b3c7d36a24facaf20b7e458bf2192/tags", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "PUT")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
th.TestJSONRequest(t, r, ModifyProjectTagsRequest)

w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, ModifyProjectTagsOutput)
})
}
func HandleDeleteProjectTagsSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/projects/966b3c7d36a24facaf20b7e458bf2192/tags", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)

w.WriteHeader(http.StatusNoContent)
})
}
33 changes: 33 additions & 0 deletions openstack/identity/v3/projects/testing/requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,37 @@ func TestUpdateProject(t *testing.T) {
actual, err := projects.Update(context.TODO(), client.ServiceClient(), "1234", updateOpts).Extract()
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, UpdatedRedTeam, *actual)
t.Log(projects.Update(context.TODO(), client.ServiceClient(), "1234", updateOpts))
}

func TestListProjectTags(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleListProjectTagsSuccessfully(t)

actual, err := projects.ListTags(context.TODO(), client.ServiceClient(), "966b3c7d36a24facaf20b7e458bf2192").Extract()
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, ExpectedTags, *actual)
}

func TestModifyProjectTags(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleModifyProjectTagsSuccessfully(t)

modifyOpts := projects.ModifyTagsOpts{
Tags: []string{"foo", "bar"},
}
actual, err := projects.ModifyTags(context.TODO(), client.ServiceClient(), "966b3c7d36a24facaf20b7e458bf2192", modifyOpts).Extract()
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, ExpectedProjects, *actual)
}

func TestDeleteTags(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleDeleteProjectTagsSuccessfully(t)

err := projects.DeleteTags(context.TODO(), client.ServiceClient(), "966b3c7d36a24facaf20b7e458bf2192").ExtractErr()
th.AssertNoErr(t, err)
}
12 changes: 12 additions & 0 deletions openstack/identity/v3/projects/urls.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,15 @@ func deleteURL(client *gophercloud.ServiceClient, projectID string) string {
func updateURL(client *gophercloud.ServiceClient, projectID string) string {
return client.ServiceURL("projects", projectID)
}

func listTagsURL(client *gophercloud.ServiceClient, projectID string) string {
return client.ServiceURL("projects", projectID, "tags")
}

func modifyTagsURL(client *gophercloud.ServiceClient, projectID string) string {
return client.ServiceURL("projects", projectID, "tags")
}

func deleteTagsURL(client *gophercloud.ServiceClient, projectID string) string {
return client.ServiceURL("projects", projectID, "tags")
}

0 comments on commit b18f271

Please sign in to comment.