feat: port user commands from influxdb (#99)

This commit is contained in:
Daniel Moran
2021-05-20 13:24:13 -04:00
committed by GitHub
parent e4af676ae5
commit 60dd8b54b5
30 changed files with 1960 additions and 80 deletions

View File

@ -42,6 +42,7 @@ var app = cli.App{
newConfigCmd(),
newOrgCmd(),
newDeleteCmd(),
newUserCmd(),
},
}

177
cmd/influx/user.go Normal file
View File

@ -0,0 +1,177 @@
package main
import (
"github.com/influxdata/influx-cli/v2/internal/cmd/user"
"github.com/influxdata/influx-cli/v2/pkg/cli/middleware"
"github.com/influxdata/influx-cli/v2/pkg/influxid"
"github.com/urfave/cli/v2"
)
func newUserCmd() *cli.Command {
return &cli.Command{
Name: "user",
Usage: "User management commands",
Subcommands: []*cli.Command{
newUserCreateCmd(),
newUserDeleteCmd(),
newUserListCmd(),
newUserUpdateCmd(),
newUserSetPasswordCmd(),
},
}
}
func newUserCreateCmd() *cli.Command {
var params user.CreateParams
return &cli.Command{
Name: "create",
Usage: "Create user",
Flags: append(
commonFlags(),
&cli.GenericFlag{
Name: "org-id",
Usage: "The ID of the organization",
EnvVars: []string{"INFLUX_ORG_ID"},
Value: &params.OrgID,
},
&cli.StringFlag{
Name: "org",
Usage: "The name of the organization",
Aliases: []string{"o"},
EnvVars: []string{"INFLUX_ORG"},
Destination: &params.OrgName,
},
&cli.StringFlag{
Name: "name",
Usage: "The user name",
Aliases: []string{"n"},
EnvVars: []string{"INFLUX_NAME"},
Required: true,
Destination: &params.Name,
},
&cli.StringFlag{
Name: "password",
Usage: "The user password",
Aliases: []string{"p"},
Destination: &params.Password,
},
),
Before: middleware.WithBeforeFns(withCli(), withApi(true)),
Action: func(ctx *cli.Context) error {
api := getAPI(ctx)
client := user.Client{
CLI: getCLI(ctx),
UsersApi: api.UsersApi,
OrganizationsApi: api.OrganizationsApi,
}
return client.Create(ctx.Context, &params)
},
}
}
func newUserDeleteCmd() *cli.Command {
var id influxid.ID
return &cli.Command{
Name: "delete",
Usage: "Delete user",
Flags: append(
commonFlags(),
&cli.GenericFlag{
Name: "id",
Usage: "The user ID",
Aliases: []string{"i"},
Required: true,
Value: &id,
},
),
Before: middleware.WithBeforeFns(withCli(), withApi(true)),
Action: func(ctx *cli.Context) error {
client := user.Client{CLI: getCLI(ctx), UsersApi: getAPI(ctx).UsersApi}
return client.Delete(ctx.Context, id)
},
}
}
func newUserListCmd() *cli.Command {
var params user.ListParams
return &cli.Command{
Name: "list",
Aliases: []string{"find", "ls"},
Usage: "List users",
Flags: append(
commonFlags(),
&cli.GenericFlag{
Name: "id",
Usage: "The user ID",
Aliases: []string{"i"},
Value: &params.Id,
},
&cli.StringFlag{
Name: "name",
Usage: "The user name",
Aliases: []string{"n"},
Destination: &params.Name,
},
),
Before: middleware.WithBeforeFns(withCli(), withApi(true)),
Action: func(ctx *cli.Context) error {
client := user.Client{CLI: getCLI(ctx), UsersApi: getAPI(ctx).UsersApi}
return client.List(ctx.Context, &params)
},
}
}
func newUserUpdateCmd() *cli.Command {
var params user.UpdateParmas
return &cli.Command{
Name: "update",
Flags: append(
commonFlags(),
&cli.GenericFlag{
Name: "id",
Usage: "The user ID",
Aliases: []string{"i"},
Required: true,
Value: &params.Id,
},
&cli.StringFlag{
Name: "name",
Usage: "The user name",
Aliases: []string{"n"},
Destination: &params.Name,
},
),
Before: middleware.WithBeforeFns(withCli(), withApi(true)),
Action: func(ctx *cli.Context) error {
client := user.Client{CLI: getCLI(ctx), UsersApi: getAPI(ctx).UsersApi}
return client.Update(ctx.Context, &params)
},
}
}
func newUserSetPasswordCmd() *cli.Command {
var params user.SetPasswordParams
return &cli.Command{
Name: "password",
Flags: append(
commonFlagsNoPrint(),
&cli.GenericFlag{
Name: "id",
Usage: "The user ID",
Aliases: []string{"i"},
Value: &params.Id,
},
&cli.StringFlag{
Name: "name",
Usage: "The user name",
Aliases: []string{"n"},
Destination: &params.Name,
},
),
Before: middleware.WithBeforeFns(withCli(), withApi(true)),
Action: func(ctx *cli.Context) error {
client := user.Client{CLI: getCLI(ctx), UsersApi: getAPI(ctx).UsersApi}
return client.SetPassword(ctx.Context, &params)
},
}
}

View File

