Skip to content

Commit

Permalink
Merge 7aac1d2 into 701d5c4
Browse files Browse the repository at this point in the history
  • Loading branch information
ozerovandrei committed Apr 7, 2018
2 parents 701d5c4 + 7aac1d2 commit f7e8245
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 0 deletions.
13 changes: 13 additions & 0 deletions selvpcclient/resell/v2/projects/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
Package projects provides the ability to retrieve and manage projects through the
Resell v2 API.
Example of getting a single project referenced by its id
project, _, err := projects.Get(context, resellClient, projectID)
if err != nil {
log.Fatal(err)
}
fmt.Println(project)
*/
package projects
33 changes: 33 additions & 0 deletions selvpcclient/resell/v2/projects/requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package projects

import (
"context"
"strings"

"github.com/selectel/go-selvpcclient/selvpcclient"
)

const resourceURL = "projects"

// Get returns a single project by its id.
func Get(ctx context.Context, client *selvpcclient.ServiceClient, id string) (*Project, *selvpcclient.ResponseResult, error) {
url := strings.Join([]string{client.Endpoint, resourceURL, id}, "/")
responseResult, err := client.DoRequest(ctx, "GET", url, nil)
if err != nil {
return nil, nil, err
}
if responseResult.Err != nil {
return nil, responseResult, responseResult.Err
}

// Extract a project from the response body.
var result struct {
Project *Project `json:"project"`
}
err = responseResult.ExtractResult(&result)
if err != nil {
return nil, responseResult, err
}

return result.Project, responseResult, nil
}
88 changes: 88 additions & 0 deletions selvpcclient/resell/v2/projects/schemas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package projects

import (
"encoding/json"

"github.com/selectel/go-selvpcclient/selvpcclient/resell/v2/quotas"
)

// Project represents a single Resell project.
type Project struct {
// ID is a unique id of a project.
ID string `json:"-"`

// Name is a human-readable name of a project.
Name string `json:"-"`

// URL is a public url of a project that is set by the admin API.
URL string `json:"-"`

// Enabled shows if project is active or it was disabled by the admin API.
Enabled bool `json:"-"`

// CustomURL is a public url of a project that can be set by a user.
CustomURL string `json:"-"`

// Theme represents project theme settings.
Theme Theme `json:"-"`

// Quotas contains information about project quotas.
Quotas []quotas.Quota `json:"-"`
}

// UnmarshalJSON implements custom unmarshalling method for the Project type.
func (result *Project) UnmarshalJSON(b []byte) error {
// Populate temporary structure with resource quotas represented as maps.
var s struct {
ID string `json:"id"`
Name string `json:"name"`
URL string `json:"url"`
Enabled bool `json:"enabled"`
CustomURL string `json:"custom_url"`
Theme Theme `json:"theme"`
Quotas map[string][]quotas.ResourceQuotaEntity `json:"quotas"`
}
err := json.Unmarshal(b, &s)
if err != nil {
return err
}

// Populate the result with the unmarshalled data.
*result = Project{
ID: s.ID,
Name: s.Name,
URL: s.URL,
Enabled: s.Enabled,
CustomURL: s.CustomURL,
Theme: s.Theme,
}

if len(s.Quotas) != 0 {
// Convert resource quota maps to the slice of Quota types.
// Here we're allocating memory in advance because we already know the length
// of a result slice from the JSON bytearray.
resourceQuotasSlice := make([]quotas.Quota, len(s.Quotas))
i := 0
for resourceName, resourceQuotas := range s.Quotas {
resourceQuotasSlice[i] = quotas.Quota{
Name: resourceName,
ResourceQuotasEntities: resourceQuotas,
}
i++
}

// Add the unmarshalled quotas slice to the result.
result.Quotas = resourceQuotasSlice
}

return nil
}

// Theme represents theme settings for a single project.
type Theme struct {
// Color is a hex string with a custom background color.
Color string `json:"color"`

// Logo contains url for the project custom header logotype.
Logo string `json:"logo"`
}
102 changes: 102 additions & 0 deletions selvpcclient/resell/v2/projects/testing/fixtures.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package testing

