diff --git a/apis/v1/example_fake_test.go b/apis/v1/example_fake_test.go index 13cd096..467a4f0 100644 --- a/apis/v1/example_fake_test.go +++ b/apis/v1/example_fake_test.go @@ -28,11 +28,12 @@ func init() { } func initFakeServer() { + siteId := "isk01" fakeServer := &server.Server{ Engine: &fake.Engine{ Clusters: []*v1.Cluster{ { - Id: "isk01", + Id: siteId, ControlPanelUrl: "https://secure.sakura.ad.jp/objectstorage/", DislpayNameEnUs: "Ishikari Site #1", @@ -47,6 +48,16 @@ func initFakeServer() { CreatedAt: v1.CreatedAt(time.Now()), ResourceId: "100000000001", }, + Buckets: []*v1.Bucket{ + { + ClusterId: siteId, + Name: "bucket1", + }, + { + ClusterId: siteId, + Name: "bucket2", + }, + }, }, } sv := httptest.NewServer(fakeServer.Handler()) diff --git a/apis/v1/example_test.go b/apis/v1/example_test.go index 30e4aff..2de3e5e 100644 --- a/apis/v1/example_test.go +++ b/apis/v1/example_test.go @@ -140,3 +140,125 @@ func Example_siteAccountKeys() { // output: // secret } + +// Example_bucket バケット操作 +func Example_bucket() { + token := os.Getenv("SAKURACLOUD_ACCESS_TOKEN") + secret := os.Getenv("SAKURACLOUD_ACCESS_TOKEN_SECRET") + + client, err := v1.NewClientWithResponses(serverURL, func(c *v1.Client) error { + c.RequestEditors = []v1.RequestEditorFn{ + v1.OjsAuthInterceptor(token, secret), + } + return nil + }) + if err != nil { + panic(err) + } + + // サイトIDが必要になるためまずサイト一覧を取得 + sitesResp, err := client.ListClustersWithResponse(context.Background()) + if err != nil { + panic(err) + } + + sites, err := sitesResp.Result() + if err != nil { + panic(err) + } + siteId := sites.Data[0].Id + + // バケット作成 + createParams := v1.CreateBucketJSONRequestBody{ + ClusterId: siteId, + } + + bucketResp, err := client.CreateBucketWithResponse(context.Background(), "bucket-name", createParams) + if err != nil { + panic(err) + } + + bucket, err := bucketResp.Result() + if err != nil { + panic(err) + } + + defer func() { + deleteParams := v1.DeleteBucketJSONRequestBody{ + ClusterId: siteId, + } + resp, err := client.DeleteBucketWithResponse(context.Background(), "bucket-name", deleteParams) + if err != nil { + panic(err) + } + if err := resp.Result(); err != nil { + panic(err) + } + }() + + fmt.Println(bucket.Data.Name) + // output: + // bucket-name +} + +// Example_permissions パーミッション操作の例 +func Example_permissions() { + token := os.Getenv("SAKURACLOUD_ACCESS_TOKEN") + secret := os.Getenv("SAKURACLOUD_ACCESS_TOKEN_SECRET") + + client, err := v1.NewClientWithResponses(serverURL, func(c *v1.Client) error { + c.RequestEditors = []v1.RequestEditorFn{ + v1.OjsAuthInterceptor(token, secret), + } + return nil + }) + if err != nil { + panic(err) + } + + // サイトIDが必要になるためまずサイト一覧を取得 + sitesResp, err := client.ListClustersWithResponse(context.Background()) + if err != nil { + panic(err) + } + + sites, err := sitesResp.Result() + if err != nil { + panic(err) + } + siteId := sites.Data[0].Id + + // パーミッション作成 + permissionResp, err := client.CreatePermissionWithResponse(context.Background(), siteId, v1.CreatePermissionJSONRequestBody{ + BucketControls: v1.BucketControls{ + { + BucketName: "bucket1", + CanRead: true, + CanWrite: true, + }, + }, + DisplayName: "foobar", + }) + if err != nil { + panic(err) + } + + permission, err := permissionResp.Result() + if err != nil { + panic(err) + } + + defer func() { + resp, err := client.DeletePermissionWithResponse(context.Background(), siteId, permission.Data.Id.String()) + if err != nil { + panic(err) + } + if err := resp.Result(); err != nil { + panic(err) + } + }() + + fmt.Println(permission.Data.DisplayName) + // output: + // foobar +} diff --git a/apis/v1/spec/swagger.yaml b/apis/v1/spec/swagger.yaml index cd3225c..fbe8e0b 100644 --- a/apis/v1/spec/swagger.yaml +++ b/apis/v1/spec/swagger.yaml @@ -840,7 +840,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Permission' + $ref: '#/components/schemas/PermissionsResponseBody' '401': description: 認証に失敗しました content: @@ -880,14 +880,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PermissionBucketControlsBody' + $ref: '#/components/schemas/PermissionRequestBody' responses: '201': description: パーミッションの作成に成功しました content: application/json: schema: - $ref: '#/components/schemas/Permission' + $ref: '#/components/schemas/PermissionResponseBody' '401': description: 認証に失敗しました content: @@ -946,7 +946,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Permission' + $ref: '#/components/schemas/PermissionResponseBody' '401': description: 認証に失敗しました content: @@ -1054,14 +1054,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PermissionBucketControlsBody' + $ref: '#/components/schemas/PermissionRequestBody' responses: '200': description: パーミッションの更新に成功しました content: application/json: schema: - $ref: '#/components/schemas/Permission' + $ref: '#/components/schemas/PermissionResponseBody' '401': description: 認証に失敗しました content: @@ -1470,6 +1470,22 @@ components: required: - data + # Note: PermissionsとPermissionに分離 + PermissionsResponseBody: + type: object + properties: + data: + $ref: '#/components/schemas/Permissions' + required: + - data + PermissionResponseBody: + type: object + properties: + data: + $ref: '#/components/schemas/Permission' + required: + - data + # Note: model.Bucketからリネーム Bucket: type: object @@ -1662,20 +1678,33 @@ components: - id - secret - created_at + + # Note: BucketControlsとBucketControlに分離 BucketControls: description: Bucket controls type: array items: - type: object - properties: - bucket_name: - $ref: '#/components/schemas/BucketName' - can_read: - $ref: '#/components/schemas/CanRead' - can_write: - $ref: '#/components/schemas/CanWrite' - created_at: - $ref: '#/components/schemas/CreatedAt' + $ref: '#/components/schemas/BucketControl' + + # Note: BucketControlsとBucketControlに分離 + BucketControl: + description: Bucket control + type: object + properties: + bucket_name: + $ref: '#/components/schemas/BucketName' + can_read: + $ref: '#/components/schemas/CanRead' + can_write: + $ref: '#/components/schemas/CanWrite' + created_at: + $ref: '#/components/schemas/CreatedAt' + required: + - bucket_name + - can_read + - can_write + - created_at + PermissionSecret: description: Permission secret key type: string @@ -1686,6 +1715,7 @@ components: # type: string # pattern: '^[\w\d_-]+$' # example: grantfullControl + # Note: ResponseBody+Accountに分離させた Account: description: Account info @@ -1701,6 +1731,7 @@ components: - resource_id - code - created_at + # BucketGrantRead: # description: Bucket Grant Read # type: boolean @@ -1808,38 +1839,44 @@ components: # permission: # type: string # example: permission + + # Note: Permissions/Permission/Permission[s]ResponseBodyに分離 + Permissions: + description: Permissions + type: array + items: + $ref: '#/components/schemas/Permission' + + # Note: Permissions/Permission/Permission[s]ResponseBodyに分離 Permission: - description: Permission type: object properties: - data: - description: data type - type: array - items: - type: object - properties: - id: - allOf: - - $ref: '#/components/schemas/PermissionID' - display_name: - allOf: - - $ref: '#/components/schemas/DisplayName' - bucket_controls: - allOf: - - $ref: '#/components/schemas/BucketControls' - created_at: - allOf: - - $ref: '#/components/schemas/CreatedAt' - PermissionBucketControlsBody: + id: + $ref: '#/components/schemas/PermissionID' + display_name: + $ref: '#/components/schemas/DisplayName' + bucket_controls: + $ref: '#/components/schemas/BucketControls' + created_at: + $ref: '#/components/schemas/CreatedAt' + required: + - id + - display_name + - bucket_controls + - created_at + + # Note: PermissionBucketControlsBodyからリネーム + PermissionRequestBody: description: Request body for bucket controls for Permission type: object properties: display_name: - allOf: - - $ref: '#/components/schemas/DisplayName' + $ref: '#/components/schemas/DisplayName' bucket_controls: - allOf: - - $ref: '#/components/schemas/BucketControls' + $ref: '#/components/schemas/BucketControls' + required: + - display_name + - bucket_controls PermissionKey: description: Permission Key type: object @@ -1849,14 +1886,16 @@ components: type: object properties: id: - allOf: - - $ref: '#/components/schemas/PermissionID' + $ref: '#/components/schemas/PermissionID' secret: - allOf: - - $ref: '#/components/schemas/PermissionSecret' + $ref: '#/components/schemas/PermissionSecret' created_at: - allOf: - - $ref: '#/components/schemas/CreatedAt' + $ref: '#/components/schemas/CreatedAt' + required: + - id + - secret + - created_at + # Session: # description: Session # type: object diff --git a/apis/v1/stringer.go b/apis/v1/stringer.go index 1a23f9e..a36fee5 100644 --- a/apis/v1/stringer.go +++ b/apis/v1/stringer.go @@ -14,6 +14,8 @@ package v1 +import "fmt" + // String . func (v *AccessKeyID) String() string { return string(*v) @@ -83,3 +85,8 @@ func (v *ResourceID) String() string { func (v *SecretAccessKey) String() string { return string(*v) } + +// String . +func (v PermissionID) String() string { + return fmt.Sprintf("%d", v) +} diff --git a/apis/v1/zz_client_gen.go b/apis/v1/zz_client_gen.go index 46094ca..b6113d7 100644 --- a/apis/v1/zz_client_gen.go +++ b/apis/v1/zz_client_gen.go @@ -1843,7 +1843,7 @@ func (r ReadAccountAccessKeyResponse) UndefinedError() error { type ListPermissionsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *Permission + JSON200 *PermissionsResponseBody JSON401 *Error401 JSONDefault *ErrorDefault } @@ -1865,7 +1865,7 @@ func (r ListPermissionsResponse) StatusCode() int { } // Result JSON200の結果、もしくは発生したエラーのいずれかを返す -func (r ListPermissionsResponse) Result() (*Permission, error) { +func (r ListPermissionsResponse) Result() (*PermissionsResponseBody, error) { return r.JSON200, eCoalesce(r.JSON401, r.JSONDefault, r.UndefinedError()) } @@ -1880,7 +1880,7 @@ func (r ListPermissionsResponse) UndefinedError() error { type CreatePermissionResponse struct { Body []byte HTTPResponse *http.Response - JSON201 *Permission + JSON201 *PermissionResponseBody JSON401 *Error401 JSON404 *Error404 JSON409 *Error409 @@ -1904,7 +1904,7 @@ func (r CreatePermissionResponse) StatusCode() int { } // Result JSON200の結果、もしくは発生したエラーのいずれかを返す -func (r CreatePermissionResponse) Result() (*Permission, error) { +func (r CreatePermissionResponse) Result() (*PermissionResponseBody, error) { return r.JSON201, eCoalesce(r.JSON401, r.JSON404, r.JSON409, r.JSONDefault, r.UndefinedError()) } @@ -1955,7 +1955,7 @@ func (r DeletePermissionResponse) UndefinedError() error { type ReadPermissionResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *Permission + JSON200 *PermissionResponseBody JSON401 *Error401 JSON404 *Error404 JSONDefault *ErrorDefault @@ -1978,7 +1978,7 @@ func (r ReadPermissionResponse) StatusCode() int { } // Result JSON200の結果、もしくは発生したエラーのいずれかを返す -func (r ReadPermissionResponse) Result() (*Permission, error) { +func (r ReadPermissionResponse) Result() (*PermissionResponseBody, error) { return r.JSON200, eCoalesce(r.JSON401, r.JSON404, r.JSONDefault, r.UndefinedError()) } @@ -1993,7 +1993,7 @@ func (r ReadPermissionResponse) UndefinedError() error { type UpdatePermissionResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *Permission + JSON200 *PermissionResponseBody JSON401 *Error401 JSON404 *Error404 JSON409 *Error409 @@ -2017,7 +2017,7 @@ func (r UpdatePermissionResponse) StatusCode() int { } // Result JSON200の結果、もしくは発生したエラーのいずれかを返す -func (r UpdatePermissionResponse) Result() (*Permission, error) { +func (r UpdatePermissionResponse) Result() (*PermissionResponseBody, error) { return r.JSON200, eCoalesce(r.JSON401, r.JSON404, r.JSON409, r.JSONDefault, r.UndefinedError()) } @@ -2936,7 +2936,7 @@ func ParseListPermissionsResponse(rsp *http.Response) (*ListPermissionsResponse, switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest Permission + var dest PermissionsResponseBody if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2976,7 +2976,7 @@ func ParseCreatePermissionResponse(rsp *http.Response) (*CreatePermissionRespons switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest Permission + var dest PermissionResponseBody if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -3063,7 +3063,7 @@ func ParseReadPermissionResponse(rsp *http.Response) (*ReadPermissionResponse, e switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest Permission + var dest PermissionResponseBody if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -3110,7 +3110,7 @@ func ParseUpdatePermissionResponse(rsp *http.Response) (*UpdatePermissionRespons switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest Permission + var dest PermissionResponseBody if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } diff --git a/apis/v1/zz_types_gen.go b/apis/v1/zz_types_gen.go index cc9995f..db407c6 100644 --- a/apis/v1/zz_types_gen.go +++ b/apis/v1/zz_types_gen.go @@ -72,21 +72,24 @@ type Bucket struct { Name string `json:"name"` } -// Bucket controls -type BucketControls []struct { +// Bucket control +type BucketControl struct { // Bucket name - BucketName *BucketName `json:"bucket_name,omitempty"` + BucketName BucketName `json:"bucket_name"` // The flag to read bucket contents - CanRead *CanRead `json:"can_read,omitempty"` + CanRead CanRead `json:"can_read"` // The flag to write bucket contents - CanWrite *CanWrite `json:"can_write,omitempty"` + CanWrite CanWrite `json:"can_write"` // Created at - CreatedAt *CreatedAt `json:"created_at,omitempty"` + CreatedAt CreatedAt `json:"created_at"` } +// Bucket controls +type BucketControls []BucketControl + // Bucket name type BucketName string @@ -273,39 +276,19 @@ type ListClustersResponseBody struct { Data []Cluster `json:"data"` } -// Permission +// Permission defines model for Permission. type Permission struct { - // data type - Data *[]struct { - BucketControls *struct { - // Embedded struct due to allOf(#/components/schemas/BucketControls) - BucketControls `yaml:",inline"` - } `json:"bucket_controls,omitempty"` - CreatedAt *struct { - // Embedded struct due to allOf(#/components/schemas/CreatedAt) - CreatedAt `yaml:",inline"` - } `json:"created_at,omitempty"` - DisplayName *struct { - // Embedded struct due to allOf(#/components/schemas/DisplayName) - DisplayName `yaml:",inline"` - } `json:"display_name,omitempty"` - Id *struct { - // Embedded struct due to allOf(#/components/schemas/PermissionID) - PermissionID `yaml:",inline"` - } `json:"id,omitempty"` - } `json:"data,omitempty"` -} + // Bucket controls + BucketControls BucketControls `json:"bucket_controls"` -// Request body for bucket controls for Permission -type PermissionBucketControlsBody struct { - BucketControls *struct { - // Embedded struct due to allOf(#/components/schemas/BucketControls) - BucketControls `yaml:",inline"` - } `json:"bucket_controls,omitempty"` - DisplayName *struct { - // Embedded struct due to allOf(#/components/schemas/DisplayName) - DisplayName `yaml:",inline"` - } `json:"display_name,omitempty"` + // Created at + CreatedAt CreatedAt `json:"created_at"` + + // Display name + DisplayName DisplayName `json:"display_name"` + + // Permission ID + Id PermissionID `json:"id"` } // Permission ID @@ -315,24 +298,43 @@ type PermissionID int64 type PermissionKey struct { // data type Data *struct { - CreatedAt *struct { - // Embedded struct due to allOf(#/components/schemas/CreatedAt) - CreatedAt `yaml:",inline"` - } `json:"created_at,omitempty"` - Id *struct { - // Embedded struct due to allOf(#/components/schemas/PermissionID) - PermissionID `yaml:",inline"` - } `json:"id,omitempty"` - Secret *struct { - // Embedded struct due to allOf(#/components/schemas/PermissionSecret) - PermissionSecret `yaml:",inline"` - } `json:"secret,omitempty"` + // Created at + CreatedAt CreatedAt `json:"created_at"` + + // Permission ID + Id PermissionID `json:"id"` + + // Permission secret key + Secret PermissionSecret `json:"secret"` } `json:"data,omitempty"` } +// Request body for bucket controls for Permission +type PermissionRequestBody struct { + // Bucket controls + BucketControls BucketControls `json:"bucket_controls"` + + // Display name + DisplayName DisplayName `json:"display_name"` +} + +// PermissionResponseBody defines model for PermissionResponseBody. +type PermissionResponseBody struct { + Data Permission `json:"data"` +} + // Permission secret key type PermissionSecret string +// Permissions +type Permissions []Permission + +// PermissionsResponseBody defines model for PermissionsResponseBody. +type PermissionsResponseBody struct { + // Permissions + Data Permissions `json:"data"` +} + // ReadClusterResponseBody defines model for ReadClusterResponseBody. type ReadClusterResponseBody struct { Data *Cluster `json:"data,omitempty"` @@ -365,10 +367,10 @@ type DeleteBucketJSONBody CreateBucketRequestBody type CreateBucketJSONBody CreateBucketRequestBody // CreatePermissionJSONBody defines parameters for CreatePermission. -type CreatePermissionJSONBody PermissionBucketControlsBody +type CreatePermissionJSONBody PermissionRequestBody // UpdatePermissionJSONBody defines parameters for UpdatePermission. -type UpdatePermissionJSONBody PermissionBucketControlsBody +type UpdatePermissionJSONBody PermissionRequestBody // DeleteBucketJSONRequestBody defines body for DeleteBucket for application/json ContentType. type DeleteBucketJSONRequestBody DeleteBucketJSONBody diff --git a/fake/accounts.go b/fake/accounts.go index cb33efa..0fb6f47 100644 --- a/fake/accounts.go +++ b/fake/accounts.go @@ -91,10 +91,8 @@ func (engine *Engine) copyAccount(source *v1.Account) (*v1.Account, error) { } func (engine *Engine) siteAndAccountExist(siteName string) error { - // Note: API定義上は定義されていないがサイトがないケースでは404が返される - if cluster := engine.getClusterById(siteName); cluster == nil { - return NewError(ErrorTypeNotFound, "account", "", - "指定のサイトは存在しません。site_name: %s", siteName) + if err := engine.siteExist(siteName); err != nil { + return err } if engine.Account == nil { diff --git a/fake/clusters.go b/fake/clusters.go index 0a393f6..ba2e0e5 100644 --- a/fake/clusters.go +++ b/fake/clusters.go @@ -72,3 +72,12 @@ func (engine *Engine) copyCluster(source *v1.Cluster) (*v1.Cluster, error) { } return &cluster, nil } + +func (engine *Engine) siteExist(siteName string) error { + // Note: API定義上は定義されていないがサイトがないケースでは404が返される + if cluster := engine.getClusterById(siteName); cluster == nil { + return NewError(ErrorTypeNotFound, "cluster", "", + "指定のサイトは存在しません。site_name: %s", siteName) + } + return nil +} diff --git a/fake/engine.go b/fake/engine.go index 0ae2614..dc40a93 100644 --- a/fake/engine.go +++ b/fake/engine.go @@ -39,6 +39,9 @@ type Engine struct { // AccountKeys アカウントのアクセスキー AccountKeys []*v1.AccountKey + // Permissions パーミッション + Permissions []*v1.Permission + // ActionInterval バックグラウンドでリソースの状態を変化させるアクションの実行間隔 ActionInterval time.Duration diff --git a/fake/permissions.go b/fake/permissions.go new file mode 100644 index 0000000..d517003 --- /dev/null +++ b/fake/permissions.go @@ -0,0 +1,152 @@ +// Copyright 2022 The sacloud/object-storage-api-go authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fake + +import ( + "fmt" + "time" + + "github.com/getlantern/deepcopy" + v1 "github.com/sacloud/object-storage-api-go/apis/v1" +) + +// ListPermissions パーミッション一覧の取得 +// (GET /{site_name}/v2/permissions) +func (engine *Engine) ListPermissions(siteName string) ([]v1.Permission, error) { + defer engine.rLock()() + + if err := engine.siteExist(siteName); err != nil { + return nil, err + } + return engine.permissions(), nil +} + +// CreatePermission パーミッションの作成 +// (POST /{site_name}/v2/permissions) +func (engine *Engine) CreatePermission(siteName string, params *v1.PermissionRequestBody) (*v1.Permission, error) { + defer engine.lock()() + + if err := engine.siteExist(siteName); err != nil { + return nil, err + } + + permission := &v1.Permission{ + BucketControls: params.BucketControls, + CreatedAt: v1.CreatedAt(time.Now()), + DisplayName: params.DisplayName, + Id: v1.PermissionID(engine.nextId()), + } + + engine.Permissions = append(engine.Permissions, permission) + return engine.copyPermission(permission) +} + +// DeletePermission パーミッションの削除 +// (DELETE /{site_name}/v2/permissions/{id}) +func (engine *Engine) DeletePermission(siteName string, id string) error { + defer engine.lock()() + + if err := engine.siteAndPermissionExist(siteName, id); err != nil { + return err + } + + var deleted []*v1.Permission + for _, p := range engine.Permissions { + if p.Id.String() != id { + deleted = append(deleted, p) + } + } + engine.Permissions = deleted + return nil +} + +// ReadPermission パーミッションの取得 +// (GET /{site_name}/v2/permissions/{id}) +func (engine *Engine) ReadPermission(siteName string, id string) (*v1.Permission, error) { + defer engine.rLock()() + + if err := engine.siteAndPermissionExist(siteName, id); err != nil { + return nil, err + } + + return engine.copyPermission(engine.getPermissionById(id)) +} + +// UpdatePermission パーミッションの更新 +// (PUT /{site_name}/v2/permissions/{id}) +func (engine *Engine) UpdatePermission(siteName string, id string, params *v1.PermissionRequestBody) (*v1.Permission, error) { + defer engine.lock()() + + if err := engine.siteAndPermissionExist(siteName, id); err != nil { + return nil, err + } + + var updated *v1.Permission + for _, p := range engine.Permissions { + if p.Id.String() == id { + p.BucketControls = params.BucketControls + p.DisplayName = params.DisplayName + updated = p + } + } + + return engine.copyPermission(updated) // チェック済みなのでupdatedはnilになり得ない +} + +// permissions engine.Permissionsを非ポインタ型にして返す +func (engine *Engine) permissions() []v1.Permission { + var permissions []v1.Permission + for _, p := range engine.Permissions { + permissions = append(permissions, *p) + } + return permissions +} + +func (engine *Engine) getPermissionById(id string) *v1.Permission { + if id == "" { + return nil + } + for _, p := range engine.Permissions { + // Note: idは一度int64に変換して比較した方が良いかもしれないが、 + // ここでは簡易に実装するために文字列比較している(実用上さほど問題ではないと判断) + if p.Id.String() == id { + return p + } + } + return nil +} + +func (engine *Engine) copyPermission(source *v1.Permission) (*v1.Permission, error) { + if source == nil { + return nil, fmt.Errorf("source is nil") + } + var permission v1.Permission + if err := deepcopy.Copy(&permission, source); err != nil { + return nil, err + } + return &permission, nil +} + +func (engine *Engine) siteAndPermissionExist(siteName string, id string) error { + if err := engine.siteExist(siteName); err != nil { + return err + } + // Note: API定義上は定義されていないがサイトがないケースでは404が返される + if permission := engine.getPermissionById(id); permission == nil { + return NewError(ErrorTypeNotFound, "permission", "", + "指定のパーミッションは存在しません。site_name: %s, id: %s", siteName, id) + } + return nil +} diff --git a/fake/permissions_test.go b/fake/permissions_test.go new file mode 100644 index 0000000..eb12a4f --- /dev/null +++ b/fake/permissions_test.go @@ -0,0 +1,120 @@ +// Copyright 2022 The sacloud/object-storage-api-go authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fake + +import ( + "testing" + "time" + + v1 "github.com/sacloud/object-storage-api-go/apis/v1" + "github.com/stretchr/testify/require" +) + +func TestEngine_Permissions(t *testing.T) { + siteId := "isk01" + engine := &Engine{ + Clusters: []*v1.Cluster{ + { + Id: siteId, + + ControlPanelUrl: "https://secure.sakura.ad.jp/objectstorage/", + DislpayNameEnUs: "Ishikari Site #1", + DislpayNameJa: "石狩第1サイト", + DisplayName: "石狩第1サイト", + DisplayOrder: 1, + EndpointBase: "isk01.sakurastorage.jp", + }, + }, + Account: &v1.Account{ + Code: v1.Code("member@account@" + siteId), + CreatedAt: v1.CreatedAt(time.Now()), + ResourceId: "100000000001", + }, + Buckets: []*v1.Bucket{ + { + ClusterId: siteId, + Name: "bucket1", + }, + { + ClusterId: siteId, + Name: "bucket2", + }, + }, + } + + var permissionId v1.PermissionID + t.Run("create permission", func(t *testing.T) { + permissions, err := engine.ListPermissions(siteId) + require.NoError(t, err) + require.Len(t, permissions, 0) + + bucketControl := v1.BucketControl{ + BucketName: "bucket1", + CanRead: true, + CanWrite: false, + } + permission, err := engine.CreatePermission(siteId, &v1.PermissionRequestBody{ + BucketControls: v1.BucketControls{bucketControl}, + DisplayName: "foobar", + }) + require.NoError(t, err) + require.NotNil(t, permission) + + require.Len(t, permission.BucketControls, 1) + require.Equal(t, bucketControl, v1.BucketControl{ + BucketName: permission.BucketControls[0].BucketName, + CanRead: permission.BucketControls[0].CanRead, + CanWrite: permission.BucketControls[0].CanWrite, + }) + permissionId = permission.Id + + permissions, err = engine.ListPermissions(siteId) + require.NoError(t, err) + require.Len(t, permissions, 1) + + }) + + t.Run("read permission", func(t *testing.T) { + key, err := engine.ReadPermission(siteId, permissionId.String()) + require.NoError(t, err) + require.NotNil(t, key) + }) + + t.Run("update permission", func(t *testing.T) { + bucketControl := v1.BucketControl{ + BucketName: "bucket2", + CanRead: false, + CanWrite: true, + } + permission, err := engine.UpdatePermission(siteId, permissionId.String(), &v1.PermissionRequestBody{ + BucketControls: v1.BucketControls{bucketControl}, + DisplayName: "foobar", + }) + require.NoError(t, err) + require.NotNil(t, permission) + + require.Equal(t, bucketControl, v1.BucketControl{ + BucketName: permission.BucketControls[0].BucketName, + CanRead: permission.BucketControls[0].CanRead, + CanWrite: permission.BucketControls[0].CanWrite, + }) + }) + + t.Run("delete permission", func(t *testing.T) { + err := engine.DeletePermission(siteId, permissionId.String()) + require.NoError(t, err) + require.Len(t, engine.Permissions, 0) + }) +} diff --git a/fake/server/interface.go b/fake/server/permission_keys.go similarity index 61% rename from fake/server/interface.go rename to fake/server/permission_keys.go index 4ecb748..8cbc6a4 100644 --- a/fake/server/interface.go +++ b/fake/server/permission_keys.go @@ -16,26 +16,6 @@ package server import "github.com/gin-gonic/gin" -// ListPermissions パーミッション一覧の取得 -// (GET /{site_name}/v2/permissions) -func (s *Server) ListPermissions(c *gin.Context, siteName string) {} - -// CreatePermission パーミッションの作成 -// (POST /{site_name}/v2/permissions) -func (s *Server) CreatePermission(c *gin.Context, siteName string) {} - -// DeletePermission パーミッションの削除 -// (DELETE /{site_name}/v2/permissions/{id}) -func (s *Server) DeletePermission(c *gin.Context, siteName string, id string) {} - -// ReadPermission パーミッションの取得 -// (GET /{site_name}/v2/permissions/{id}) -func (s *Server) ReadPermission(c *gin.Context, siteName string, id string) {} - -// UpdatePermission パーミッションの更新 -// (PUT /{site_name}/v2/permissions/{id}) -func (s *Server) UpdatePermission(c *gin.Context, siteName string, id string) {} - // ListPermissionAccessKeys パーミッションが保有するアクセスキー一覧の取得 // (GET /{site_name}/v2/permissions/{id}/keys) func (s *Server) ListPermissionAccessKeys(c *gin.Context, siteName string, id string) {} @@ -52,7 +32,3 @@ func (s *Server) DeletePermissionAccessKey(c *gin.Context, siteName string, id s // ReadPermissionAccessKey パーミッションが保有するアクセスキーの取得 // (GET /{site_name}/v2/permissions/{id}/keys/{key_id}) func (s *Server) ReadPermissionAccessKey(c *gin.Context, siteName string, id string, keyId string) {} - -// ReadSiteStatus サイトのステータスの取得 -// (GET /{site_name}/v2/status) -func (s *Server) ReadSiteStatus(c *gin.Context, siteName string) {} diff --git a/fake/server/permissions.go b/fake/server/permissions.go new file mode 100644 index 0000000..db966c5 --- /dev/null +++ b/fake/server/permissions.go @@ -0,0 +1,97 @@ +// Copyright 2022 The sacloud/object-storage-api-go authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "net/http" + + "github.com/gin-gonic/gin" + v1 "github.com/sacloud/object-storage-api-go/apis/v1" +) + +// ListPermissions パーミッション一覧の取得 +// (GET /{site_name}/v2/permissions) +func (s *Server) ListPermissions(c *gin.Context, siteName string) { + permissions, err := s.Engine.ListPermissions(siteName) + if err != nil { + s.handleError(c, err) + return + } + + c.JSON(http.StatusOK, &v1.PermissionsResponseBody{ + Data: permissions, + }) +} + +// CreatePermission パーミッションの作成 +// (POST /{site_name}/v2/permissions) +func (s *Server) CreatePermission(c *gin.Context, siteName string) { + var paramJSON v1.PermissionRequestBody + if err := c.ShouldBindJSON(¶mJSON); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + permission, err := s.Engine.CreatePermission(siteName, ¶mJSON) + if err != nil { + s.handleError(c, err) + return + } + c.JSON(http.StatusCreated, &v1.PermissionResponseBody{ + Data: *permission, + }) +} + +// DeletePermission パーミッションの削除 +// (DELETE /{site_name}/v2/permissions/{id}) +func (s *Server) DeletePermission(c *gin.Context, siteName string, id string) { + if err := s.Engine.DeletePermission(siteName, id); err != nil { + s.handleError(c, err) + return + } + c.Status(http.StatusNoContent) +} + +// ReadPermission パーミッションの取得 +// (GET /{site_name}/v2/permissions/{id}) +func (s *Server) ReadPermission(c *gin.Context, siteName string, id string) { + permission, err := s.Engine.ReadPermission(siteName, id) + if err != nil { + s.handleError(c, err) + return + } + c.JSON(http.StatusCreated, &v1.PermissionResponseBody{ + Data: *permission, + }) +} + +// UpdatePermission パーミッションの更新 +// (PUT /{site_name}/v2/permissions/{id}) +func (s *Server) UpdatePermission(c *gin.Context, siteName string, id string) { + var paramJSON v1.PermissionRequestBody + if err := c.ShouldBindJSON(¶mJSON); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + permission, err := s.Engine.UpdatePermission(siteName, id, ¶mJSON) + if err != nil { + s.handleError(c, err) + return + } + c.JSON(http.StatusOK, &v1.PermissionResponseBody{ + Data: *permission, + }) +} diff --git a/fake/server/site_status.go b/fake/server/site_status.go new file mode 100644 index 0000000..69d82aa --- /dev/null +++ b/fake/server/site_status.go @@ -0,0 +1,21 @@ +// Copyright 2022 The sacloud/object-storage-api-go authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import "github.com/gin-gonic/gin" + +// ReadSiteStatus サイトのステータスの取得 +// (GET /{site_name}/v2/status) +func (s *Server) ReadSiteStatus(c *gin.Context, siteName string) {}