@ -40,6 +40,19 @@ type UsersApi interface {
*/
DeleteUsersIDExecute(r ApiDeleteUsersIDRequest) error
/*
* GetUsers List all users
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
* @return ApiGetUsersRequest
*/
GetUsers(ctx _context.Context) ApiGetUsersRequest
/*
* GetUsersExecute executes the request
* @return Users
*/
GetUsersExecute(r ApiGetUsersRequest) (Users, error)
/*
* GetUsersID Retrieve a user
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
@ -67,6 +80,32 @@ type UsersApi interface {
* @return UserResponse
*/
PatchUsersIDExecute(r ApiPatchUsersIDRequest) (UserResponse, error)
/*
* PostUsers Create a user
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
* @return ApiPostUsersRequest
*/
PostUsers(ctx _context.Context) ApiPostUsersRequest
/*
* PostUsersExecute executes the request
* @return UserResponse
*/
PostUsersExecute(r ApiPostUsersRequest) (UserResponse, error)
/*
* PostUsersIDPassword Update a password
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
* @param userID The user ID.
* @return ApiPostUsersIDPasswordRequest
*/
PostUsersIDPassword(ctx _context.Context, userID string) ApiPostUsersIDPasswordRequest
/*
* PostUsersIDPasswordExecute executes the request
*/
PostUsersIDPasswordExecute(r ApiPostUsersIDPasswordRequest) error
}
// usersApiGzipReadCloser supports streaming gzip response-bodies directly from the server.
@ -216,6 +255,198 @@ func (a *UsersApiService) DeleteUsersIDExecute(r ApiDeleteUsersIDRequest) error
return nil
}
type ApiGetUsersRequest struct {
ctx _context.Context
ApiService UsersApi
zapTraceSpan *string
offset *int32
limit *int32
after *string
name *string
id *string
}
func (r ApiGetUsersRequest) ZapTraceSpan(zapTraceSpan string) ApiGetUsersRequest {
r.zapTraceSpan = &zapTraceSpan
return r
}
func (r ApiGetUsersRequest) GetZapTraceSpan() *string {
return r.zapTraceSpan
}
func (r ApiGetUsersRequest) Offset(offset int32) ApiGetUsersRequest {
r.offset = &offset
return r
}
func (r ApiGetUsersRequest) GetOffset() *int32 {
return r.offset
}
func (r ApiGetUsersRequest) Limit(limit int32) ApiGetUsersRequest {
r.limit = &limit
return r
}
func (r ApiGetUsersRequest) GetLimit() *int32 {
return r.limit
}
func (r ApiGetUsersRequest) After(after string) ApiGetUsersRequest {
r.after = &after
return r
}
func (r ApiGetUsersRequest) GetAfter() *string {
return r.after
}
func (r ApiGetUsersRequest) Name(name string) ApiGetUsersRequest {
r.name = &name
return r
}
func (r ApiGetUsersRequest) GetName() *string {
return r.name
}
func (r ApiGetUsersRequest) Id(id string) ApiGetUsersRequest {
r.id = &id
return r
}
func (r ApiGetUsersRequest) GetId() *string {
return r.id
}
func (r ApiGetUsersRequest) Execute() (Users, error) {
return r.ApiService.GetUsersExecute(r)
}
/*
* GetUsers List all users
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
* @return ApiGetUsersRequest
*/
func (a *UsersApiService) GetUsers(ctx _context.Context) ApiGetUsersRequest {
return ApiGetUsersRequest{
ApiService: a,
ctx: ctx,
}
}
/*
* Execute executes the request
* @return Users
*/
func (a *UsersApiService) GetUsersExecute(r ApiGetUsersRequest) (Users, error) {
var (
localVarHTTPMethod = _nethttp.MethodGet
localVarPostBody interface{}
localVarFormFileName string
localVarFileName string
localVarFileBytes []byte
localVarReturnValue Users
)
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "UsersApiService.GetUsers")
if err != nil {
return localVarReturnValue, GenericOpenAPIError{error: err.Error()}
}
localVarPath := localBasePath + "/users"
localVarHeaderParams := make(map[string]string)
localVarQueryParams := _neturl.Values{}
localVarFormParams := _neturl.Values{}
if r.offset != nil {
localVarQueryParams.Add("offset", parameterToString(*r.offset, ""))
}
if r.limit != nil {
localVarQueryParams.Add("limit", parameterToString(*r.limit, ""))
}
if r.after != nil {
localVarQueryParams.Add("after", parameterToString(*r.after, ""))
}
if r.name != nil {
localVarQueryParams.Add("name", parameterToString(*r.name, ""))
}
if r.id != nil {
localVarQueryParams.Add("id", parameterToString(*r.id, ""))
}
// to determine the Content-Type header
localVarHTTPContentTypes := []string{}
// set Content-Type header
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
if localVarHTTPContentType != "" {
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
}
// to determine the Accept header
localVarHTTPHeaderAccepts := []string{"application/json"}
// set Accept header
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
if localVarHTTPHeaderAccept != "" {
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
}
if r.zapTraceSpan != nil {
localVarHeaderParams["Zap-Trace-Span"] = parameterToString(*r.zapTraceSpan, "")
}
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes)
if err != nil {
return localVarReturnValue, err
}
localVarHTTPResponse, err := a.client.callAPI(req)
if err != nil || localVarHTTPResponse == nil {
return localVarReturnValue, err
}
var body _io.ReadCloser = localVarHTTPResponse.Body
if localVarHTTPResponse.Header.Get("Content-Encoding") == "gzip" {
gzr, err := _gzip.NewReader(body)
if err != nil {
body.Close()
return localVarReturnValue, err
}
body = &usersApiGzipReadCloser{underlying: body, gzip: gzr}
}
if localVarHTTPResponse.StatusCode >= 300 {
localVarBody, err := _ioutil.ReadAll(body)
body.Close()
if err != nil {
return localVarReturnValue, err
}
newErr := GenericOpenAPIError{
body: localVarBody,
error: localVarHTTPResponse.Status,
}
var v Error
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
if err != nil {
newErr.error = err.Error()
return localVarReturnValue, newErr
}
newErr.model = &v
return localVarReturnValue, newErr
}
localVarBody, err := _ioutil.ReadAll(body)
body.Close()
if err != nil {
return localVarReturnValue, err
}
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
if err != nil {
newErr := GenericOpenAPIError{
body: localVarBody,
error: err.Error(),
}
return localVarReturnValue, newErr
}
return localVarReturnValue, nil
}
type ApiGetUsersIDRequest struct {
ctx _context.Context
ApiService UsersApi
@ -517,3 +748,291 @@ func (a *UsersApiService) PatchUsersIDExecute(r ApiPatchUsersIDRequest) (UserRes
return localVarReturnValue, nil
}
type ApiPostUsersRequest struct {
ctx _context.Context
ApiService UsersApi
user *User
zapTraceSpan *string
}
func (r ApiPostUsersRequest) User(user User) ApiPostUsersRequest {
r.user = &user
return r
}
func (r ApiPostUsersRequest) GetUser() *User {
return r.user
}
func (r ApiPostUsersRequest) ZapTraceSpan(zapTraceSpan string) ApiPostUsersRequest {
r.zapTraceSpan = &zapTraceSpan
return r
}
func (r ApiPostUsersRequest) GetZapTraceSpan() *string {
return r.zapTraceSpan
}
func (r ApiPostUsersRequest) Execute() (UserResponse, error) {
return r.ApiService.PostUsersExecute(r)
}
/*
* PostUsers Create a user
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
* @return ApiPostUsersRequest
*/
func (a *UsersApiService) PostUsers(ctx _context.Context) ApiPostUsersRequest {
return ApiPostUsersRequest{
ApiService: a,
ctx: ctx,
}
}
/*
* Execute executes the request
* @return UserResponse
*/
func (a *UsersApiService) PostUsersExecute(r ApiPostUsersRequest) (UserResponse, error) {
var (
localVarHTTPMethod = _nethttp.MethodPost
localVarPostBody interface{}
localVarFormFileName string
localVarFileName string
localVarFileBytes []byte
localVarReturnValue UserResponse
)
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "UsersApiService.PostUsers")
if err != nil {
return localVarReturnValue, GenericOpenAPIError{error: err.Error()}
}
localVarPath := localBasePath + "/users"
localVarHeaderParams := make(map[string]string)
localVarQueryParams := _neturl.Values{}
localVarFormParams := _neturl.Values{}
if r.user == nil {
return localVarReturnValue, reportError("user is required and must be specified")
}
// to determine the Content-Type header
localVarHTTPContentTypes := []string{"application/json"}
// set Content-Type header
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
if localVarHTTPContentType != "" {
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
}
// to determine the Accept header
localVarHTTPHeaderAccepts := []string{"application/json"}
// set Accept header
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
if localVarHTTPHeaderAccept != "" {
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
}
if r.zapTraceSpan != nil {
localVarHeaderParams["Zap-Trace-Span"] = parameterToString(*r.zapTraceSpan, "")
}
// body params
localVarPostBody = r.user
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes)
if err != nil {
return localVarReturnValue, err
}
localVarHTTPResponse, err := a.client.callAPI(req)
if err != nil || localVarHTTPResponse == nil {
return localVarReturnValue, err
}
var body _io.ReadCloser = localVarHTTPResponse.Body
if localVarHTTPResponse.Header.Get("Content-Encoding") == "gzip" {
gzr, err := _gzip.NewReader(body)
if err != nil {
body.Close()
return localVarReturnValue, err
}
body = &usersApiGzipReadCloser{underlying: body, gzip: gzr}
}
if localVarHTTPResponse.StatusCode >= 300 {
localVarBody, err := _ioutil.ReadAll(body)
body.Close()
if err != nil {
return localVarReturnValue, err
}
newErr := GenericOpenAPIError{
body: localVarBody,
error: localVarHTTPResponse.Status,
}
var v Error
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
if err != nil {
newErr.error = err.Error()
return localVarReturnValue, newErr
}
newErr.model = &v
return localVarReturnValue, newErr
}
localVarBody, err := _ioutil.ReadAll(body)
body.Close()
if err != nil {
return localVarReturnValue, err
}
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
if err != nil {
newErr := GenericOpenAPIError{
body: localVarBody,
error: err.Error(),
}
return localVarReturnValue, newErr
}
return localVarReturnValue, nil
}
type ApiPostUsersIDPasswordRequest struct {
ctx _context.Context
ApiService UsersApi
userID string
passwordResetBody *PasswordResetBody
zapTraceSpan *string
}
func (r ApiPostUsersIDPasswordRequest) UserID(userID string) ApiPostUsersIDPasswordRequest {
r.userID = userID
return r
}
func (r ApiPostUsersIDPasswordRequest) GetUserID() string {
return r.userID
}
func (r ApiPostUsersIDPasswordRequest) PasswordResetBody(passwordResetBody PasswordResetBody) ApiPostUsersIDPasswordRequest {
r.passwordResetBody = &passwordResetBody
return r
}
func (r ApiPostUsersIDPasswordRequest) GetPasswordResetBody() *PasswordResetBody {
return r.passwordResetBody
}
func (r ApiPostUsersIDPasswordRequest) ZapTraceSpan(zapTraceSpan string) ApiPostUsersIDPasswordRequest {
r.zapTraceSpan = &zapTraceSpan
return r
}
func (r ApiPostUsersIDPasswordRequest) GetZapTraceSpan() *string {
return r.zapTraceSpan
}
func (r ApiPostUsersIDPasswordRequest) Execute() error {
return r.ApiService.PostUsersIDPasswordExecute(r)
}
/*
* PostUsersIDPassword Update a password
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
* @param userID The user ID.
* @return ApiPostUsersIDPasswordRequest
*/
func (a *UsersApiService) PostUsersIDPassword(ctx _context.Context, userID string) ApiPostUsersIDPasswordRequest {
return ApiPostUsersIDPasswordRequest{
ApiService: a,
ctx: ctx,
userID: userID,
}
}
/*
* Execute executes the request
*/
func (a *UsersApiService) PostUsersIDPasswordExecute(r ApiPostUsersIDPasswordRequest) error {
var (
localVarHTTPMethod = _nethttp.MethodPost
localVarPostBody interface{}
localVarFormFileName string
localVarFileName string
localVarFileBytes []byte
)
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "UsersApiService.PostUsersIDPassword")
if err != nil {
return GenericOpenAPIError{error: err.Error()}
}
localVarPath := localBasePath + "/users/{userID}/password"
localVarPath = strings.Replace(localVarPath, "{"+"userID"+"}", _neturl.PathEscape(parameterToString(r.userID, "")), -1)
localVarHeaderParams := make(map[string]string)
localVarQueryParams := _neturl.Values{}
localVarFormParams := _neturl.Values{}
if r.passwordResetBody == nil {
return reportError("passwordResetBody is required and must be specified")
}
// to determine the Content-Type header
localVarHTTPContentTypes := []string{"application/json"}
// set Content-Type header
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
if localVarHTTPContentType != "" {
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
}
// to determine the Accept header
localVarHTTPHeaderAccepts := []string{"application/json"}
// set Accept header
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
if localVarHTTPHeaderAccept != "" {
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
}
if r.zapTraceSpan != nil {
localVarHeaderParams["Zap-Trace-Span"] = parameterToString(*r.zapTraceSpan, "")
}
// body params
localVarPostBody = r.passwordResetBody
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes)
if err != nil {
return err
}
localVarHTTPResponse, err := a.client.callAPI(req)
if err != nil || localVarHTTPResponse == nil {
return err
}
var body _io.ReadCloser = localVarHTTPResponse.Body
if localVarHTTPResponse.Header.Get("Content-Encoding") == "gzip" {
gzr, err := _gzip.NewReader(body)
if err != nil {
body.Close()
return err
}
body = &usersApiGzipReadCloser{underlying: body, gzip: gzr}
}
if localVarHTTPResponse.StatusCode >= 300 {
localVarBody, err := _ioutil.ReadAll(body)
body.Close()
if err != nil {
return err
}
newErr := GenericOpenAPIError{
body: localVarBody,
error: localVarHTTPResponse.Status,
}
var v Error
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
if err != nil {
newErr.error = err.Error()
return newErr
}
newErr.model = &v
return newErr
}
return nil
}