import (
"github.com/selectel/go-selvpcclient/selvpcclient/resell/v2/projects"
"github.com/selectel/go-selvpcclient/selvpcclient/resell/v2/quotas"
)

// TestGetProjectResponseRaw represents a raw response from the Get request.
const TestGetProjectResponseRaw = `
{
"project": {
"custom_url": null,
"enabled": true,
"id": "49338ac045f448e294b25d013f890317",
"name": "Project1",
"quotas": {
"compute_cores": [
{
"region": "ru-1",
"used": 2,
"value": 10,
"zone": "ru-1b"
}
],
"compute_ram": [
{
"region": "ru-1",
"used": 8192,
"value": 10240,
"zone": "ru-1b"
}
],
"image_gigabytes": [
{
"region": "ru-1",
"used": 4,
"value": 12,
"zone": null
}
]
},
"theme": {
"color": "#581845",
"logo": null
},
"url": "https://xxxxxx.selvpc.ru"
}
}
`

// TestGetProjectResponseSingleQuotaRaw represents a raw response with a single quota from the Get request.
const TestGetProjectResponseSingleQuotaRaw = `
{
"project": {
"custom_url": null,
"enabled": true,
"id": "49338ac045f448e294b25d013f890317",
"name": "Project1",
"quotas": {
"compute_cores": [
{
"region": "ru-1",
"used": 2,
"value": 10,
"zone": "ru-1b"
}
]
},
"theme": {
"color": "#581845",
"logo": null
},
"url": "https://xxxxxx.selvpc.ru"
}
}
`

// TestGetProjectSingleQuotaResponse represents the unmarshalled TestGetProjectResponseSingleQuotaRaw response.
var TestGetProjectSingleQuotaResponse = &projects.Project{
ID: "49338ac045f448e294b25d013f890317",
Name: "Project1",
URL: "https://xxxxxx.selvpc.ru",
Enabled: true,
CustomURL: "",
Theme: projects.Theme{
Color: "#581845",
Logo: "",
},
Quotas: []quotas.Quota{
{
Name: "compute_cores",
ResourceQuotasEntities: []quotas.ResourceQuotaEntity{
{
Region: "ru-1",
Used: 2,
Value: 10,
Zone: "ru-1b",
},
},
},
},
}
57 changes: 57 additions & 0 deletions selvpcclient/resell/v2/projects/testing/requests_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package testing

import (
"context"
"fmt"
"net/http"
"reflect"
"testing"

"github.com/selectel/go-selvpcclient/selvpcclient/resell/v2/projects"
"github.com/selectel/go-selvpcclient/selvpcclient/testutils"
)

func TestGetProject(t *testing.T) {
testEnv := testutils.SetupTestEnv()
defer testEnv.TearDownTestEnv()
testEnv.NewTestResellV2Client()
testEnv.Mux.HandleFunc("/resell/v2/projects/49338ac045f448e294b25d013f890317", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, TestGetProjectResponseRaw)
})

ctx := context.Background()
actual, _, err := projects.Get(ctx, testEnv.Client, "49338ac045f448e294b25d013f890317")
if err != nil {
t.Fatal(err)
}

if actual == nil {
t.Fatal("didn't get project")
}
if len(actual.Quotas) != 3 {
t.Errorf("expected 3 quotas in project, but got %d", len(actual.Quotas))
}
}

func TestGetProjectSingleQuota(t *testing.T) {
testEnv := testutils.SetupTestEnv()
defer testEnv.TearDownTestEnv()
testEnv.NewTestResellV2Client()
testEnv.Mux.HandleFunc("/resell/v2/projects/49338ac045f448e294b25d013f890317", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, TestGetProjectResponseSingleQuotaRaw)
})

ctx := context.Background()
actual, _, err := projects.Get(ctx, testEnv.Client, "49338ac045f448e294b25d013f890317")
if err != nil {
t.Fatal(err)
}

expected := TestGetProjectSingleQuotaResponse

if !reflect.DeepEqual(actual, expected) {
t.Fatalf("expected %#v, but got %#v", expected, actual)
}
}

0 comments on commit f7e8245

Please sign in to comment.