feat: Add project API endpoints (#51)

* feat: Add project models

* Add project query functions

* Add organization parameter query

* Add project URL parameter parse

* Add project create and list endpoints

* Add test for organization provided

* Remove unimplemented routes

* Decrease conn timeout

* Add test for UnbiasedModulo32

* Fix expected value

* Add single user endpoint

* Add query for project versions

* Fix linting errors

* Add comments

* Add test for invalid archive

* Check unauthenticated endpoints

* Add check if no change happened

* Ensure context close ends listener

* Fix parallel test run

* Test empty

* Fix organization param comment
This commit is contained in:
Kyle Carberry
2022-01-24 11:07:42 -06:00
committed by GitHub
parent 52e50fc9ca
commit a44056cff5
37 changed files with 2121 additions and 22 deletions

86
codersdk/projects.go Normal file
View File

@ -0,0 +1,86 @@
package codersdk
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/coder/coder/coderd"
)
// Projects lists projects inside an organization.
// If organization is an empty string, all projects will be returned
// for the authenticated user.
func (c *Client) Projects(ctx context.Context, organization string) ([]coderd.Project, error) {
route := "/api/v2/projects"
if organization != "" {
route = fmt.Sprintf("/api/v2/projects/%s", organization)
}
res, err := c.request(ctx, http.MethodGet, route, nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, readBodyAsError(res)
}
var projects []coderd.Project
return projects, json.NewDecoder(res.Body).Decode(&projects)
}
// Project returns a single project.
func (c *Client) Project(ctx context.Context, organization, project string) (coderd.Project, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/projects/%s/%s", organization, project), nil)
if err != nil {
return coderd.Project{}, nil
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return coderd.Project{}, readBodyAsError(res)
}
var resp coderd.Project
return resp, json.NewDecoder(res.Body).Decode(&resp)
}
// CreateProject creates a new project inside an organization.
func (c *Client) CreateProject(ctx context.Context, organization string, request coderd.CreateProjectRequest) (coderd.Project, error) {
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/projects/%s", organization), request)
if err != nil {
return coderd.Project{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return coderd.Project{}, readBodyAsError(res)
}
var project coderd.Project
return project, json.NewDecoder(res.Body).Decode(&project)
}
// ProjectVersions lists history for a project.
func (c *Client) ProjectVersions(ctx context.Context, organization, project string) ([]coderd.ProjectVersion, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/projects/%s/%s/versions", organization, project), nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, readBodyAsError(res)
}
var projectVersions []coderd.ProjectVersion
return projectVersions, json.NewDecoder(res.Body).Decode(&projectVersions)
}
// CreateProjectVersion inserts a new version for the project.
func (c *Client) CreateProjectVersion(ctx context.Context, organization, project string, request coderd.CreateProjectVersionRequest) (coderd.ProjectVersion, error) {
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/projects/%s/%s/versions", organization, project), request)
if err != nil {
return coderd.ProjectVersion{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return coderd.ProjectVersion{}, readBodyAsError(res)
}
var projectVersion coderd.ProjectVersion
return projectVersion, json.NewDecoder(res.Body).Decode(&projectVersion)
}

130
codersdk/projects_test.go Normal file
View File

@ -0,0 +1,130 @@
package codersdk_test
import (
"archive/tar"
"bytes"
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/coder/coder/coderd"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/database"
)
func TestProjects(t *testing.T) {
t.Parallel()
t.Run("UnauthenticatedList", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
_, err := server.Client.Projects(context.Background(), "")
require.Error(t, err)
})
t.Run("List", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
user := server.RandomInitialUser(t)
_, err := server.Client.Projects(context.Background(), "")
require.NoError(t, err)
_, err = server.Client.Projects(context.Background(), user.Organization)
require.NoError(t, err)
})
t.Run("UnauthenticatedCreate", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
_, err := server.Client.CreateProject(context.Background(), "", coderd.CreateProjectRequest{})
require.Error(t, err)
})
t.Run("Create", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
user := server.RandomInitialUser(t)
_, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
Name: "bananas",
Provisioner: database.ProvisionerTypeTerraform,
})
require.NoError(t, err)
})
t.Run("UnauthenticatedSingle", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
_, err := server.Client.Project(context.Background(), "wow", "example")
require.Error(t, err)
})
t.Run("Single", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
user := server.RandomInitialUser(t)
_, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
Name: "bananas",
Provisioner: database.ProvisionerTypeTerraform,
})
require.NoError(t, err)
_, err = server.Client.Project(context.Background(), user.Organization, "bananas")
require.NoError(t, err)
})
t.Run("UnauthenticatedVersions", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
_, err := server.Client.ProjectVersions(context.Background(), "org", "project")
require.Error(t, err)
})
t.Run("Versions", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
user := server.RandomInitialUser(t)
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
Name: "bananas",
Provisioner: database.ProvisionerTypeTerraform,
})
require.NoError(t, err)
_, err = server.Client.ProjectVersions(context.Background(), user.Organization, project.Name)
require.NoError(t, err)
})
t.Run("CreateVersionUnauthenticated", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
_, err := server.Client.CreateProjectVersion(context.Background(), "org", "project", coderd.CreateProjectVersionRequest{
Name: "hello",
StorageMethod: database.ProjectStorageMethodInlineArchive,
StorageSource: []byte{},
})
require.Error(t, err)
})
t.Run("CreateVersion", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
user := server.RandomInitialUser(t)
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
Name: "bananas",
Provisioner: database.ProvisionerTypeTerraform,
})
require.NoError(t, err)
var buffer bytes.Buffer
writer := tar.NewWriter(&buffer)
err = writer.WriteHeader(&tar.Header{
Name: "file",
Size: 1 << 10,
})
require.NoError(t, err)
_, err = writer.Write(make([]byte, 1<<10))
require.NoError(t, err)
_, err = server.Client.CreateProjectVersion(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{
Name: "hello",
StorageMethod: database.ProjectStorageMethodInlineArchive,
StorageSource: buffer.Bytes(),
})
require.NoError(t, err)
})
}

View File

@ -11,7 +11,9 @@ import (
)
func TestUsers(t *testing.T) {
t.Parallel()
t.Run("CreateInitial", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
_, err := server.Client.CreateInitialUser(context.Background(), coderd.CreateInitialUserRequest{
Email: "wowie@coder.com",
@ -23,12 +25,14 @@ func TestUsers(t *testing.T) {
})
t.Run("NoUser", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
_, err := server.Client.User(context.Background(), "")
require.Error(t, err)
})
t.Run("User", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
_ = server.RandomInitialUser(t)
_, err := server.Client.User(context.Background(), "")
@ -36,6 +40,7 @@ func TestUsers(t *testing.T) {
})
t.Run("UserOrganizations", func(t *testing.T) {
t.Parallel()
server := coderdtest.New(t)
_ = server.RandomInitialUser(t)
orgs, err := server.Client.UserOrganizations(context.Background(), "")