Adding create users operation :man_with_gua_pi_mao:
diff --git a/openstack/identity/v2/users/fixtures.go b/openstack/identity/v2/users/fixtures.go
index 2ebc488..745f572 100644
--- a/openstack/identity/v2/users/fixtures.go
+++ b/openstack/identity/v2/users/fixtures.go
@@ -41,3 +41,25 @@
`)
})
}
+
+func MockCreateUser(t *testing.T) {
+ th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "POST")
+ th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+
+ fmt.Fprintf(w, `
+{
+ "user": {
+ "name": "new_user",
+ "tenant_id": "12345",
+ "enabled": false,
+ "email": "new_user@foo.com",
+ "id": "c39e3de9be2d4c779f1dfd6abacc176d"
+ }
+}
+`)
+ })
+}
diff --git a/openstack/identity/v2/users/requests.go b/openstack/identity/v2/users/requests.go
index 85621f1..ec17c20 100644
--- a/openstack/identity/v2/users/requests.go
+++ b/openstack/identity/v2/users/requests.go
@@ -1,6 +1,9 @@
package users
import (
+ "errors"
+
+ "github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@@ -12,3 +15,81 @@
return pagination.NewPager(client, rootURL(client), createPage)
}
+
+// EnabledState represents whether the user is enabled or not.
+type EnabledState *bool
+
+// Useful variables to use when creating or updating users.
+var (
+ iTrue = true
+ iFalse = false
+
+ Enabled EnabledState = &iTrue
+ Disabled EnabledState = &iFalse
+)
+
+// CreateOpts represents the options needed when creating new users.
+type CreateOpts struct {
+ // Either a name or username is required. When provided, the value must be
+ // unique or a 409 conflict error will be returned. If you provide a name but
+ // omit a username, the latter will be set to the former; and vice versa.
+ Name, Username string
+
+ // The ID of the tenant to which you want to assign this user.
+ TenantID string
+
+ // Indicates whether this user is enabled or not.
+ Enabled EnabledState
+
+ // The email address of this user.
+ Email string
+}
+
+// CreateOptsBuilder describes struct types that can be accepted by the Create call.
+type CreateOptsBuilder interface {
+ ToUserCreateMap() (map[string]interface{}, error)
+}
+
+// ToUserCreateMap assembles a request body based on the contents of a CreateOpts.
+func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) {
+ m := make(map[string]interface{})
+
+ if opts.Name == "" && opts.Username == "" {
+ return m, errors.New("Either a Name or Username must be provided")
+ }
+
+ if opts.Name != "" {
+ m["name"] = opts.Name
+ }
+ if opts.Username != "" {
+ m["username"] = opts.Username
+ }
+ if opts.Enabled != nil {
+ m["enabled"] = &opts.Enabled
+ }
+ if opts.Email != "" {
+ m["email"] = opts.Email
+ }
+
+ return m, nil
+}
+
+// Create is the operation responsible for creating new users.
+func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+ var res CreateResult
+
+ reqBody, err := opts.ToUserCreateMap()
+ if err != nil {
+ res.Err = err
+ return res
+ }
+
+ _, res.Err = perigee.Request("POST", rootURL(client), perigee.Options{
+ Results: &res.Body,
+ ReqBody: reqBody,
+ MoreHeaders: client.AuthenticatedHeaders(),
+ OkCodes: []int{200, 201},
+ })
+
+ return res
+}
diff --git a/openstack/identity/v2/users/requests_test.go b/openstack/identity/v2/users/requests_test.go
index 1bfad32..6a4d78f 100644
--- a/openstack/identity/v2/users/requests_test.go
+++ b/openstack/identity/v2/users/requests_test.go
@@ -51,3 +51,31 @@
th.AssertNoErr(t, err)
th.AssertEquals(t, 1, count)
}
+
+func TestCreateUser(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ MockCreateUser(t)
+
+ opts := CreateOpts{
+ Name: "new_user",
+ TenantID: "12345",
+ Enabled: Disabled,
+ Email: "new_user@foo.com",
+ }
+
+ user, err := Create(client.ServiceClient(), opts).Extract()
+
+ th.AssertNoErr(t, err)
+
+ expected := &User{
+ Name: "new_user",
+ ID: "c39e3de9be2d4c779f1dfd6abacc176d",
+ Email: "new_user@foo.com",
+ Enabled: false,
+ TenantID: "12345",
+ }
+
+ th.AssertDeepEquals(t, expected, user)
+}
diff --git a/openstack/identity/v2/users/results.go b/openstack/identity/v2/users/results.go
index db27d3c..fe62ada 100644
--- a/openstack/identity/v2/users/results.go
+++ b/openstack/identity/v2/users/results.go
@@ -2,6 +2,8 @@
import (
"github.com/mitchellh/mapstructure"
+
+ "github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@@ -50,3 +52,26 @@
err := mapstructure.Decode(casted, &response)
return response.Users, err
}
+
+type commonResult struct {
+ gophercloud.Result
+}
+
+// Extract interprets any commonResult as a User, if possible.
+func (r commonResult) Extract() (*User, error) {
+ if r.Err != nil {
+ return nil, r.Err
+ }
+
+ var response struct {
+ User User `mapstructure:"user"`
+ }
+
+ err := mapstructure.Decode(r.Body, &response)
+
+ return &response.User, err
+}
+
+type CreateResult struct {
+ commonResult
+}