diff --git a/test/volume_test.go b/test/volume_test.go index 3e55419..a3b77bd 100644 --- a/test/volume_test.go +++ b/test/volume_test.go @@ -68,3 +68,55 @@ func TestDeleteVolumeByIdSuccess(t *ltesting.T) { t.Log("Result: ", sdkerr) t.Log("PASS") } + +func TestListBlockVolumeSuccess(t *ltesting.T) { + vngcloud := validSdkConfig() + opt := v2.NewListBlockVolumesRequest(1, 10) + volumes, sdkerr := vngcloud.VServerGateway().V2().VolumeService().ListBlockVolumes(opt) + if sdkerr != nil { + t.Fatalf("Expect nil but got %v", sdkerr) + } + + if volumes == nil { + t.Fatalf("Expect not nil but got nil") + } + + t.Log("Result: ", volumes) + t.Log("PASS") +} + +func TestListBlockVolumeWithNameSuccess(t *ltesting.T) { + vngcloud := validSdkConfig() + opt := v2.NewListBlockVolumesRequest(1, 10).WithName("pvc-24182151-aa4a-4a55-9572-f551c3d003aa") + volumes, sdkerr := vngcloud.VServerGateway().V2().VolumeService().ListBlockVolumes(opt) + if sdkerr != nil { + t.Fatalf("Expect nil but got %v", sdkerr) + } + + if volumes == nil { + t.Fatalf("Expect not nil but got nil") + } + + if volumes.Len() != 1 { + t.Fatalf("Expect 1 but got %d", volumes.Len()) + } + + t.Log("Result: ", volumes) + t.Log("PASS") +} + +func TestListBlockVolumeWithFailure(t *ltesting.T) { + vngcloud := validSdkConfig() + opt := v2.NewListBlockVolumesRequest(1, -10) + volumes, sdkerr := vngcloud.VServerGateway().V2().VolumeService().ListBlockVolumes(opt) + if sdkerr == nil { + t.Fatalf("Expect error but got nil") + } + + if volumes != nil { + t.Fatalf("Expect nil but got %v", volumes) + } + + t.Log("Result: ", sdkerr) + t.Log("PASS") +} diff --git a/vngcloud/entity/volume.go b/vngcloud/entity/volume.go index 6455237..23c73d2 100644 --- a/vngcloud/entity/volume.go +++ b/vngcloud/entity/volume.go @@ -14,3 +14,11 @@ type Volume struct { PersistentVolume bool AttachedMachine []string } + +type ListVolumes struct { + Items []*Volume +} + +func (s *ListVolumes) Len() int { + return len(s.Items) +} diff --git a/vngcloud/sdk_error/common.go b/vngcloud/sdk_error/common.go index 152252c..b0bfa4e 100644 --- a/vngcloud/sdk_error/common.go +++ b/vngcloud/sdk_error/common.go @@ -6,7 +6,8 @@ import ( ) const ( - patternOutOfPoc = "you do not have sufficient credits to complete the purchase" + patternOutOfPoc = "you do not have sufficient credits to complete the purchase" + patternPagingInvalid = "page or size invalid" ) func ErrorHandler(perr error, popts ...func(psdkErr ISdkError)) ISdkError { @@ -75,3 +76,18 @@ func WithErrorOutOfPoc(perrResp IErrorRespone) func(sdkError ISdkError) { } } } + +func WithErrorPagingInvalid(perrResp IErrorRespone) func(sdkError ISdkError) { + return func(sdkError ISdkError) { + if perrResp == nil { + return + } + + errMsg := perrResp.GetMessage() + if lstr.Contains(lstr.ToLower(lstr.TrimSpace(errMsg)), patternPagingInvalid) { + sdkError.WithErrorCode(EcPagingInvalid). + WithMessage(errMsg). + WithErrors(perrResp.GetError()) + } + } +} diff --git a/vngcloud/sdk_error/error_codes.go b/vngcloud/sdk_error/error_codes.go index c61fe30..50c76ec 100644 --- a/vngcloud/sdk_error/error_codes.go +++ b/vngcloud/sdk_error/error_codes.go @@ -6,6 +6,7 @@ const ( EcUnknownError = ErrorCode("UnknownError") EcInternalServerError = ErrorCode("VngCloudApiInternalServerError") + EcPagingInvalid = ErrorCode("VngCloudApiPagingInvalid") ) // Internal SDK error diff --git a/vngcloud/services/volume/ivolume.go b/vngcloud/services/volume/ivolume.go index 786450f..f33492a 100644 --- a/vngcloud/services/volume/ivolume.go +++ b/vngcloud/services/volume/ivolume.go @@ -9,4 +9,5 @@ import ( type IVolumeServiceV2 interface { CreateBlockVolume(popts lsvolumeSvcV2.ICreateBlockVolumeRequest) (*lsentity.Volume, lserr.ISdkError) DeleteBlockVolumeById(popts lsvolumeSvcV2.IDeleteBlockVolumeByIdRequest) lserr.ISdkError + ListBlockVolumes(popts lsvolumeSvcV2.IListBlockVolumesRequest) (*lsentity.ListVolumes, lserr.ISdkError) } diff --git a/vngcloud/services/volume/v2/base.go b/vngcloud/services/volume/v2/base.go index 99da1ee..8a8ef61 100644 --- a/vngcloud/services/volume/v2/base.go +++ b/vngcloud/services/volume/v2/base.go @@ -9,3 +9,8 @@ type VolumeServiceV2 struct { func (s *VolumeServiceV2) getProjectId() string { return s.VServerClient.GetProjectId() } + +const ( + defaultPageListBlockVolumesRequest = 1 + defaultSizeListBlockVolumesRequest = 10000 +) diff --git a/vngcloud/services/volume/v2/blockvolume.go b/vngcloud/services/volume/v2/blockvolume.go index ed88943..d77daa8 100644 --- a/vngcloud/services/volume/v2/blockvolume.go +++ b/vngcloud/services/volume/v2/blockvolume.go @@ -45,3 +45,22 @@ func (s *VolumeServiceV2) DeleteBlockVolumeById(popts IDeleteBlockVolumeByIdRequ return nil } + +func (s *VolumeServiceV2) ListBlockVolumes(popts IListBlockVolumesRequest) (*lsentity.ListVolumes, lserr.ISdkError) { + url := listBlockVolumesUrl(s.VServerClient, popts) + resp := new(ListBlockVolumesResponse) + errResp := lserr.NewErrorResponse(lserr.NormalErrorType) + req := lsclient.NewRequest(). + WithOkCodes(200). + WithJsonResponse(resp). + WithJsonError(errResp) + + if _, sdkErr := s.VServerClient.Get(url, req); sdkErr != nil { + return nil, lserr.SdkErrorHandler(sdkErr, errResp, + lserr.WithErrorPagingInvalid(errResp)). + WithKVparameters("projectId", s.getProjectId()). + WithParameters(popts.ToMap()) + } + + return resp.ToEntityListVolumes(), nil +} diff --git a/vngcloud/services/volume/v2/blockvolume_request.go b/vngcloud/services/volume/v2/blockvolume_request.go index acac041..cb75d30 100644 --- a/vngcloud/services/volume/v2/blockvolume_request.go +++ b/vngcloud/services/volume/v2/blockvolume_request.go @@ -1,5 +1,10 @@ package v2 +import ( + lfmt "fmt" + ljparser "github.com/cuongpiger/joat/parser" +) + func NewCreateBlockVolumeRequest(pvolumeName, pvolumeType string, psize int64) ICreateBlockVolumeRequest { opt := new(CreateBlockVolumeRequest) opt.VolumeTypeId = pvolumeType @@ -16,6 +21,13 @@ func NewDeleteBlockVolumeByIdRequest(pvolumeId string) IDeleteBlockVolumeByIdReq return opt } +func NewListBlockVolumesRequest(ppage, psize int) IListBlockVolumesRequest { + opt := new(ListBlockVolumesRequest) + opt.Page = ppage + opt.Size = psize + return opt +} + const ( CreateFromNew = CreateVolumeFrom("NEW") CreateFromSnapshot = CreateVolumeFrom("SNAPSHOT") @@ -39,6 +51,12 @@ type DeleteBlockVolumeByIdRequest struct { BlockVolumeCommon } +type ListBlockVolumesRequest struct { + Name string `q:"name,beempty"` + Page int `q:"page"` + Size int `q:"size"` +} + type ( CreateVolumeFrom string @@ -123,3 +141,31 @@ func (s *CreateBlockVolumeRequest) WithTags(ptags ...string) ICreateBlockVolumeR return s } + +func (s *ListBlockVolumesRequest) ToQuery() (string, error) { + parser, _ := ljparser.GetParser() + url, err := parser.UrlMe(s) + + if err != nil { + return "", err + } + + return url.String(), err +} + +func (s *ListBlockVolumesRequest) GetDefaultQuery() string { + return lfmt.Sprintf("page=%d&size=%d&name=", defaultPageListBlockVolumesRequest, defaultSizeListBlockVolumesRequest) +} + +func (s *ListBlockVolumesRequest) ToMap() map[string]interface{} { + return map[string]interface{}{ + "name": s.Name, + "page": s.Page, + "size": s.Size, + } +} + +func (s *ListBlockVolumesRequest) WithName(pname string) IListBlockVolumesRequest { + s.Name = pname + return s +} diff --git a/vngcloud/services/volume/v2/blockvolume_response.go b/vngcloud/services/volume/v2/blockvolume_response.go index 1d6d952..738a0b4 100644 --- a/vngcloud/services/volume/v2/blockvolume_response.go +++ b/vngcloud/services/volume/v2/blockvolume_response.go @@ -6,6 +6,14 @@ type CreateBlockVolumeResponse struct { Data BlockVolume `json:"data"` } +type ListBlockVolumesResponse struct { + Page int64 `json:"page"` + PageSize int64 `json:"pageSize"` + TotalPage int64 `json:"totalPage"` + TotalItem int64 `json:"totalItem"` + ListData []BlockVolume `json:"listData"` +} + type ( BlockVolume struct { UUID string `json:"uuid"` @@ -31,17 +39,47 @@ type ( ) func (s *CreateBlockVolumeResponse) ToEntityVolume() *lsentity.Volume { + return s.Data.toEntityVolume() +} + +func (s *ListBlockVolumesResponse) ToEntityListVolumes() *lsentity.ListVolumes { + lstVolumes := new(lsentity.ListVolumes) + for _, vol := range s.ListData { + lstVolumes.Items = append(lstVolumes.Items, vol.toEntityVolume()) + } + + return lstVolumes +} + +func (s *ListBlockVolumesResponse) toEntityVolume(pIdx int) *lsentity.Volume { if s == nil { return nil } + if pIdx >= 0 && pIdx < len(s.ListData) { + vol := s.ListData[pIdx] + return &lsentity.Volume{ + Id: vol.UUID, + Name: vol.Name, + Size: vol.Size, + Status: vol.Status, + CreatedAt: vol.CreatedAt, + UpdatedAt: vol.UpdatedAt, + VmId: vol.ServerID, + } + } + + return nil +} + +func (s *BlockVolume) toEntityVolume() *lsentity.Volume { return &lsentity.Volume{ - Id: s.Data.UUID, - Name: s.Data.Name, - Size: s.Data.Size, - Status: s.Data.Status, - CreatedAt: s.Data.CreatedAt, - UpdatedAt: s.Data.UpdatedAt, - VmId: s.Data.ServerID, + Id: s.UUID, + Name: s.Name, + Size: s.Size, + Status: s.Status, + CreatedAt: s.CreatedAt, + UpdatedAt: s.UpdatedAt, + VmId: s.ServerID, } } diff --git a/vngcloud/services/volume/v2/irequest.go b/vngcloud/services/volume/v2/irequest.go index 6df0873..eeba398 100644 --- a/vngcloud/services/volume/v2/irequest.go +++ b/vngcloud/services/volume/v2/irequest.go @@ -15,3 +15,10 @@ type ICreateBlockVolumeRequest interface { type IDeleteBlockVolumeByIdRequest interface { GetBlockVolumeId() string } + +type IListBlockVolumesRequest interface { + WithName(pname string) IListBlockVolumesRequest + ToQuery() (string, error) + GetDefaultQuery() string + ToMap() map[string]interface{} +} diff --git a/vngcloud/services/volume/v2/url.go b/vngcloud/services/volume/v2/url.go index 363bc05..4b3cdb1 100644 --- a/vngcloud/services/volume/v2/url.go +++ b/vngcloud/services/volume/v2/url.go @@ -14,3 +14,14 @@ func deleteBlockVolumeByIdUrl(psc lsclient.IServiceClient, popts IDeleteBlockVol "volumes", popts.GetBlockVolumeId()) } + +func listBlockVolumesUrl(psc lsclient.IServiceClient, popts IListBlockVolumesRequest) string { + query, err := popts.ToQuery() + if err != nil { + query = popts.GetDefaultQuery() + } + + return psc.ServiceURL( + psc.GetProjectId(), + "volumes") + query +}