View File

@ -31,8 +31,12 @@ paths:
$ref: "./openapi/src/cloud/paths/measurements_measurementID.yml"
/query:
$ref: "./overrides/paths/query.yml"
/users:
$ref: "./openapi/src/common/paths/users.yml"
/users/{userID}:
$ref: "./openapi/src/common/paths/users_userID.yml"
"/users/{userID}/password":
$ref: "./openapi/src/common/paths/users_userID_password.yml"
/delete:
$ref: "./openapi/src/common/paths/delete.yml"
components:
@ -63,10 +67,14 @@ components:
$ref: "./openapi/src/common/schemas/OnboardingRequest.yml"
OnboardingResponse:
$ref: "./openapi/src/common/schemas/OnboardingResponse.yml"
Users:
$ref: "./openapi/src/common/schemas/Users.yml"
User:
$ref: "./openapi/src/common/schemas/User.yml"
UserResponse:
$ref: "./openapi/src/common/schemas/UserResponse.yml"
PasswordResetBody:
$ref: "./openapi/src/common/schemas/PasswordResetBody.yml"
Links:
$ref: "./openapi/src/common/schemas/Links.yml"
Link:

View File

@ -32,6 +32,7 @@ const (
ERRORCODE_UNAUTHORIZED ErrorCode = "unauthorized"
ERRORCODE_METHOD_NOT_ALLOWED ErrorCode = "method not allowed"
ERRORCODE_REQUEST_TOO_LARGE ErrorCode = "request too large"
ERRORCODE_UNSUPPORTED_MEDIA_TYPE ErrorCode = "unsupported media type"
)
func (v *ErrorCode) UnmarshalJSON(src []byte) error {
@ -41,7 +42,7 @@ func (v *ErrorCode) UnmarshalJSON(src []byte) error {
return err
}
enumTypeValue := ErrorCode(value)
for _, existing := range []ErrorCode{"internal error", "not found", "conflict", "invalid", "unprocessable entity", "empty value", "unavailable", "forbidden", "too many requests", "unauthorized", "method not allowed", "request too large"} {
for _, existing := range []ErrorCode{"internal error", "not found", "conflict", "invalid", "unprocessable entity", "empty value", "unavailable", "forbidden", "too many requests", "unauthorized", "method not allowed", "request too large", "unsupported media type"} {
if existing == enumTypeValue {
*v = enumTypeValue
return nil

View File

@ -0,0 +1,106 @@
/*
* Subset of Influx API covered by Influx CLI
*
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* API version: 2.0.0
*/
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
package api
import (
"encoding/json"
)
// PasswordResetBody struct for PasswordResetBody
type PasswordResetBody struct {
Password string `json:"password"`
}
// NewPasswordResetBody instantiates a new PasswordResetBody object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewPasswordResetBody(password string) *PasswordResetBody {
this := PasswordResetBody{}
this.Password = password
return &this
}
// NewPasswordResetBodyWithDefaults instantiates a new PasswordResetBody object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewPasswordResetBodyWithDefaults() *PasswordResetBody {
this := PasswordResetBody{}
return &this
}
// GetPassword returns the Password field value
func (o *PasswordResetBody) GetPassword() string {
if o == nil {
var ret string
return ret
}
return o.Password
}
// GetPasswordOk returns a tuple with the Password field value
// and a boolean to check if the value has been set.
func (o *PasswordResetBody) GetPasswordOk() (*string, bool) {
if o == nil {
return nil, false
}
return &o.Password, true
}
// SetPassword sets field value
func (o *PasswordResetBody) SetPassword(v string) {
o.Password = v
}
func (o PasswordResetBody) MarshalJSON() ([]byte, error) {
toSerialize := map[string]interface{}{}
if true {
toSerialize["password"] = o.Password
}
return json.Marshal(toSerialize)
}
type NullablePasswordResetBody struct {
value *PasswordResetBody
isSet bool
}
func (v NullablePasswordResetBody) Get() *PasswordResetBody {
return v.value
}
func (v *NullablePasswordResetBody) Set(val *PasswordResetBody) {
v.value = val
v.isSet = true
}
func (v NullablePasswordResetBody) IsSet() bool {
return v.isSet
}
func (v *NullablePasswordResetBody) Unset() {
v.value = nil
v.isSet = false
}
func NewNullablePasswordResetBody(val *PasswordResetBody) *NullablePasswordResetBody {
return &NullablePasswordResetBody{value: val, isSet: true}
}
func (v NullablePasswordResetBody) MarshalJSON() ([]byte, error) {
return json.Marshal(v.value)
}
func (v *NullablePasswordResetBody) UnmarshalJSON(src []byte) error {
v.isSet = true
return json.Unmarshal(src, &v.value)
}

View File

@ -16,7 +16,7 @@ import (
// ResourceMembers struct for ResourceMembers
type ResourceMembers struct {
Links *ResourceMembersLinks `json:"links,omitempty"`
Links *UsersLinks `json:"links,omitempty"`
Users *[]ResourceMember `json:"users,omitempty"`
}
@ -38,9 +38,9 @@ func NewResourceMembersWithDefaults() *ResourceMembers {
}
// GetLinks returns the Links field value if set, zero value otherwise.
func (o *ResourceMembers) GetLinks() ResourceMembersLinks {
func (o *ResourceMembers) GetLinks() UsersLinks {
if o == nil || o.Links == nil {
var ret ResourceMembersLinks
var ret UsersLinks
return ret
}
return *o.Links
@ -48,7 +48,7 @@ func (o *ResourceMembers) GetLinks() ResourceMembersLinks {
// GetLinksOk returns a tuple with the Links field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ResourceMembers) GetLinksOk() (*ResourceMembersLinks, bool) {
func (o *ResourceMembers) GetLinksOk() (*UsersLinks, bool) {
if o == nil || o.Links == nil {
return nil, false
}
@ -64,8 +64,8 @@ func (o *ResourceMembers) HasLinks() bool {
return false
}
// SetLinks gets a reference to the given ResourceMembersLinks and assigns it to the Links field.
func (o *ResourceMembers) SetLinks(v ResourceMembersLinks) {
// SetLinks gets a reference to the given UsersLinks and assigns it to the Links field.
func (o *ResourceMembers) SetLinks(v UsersLinks) {
o.Links = &v
}

View File

@ -0,0 +1,149 @@
/*
* Subset of Influx API covered by Influx CLI
*
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* API version: 2.0.0
*/
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
package api
import (
"encoding/json"
)
// Users struct for Users
type Users struct {
Links *UsersLinks `json:"links,omitempty"`
Users *[]UserResponse `json:"users,omitempty"`
}
// NewUsers instantiates a new Users object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewUsers() *Users {
this := Users{}
return &this
}
// NewUsersWithDefaults instantiates a new Users object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewUsersWithDefaults() *Users {
this := Users{}
return &this
}
// GetLinks returns the Links field value if set, zero value otherwise.
func (o *Users) GetLinks() UsersLinks {
if o == nil || o.Links == nil {
var ret UsersLinks
return ret
}
return *o.Links
}
// GetLinksOk returns a tuple with the Links field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Users) GetLinksOk() (*UsersLinks, bool) {
if o == nil || o.Links == nil {
return nil, false
}
return o.Links, true
}
// HasLinks returns a boolean if a field has been set.
func (o *Users) HasLinks() bool {
if o != nil && o.Links != nil {
return true
}
return false
}
// SetLinks gets a reference to the given UsersLinks and assigns it to the Links field.
func (o *Users) SetLinks(v UsersLinks) {
o.Links = &v
}
// GetUsers returns the Users field value if set, zero value otherwise.
func (o *Users) GetUsers() []UserResponse {
if o == nil || o.Users == nil {
var ret []UserResponse
return ret
}
return *o.Users
}
// GetUsersOk returns a tuple with the Users field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Users) GetUsersOk() (*[]UserResponse, bool) {
if o == nil || o.Users == nil {
return nil, false
}
return o.Users, true
}
// HasUsers returns a boolean if a field has been set.
func (o *Users) HasUsers() bool {
if o != nil && o.Users != nil {
return true
}
return false
}
// SetUsers gets a reference to the given []UserResponse and assigns it to the Users field.
func (o *Users) SetUsers(v []UserResponse) {
o.Users = &v
}
func (o Users) MarshalJSON() ([]byte, error) {
toSerialize := map[string]interface{}{}
if o.Links != nil {
toSerialize["links"] = o.Links
}
if o.Users != nil {
toSerialize["users"] = o.Users
}
return json.Marshal(toSerialize)
}
type NullableUsers struct {
value *Users
isSet bool
}
func (v NullableUsers) Get() *Users {
return v.value
}
func (v *NullableUsers) Set(val *Users) {
v.value = val
v.isSet = true
}
func (v NullableUsers) IsSet() bool {
return v.isSet
}
func (v *NullableUsers) Unset() {
v.value = nil
v.isSet = false
}
func NewNullableUsers(val *Users) *NullableUsers {
return &NullableUsers{value: val, isSet: true}
}
func (v NullableUsers) MarshalJSON() ([]byte, error) {
return json.Marshal(v.value)
}
func (v *NullableUsers) UnmarshalJSON(src []byte) error {
v.isSet = true
return json.Unmarshal(src, &v.value)
}

View File

@ -14,30 +14,30 @@ import (
"encoding/json"
)
// ResourceMembersLinks struct for ResourceMembersLinks
type ResourceMembersLinks struct {
// UsersLinks struct for UsersLinks
type UsersLinks struct {
Self *string `json:"self,omitempty"`
}
// NewResourceMembersLinks instantiates a new ResourceMembersLinks object
// NewUsersLinks instantiates a new UsersLinks object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewResourceMembersLinks() *ResourceMembersLinks {
this := ResourceMembersLinks{}
func NewUsersLinks() *UsersLinks {
this := UsersLinks{}
return &this
}
// NewResourceMembersLinksWithDefaults instantiates a new ResourceMembersLinks object
// NewUsersLinksWithDefaults instantiates a new UsersLinks object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewResourceMembersLinksWithDefaults() *ResourceMembersLinks {
this := ResourceMembersLinks{}
func NewUsersLinksWithDefaults() *UsersLinks {
this := UsersLinks{}
return &this
}
// GetSelf returns the Self field value if set, zero value otherwise.
func (o *ResourceMembersLinks) GetSelf() string {
func (o *UsersLinks) GetSelf() string {
if o == nil || o.Self == nil {
var ret string
return ret
@ -47,7 +47,7 @@ func (o *ResourceMembersLinks) GetSelf() string {
// GetSelfOk returns a tuple with the Self field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *ResourceMembersLinks) GetSelfOk() (*string, bool) {
func (o *UsersLinks) GetSelfOk() (*string, bool) {
if o == nil || o.Self == nil {
return nil, false
}
@ -55,7 +55,7 @@ func (o *ResourceMembersLinks) GetSelfOk() (*string, bool) {
}
// HasSelf returns a boolean if a field has been set.
func (o *ResourceMembersLinks) HasSelf() bool {
func (o *UsersLinks) HasSelf() bool {
if o != nil && o.Self != nil {
return true
}
@ -64,11 +64,11 @@ func (o *ResourceMembersLinks) HasSelf() bool {
}
// SetSelf gets a reference to the given string and assigns it to the Self field.
func (o *ResourceMembersLinks) SetSelf(v string) {
func (o *UsersLinks) SetSelf(v string) {
o.Self = &v
}
func (o ResourceMembersLinks) MarshalJSON() ([]byte, error) {
func (o UsersLinks) MarshalJSON() ([]byte, error) {
toSerialize := map[string]interface{}{}
if o.Self != nil {
toSerialize["self"] = o.Self
@ -76,38 +76,38 @@ func (o ResourceMembersLinks) MarshalJSON() ([]byte, error) {
return json.Marshal(toSerialize)
}
type NullableResourceMembersLinks struct {
value *ResourceMembersLinks
type NullableUsersLinks struct {
value *UsersLinks
isSet bool
}
func (v NullableResourceMembersLinks) Get() *ResourceMembersLinks {
func (v NullableUsersLinks) Get() *UsersLinks {
return v.value
}
func (v *NullableResourceMembersLinks) Set(val *ResourceMembersLinks) {
func (v *NullableUsersLinks) Set(val *UsersLinks) {
v.value = val
v.isSet = true
}
func (v NullableResourceMembersLinks) IsSet() bool {
func (v NullableUsersLinks) IsSet() bool {
return v.isSet
}
func (v *NullableResourceMembersLinks) Unset() {
func (v *NullableUsersLinks) Unset() {
v.value = nil
v.isSet = false
}
func NewNullableResourceMembersLinks(val *ResourceMembersLinks) *NullableResourceMembersLinks {
return &NullableResourceMembersLinks{value: val, isSet: true}
func NewNullableUsersLinks(val *UsersLinks) *NullableUsersLinks {
return &NullableUsersLinks{value: val, isSet: true}
}
func (v NullableResourceMembersLinks) MarshalJSON() ([]byte, error) {
func (v NullableUsersLinks) MarshalJSON() ([]byte, error) {
return json.Marshal(v.value)
}
func (v *NullableResourceMembersLinks) UnmarshalJSON(src []byte) error {
func (v *NullableUsersLinks) UnmarshalJSON(src []byte) error {
v.isSet = true
return json.Unmarshal(src, &v.value)
}

View File

@ -1,7 +1,7 @@
package bucket
import (
"errors"
"fmt"
"time"
"github.com/influxdata/influx-cli/v2/internal/api"
@ -10,11 +10,7 @@ import (
const InfiniteRetention = 0
var (
ErrMustSpecifyOrg = errors.New("must specify org ID or org name")
ErrMustSpecifyOrgDeleteByName = errors.New("must specify org ID or org name when deleting bucket by name")
ErrMustSpecifyBucket = errors.New("must specify bucket ID or bucket name")
)
var ErrMustSpecifyOrgDeleteByName = fmt.Errorf("%s when deleting a bucket by name", cmd.ErrMustSpecifyOrg)
type Client struct {
cmd.CLI

View File

@ -6,6 +6,7 @@ import (
"time"
"github.com/influxdata/influx-cli/v2/internal/api"
"github.com/influxdata/influx-cli/v2/internal/cmd"
"github.com/influxdata/influx-cli/v2/internal/duration"
)
@ -21,7 +22,7 @@ type BucketsCreateParams struct {
func (c Client) Create(ctx context.Context, params *BucketsCreateParams) error {
if params.OrgID == "" && params.OrgName == "" && c.ActiveConfig.Org == "" {
return ErrMustSpecifyOrg
return cmd.ErrMustSpecifyOrg
}
rp, err := duration.RawDurationToTimeDuration(params.Retention)

View File

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/influxdata/influx-cli/v2/internal/api"
"github.com/influxdata/influx-cli/v2/internal/cmd"
)
type BucketsDeleteParams struct {
@ -16,7 +17,7 @@ type BucketsDeleteParams struct {
func (c Client) Delete(ctx context.Context, params *BucketsDeleteParams) error {
if params.ID == "" && params.Name == "" {
return ErrMustSpecifyBucket
return cmd.ErrMustSpecifyBucket
}
var bucket api.Bucket

View File

@ -3,6 +3,8 @@ package bucket
import (
"context"
"fmt"
"github.com/influxdata/influx-cli/v2/internal/cmd"
)
type BucketsListParams struct {
@ -14,7 +16,7 @@ type BucketsListParams struct {
func (c Client) List(ctx context.Context, params *BucketsListParams) error {
if params.OrgID == "" && params.OrgName == "" && c.ActiveConfig.Org == "" {
return ErrMustSpecifyOrg
return cmd.ErrMustSpecifyOrg
}
req := c.GetBuckets(ctx)

View File

@ -8,6 +8,8 @@ import (
"github.com/influxdata/influx-cli/v2/internal/tabwriter"
)
const MinPasswordLen = 8
// CLI is a container for common functionality used to execute commands.
type CLI struct {
StdIO stdio.StdIO

View File

@ -22,14 +22,11 @@ type Params struct {
Stop string
}
var (
ErrMustSpecifyOrg = errors.New("must specify org ID or org name")
ErrMustSpecifyBucket = errors.New("must specify bucket ID or bucket name")
)
var ErrMustSpecifyBucket = errors.New("must specify bucket ID or bucket name")
func (c Client) Delete(ctx context.Context, params *Params) error {
if !params.OrgID.Valid() && params.OrgName == "" && c.ActiveConfig.Org == "" {
return ErrMustSpecifyOrg
return cmd.ErrMustSpecifyOrg
}
if !params.BucketID.Valid() && params.BucketName == "" {
return ErrMustSpecifyBucket

View File

@ -155,7 +155,7 @@ func TestClient_Delete(t *testing.T) {
},
{
name: "no org",
expectedErr: delete.ErrMustSpecifyOrg.Error(),
expectedErr: cmd.ErrMustSpecifyOrg.Error(),
},
{
name: "no bucket",

9
internal/cmd/errors.go Normal file
View File

@ -0,0 +1,9 @@
package cmd
import "errors"
var (
ErrPasswordIsTooShort = errors.New("password is too short")
ErrMustSpecifyOrg = errors.New("must specify org ID or org name")
ErrMustSpecifyBucket = errors.New("must specify bucket ID or bucket name")
)

View File

@ -2,15 +2,13 @@ package org
import (
"context"
"errors"
"fmt"
"github.com/influxdata/influx-cli/v2/internal/api"
"github.com/influxdata/influx-cli/v2/internal/cmd"
"github.com/influxdata/influx-cli/v2/pkg/influxid"
)
var ErrMustSpecifyOrg = errors.New("must specify org ID or org name")
type AddMemberParams struct {
MemberId influxid.ID
OrgName string
@ -19,7 +17,7 @@ type AddMemberParams struct {
func (c Client) AddMember(ctx context.Context, params *AddMemberParams) (err error) {
if !params.OrgID.Valid() && params.OrgName == "" && c.ActiveConfig.Org == "" {
return ErrMustSpecifyOrg
return cmd.ErrMustSpecifyOrg
}
orgID := params.OrgID.String()
@ -49,7 +47,7 @@ const maxConcurrentGetUserRequests = 10
func (c Client) ListMembers(ctx context.Context, params *ListMemberParams) (err error) {
if !params.OrgID.Valid() && params.OrgName == "" && c.ActiveConfig.Org == "" {
return ErrMustSpecifyOrg
return cmd.ErrMustSpecifyOrg
}
orgID := params.OrgID.String()
@ -128,7 +126,7 @@ type RemoveMemberParams struct {
func (c Client) RemoveMember(ctx context.Context, params *RemoveMemberParams) (err error) {
if !params.OrgID.Valid() && params.OrgName == "" && c.ActiveConfig.Org == "" {
return ErrMustSpecifyOrg
return cmd.ErrMustSpecifyOrg
}
orgID := params.OrgID.String()

View File

@ -121,7 +121,7 @@ func TestClient_AddMember(t *testing.T) {
},
{
name: "missing org",
expectedErr: org.ErrMustSpecifyOrg.Error(),
expectedErr: cmd.ErrMustSpecifyOrg.Error(),
},
}
@ -290,7 +290,7 @@ func TestClient_ListMembers(t *testing.T) {
},
{
name: "missing org",
expectedErr: org.ErrMustSpecifyOrg.Error(),
expectedErr: cmd.ErrMustSpecifyOrg.Error(),
},
}
@ -416,7 +416,7 @@ func TestClient_RemoveMembers(t *testing.T) {
},
{
name: "missing org",
expectedErr: org.ErrMustSpecifyOrg.Error(),
expectedErr: cmd.ErrMustSpecifyOrg.Error(),
},
}

View File

@ -2,7 +2,6 @@ package query
import (
"context"
"errors"
"fmt"
"io"
@ -10,8 +9,6 @@ import (
"github.com/influxdata/influx-cli/v2/internal/cmd"
)
var ErrMustSpecifyOrg = errors.New("must specify org ID or org name")
type ResultPrinter interface {
PrintQueryResults(resultStream io.ReadCloser, out io.Writer) error
}
@ -124,7 +121,7 @@ func BuildExternAST(profilers []string) *api.Extern {
func (c Client) Query(ctx context.Context, params *Params) error {
if !params.OrgID.Valid() && params.OrgName == "" && c.ActiveConfig.Org == "" {
return ErrMustSpecifyOrg
return cmd.ErrMustSpecifyOrg
}
query := BuildDefaultAST(params.Query)

View File

@ -164,7 +164,7 @@ func TestQuery(t *testing.T) {
OrgParams: cmd.OrgParams{},
Query: fakeQuery.Query,
},
expectInErr: query.ErrMustSpecifyOrg.Error(),
expectInErr: cmd.ErrMustSpecifyOrg.Error(),
},
{
name: "with profilers",

View File

@ -16,14 +16,11 @@ import (
)
var (
ErrPasswordIsTooShort = errors.New("password is too short")
ErrAlreadySetUp = errors.New("instance has already been set up")
ErrConfigNameRequired = errors.New("config name is required if you already have existing configs")
ErrSetupCanceled = errors.New("setup was canceled")
)
const MinPasswordLen = 8
type Client struct {
cmd.CLI
api.SetupApi
@ -124,8 +121,8 @@ func (c Client) validateNoNameCollision(configName string) error {
// Unless the 'force' parameter is set, the user will be prompted to enter any missing information
// and to confirm the final request parameters.
func (c Client) onboardingRequest(params *Params) (req api.OnboardingRequest, err error) {
if (params.Force || params.Password != "") && len(params.Password) < MinPasswordLen {
return req, ErrPasswordIsTooShort
if (params.Force || params.Password != "") && len(params.Password) < cmd.MinPasswordLen {
return req, cmd.ErrPasswordIsTooShort
}
// Populate the request with CLI args.
@ -168,7 +165,7 @@ func (c Client) onboardingRequest(params *Params) (req api.OnboardingRequest, er
}
if params.Password == "" {
for {
pass1, err := c.StdIO.GetPassword("Please type your password", MinPasswordLen)
pass1, err := c.StdIO.GetPassword("Please type your password", cmd.MinPasswordLen)
if err != nil {
return req, err
}

View File

@ -261,7 +261,7 @@ func Test_SetupPasswordParamToShort(t *testing.T) {
SetupApi: client,
}
err := cli.Setup(context.Background(), &params)
require.Equal(t, setup.ErrPasswordIsTooShort, err)
require.Equal(t, cmd.ErrPasswordIsTooShort, err)
}
func Test_SetupCancelAtConfirmation(t *testing.T) {

221
internal/cmd/user/user.go Normal file
View File

@ -0,0 +1,221 @@
package user
import (
"context"
"errors"
"fmt"
"github.com/influxdata/influx-cli/v2/internal/api"
"github.com/influxdata/influx-cli/v2/internal/cmd"
"github.com/influxdata/influx-cli/v2/pkg/influxid"
)
type Client struct {
cmd.CLI
api.UsersApi
api.OrganizationsApi
}
type CreateParams struct {
cmd.OrgParams
Name string
Password string
}
var ErrMustSpecifyUser = errors.New("must specify user ID or user name")
func (c Client) Create(ctx context.Context, params *CreateParams) error {
if !params.OrgID.Valid() && params.OrgName == "" && c.ActiveConfig.Org == "" {
return cmd.ErrMustSpecifyOrg
}
if params.Password != "" && len(params.Password) < cmd.MinPasswordLen {
return cmd.ErrPasswordIsTooShort
}
orgId := params.OrgID.String()
if !params.OrgID.Valid() {
req := c.GetOrgs(ctx)
if params.OrgName != "" {
req = req.Org(params.OrgName)
} else {
req = req.Org(c.ActiveConfig.Org)
}
orgs, err := req.Execute()
if err != nil {
return fmt.Errorf("failed to find org %q: %w", params.OrgName, err)
}
if orgs.Orgs == nil || len(*orgs.Orgs) == 0 {
return fmt.Errorf("no org found with name %q", params.OrgName)
}
orgId = (*orgs.Orgs)[0].GetId()
}
user, err := c.PostUsers(ctx).User(api.User{Name: params.Name}).Execute()
if err != nil {
return fmt.Errorf("failed to create user %q: %w", params.Name, err)
}
if err := c.printUsers(printUserOpts{user: &user}); err != nil {
return err
}
memberBody := api.AddResourceMemberRequestBody{Id: *user.Id}
if _, err := c.PostOrgsIDMembers(ctx, orgId).AddResourceMemberRequestBody(memberBody).Execute(); err != nil {
_, _ = c.StdIO.WriteErr([]byte("WARN: initial password not set for user, use `influx user password` to set it\n"))
return fmt.Errorf("failed setting org membership for user %q, use `influx org members add` to retry: %w", user.Name, err)
}
if params.Password == "" {
_, _ = c.StdIO.WriteErr([]byte("WARN: initial password not set for user, use `influx user password` to set it\n"))
return nil
}
passwordBody := api.PasswordResetBody{Password: params.Password}
if err := c.PostUsersIDPassword(ctx, user.GetId()).PasswordResetBody(passwordBody).Execute(); err != nil {
return fmt.Errorf("failed setting password for user %q, use `influx user password` to retry: %w", user.Name, err)
}
return nil
}
func (c Client) Delete(ctx context.Context, id influxid.ID) error {
user, err := c.GetUsersID(ctx, id.String()).Execute()
if err != nil {
return fmt.Errorf("user %q not found: %w", id, err)
}
if err := c.DeleteUsersID(ctx, id.String()).Execute(); err != nil {
return fmt.Errorf("failed to delete user %q: %w", id, err)
}
return c.printUsers(printUserOpts{user: &user, deleted: true})
}
type ListParams struct {
Id influxid.ID
Name string
}
func (c Client) List(ctx context.Context, params *ListParams) error {
req := c.GetUsers(ctx)
if params.Id.Valid() {
req = req.Id(params.Id.String())
}
if params.Name != "" {
req = req.Name(params.Name)
}
users, err := req.Execute()
if err != nil {
return fmt.Errorf("failed to list users: %w", err)
}
printOpts := printUserOpts{}
if users.Users != nil {
printOpts.users = *users.Users
}
return c.printUsers(printOpts)
}
type UpdateParmas struct {
Id influxid.ID
Name string
}
func (c Client) Update(ctx context.Context, params *UpdateParmas) error {
update := api.User{}
if params.Name != "" {
update.SetName(params.Name)
}
user, err := c.PatchUsersID(ctx, params.Id.String()).User(update).Execute()
if err != nil {
return fmt.Errorf("failed to update user %q: %w", params.Id, err)
}
return c.printUsers(printUserOpts{user: &user})
}
type SetPasswordParams struct {
Id influxid.ID
Name string
}
func (c Client) SetPassword(ctx context.Context, params *SetPasswordParams) error {
if !params.Id.Valid() && params.Name == "" {
return ErrMustSpecifyUser
}
id := params.Id.String()
displayName := id
if !params.Id.Valid() {
displayName = params.Name
users, err := c.GetUsers(ctx).Name(params.Name).Execute()
if err != nil {
return fmt.Errorf("failed to find user %q: %w", params.Name, err)
}
if users.Users == nil || len(*users.Users) == 0 {
return fmt.Errorf("no user found with name %q", params.Name)
}
id = (*users.Users)[0].GetId()
}
var password string
for {
pass1, err := c.StdIO.GetPassword(fmt.Sprintf("Please type new password for %q", displayName), cmd.MinPasswordLen)
if err != nil {
return err
}
// Don't bother with the length check the 2nd time, since we check equality to pass1.
pass2, err := c.StdIO.GetPassword("Please type new password again", 0)
if err != nil {
return err
}
if pass1 == pass2 {
password = pass1
break
}
if err := c.StdIO.Error("Passwords do not match"); err != nil {
return err
}
}
body := api.PasswordResetBody{Password: password}
if err := c.PostUsersIDPassword(ctx, id).PasswordResetBody(body).Execute(); err != nil {
return fmt.Errorf("failed to set password for user %q: %w", params.Id.String(), err)
}
_, err := c.StdIO.Write([]byte(fmt.Sprintf("Successfully updated password for user %q\n", displayName)))
return err
}
type printUserOpts struct {
user *api.UserResponse
users []api.UserResponse
deleted bool
}
func (c Client) printUsers(opts printUserOpts) error {
if c.PrintAsJSON {
var v interface{}
if opts.user != nil {
v = opts.user
} else {
v = opts.users
}
return c.PrintJSON(v)
}
headers := []string{"ID", "Name"}
if opts.deleted {
headers = append(headers, "Deleted")
}
if opts.user != nil {
opts.users = append(opts.users, *opts.user)
}
var rows []map[string]interface{}
for _, u := range opts.users {
row := map[string]interface{}{
"ID": u.GetId(),
"Name": u.GetName(),
}
if opts.deleted {
row["Deleted"] = true
}
rows = append(rows, row)
}
return c.PrintTable(headers, rows...)
}

View File

@ -0,0 +1,591 @@
package user_test
import (
"bytes"
"context"
"errors"
"strings"
"testing"
"github.com/golang/mock/gomock"
"github.com/influxdata/influx-cli/v2/internal/api"
"github.com/influxdata/influx-cli/v2/internal/cmd"
"github.com/influxdata/influx-cli/v2/internal/cmd/user"
"github.com/influxdata/influx-cli/v2/internal/config"
"github.com/influxdata/influx-cli/v2/internal/mock"
"github.com/influxdata/influx-cli/v2/internal/testutils"
"github.com/influxdata/influx-cli/v2/pkg/influxid"
"github.com/stretchr/testify/assert"
tmock "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
var id1, _ = influxid.IDFromString("1111111111111111")
var id2, _ = influxid.IDFromString("2222222222222222")
func TestClient_Create(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
params user.CreateParams
defaultOrgName string
registerUserExpectations func(*testing.T, *mock.MockUsersApi)
registerOrgExpectations func(*testing.T, *mock.MockOrganizationsApi)
expectedOut string
expectedStderr string
expectedErr string
}{
{
name: "in org by ID",
params: user.CreateParams{
Name: "my-user",
Password: "my-password",
OrgParams: cmd.OrgParams{
OrgID: id1,
},
},
defaultOrgName: "my-default-org",
registerUserExpectations: func(t *testing.T, userApi *mock.MockUsersApi) {
userApi.EXPECT().PostUsers(gomock.Any()).Return(api.ApiPostUsersRequest{ApiService: userApi})
userApi.EXPECT().PostUsersExecute(tmock.MatchedBy(func(in api.ApiPostUsersRequest) bool {
body := in.GetUser()
return assert.NotNil(t, body) && assert.Equal(t, "my-user", body.GetName())
})).Return(api.UserResponse{Id: api.PtrString(id2.String()), Name: "my-user"}, nil)
userApi.EXPECT().PostUsersIDPassword(gomock.Any(), gomock.Eq(id2.String())).
Return(api.ApiPostUsersIDPasswordRequest{ApiService: userApi}.UserID(id2.String()))
userApi.EXPECT().
PostUsersIDPasswordExecute(tmock.MatchedBy(func(in api.ApiPostUsersIDPasswordRequest) bool {
body := in.GetPasswordResetBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id2.String(), in.GetUserID()) &&
assert.Equal(t, "my-password", body.GetPassword())
})).Return(nil)
},
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().PostOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).
Return(api.ApiPostOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String()))
orgApi.EXPECT().PostOrgsIDMembersExecute(tmock.MatchedBy(func(in api.ApiPostOrgsIDMembersRequest) bool {
body := in.GetAddResourceMemberRequestBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id1.String(), in.GetOrgID()) &&
assert.Equal(t, id2.String(), body.GetId())
})).Return(api.ResourceMember{Id: api.PtrString(id2.String())}, nil)
},
expectedOut: `2222222222222222\s+my-user`,
},
{
name: "in org by name",
params: user.CreateParams{
Name: "my-user",
Password: "my-password",
OrgParams: cmd.OrgParams{
OrgName: "my-org",
},
},
defaultOrgName: "my-default-org",
registerUserExpectations: func(t *testing.T, userApi *mock.MockUsersApi) {
userApi.EXPECT().PostUsers(gomock.Any()).Return(api.ApiPostUsersRequest{ApiService: userApi})
userApi.EXPECT().PostUsersExecute(tmock.MatchedBy(func(in api.ApiPostUsersRequest) bool {
body := in.GetUser()
return assert.NotNil(t, body) && assert.Equal(t, "my-user", body.GetName())
})).Return(api.UserResponse{Id: api.PtrString(id2.String()), Name: "my-user"}, nil)
userApi.EXPECT().PostUsersIDPassword(gomock.Any(), gomock.Eq(id2.String())).
Return(api.ApiPostUsersIDPasswordRequest{ApiService: userApi}.UserID(id2.String()))
userApi.EXPECT().
PostUsersIDPasswordExecute(tmock.MatchedBy(func(in api.ApiPostUsersIDPasswordRequest) bool {
body := in.GetPasswordResetBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id2.String(), in.GetUserID()) &&
assert.Equal(t, "my-password", body.GetPassword())
})).Return(nil)
},
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "my-org", *in.GetOrg())
})).Return(api.Organizations{Orgs: &[]api.Organization{{Id: api.PtrString(id1.String())}}}, nil)
orgApi.EXPECT().PostOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).
Return(api.ApiPostOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String()))
orgApi.EXPECT().PostOrgsIDMembersExecute(tmock.MatchedBy(func(in api.ApiPostOrgsIDMembersRequest) bool {
body := in.GetAddResourceMemberRequestBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id1.String(), in.GetOrgID()) &&
assert.Equal(t, id2.String(), body.GetId())
})).Return(api.ResourceMember{Id: api.PtrString(id2.String())}, nil)
},
expectedOut: `2222222222222222\s+my-user`,
},
{
name: "in default org",
params: user.CreateParams{
Name: "my-user",
Password: "my-password",
},
defaultOrgName: "my-default-org",
registerUserExpectations: func(t *testing.T, userApi *mock.MockUsersApi) {
userApi.EXPECT().PostUsers(gomock.Any()).Return(api.ApiPostUsersRequest{ApiService: userApi})
userApi.EXPECT().PostUsersExecute(tmock.MatchedBy(func(in api.ApiPostUsersRequest) bool {
body := in.GetUser()
return assert.NotNil(t, body) && assert.Equal(t, "my-user", body.GetName())
})).Return(api.UserResponse{Id: api.PtrString(id2.String()), Name: "my-user"}, nil)
userApi.EXPECT().PostUsersIDPassword(gomock.Any(), gomock.Eq(id2.String())).
Return(api.ApiPostUsersIDPasswordRequest{ApiService: userApi}.UserID(id2.String()))
userApi.EXPECT().
PostUsersIDPasswordExecute(tmock.MatchedBy(func(in api.ApiPostUsersIDPasswordRequest) bool {
body := in.GetPasswordResetBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id2.String(), in.GetUserID()) &&
assert.Equal(t, "my-password", body.GetPassword())
})).Return(nil)
},
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "my-default-org", *in.GetOrg())
})).Return(api.Organizations{Orgs: &[]api.Organization{{Id: api.PtrString(id1.String())}}}, nil)
orgApi.EXPECT().PostOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).
Return(api.ApiPostOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String()))
orgApi.EXPECT().PostOrgsIDMembersExecute(tmock.MatchedBy(func(in api.ApiPostOrgsIDMembersRequest) bool {
body := in.GetAddResourceMemberRequestBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id1.String(), in.GetOrgID()) &&
assert.Equal(t, id2.String(), body.GetId())
})).Return(api.ResourceMember{Id: api.PtrString(id2.String())}, nil)
},
expectedOut: `2222222222222222\s+my-user`,
},
{
name: "no password",
params: user.CreateParams{
Name: "my-user",
OrgParams: cmd.OrgParams{
OrgID: id1,
},
},
defaultOrgName: "my-default-org",
registerUserExpectations: func(t *testing.T, userApi *mock.MockUsersApi) {
userApi.EXPECT().PostUsers(gomock.Any()).Return(api.ApiPostUsersRequest{ApiService: userApi})
userApi.EXPECT().PostUsersExecute(tmock.MatchedBy(func(in api.ApiPostUsersRequest) bool {
body := in.GetUser()
return assert.NotNil(t, body) && assert.Equal(t, "my-user", body.GetName())
})).Return(api.UserResponse{Id: api.PtrString(id2.String()), Name: "my-user"}, nil)
},
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().PostOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).
Return(api.ApiPostOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String()))
orgApi.EXPECT().PostOrgsIDMembersExecute(tmock.MatchedBy(func(in api.ApiPostOrgsIDMembersRequest) bool {
body := in.GetAddResourceMemberRequestBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id1.String(), in.GetOrgID()) &&
assert.Equal(t, id2.String(), body.GetId())
})).Return(api.ResourceMember{Id: api.PtrString(id2.String())}, nil)
},
expectedOut: `2222222222222222\s+my-user`,
expectedStderr: `initial password not set`,
},
{
name: "no org",
params: user.CreateParams{
Name: "my-user",
Password: "my-password",
},
expectedErr: cmd.ErrMustSpecifyOrg.Error(),
},
{
name: "org not found",
params: user.CreateParams{
Name: "my-user",
Password: "my-password",
},
defaultOrgName: "my-default-org",
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "my-default-org", *in.GetOrg())
})).Return(api.Organizations{Orgs: &[]api.Organization{}}, nil)
},
expectedErr: "no org found",
},
{
name: "assigning membership failed",
params: user.CreateParams{
Name: "my-user",
Password: "my-password",
OrgParams: cmd.OrgParams{
OrgID: id1,
},
},
defaultOrgName: "my-default-org",
registerUserExpectations: func(t *testing.T, userApi *mock.MockUsersApi) {
userApi.EXPECT().PostUsers(gomock.Any()).Return(api.ApiPostUsersRequest{ApiService: userApi})
userApi.EXPECT().PostUsersExecute(tmock.MatchedBy(func(in api.ApiPostUsersRequest) bool {
body := in.GetUser()
return assert.NotNil(t, body) && assert.Equal(t, "my-user", body.GetName())
})).Return(api.UserResponse{Id: api.PtrString(id2.String()), Name: "my-user"}, nil)
},
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().PostOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).
Return(api.ApiPostOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String()))
orgApi.EXPECT().PostOrgsIDMembersExecute(tmock.MatchedBy(func(in api.ApiPostOrgsIDMembersRequest) bool {
body := in.GetAddResourceMemberRequestBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id1.String(), in.GetOrgID()) &&
assert.Equal(t, id2.String(), body.GetId())
})).Return(api.ResourceMember{}, errors.New("I broke"))
},
expectedOut: `2222222222222222\s+my-user`,
expectedErr: "I broke",
expectedStderr: "initial password not set",
},
{
name: "setting password failed",
params: user.CreateParams{
Name: "my-user",
Password: "my-password",
OrgParams: cmd.OrgParams{
OrgID: id1,
},
},
defaultOrgName: "my-default-org",
registerUserExpectations: func(t *testing.T, userApi *mock.MockUsersApi) {
userApi.EXPECT().PostUsers(gomock.Any()).Return(api.ApiPostUsersRequest{ApiService: userApi})
userApi.EXPECT().PostUsersExecute(tmock.MatchedBy(func(in api.ApiPostUsersRequest) bool {
body := in.GetUser()
return assert.NotNil(t, body) && assert.Equal(t, "my-user", body.GetName())
})).Return(api.UserResponse{Id: api.PtrString(id2.String()), Name: "my-user"}, nil)
userApi.EXPECT().PostUsersIDPassword(gomock.Any(), gomock.Eq(id2.String())).
Return(api.ApiPostUsersIDPasswordRequest{ApiService: userApi}.UserID(id2.String()))
userApi.EXPECT().
PostUsersIDPasswordExecute(tmock.MatchedBy(func(in api.ApiPostUsersIDPasswordRequest) bool {
body := in.GetPasswordResetBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id2.String(), in.GetUserID()) &&
assert.Equal(t, "my-password", body.GetPassword())
})).Return(errors.New("I broke"))
},
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().PostOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).
Return(api.ApiPostOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String()))
orgApi.EXPECT().PostOrgsIDMembersExecute(tmock.MatchedBy(func(in api.ApiPostOrgsIDMembersRequest) bool {
body := in.GetAddResourceMemberRequestBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id1.String(), in.GetOrgID()) &&
assert.Equal(t, id2.String(), body.GetId())
})).Return(api.ResourceMember{Id: api.PtrString(id2.String())}, nil)
},
expectedOut: `2222222222222222\s+my-user`,
expectedErr: "I broke",
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
orgApi := mock.NewMockOrganizationsApi(ctrl)
if tc.registerOrgExpectations != nil {
tc.registerOrgExpectations(t, orgApi)
}
userApi := mock.NewMockUsersApi(ctrl)
if tc.registerUserExpectations != nil {
tc.registerUserExpectations(t, userApi)
}
stdout := bytes.Buffer{}
stderr := bytes.Buffer{}
stdio := mock.NewMockStdIO(ctrl)
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(stdout.Write).AnyTimes()
stdio.EXPECT().WriteErr(gomock.Any()).DoAndReturn(stderr.Write).AnyTimes()
cli := user.Client{
CLI: cmd.CLI{StdIO: stdio, ActiveConfig: config.Config{Org: tc.defaultOrgName}},
OrganizationsApi: orgApi,
UsersApi: userApi,
}
err := cli.Create(context.Background(), &tc.params)
require.Contains(t, stderr.String(), tc.expectedStderr)
if tc.expectedOut != "" {
testutils.MatchLines(t, []string{`ID\s+Name`, tc.expectedOut}, strings.Split(stdout.String(), "\n"))
}
if tc.expectedErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
return
}
require.NoError(t, err)
})
}
}
func TestClient_Delete(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
notFound bool
}{
{
name: "delete existing",
},
{
name: "delete non-existing",
notFound: true,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
userApi := mock.NewMockUsersApi(ctrl)
stdout := bytes.Buffer{}
stdio := mock.NewMockStdIO(ctrl)
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(stdout.Write).AnyTimes()
cli := user.Client{CLI: cmd.CLI{StdIO: stdio}, UsersApi: userApi}
getReq := api.ApiGetUsersIDRequest{ApiService: userApi}.UserID(id2.String())
userApi.EXPECT().GetUsersID(gomock.Any(), gomock.Eq(id2.String())).Return(getReq)
userApi.EXPECT().GetUsersIDExecute(gomock.Eq(getReq)).
DoAndReturn(func(api.ApiGetUsersIDRequest) (api.UserResponse, error) {
if tc.notFound {
return api.UserResponse{}, &api.Error{Code: api.ERRORCODE_NOT_FOUND}
}
return api.UserResponse{Id: api.PtrString(id2.String()), Name: "my-user"}, nil
})
if tc.notFound {
require.Error(t, cli.Delete(context.Background(), id2))
require.Empty(t, stdout.String())
return
}
delReq := api.ApiDeleteUsersIDRequest{ApiService: userApi}.UserID(id2.String())
userApi.EXPECT().DeleteUsersID(gomock.Any(), gomock.Eq(id2.String())).Return(delReq)
userApi.EXPECT().DeleteUsersIDExecute(delReq).Return(nil)
err := cli.Delete(context.Background(), id2)
require.NoError(t, err)
testutils.MatchLines(t, []string{
`ID\s+Name\s+Deleted`,
`2222222222222222\s+my-user\s+true`,
}, strings.Split(stdout.String(), "\n"))
})
}
}
func TestClient_List(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
params user.ListParams
registerExpectations func(*testing.T, *mock.MockUsersApi)
outLines []string
}{
{
name: "no results",
registerExpectations: func(t *testing.T, usersApi *mock.MockUsersApi) {
usersApi.EXPECT().GetUsers(gomock.Any()).Return(api.ApiGetUsersRequest{ApiService: usersApi})
usersApi.EXPECT().GetUsersExecute(gomock.Any()).Return(api.Users{}, nil)
},
},
{
name: "many results",
registerExpectations: func(t *testing.T, usersApi *mock.MockUsersApi) {
usersApi.EXPECT().GetUsers(gomock.Any()).Return(api.ApiGetUsersRequest{ApiService: usersApi})
usersApi.EXPECT().GetUsersExecute(gomock.Any()).Return(api.Users{
Users: &[]api.UserResponse{
{Id: api.PtrString("123"), Name: "user1"},
{Id: api.PtrString("456"), Name: "user2"},
},
}, nil)
},
outLines: []string{`123\s+user1`, `456\s+user2`},
},
{
name: "by name",
params: user.ListParams{Name: "user1"},
registerExpectations: func(t *testing.T, usersApi *mock.MockUsersApi) {
usersApi.EXPECT().GetUsers(gomock.Any()).Return(api.ApiGetUsersRequest{ApiService: usersApi})
usersApi.EXPECT().GetUsersExecute(tmock.MatchedBy(func(in api.ApiGetUsersRequest) bool {
return assert.Equal(t, "user1", *in.GetName()) && assert.Nil(t, in.GetId())
})).Return(api.Users{
Users: &[]api.UserResponse{
{Id: api.PtrString("123"), Name: "user1"},
},
}, nil)
},
outLines: []string{`123\s+user1`},
},
{
name: "by ID",
params: user.ListParams{Id: id2},
registerExpectations: func(t *testing.T, usersApi *mock.MockUsersApi) {
usersApi.EXPECT().GetUsers(gomock.Any()).Return(api.ApiGetUsersRequest{ApiService: usersApi})
usersApi.EXPECT().GetUsersExecute(tmock.MatchedBy(func(in api.ApiGetUsersRequest) bool {
return assert.Equal(t, id2.String(), *in.GetId()) && assert.Nil(t, in.GetName())
})).Return(api.Users{
Users: &[]api.UserResponse{
{Id: api.PtrString(id2.String()), Name: "user11"},
},
}, nil)
},
outLines: []string{`2222222222222222\s+user11`},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
userApi := mock.NewMockUsersApi(ctrl)
if tc.registerExpectations != nil {
tc.registerExpectations(t, userApi)
}
stdout := bytes.Buffer{}
stdio := mock.NewMockStdIO(ctrl)
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(stdout.Write).AnyTimes()
cli := user.Client{CLI: cmd.CLI{StdIO: stdio}, UsersApi: userApi}
require.NoError(t, cli.List(context.Background(), &tc.params))
testutils.MatchLines(t, append([]string{`ID\s+Name`}, tc.outLines...), strings.Split(stdout.String(), "\n"))
})
}
}
func TestClient_Update(t *testing.T) {
t.Parallel()
newName := "my-new-name"
ctrl := gomock.NewController(t)
userApi := mock.NewMockUsersApi(ctrl)
userApi.EXPECT().PatchUsersID(gomock.Any(), gomock.Eq(id2.String())).
Return(api.ApiPatchUsersIDRequest{ApiService: userApi}.UserID(id2.String()))
userApi.EXPECT().PatchUsersIDExecute(tmock.MatchedBy(func(in api.ApiPatchUsersIDRequest) bool {
body := in.GetUser()
return assert.NotNil(t, body) &&
assert.Equal(t, id2.String(), in.GetUserID()) &&
assert.Equal(t, newName, body.GetName())
})).Return(api.UserResponse{Id: api.PtrString(id2.String()), Name: newName}, nil)
stdout := bytes.Buffer{}
stdio := mock.NewMockStdIO(ctrl)
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(stdout.Write).AnyTimes()
cli := user.Client{CLI: cmd.CLI{StdIO: stdio}, UsersApi: userApi}
require.NoError(t, cli.Update(context.Background(), &user.UpdateParmas{Id: id2, Name: newName}))
testutils.MatchLines(t, []string{`ID\s+Name`, `2222222222222222\s+my-new-name`}, strings.Split(stdout.String(), "\n"))
}
func TestClient_SetPassword(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
params user.SetPasswordParams
registerExpectations func(*testing.T, *mock.MockUsersApi)
noExpectAsk bool
expectedErr string
}{
{
name: "by ID",
params: user.SetPasswordParams{
Id: id2,
},
registerExpectations: func(t *testing.T, usersApi *mock.MockUsersApi) {
usersApi.EXPECT().PostUsersIDPassword(gomock.Any(), gomock.Eq(id2.String())).
Return(api.ApiPostUsersIDPasswordRequest{ApiService: usersApi}.UserID(id2.String()))
usersApi.EXPECT().PostUsersIDPasswordExecute(tmock.MatchedBy(func(in api.ApiPostUsersIDPasswordRequest) bool {
body := in.GetPasswordResetBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id2.String(), in.GetUserID()) &&
assert.Equal(t, "mypassword", body.GetPassword())
})).Return(nil)
},
},
{
name: "by name",
params: user.SetPasswordParams{
Name: "my-user",
},
registerExpectations: func(t *testing.T, usersApi *mock.MockUsersApi) {
usersApi.EXPECT().GetUsers(gomock.Any()).Return(api.ApiGetUsersRequest{ApiService: usersApi})
usersApi.EXPECT().GetUsersExecute(tmock.MatchedBy(func(in api.ApiGetUsersRequest) bool {
return assert.Equal(t, "my-user", *in.GetName())
})).Return(api.Users{Users: &[]api.UserResponse{{Id: api.PtrString(id2.String())}}}, nil)
usersApi.EXPECT().PostUsersIDPassword(gomock.Any(), gomock.Eq(id2.String())).
Return(api.ApiPostUsersIDPasswordRequest{ApiService: usersApi}.UserID(id2.String()))
usersApi.EXPECT().PostUsersIDPasswordExecute(tmock.MatchedBy(func(in api.ApiPostUsersIDPasswordRequest) bool {
body := in.GetPasswordResetBody()
return assert.NotNil(t, body) &&
assert.Equal(t, id2.String(), in.GetUserID()) &&
assert.Equal(t, "mypassword", body.GetPassword())
})).Return(nil)
},
},
{
name: "user not found",
params: user.SetPasswordParams{
Name: "my-user",
},
registerExpectations: func(t *testing.T, usersApi *mock.MockUsersApi) {
usersApi.EXPECT().GetUsers(gomock.Any()).Return(api.ApiGetUsersRequest{ApiService: usersApi})
usersApi.EXPECT().GetUsersExecute(tmock.MatchedBy(func(in api.ApiGetUsersRequest) bool {
return assert.Equal(t, "my-user", *in.GetName())
})).Return(api.Users{}, nil)
},
noExpectAsk: true,
expectedErr: "no user found",
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
userApi := mock.NewMockUsersApi(ctrl)
if tc.registerExpectations != nil {
tc.registerExpectations(t, userApi)
}
stdout := bytes.Buffer{}
stdio := mock.NewMockStdIO(ctrl)
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(stdout.Write).AnyTimes()
if !tc.noExpectAsk {
stdio.EXPECT().GetPassword(gomock.Any(), gomock.Any()).Return("mypassword", nil).Times(2)
}
cli := user.Client{CLI: cmd.CLI{StdIO: stdio}, UsersApi: userApi}
err := cli.SetPassword(context.Background(), &tc.params)
if tc.expectedErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
return
}
require.NoError(t, err)
require.Contains(t, stdout.String(), "Successfully updated password")
})
}
}

