Skip to content

Commit

Permalink
Merge pull request #66 from whywaita/fix/64
Browse files Browse the repository at this point in the history
Remove sql.NullString from HTTP response
  • Loading branch information
whywaita authored Jun 9, 2021
2 parents 6515d27 + b512e91 commit 554f964
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 83 deletions.
8 changes: 4 additions & 4 deletions pkg/gh/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func listInstallations(gheDomain string) ([]*github.Installation, error) {
"installations")
jb, err := callAPIPrivateKey(http.MethodGet, p, gheDomain)
if err != nil {
return nil, fmt.Errorf("failed to call API: %w", err)
return nil, fmt.Errorf("failed to call API (body: %s): %w", string(jb), err)
}

is := new([]*github.Installation)
Expand Down Expand Up @@ -164,14 +164,14 @@ func callAPIPrivateKey(method, apiPath, gheDomain string) ([]byte, error) {
return nil, fmt.Errorf("failed to POST request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode > 400 {
return nil, fmt.Errorf("invalid status code (%d)", resp.StatusCode)
}

jb, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode > 400 {
return jb, fmt.Errorf("invalid status code (%d)", resp.StatusCode)
}

return jb, nil
}
Expand Down
63 changes: 55 additions & 8 deletions pkg/web/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"net/url"
"sort"
"strings"
"time"

Expand All @@ -26,6 +27,37 @@ type targetCreateParam struct {
ProviderURL string `json:"provider_url"`
}