View File

@ -63,6 +63,35 @@ func (mr *MockUsersApiMockRecorder) DeleteUsersIDExecute(arg0 interface{}) *gomo
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUsersIDExecute", reflect.TypeOf((*MockUsersApi)(nil).DeleteUsersIDExecute), arg0)
}
// GetUsers mocks base method.
func (m *MockUsersApi) GetUsers(arg0 context.Context) api.ApiGetUsersRequest {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUsers", arg0)
ret0, _ := ret[0].(api.ApiGetUsersRequest)
return ret0
}
// GetUsers indicates an expected call of GetUsers.
func (mr *MockUsersApiMockRecorder) GetUsers(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsers", reflect.TypeOf((*MockUsersApi)(nil).GetUsers), arg0)
}
// GetUsersExecute mocks base method.
func (m *MockUsersApi) GetUsersExecute(arg0 api.ApiGetUsersRequest) (api.Users, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUsersExecute", arg0)
ret0, _ := ret[0].(api.Users)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetUsersExecute indicates an expected call of GetUsersExecute.
func (mr *MockUsersApiMockRecorder) GetUsersExecute(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsersExecute", reflect.TypeOf((*MockUsersApi)(nil).GetUsersExecute), arg0)
}
// GetUsersID mocks base method.
func (m *MockUsersApi) GetUsersID(arg0 context.Context, arg1 string) api.ApiGetUsersIDRequest {
m.ctrl.T.Helper()
@ -120,3 +149,60 @@ func (mr *MockUsersApiMockRecorder) PatchUsersIDExecute(arg0 interface{}) *gomoc
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchUsersIDExecute", reflect.TypeOf((*MockUsersApi)(nil).PatchUsersIDExecute), arg0)
}
// PostUsers mocks base method.
func (m *MockUsersApi) PostUsers(arg0 context.Context) api.ApiPostUsersRequest {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PostUsers", arg0)
ret0, _ := ret[0].(api.ApiPostUsersRequest)
return ret0
}
// PostUsers indicates an expected call of PostUsers.
func (mr *MockUsersApiMockRecorder) PostUsers(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PostUsers", reflect.TypeOf((*MockUsersApi)(nil).PostUsers), arg0)
}
// PostUsersExecute mocks base method.
func (m *MockUsersApi) PostUsersExecute(arg0 api.ApiPostUsersRequest) (api.UserResponse, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PostUsersExecute", arg0)
ret0, _ := ret[0].(api.UserResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// PostUsersExecute indicates an expected call of PostUsersExecute.
func (mr *MockUsersApiMockRecorder) PostUsersExecute(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PostUsersExecute", reflect.TypeOf((*MockUsersApi)(nil).PostUsersExecute), arg0)
}
// PostUsersIDPassword mocks base method.
func (m *MockUsersApi) PostUsersIDPassword(arg0 context.Context, arg1 string) api.ApiPostUsersIDPasswordRequest {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PostUsersIDPassword", arg0, arg1)
ret0, _ := ret[0].(api.ApiPostUsersIDPasswordRequest)
return ret0
}
// PostUsersIDPassword indicates an expected call of PostUsersIDPassword.
func (mr *MockUsersApiMockRecorder) PostUsersIDPassword(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PostUsersIDPassword", reflect.TypeOf((*MockUsersApi)(nil).PostUsersIDPassword), arg0, arg1)
}
// PostUsersIDPasswordExecute mocks base method.
func (m *MockUsersApi) PostUsersIDPasswordExecute(arg0 api.ApiPostUsersIDPasswordRequest) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PostUsersIDPasswordExecute", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// PostUsersIDPasswordExecute indicates an expected call of PostUsersIDPasswordExecute.
func (mr *MockUsersApiMockRecorder) PostUsersIDPasswordExecute(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PostUsersIDPasswordExecute", reflect.TypeOf((*MockUsersApi)(nil).PostUsersIDPasswordExecute), arg0)
}

View File

@ -119,3 +119,18 @@ func (mr *MockStdIOMockRecorder) Write(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockStdIO)(nil).Write), arg0)
}
// WriteErr mocks base method.
func (m *MockStdIO) WriteErr(arg0 []byte) (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "WriteErr", arg0)
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// WriteErr indicates an expected call of WriteErr.
func (mr *MockStdIOMockRecorder) WriteErr(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteErr", reflect.TypeOf((*MockStdIO)(nil).WriteErr), arg0)
}

View File

@ -28,6 +28,11 @@ func (t *terminalStdio) Write(p []byte) (int, error) {
return t.Stdout.Write(p)
}
// WriteErr prints some bytes to stderr.
func (t *terminalStdio) WriteErr(p []byte) (int, error) {
return t.Stderr.Write(p)
}
type bannerTemplateData struct {
Message string
}

View File

@ -4,6 +4,7 @@ import "io"
type StdIO interface {
io.Writer
WriteErr(p []byte) (n int, err error)
Banner(message string) error
Error(message string) error
GetStringInput(prompt, defaultValue string) (string, error)