// UserTarget is format for user
type UserTarget struct {
UUID uuid.UUID `json:"id"`
Scope string `json:"scope"`
TokenExpiredAt time.Time `json:"token_expired_at"`
GHEDomain string `json:"ghe_domain"`
ResourceType string `json:"resource_type"`
RunnerUser string `json:"runner_user"`
RunnerVersion string `json:"runner_version"`
ProviderURL string `json:"provider_url"`
Status datastore.TargetStatus `json:"status"`
StatusDescription string `json:"status_description"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

func sortUserTarget(uts []UserTarget) []UserTarget {
sort.SliceStable(uts, func(i, j int) bool {
if uts[i].CreatedAt != uts[j].CreatedAt {
return uts[i].CreatedAt.After(uts[j].CreatedAt)
}

iType := datastore.UnmarshalResourceTypeString(uts[i].ResourceType)
jType := datastore.UnmarshalResourceTypeString(uts[j].ResourceType)

return iType < jType
})

return uts
}

// function pointer (for testing)
var (
GHExistGitHubRepositoryFunc = gh.ExistGitHubRepository
Expand Down Expand Up @@ -101,12 +133,14 @@ func handleTargetList(w http.ResponseWriter, r *http.Request, ds datastore.Datas
outputErrorMsg(w, http.StatusInternalServerError, "datastore read error")
}

var targets []datastore.Target
var targets []UserTarget
for _, t := range ts {
target := sanitizeTarget(&t)
targets = append(targets, *target)
ut := sanitizeTarget(&t)
targets = append(targets, ut)
}

targets = sortUserTarget(targets)

w.Header().Set("Content-Type", "application/json;charset=utf-8")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(targets)
Expand All @@ -129,18 +163,31 @@ func handleTargetRead(w http.ResponseWriter, r *http.Request, ds datastore.Datas
return
}

target = sanitizeTarget(target)
ut := sanitizeTarget(target)

w.Header().Set("Content-Type", "application/json;charset=utf-8")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(target)
json.NewEncoder(w).Encode(ut)
return
}

func sanitizeTarget(t *datastore.Target) *datastore.Target {
t.GitHubToken = ""
func sanitizeTarget(t *datastore.Target) UserTarget {
ut := UserTarget{
UUID: t.UUID,
Scope: t.Scope,
TokenExpiredAt: t.TokenExpiredAt,
GHEDomain: t.GHEDomain.String,
ResourceType: t.ResourceType.String(),
RunnerUser: t.RunnerUser.String,
RunnerVersion: t.RunnerVersion.String,
ProviderURL: t.ProviderURL.String,
Status: t.Status,
StatusDescription: t.StatusDescription.String,
CreatedAt: t.CreatedAt,
UpdatedAt: t.UpdatedAt,
}

return t
return ut
}

func handleTargetUpdate(w http.ResponseWriter, r *http.Request, ds datastore.Datastore) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/web/target_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ func handleTargetCreate(w http.ResponseWriter, r *http.Request, ds datastore.Dat
outputErrorMsg(w, http.StatusInternalServerError, "datastore get error")
return
}
sanitized := sanitizeTarget(createdTarget)
ut := sanitizeTarget(createdTarget)

w.Header().Set("Content-Type", "application/json;charset=utf-8")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(sanitized)
json.NewEncoder(w).Encode(ut)
return
}

Expand Down
99 changes: 30 additions & 69 deletions pkg/web/target_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"fmt"
"io"
"net/http"
"sort"
"testing"
"time"

Expand Down Expand Up @@ -55,47 +54,36 @@ func setStubFunctions() {

func Test_handleTargetCreate(t *testing.T) {
testURL := testutils.GetTestURL()
testDatastore, teardown := testutils.GetTestDatastore()
_, teardown := testutils.GetTestDatastore()
defer teardown()

setStubFunctions()

tests := []struct {
input string
want *datastore.Target
want *web.UserTarget
err bool
}{
{
input: `{"scope": "octocat", "ghe_domain": "", "resource_type": "micro", "runner_user": "ubuntu"}`,
want: &datastore.Target{
want: &web.UserTarget{
Scope: "octocat",
GitHubToken: testGitHubAppToken,
TokenExpiredAt: testTime,
ResourceType: datastore.ResourceTypeMicro,
RunnerUser: sql.NullString{
Valid: true,
String: "ubuntu",
},
Status: datastore.TargetStatusActive,
ResourceType: datastore.ResourceTypeMicro.String(),
RunnerUser: "ubuntu",
Status: datastore.TargetStatusActive,
},
err: false,
},
{
input: `{"scope": "whywaita/whywaita", "ghe_domain": "github.example.com", "resource_type": "nano", "runner_user": "ubuntu"}`,
want: &datastore.Target{
want: &web.UserTarget{
Scope: "whywaita/whywaita",
GitHubToken: testGitHubAppToken,
TokenExpiredAt: testTime,
GHEDomain: sql.NullString{
Valid: true,
String: "github.example.com",
},
ResourceType: datastore.ResourceTypeNano,
RunnerUser: sql.NullString{
Valid: true,
String: "ubuntu",
},
Status: datastore.TargetStatusActive,
GHEDomain: "github.example.com",
ResourceType: datastore.ResourceTypeNano.String(),
RunnerUser: "ubuntu",
Status: datastore.TargetStatusActive,
},
},
}
Expand All @@ -110,32 +98,18 @@ func Test_handleTargetCreate(t *testing.T) {
t.Fatalf("must be response statuscode is 201, but got %d", code)
}

var gotContent datastore.Target
var gotContent web.UserTarget
if err := json.Unmarshal(content, &gotContent); err != nil {
t.Fatalf("failed to unmarshal resoponse content: %+v", err)
}

u := gotContent.UUID
gotContent.UUID = uuid.UUID{}
gotContent.GitHubToken = testGitHubAppToken // http response hasn't a token
gotContent.CreatedAt = time.Time{}
gotContent.UpdatedAt = time.Time{}

if diff := cmp.Diff(test.want, &gotContent); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}

got, err := testDatastore.GetTarget(context.Background(), u)
if err != nil {
t.Fatalf("failed to retrieve target from datastore: %+v", err)
}
got.UUID = uuid.UUID{}
got.CreatedAt = time.Time{}
got.UpdatedAt = time.Time{}

if diff := cmp.Diff(test.want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}
}
}

Expand Down Expand Up @@ -187,7 +161,7 @@ func Test_handleTargetCreate_recreated(t *testing.T) {
if code != http.StatusCreated {
t.Fatalf("must be response statuscode is 201, but got %d: %+v", code, string(content))
}
var gotContent datastore.Target
var gotContent web.UserTarget
if err := json.Unmarshal(content, &gotContent); err != nil {
t.Fatalf("failed to unmarshal resoponse content: %+v", err)
}
Expand Down Expand Up @@ -249,31 +223,25 @@ func Test_handleTargetList(t *testing.T) {

tests := []struct {
input interface{}
want *[]datastore.Target
want *[]web.UserTarget
err bool
}{
{
input: nil,
want: &[]datastore.Target{
want: &[]web.UserTarget{
{
Scope: "reponano",
TokenExpiredAt: testTime,
ResourceType: datastore.ResourceTypeNano,
RunnerUser: sql.NullString{
Valid: true,
String: "ubuntu",
},
Status: datastore.TargetStatusActive,
ResourceType: datastore.ResourceTypeNano.String(),
RunnerUser: "ubuntu",
Status: datastore.TargetStatusActive,
},
{
Scope: "repomicro",
TokenExpiredAt: testTime,
ResourceType: datastore.ResourceTypeMicro,
RunnerUser: sql.NullString{
Valid: true,
String: "ubuntu",
},
Status: datastore.TargetStatusActive,
ResourceType: datastore.ResourceTypeMicro.String(),
RunnerUser: "ubuntu",
Status: datastore.TargetStatusActive,
},
},
},
Expand All @@ -289,15 +257,11 @@ func Test_handleTargetList(t *testing.T) {
t.Fatalf("must be response statuscode is 201, but got %d: %+v", code, string(content))
}

var gotContents []datastore.Target
var gotContents []web.UserTarget
if err := json.Unmarshal(content, &gotContents); err != nil {
t.Fatalf("failed to unmarshal resoponse content: %+v", err)
}

sort.Slice(gotContents, func(i, j int) bool {
return gotContents[i].ResourceType < gotContents[j].ResourceType
})

for i := range gotContents {
gotContents[i].UUID = uuid.UUID{}
gotContents[i].CreatedAt = time.Time{}
Expand Down Expand Up @@ -327,29 +291,26 @@ func Test_handleTargetRead(t *testing.T) {
if statusCode != http.StatusCreated {
t.Fatalf("must be response statuscode is 201, but got %d: %+v", resp.StatusCode, string(content))
}
var respTarget datastore.Target
var respTarget web.UserTarget
if err := json.Unmarshal(content, &respTarget); err != nil {
t.Fatalf("failed to unmarshal response JSON: %+v", err)
}
targetUUID := respTarget.UUID

tests := []struct {
input uuid.UUID
want *datastore.Target
want *web.UserTarget
err bool
}{
{
input: targetUUID,
want: &datastore.Target{
want: &web.UserTarget{
UUID: targetUUID,
Scope: "repo",
TokenExpiredAt: testTime,
ResourceType: datastore.ResourceTypeMicro,
RunnerUser: sql.NullString{
Valid: true,
String: "ubuntu",
},
Status: datastore.TargetStatusActive,
ResourceType: datastore.ResourceTypeMicro.String(),
RunnerUser: "ubuntu",
Status: datastore.TargetStatusActive,
},
},
}
Expand All @@ -364,7 +325,7 @@ func Test_handleTargetRead(t *testing.T) {
t.Fatalf("must be response statuscode is 201, but got %d: %+v", code, string(content))
}

var got datastore.Target
var got web.UserTarget
if err := json.Unmarshal(content, &got); err != nil {
t.Fatalf("failed to unmarshal resoponse content: %+v", err)
}
Expand Down Expand Up @@ -395,7 +356,7 @@ func Test_handleTargetDelete(t *testing.T) {
if statusCode != http.StatusCreated {
t.Fatalf("must be response statuscode is 201, but got %d: %+v", resp.StatusCode, string(content))
}
var respTarget datastore.Target
var respTarget web.UserTarget
if err := json.Unmarshal(content, &respTarget); err != nil {
t.Fatalf("failed to unmarshal response JSON: %+v", err)
}
Expand Down

0 comments on commit 554f964

Please sign in to comment.