Explorar o código

update client api and fix cli

Alexander Belanger %!s(int64=4) %!d(string=hai) anos
pai
achega
c51dd74248
Modificáronse 50 ficheiros con 978 adicións e 2895 borrados
  1. 95 75
      api/client/api.go
  2. 47 174
      api/client/deploy.go
  3. 12 43
      api/client/domain.go
  4. 26 0
      api/client/event.go
  5. 42 87
      api/client/git_repo.go
  6. 0 66
      api/client/github_action.go
  7. 0 123
      api/client/helm_repo.go
  8. 0 74
      api/client/helper_test.go
  9. 43 104
      api/client/integration.go
  10. 43 108
      api/client/k8s.go
  11. 99 252
      api/client/project.go
  12. 0 431
      api/client/project_test.go
  13. 110 420
      api/client/registry.go
  14. 24 46
      api/client/template.go
  15. 51 181
      api/client/user.go
  16. 0 164
      api/client/user_test.go
  17. 1 1
      api/server/handlers/cluster/create_candidate.go
  18. 1 1
      api/server/handlers/cluster/list.go
  19. 1 1
      api/server/handlers/cluster/list_candidates.go
  20. 8 0
      api/server/handlers/release/create.go
  21. 1 1
      api/server/handlers/release/update_steps.go
  22. 6 0
      api/types/cluster.go
  23. 34 0
      api/types/registry.go
  24. 5 7
      api/types/release.go
  25. 2 0
      api/types/user.go
  26. 15 10
      cli/cmd/auth.go
  27. 7 4
      cli/cmd/cluster.go
  28. 7 33
      cli/cmd/connect.go
  29. 3 2
      cli/cmd/connect/dockerhub.go
  30. 8 4
      cli/cmd/connect/docr.go
  31. 4 3
      cli/cmd/connect/ecr.go
  32. 2 2
      cli/cmd/connect/gcr.go
  33. 0 98
      cli/cmd/connect/helm.go
  34. 27 27
      cli/cmd/connect/kubeconfig.go
  35. 3 2
      cli/cmd/connect/registry.go
  36. 2 1
      cli/cmd/create.go
  37. 78 77
      cli/cmd/deploy.go
  38. 58 51
      cli/cmd/deploy/create.go
  39. 28 10
      cli/cmd/deploy/deploy.go
  40. 5 2
      cli/cmd/docker.go
  41. 6 3
      cli/cmd/docker/auth.go
  42. 3 2
      cli/cmd/errors.go
  43. 0 124
      cli/cmd/helm_repo.go
  44. 3 2
      cli/cmd/job.go
  45. 2 1
      cli/cmd/logs.go
  46. 13 8
      cli/cmd/project.go
  47. 14 7
      cli/cmd/registry.go
  48. 5 2
      cli/cmd/run.go
  49. 1 0
      internal/models/release.go
  50. 33 61
      internal/registry/registry.go

+ 95 - 75
api/client/api.go

@@ -1,17 +1,18 @@
 package client
 
 import (
-	"bytes"
-	"context"
 	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"net/http"
+	"net/url"
 	"path/filepath"
 	"strings"
 	"time"
 
+	"github.com/gorilla/schema"
+	"github.com/porter-dev/porter/api/types"
 	"k8s.io/client-go/util/homedir"
 )
 
@@ -24,38 +25,6 @@ type Client struct {
 	Token          string
 }
 
-// HTTPError is the Porter error response returned if a request fails
-type HTTPError struct {
-	Code   uint     `json:"code"`
-	Errors []string `json:"errors"`
-}
-
-type EventStatus int64
-
-const (
-	EventStatusSuccess    EventStatus = 1
-	EventStatusInProgress             = 2
-	EventStatusFailed                 = 3
-)
-
-// Event represents an event that happens during
-type Event struct {
-	ID     string      `json:"event_id"` // events with the same id wil be treated the same, and the highest index one is retained
-	Name   string      `json:"name"`
-	Index  int64       `json:"index"` // priority of the event, used for sorting
-	Status EventStatus `json:"status"`
-	Info   string      `json:"info"` // extra information (can be error or success)
-}
-
-// StreamEventForm is used to send event data to the api
-type StreamEventForm struct {
-	Event     `json:"event"`
-	Token     string `json:"token"`
-	ClusterID uint   `json:"cluster_id"`
-	Name      string `json:"name"`
-	Namespace string `json:"namespace"`
-}
-
 // NewClient constructs a new client based on a set of options
 func NewClient(baseURL string, cookieFileName string) *Client {
 	home := homedir.HomeDir()
@@ -90,7 +59,90 @@ func NewClientWithToken(baseURL, token string) *Client {
 	return client
 }
 
-func (c *Client) sendRequest(req *http.Request, v interface{}, useCookie bool) (*HTTPError, error) {
+func (c *Client) getRequest(relPath string, data interface{}, response interface{}) error {
+	vals := make(map[string][]string)
+	err := schema.NewEncoder().Encode(data, vals)
+
+	urlVals := url.Values(vals)
+
+	req, err := http.NewRequest(
+		"GET",
+		fmt.Sprintf("%s/%s?%s", c.BaseURL, relPath, urlVals.Encode()),
+		nil,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	if httpErr, err := c.sendRequest(req, response, true); httpErr != nil || err != nil {
+		if httpErr != nil {
+			return fmt.Errorf("%v", httpErr.Error)
+		}
+
+		return err
+	}
+
+	return nil
+}
+
+func (c *Client) postRequest(relPath string, data interface{}, response interface{}) error {
+	strData, err := json.Marshal(data)
+
+	if err != nil {
+		return nil
+	}
+
+	req, err := http.NewRequest(
+		"POST",
+		fmt.Sprintf("%s/%s", c.BaseURL, relPath),
+		strings.NewReader(string(strData)),
+	)
+
+	if err != nil {
+		return err
+	}
+
+	if httpErr, err := c.sendRequest(req, response, true); httpErr != nil || err != nil {
+		if httpErr != nil {
+			return fmt.Errorf("%v", httpErr.Error)
+		}
+
+		return err
+	}
+
+	return nil
+}
+
+func (c *Client) deleteRequest(relPath string, data interface{}, response interface{}) error {
+	strData, err := json.Marshal(data)
+
+	if err != nil {
+		return nil
+	}
+
+	req, err := http.NewRequest(
+		"DELETE",
+		fmt.Sprintf("%s/%s", c.BaseURL, relPath),
+		strings.NewReader(string(strData)),
+	)
+
+	if err != nil {
+		return err
+	}
+
+	if httpErr, err := c.sendRequest(req, response, true); httpErr != nil || err != nil {
+		if httpErr != nil {
+			return fmt.Errorf("%v", httpErr.Error)
+		}
+
+		return err
+	}
+
+	return nil
+}
+
+func (c *Client) sendRequest(req *http.Request, v interface{}, useCookie bool) (*types.ExternalError, error) {
 	req.Header.Set("Content-Type", "application/json; charset=utf-8")
 	req.Header.Set("Accept", "application/json; charset=utf-8")
 
@@ -114,7 +166,7 @@ func (c *Client) sendRequest(req *http.Request, v interface{}, useCookie bool) (
 	}
 
 	if res.StatusCode < http.StatusOK || res.StatusCode >= http.StatusBadRequest {
-		var errRes HTTPError
+		var errRes types.ExternalError
 		if err = json.NewDecoder(res.Body).Decode(&errRes); err == nil {
 			return &errRes, nil
 		}
@@ -123,7 +175,13 @@ func (c *Client) sendRequest(req *http.Request, v interface{}, useCookie bool) (
 	}
 
 	if v != nil {
-		if err = json.NewDecoder(res.Body).Decode(v); err != nil {
+		debugBytes, _ := ioutil.ReadAll(res.Body)
+
+		fmt.Println(string(debugBytes))
+
+		copyBody := strings.NewReader(string(debugBytes))
+
+		if err = json.NewDecoder(copyBody).Decode(v); err != nil {
 			return nil, err
 		}
 	}
@@ -149,44 +207,6 @@ func (c *Client) saveCookie(cookie *http.Cookie) error {
 	return ioutil.WriteFile(c.CookieFilePath, data, 0644)
 }
 
-// StreamEvent sends an event from deployment to the api
-func (c *Client) StreamEvent(event Event, projID uint, clusterID uint, name string, namespace string) error {
-	form := StreamEventForm{
-		Event:     event,
-		ClusterID: clusterID,
-		Name:      name,
-		Namespace: namespace,
-	}
-
-	body := new(bytes.Buffer)
-	err := json.NewEncoder(body).Encode(form)
-
-	if err != nil {
-		return err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/releases/%s/steps", c.BaseURL, projID, name),
-		body,
-	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(context.Background())
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-		return err
-	}
-
-	return nil
-}
-
 // retrieves single cookie from file
 func (c *Client) getCookie() (*http.Cookie, error) {
 	data, err := ioutil.ReadFile(c.CookieFilePath)

+ 47 - 174
api/client/deploy.go

@@ -2,219 +2,92 @@ package client
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
-	"net/http"
-	"net/url"
-	"strings"
 
-	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/api/types"
 )
 
-// GetReleaseLatestRevision gets the latest revision of a Helm release
-type GetReleaseWebhookResponse models.ReleaseExternal
-
+// GetReleaseWebhook retrieves the release webhook for a given release
 func (c *Client) GetReleaseWebhook(
 	ctx context.Context,
 	projID, clusterID uint,
 	name, namespace string,
-) (*GetReleaseWebhookResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/releases/%s/webhook_token?"+url.Values{
-			"cluster_id": []string{fmt.Sprintf("%d", clusterID)},
-			"namespace":  []string{namespace},
-			"storage":    []string{"secret"},
-		}.Encode(), c.BaseURL, projID, name),
+) (*types.PorterRelease, error) {
+	resp := &types.PorterRelease{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/%d/namespaces/%s/releases/%s/webhook",
+			projID,
+			clusterID,
+			namespace,
+			name,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &GetReleaseWebhookResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
 // DeployWithWebhook deploys an application with an image tag using a unique webhook URI
 func (c *Client) DeployWithWebhook(
 	ctx context.Context,
-	webhook, tag string,
+	webhook string,
+	req *types.WebhookRequest,
 ) error {
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/webhooks/deploy/%s?commit=%s", c.BaseURL, webhook, tag),
+	return c.postRequest(
+		fmt.Sprintf(
+			"/webhooks/deploy/%s",
+			webhook,
+		),
+		req,
 		nil,
 	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return err
-	}
-
-	return nil
-}
-
-type UpdateBatchImageRequest struct {
-	ImageRepoURI string `json:"image_repo_uri"`
-	Tag          string `json:"tag"`
 }
 
-// UpdateBatchImage updates all releases that use a certain image with a new tag
+// UpdateBatchImage updates all releases that use a certain image with a new tag,
+// within a single namespace
 func (c *Client) UpdateBatchImage(
 	ctx context.Context,
 	projID, clusterID uint,
 	namespace string,
-	updateImageReq *UpdateBatchImageRequest,
+	req *types.UpdateImageBatchRequest,
 ) error {
-	data, err := json.Marshal(updateImageReq)
-
-	if err != nil {
-		return nil
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/clusters/%d/namespaces/%s/releases/image/batch", c.BaseURL, projID, clusterID, namespace),
-		strings.NewReader(string(data)),
+	return c.postRequest(
+		fmt.Sprintf("/projects/%d/clusters/%d/namespaces/%s/releases/image/batch", projID, clusterID, namespace),
+		req,
+		nil,
 	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return err
-	}
-
-	return nil
-}
-
-type DeployTemplateGitAction struct {
-	GitRepo        string            `json:"git_repo"`
-	GitBranch      string            `json:"git_branch"`
-	ImageRepoURI   string            `json:"image_repo_uri"`
-	DockerfilePath string            `json:"dockerfile_path"`
-	FolderPath     string            `json:"folder_path"`
-	GitRepoID      uint              `json:"git_repo_id"`
-	BuildEnv       map[string]string `json:"env"`
-	RegistryID     uint              `json:"registry_id"`
-}
-
-type DeployTemplateRequest struct {
-	TemplateName string                   `json:"templateName"`
-	ImageURL     string                   `json:"imageURL"`
-	FormValues   map[string]interface{}   `json:"formValues"`
-	Namespace    string                   `json:"namespace"`
-	Name         string                   `json:"name"`
-	GitAction    *DeployTemplateGitAction `json:"github_action"`
 }
 
 func (c *Client) DeployTemplate(
 	ctx context.Context,
 	projID, clusterID uint,
-	templateName string,
-	templateVersion string,
-	deployReq *DeployTemplateRequest,
+	namespace string,
+	req *types.CreateReleaseRequest,
 ) error {
-	data, err := json.Marshal(deployReq)
-
-	if err != nil {
-		return err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/deploy/%s/%s?"+url.Values{
-			"cluster_id": []string{fmt.Sprintf("%d", clusterID)},
-			"storage":    []string{"secret"},
-		}.Encode(), c.BaseURL, projID, templateName, templateVersion),
-		strings.NewReader(string(data)),
+	return c.postRequest(
+		fmt.Sprintf("/projects/%d/clusters/%d/namespaces/%s/releases", projID, clusterID, namespace),
+		req,
+		nil,
 	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return err
-	}
-
-	return nil
-}
-
-type UpgradeReleaseRequest struct {
-	Values    string `json:"values"`
-	Namespace string `json:"namespace"`
 }
 
+// UpgradeRelease upgrades a specific release with new values or chart version
 func (c *Client) UpgradeRelease(
 	ctx context.Context,
 	projID, clusterID uint,
-	name string,
-	upgradeReq *UpgradeReleaseRequest,
+	namespace, name string,
+	req *types.UpgradeReleaseRequest,
 ) error {
-	data, err := json.Marshal(upgradeReq)
-
-	if err != nil {
-		return err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/releases/%s/upgrade?"+url.Values{
-			"namespace":  []string{upgradeReq.Namespace},
-			"cluster_id": []string{fmt.Sprintf("%d", clusterID)},
-			"storage":    []string{"secret"},
-		}.Encode(), c.BaseURL, projID, name),
-		strings.NewReader(string(data)),
+	return c.postRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/%d/namespaces/%s/releases/%s/0/upgrade",
+			projID, clusterID,
+			namespace, name,
+		),
+		req,
+		nil,
 	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return err
-	}
-
-	return nil
 }

+ 12 - 43
api/client/domain.go

@@ -2,59 +2,28 @@ package client
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
-	"net/http"
-	"strings"
 
-	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/api/types"
 )
 
-type CreateDNSRecordRequest struct {
-	ReleaseName string `json:"release_name"`
-}
-
-// CreateDNSRecordResponse is the DNS record that was created
-type CreateDNSRecordResponse models.DNSRecordExternal
-
 // CreateDNSRecord creates a Github action with basic authentication
 func (c *Client) CreateDNSRecord(
 	ctx context.Context,
-	projectID, clusterID uint,
-	createDNS *CreateDNSRecordRequest,
-) (*CreateDNSRecordResponse, error) {
-	data, err := json.Marshal(createDNS)
+	projID, clusterID uint,
+	namespace, name string,
+) (*types.DNSRecord, error) {
+	resp := &types.DNSRecord{}
 
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
+	err := c.postRequest(
 		fmt.Sprintf(
-			"%s/projects/%d/k8s/subdomain?cluster_id=%d",
-			c.BaseURL,
-			projectID,
-			clusterID,
+			"/projects/%d/clusters/%d/namespaces/%s/releases/%s/subdomain",
+			projID, clusterID,
+			namespace, name,
 		),
-		strings.NewReader(string(data)),
+		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-
-	res := &CreateDNSRecordResponse{}
-
-	if httpErr, err := c.sendRequest(req, res, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return res, nil
+	return resp, err
 }

+ 26 - 0
api/client/event.go

@@ -0,0 +1,26 @@
+package client
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/porter-dev/porter/api/types"
+)
+
+// CreateEvent sends an event from deployment to the api
+func (c *Client) CreateEvent(
+	ctx context.Context,
+	projID, clusterID uint,
+	namespace, name string,
+	req *types.UpdateReleaseStepsRequest,
+) error {
+	return c.postRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/%d/namespaces/%s/releases/%s/steps",
+			projID, clusterID,
+			namespace, name,
+		),
+		req,
+		nil,
+	)
+}

+ 42 - 87
api/client/git_repo.go

@@ -3,112 +3,67 @@ package client
 import (
 	"context"
 	"fmt"
-	"net/http"
 
-	"github.com/porter-dev/porter/internal/models"
-	"github.com/porter-dev/porter/server/api"
+	"github.com/porter-dev/porter/api/types"
 )
 
-// ListGitRepoResponse is the list of Git repo integrations for a project
-type ListGitRepoResponse []uint
-
-// ListGitRepos returns a list of Git repos for a project
-func (c *Client) ListGitRepos(
+// ListGitInstallationIDs returns a list of Git installation IDs for a user
+func (c *Client) ListGitInstallationIDs(
 	ctx context.Context,
-	projectID uint,
-) (ListGitRepoResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/gitrepos", c.BaseURL, projectID),
+	projID uint,
+) (*types.ListGitInstallationIDsResponse, error) {
+	resp := &types.ListGitInstallationIDsResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/gitrepos",
+			projID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &ListGitRepoResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return *bodyResp, nil
+	return resp, err
 }
 
-type GetRepoTarballDownloadURLResp api.HandleGetRepoZIPDownloadURLResp
-
-// ListGitRepos returns a list of Git repos for a project
-func (c *Client) GetRepoZIPDownloadURL(
+// ListGitRepos returns a list of Git installation IDs for a user
+func (c *Client) ListGitRepos(
 	ctx context.Context,
-	projectID uint,
-	gitActionConfig *models.GitActionConfigExternal,
-) (*GetRepoTarballDownloadURLResp, error) {
-	req, err := http.NewRequest(
-		"GET",
+	projID uint,
+	gitInstallationID int64,
+) (*types.ListReposResponse, error) {
+	resp := &types.ListReposResponse{}
+
+	err := c.getRequest(
 		fmt.Sprintf(
-			"%s/projects/%d/gitrepos/%d/repos/%s/%s/%s/tarball_url",
-			c.BaseURL,
-			projectID,
-			gitActionConfig.GitRepoID,
-			"github",
-			gitActionConfig.GitRepo,
-			gitActionConfig.GitBranch,
+			"/projects/%d/gitrepos/%d/repos",
+			projID,
+			gitInstallationID,
 		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &GetRepoTarballDownloadURLResp{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// ListGitRepoResponse is the list of Git repo integrations for a project
-type ListGithubReposResponse []*api.Repo
-
-// ListGitRepos returns a list of Git repos for a project
-func (c *Client) ListGithubRepos(
+func (c *Client) GetRepoZIPDownloadURL(
 	ctx context.Context,
-	projectID, gitRepoID uint,
-) (ListGithubReposResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/gitrepos/%d/repos", c.BaseURL, projectID, gitRepoID),
+	projID uint,
+	gitInstallationID int64,
+	kind, owner, name, branch string,
+) (*types.GetTarballURLResponse, error) {
+	resp := &types.GetTarballURLResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/gitrepos/%d/repos/%s/%s/%s/%s/tarball_url",
+			projID, gitInstallationID,
+			kind, owner, name, branch,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &ListGithubReposResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return *bodyResp, nil
+	return resp, err
 }

+ 0 - 66
api/client/github_action.go

@@ -1,66 +0,0 @@
-package client
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"net/http"
-	"strings"
-)
-
-// CreateGithubActionRequest represents the accepted fields for creating
-// a Github action
-type CreateGithubActionRequest struct {
-	ReleaseID            uint   `json:"release_id" form:"required"`
-	GitRepo              string `json:"git_repo" form:"required"`
-	GitBranch            string `json:"git_branch"`
-	ImageRepoURI         string `json:"image_repo_uri" form:"required"`
-	DockerfilePath       string `json:"dockerfile_path"`
-	FolderPath           string `json:"folder_path"`
-	GitRepoID            uint   `json:"git_repo_id" form:"required"`
-	RegistryID           uint   `json:"registry_id"`
-	ShouldCreateWorkflow bool   `json:"should_create_workflow"`
-}
-
-// CreateGithubAction creates a Github action with basic authentication
-func (c *Client) CreateGithubAction(
-	ctx context.Context,
-	projectID, clusterID uint,
-	releaseName, releaseNamespace string,
-	createGH *CreateGithubActionRequest,
-) error {
-	data, err := json.Marshal(createGH)
-
-	if err != nil {
-		return err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf(
-			"%s/projects/%d/ci/actions/create?cluster_id=%d&name=%s&namespace=%s",
-			c.BaseURL,
-			projectID,
-			clusterID,
-			releaseName,
-			releaseNamespace,
-		),
-		strings.NewReader(string(data)),
-	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return err
-	}
-
-	return nil
-}

+ 0 - 123
api/client/helm_repo.go

@@ -1,123 +0,0 @@
-package client
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"net/http"
-	"strings"
-
-	"github.com/porter-dev/porter/internal/models"
-)
-
-// CreateHelmRepoRequest represents the accepted fields for creating
-// a Helm repository with basic authentication
-type CreateHelmRepoRequest struct {
-	Name               string `json:"name"`
-	RepoURL            string `json:"repo_url"`
-	BasicIntegrationID uint   `json:"basic_integration_id"`
-}
-
-// CreateHelmRepoResponse is the resulting helm repo after creation
-type CreateHelmRepoResponse models.HelmRepoExternal
-
-// CreateHelmRepo creates an Helm repository integration with basic authentication
-func (c *Client) CreateHelmRepo(
-	ctx context.Context,
-	projectID uint,
-	createHR *CreateHelmRepoRequest,
-) (*CreateHelmRepoResponse, error) {
-	data, err := json.Marshal(createHR)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/helmrepos", c.BaseURL, projectID),
-		strings.NewReader(string(data)),
-	)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &CreateHelmRepoResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
-}
-
-// ListHelmRepoResponse is the list of Helm repos for a project
-type ListHelmRepoResponse []models.HelmRepoExternal
-
-// ListHelmRepos returns a list of Helm repos for a project
-func (c *Client) ListHelmRepos(
-	ctx context.Context,
-	projectID uint,
-) (ListHelmRepoResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/helmrepos", c.BaseURL, projectID),
-		nil,
-	)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &ListHelmRepoResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return *bodyResp, nil
-}
-
-// ListChartsResponse is the list of charts in a Helm repository
-type ListChartsResponse []models.PorterChartList
-
-// ListCharts lists the charts in a Helm repository
-func (c *Client) ListCharts(
-	ctx context.Context,
-	projectID uint,
-	helmID uint,
-) (ListChartsResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/helmrepos/%d/charts", c.BaseURL, projectID, helmID),
-		nil,
-	)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &ListChartsResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return *bodyResp, nil
-}

+ 0 - 74
api/client/helper_test.go

@@ -1,74 +0,0 @@
-package client_test
-
-// import (
-// 	"fmt"
-// 	"os"
-// 	"testing"
-
-// 	"github.com/porter-dev/porter/cli/cmd/docker"
-// )
-
-// const baseURL string = "http://localhost:10000/api"
-
-// func TestMain(m *testing.M) {
-// 	err := startPorterServerWithDocker("user", 10000, docker.SQLite)
-
-// 	if err != nil {
-// 		fmt.Printf("%v\n", err)
-// 		os.Exit(1)
-// 	}
-
-// 	code := m.Run()
-// 	stopPorterServerWithDocker("user")
-
-// 	os.Exit(code)
-// }
-
-// type db int
-
-// const (
-// 	pg db = iota
-// 	sqlite
-// )
-
-// // Spins up and shuts down the Docker api server with the given options
-// func startPorterServerWithDocker(processID string, port int, db docker.PorterDB) error {
-// 	env := []string{
-// 		"ADMIN_INIT=false",
-// 	}
-
-// 	startOpts := &docker.PorterStartOpts{
-// 		ProcessID:      processID,
-// 		ServerImageTag: "testing",
-// 		ServerPort:     port,
-// 		DB:             db,
-// 		Env:            env,
-// 	}
-
-// 	_, _, err := docker.StartPorter(startOpts)
-
-// 	if err != nil {
-// 		return err
-// 	}
-
-// 	return nil
-// }
-
-// func stopPorterServerWithDocker(processID string) error {
-// 	agent, err := docker.NewAgentFromEnv()
-
-// 	if err != nil {
-// 		return err
-// 	}
-
-// 	err = agent.StopPorterContainersWithProcessID(processID, true)
-
-// 	if err != nil {
-// 		return err
-// 	}
-
-// 	// remove volumes
-// 	err = agent.RemoveLocalVolume("porter_sqlite_" + processID)
-
-// 	return nil
-// }

+ 43 - 104
api/client/integration.go

@@ -2,10 +2,7 @@ package client
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
-	"net/http"
-	"strings"
 
 	"github.com/porter-dev/porter/api/types"
 )
@@ -14,135 +11,77 @@ import (
 func (c *Client) CreateAWSIntegration(
 	ctx context.Context,
 	projectID uint,
-	createAWS *types.CreateAWSRequest,
+	req *types.CreateAWSRequest,
 ) (*types.CreateAWSResponse, error) {
-	data, err := json.Marshal(createAWS)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/integrations/aws", c.BaseURL, projectID),
-		strings.NewReader(string(data)),
+	resp := &types.CreateAWSResponse{}
+
+	err := c.postRequest(
+		fmt.Sprintf(
+			"/projects/%d/integrations/aws",
+			projectID,
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &types.CreateAWSResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
 // CreateGCPIntegration creates a GCP integration with the given request options
 func (c *Client) CreateGCPIntegration(
 	ctx context.Context,
 	projectID uint,
-	createGCP *types.CreateGCPRequest,
+	req *types.CreateGCPRequest,
 ) (*types.CreateGCPResponse, error) {
-	data, err := json.Marshal(createGCP)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/integrations/gcp", c.BaseURL, projectID),
-		strings.NewReader(string(data)),
+	resp := &types.CreateGCPResponse{}
+
+	err := c.postRequest(
+		fmt.Sprintf(
+			"/projects/%d/integrations/gcp",
+			projectID,
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &types.CreateGCPResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
 // CreateBasicAuthIntegration creates a "basic auth" integration
 func (c *Client) CreateBasicAuthIntegration(
 	ctx context.Context,
 	projectID uint,
-	createBasic *types.CreateBasicRequest,
+	req *types.CreateBasicRequest,
 ) (*types.CreateBasicResponse, error) {
-	data, err := json.Marshal(createBasic)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/integrations/basic", c.BaseURL, projectID),
-		strings.NewReader(string(data)),
+	resp := &types.CreateBasicResponse{}
+
+	err := c.postRequest(
+		fmt.Sprintf(
+			"/projects/%d/integrations/basic",
+			projectID,
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &types.CreateBasicResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
 // ListOAuthIntegrations lists the oauth integrations in a project
 func (c *Client) ListOAuthIntegrations(
 	ctx context.Context,
 	projectID uint,
-) (types.ListOAuthResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/integrations/oauth", c.BaseURL, projectID),
+) (*types.ListOAuthResponse, error) {
+	resp := &types.ListOAuthResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/integrations/oauth",
+			projectID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &types.ListOAuthResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return *bodyResp, nil
+	return resp, err
 }

+ 43 - 108
api/client/k8s.go

@@ -3,151 +3,86 @@ package client
 import (
 	"context"
 	"fmt"
-	"net/http"
-	"net/url"
 
-	"github.com/porter-dev/porter/server/api"
-	v1 "k8s.io/api/core/v1"
+	"github.com/porter-dev/porter/api/types"
 )
 
-// GetK8sNamespacesResponse is the list of namespaces returned when a
-// user has successfully authenticated
-type GetK8sNamespacesResponse v1.NamespaceList
-
 // GetK8sNamespaces gets a namespaces list in a k8s cluster
 func (c *Client) GetK8sNamespaces(
 	ctx context.Context,
 	projectID uint,
 	clusterID uint,
-) (*GetK8sNamespacesResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/clusters/%d/namespaces", c.BaseURL, projectID, clusterID),
+) (*types.ListNamespacesResponse, error) {
+	resp := &types.ListNamespacesResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/%d/namespaces",
+			projectID, clusterID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &GetK8sNamespacesResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
-}
-
-// GetKubeconfigResponse is the list of namespaces returned when a
-// user has successfully authenticated
-type GetKubeconfigResponse struct {
-	Kubeconfig []byte `json:"kubeconfig"`
+	return resp, err
 }
 
-// GetK8sNamespaces gets a namespaces list in a k8s cluster
 func (c *Client) GetKubeconfig(
 	ctx context.Context,
 	projectID uint,
 	clusterID uint,
-) (*GetKubeconfigResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/clusters/%d/kubeconfig", c.BaseURL, projectID, clusterID),
+) (*types.GetTemporaryKubeconfigResponse, error) {
+	resp := &types.GetTemporaryKubeconfigResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/%d/kubeconfig",
+			projectID, clusterID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &GetKubeconfigResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// GetReleaseLatestRevision gets the latest revision of a Helm release
-type GetReleaseResponse api.PorterRelease
-
-// GetK8sAllPods gets all pods for a given release
 func (c *Client) GetRelease(
 	ctx context.Context,
 	projectID, clusterID uint,
 	namespace, name string,
-) (*GetReleaseResponse, error) {
-	cl := fmt.Sprintf("%d", clusterID)
-
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/releases/%s/0?"+url.Values{
-			"cluster_id": []string{cl},
-			"namespace":  []string{namespace},
-			"storage":    []string{"secret"},
-		}.Encode(), c.BaseURL, projectID, name),
+) (*types.GetReleaseResponse, error) {
+	resp := &types.GetReleaseResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/%d/namespaces/%s/releases/%s/0",
+			projectID, clusterID,
+			namespace, name,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &GetReleaseResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// GetReleaseAllPodsResponse is the list of all pods for a given Helm release
-type GetReleaseAllPodsResponse []v1.Pod
-
 // GetK8sAllPods gets all pods for a given release
 func (c *Client) GetK8sAllPods(
 	ctx context.Context,
 	projectID, clusterID uint,
 	namespace, name string,
-) (GetReleaseAllPodsResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/clusters/%d/namespaces/%s/releases/%s/0/pods/all", c.BaseURL, projectID, clusterID, namespace, name),
+) (*types.GetReleaseAllPodsResponse, error) {
+	resp := &types.GetReleaseAllPodsResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/%d/namespaces/%s/releases/%s/0/pods/all",
+			projectID, clusterID,
+			namespace, name,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &GetReleaseAllPodsResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return *bodyResp, nil
+	return resp, err
 }

+ 99 - 252
api/client/project.go

@@ -2,275 +2,148 @@ package client
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
-	"net/http"
-	"strings"
 
 	"github.com/porter-dev/porter/api/types"
-	"github.com/porter-dev/porter/internal/models"
 )
 
-// GetProjectResponse is the response returned after querying for a
-// given project
-type GetProjectResponse types.Project
-
 // GetProject retrieves a project by id
-func (c *Client) GetProject(ctx context.Context, projectID uint) (*GetProjectResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d", c.BaseURL, projectID),
+func (c *Client) GetProject(
+	ctx context.Context,
+	projectID uint,
+) (*types.ReadProjectResponse, error) {
+	resp := &types.ReadProjectResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d",
+			projectID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &GetProjectResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// GetProjectClusterResponse is the response returned after querying for a
-// given project's cluster
-type GetProjectClusterResponse models.ClusterExternal
-
 // GetProjectCluster retrieves a project's cluster by id
 func (c *Client) GetProjectCluster(
 	ctx context.Context,
 	projectID uint,
 	clusterID uint,
-) (*GetProjectClusterResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/clusters/%d", c.BaseURL, projectID, clusterID),
+) (*types.ClusterGetResponse, error) {
+	resp := &types.ClusterGetResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/%d",
+			projectID, clusterID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &GetProjectClusterResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// ListProjectClustersResponse lists the linked clusters for a project
-type ListProjectClustersResponse []models.ClusterExternal
-
 // ListProjectClusters creates a list of clusters for a given project
 func (c *Client) ListProjectClusters(
 	ctx context.Context,
 	projectID uint,
-) (ListProjectClustersResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/clusters", c.BaseURL, projectID),
+) (*types.ListClusterResponse, error) {
+	resp := &types.ListClusterResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters",
+			projectID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := ListProjectClustersResponse{}
-
-	if httpErr, err := c.sendRequest(req, &bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
-}
-
-// CreateProjectRequest represents the accepted fields for creating a project
-type CreateProjectRequest struct {
-	Name string `json:"name" form:"required"`
+	return resp, err
 }
 
-// CreateProjectResponse is the resulting project after creation
-type CreateProjectResponse types.Project
-
 // CreateProject creates a project with the given request options
 func (c *Client) CreateProject(
 	ctx context.Context,
-	createProjectRequest *CreateProjectRequest,
-) (*CreateProjectResponse, error) {
-	data, err := json.Marshal(createProjectRequest)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects", c.BaseURL),
-		strings.NewReader(string(data)),
+	req *types.CreateProjectRequest,
+) (*types.CreateProjectResponse, error) {
+	resp := &types.CreateProjectResponse{}
+
+	err := c.postRequest(
+		fmt.Sprintf(
+			"/projects",
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &CreateProjectResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
-}
-
-// CreateProjectCandidatesRequest creates a project service account candidate,
-// which can be resolved to create a service account
-type CreateProjectCandidatesRequest struct {
-	Kubeconfig string `json:"kubeconfig"`
-	IsLocal    bool   `json:"is_local"`
+	return resp, err
 }
 
-// CreateProjectCandidatesResponse is the list of candidates returned after
-// creating the candidates
-type CreateProjectCandidatesResponse []*models.ClusterCandidateExternal
-
 // CreateProjectCandidates creates a service account candidate for a given project,
 // accepting a kubeconfig that gets parsed into a candidate
 func (c *Client) CreateProjectCandidates(
 	ctx context.Context,
 	projectID uint,
-	createCandidatesRequest *CreateProjectCandidatesRequest,
-) (CreateProjectCandidatesResponse, error) {
-	data, err := json.Marshal(createCandidatesRequest)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/clusters/candidates", c.BaseURL, projectID),
-		strings.NewReader(string(data)),
+	req *types.CreateClusterCandidateRequest,
+) (*types.CreateClusterCandidateResponse, error) {
+	resp := &types.CreateClusterCandidateResponse{}
+
+	err := c.postRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/candidates",
+			projectID,
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := CreateProjectCandidatesResponse{}
-
-	if httpErr, err := c.sendRequest(req, &bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// GetProjectCandidatesResponse is the list of service account candidates
-type GetProjectCandidatesResponse []*models.ClusterCandidateExternal
-
-// GetProjectCandidates returns the service account candidates for a given
+// GetProjectCandidates returns the cluster candidates for a given
 // project id
 func (c *Client) GetProjectCandidates(
 	ctx context.Context,
 	projectID uint,
-) (GetProjectCandidatesResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/clusters/candidates", c.BaseURL, projectID),
+) (*types.ListClusterCandidateResponse, error) {
+	resp := &types.ListClusterCandidateResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/candidates",
+			projectID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := GetProjectCandidatesResponse{}
-
-	if httpErr, err := c.sendRequest(req, &bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// CreateProjectClusterResponse is the cluster that gets
-// returned after the candidate has been resolved
-type CreateProjectClusterResponse models.ClusterExternal
-
 // CreateProjectCluster creates a cluster given a project id
 // and a candidate id, which gets resolved using the list of actions
 func (c *Client) CreateProjectCluster(
 	ctx context.Context,
-	projectID uint,
-	candidateID uint,
-	createReq *models.ClusterResolverAll,
-) (*CreateProjectClusterResponse, error) {
-	data, err := json.Marshal(&createReq)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/clusters/candidates/%d/resolve", c.BaseURL, projectID, candidateID),
-		strings.NewReader(string(data)),
+	projectID, candidateID uint,
+	req *types.ClusterResolverAll,
+) (*types.Cluster, error) {
+	resp := &types.Cluster{}
+
+	err := c.postRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/candidates/%d/resolve",
+			projectID,
+			candidateID,
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &CreateProjectClusterResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
 // DeleteProjectCluster deletes a cluster given a project id and cluster id
@@ -279,54 +152,28 @@ func (c *Client) DeleteProjectCluster(
 	projectID uint,
 	clusterID uint,
 ) error {
-	req, err := http.NewRequest(
-		"DELETE",
-		fmt.Sprintf("%s/projects/%d/clusters/%d", c.BaseURL, projectID, clusterID),
+	return c.deleteRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/%d",
+			projectID,
+			clusterID,
+		),
+		nil,
 		nil,
 	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return err
-	}
-
-	return nil
 }
 
-// DeleteProjectResponse is the object returned after project deletion
-type DeleteProjectResponse types.Project
-
-// DeleteProject deletes a project by id
-func (c *Client) DeleteProject(ctx context.Context, projectID uint) (*DeleteProjectResponse, error) {
-	req, err := http.NewRequest(
-		"DELETE",
-		fmt.Sprintf("%s/projects/%d", c.BaseURL, projectID),
+// // DeleteProject deletes a project by id
+func (c *Client) DeleteProject(
+	ctx context.Context,
+	projectID uint,
+) error {
+	return c.deleteRequest(
+		fmt.Sprintf(
+			"/projects/%d",
+			projectID,
+		),
+		nil,
 		nil,
 	)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &DeleteProjectResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
 }

+ 0 - 431
api/client/project_test.go

@@ -1,431 +0,0 @@
-package client_test
-
-// import (
-// 	"context"
-// 	"testing"
-
-// 	api "github.com/porter-dev/porter/api/client"
-// 	"github.com/porter-dev/porter/internal/models"
-
-// 	"github.com/porter-dev/porter/client"
-// )
-
-// func initProject(name string, client *client.Client, t *testing.T) *client.CreateProjectResponse {
-// 	t.Helper()
-
-// 	resp, err := client.CreateProject(context.Background(), &client.CreateProjectRequest{
-// 		Name: name,
-// 	})
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	return resp
-// }
-
-// func initProjectCandidate(
-// 	projectID uint,
-// 	kubeconfig string,
-// 	client *api.Client,
-// 	t *testing.T,
-// ) *models.ClusterCandidateExternal {
-// 	t.Helper()
-
-// 	resp, err := client.CreateProjectCandidates(
-// 		context.Background(),
-// 		projectID,
-// 		&api.CreateProjectCandidatesRequest{
-// 			Kubeconfig: kubeconfig,
-// 		},
-// 	)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	return resp[0]
-// }
-
-// func initProjectCluster(
-// 	projectID uint,
-// 	candidateID uint,
-// 	client *api.Client,
-// 	t *testing.T,
-// ) *api.CreateProjectClusterResponse {
-// 	t.Helper()
-
-// 	resp, err := client.CreateProjectCluster(
-// 		context.Background(),
-// 		projectID,
-// 		candidateID,
-// 		&models.ClusterResolverAll{
-// 			OIDCIssuerCAData: "LS0tLS1CRUdJTiBDRVJ=",
-// 		},
-// 	)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	return resp
-// }
-
-// func TestCreateProject(t *testing.T) {
-// 	email := "create_project_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_create_project_test.json")
-// 	user := initUser(email, client, t)
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-
-// 	resp, err := client.CreateProject(context.Background(), &api.CreateProjectRequest{
-// 		Name: "project-test",
-// 	})
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	// make sure user is admin and project name is correct
-// 	if resp.Name != "project-test" {
-// 		t.Errorf("project name incorrect: expected %s, got %s\n", "project-test", resp.Name)
-// 	}
-
-// 	if len(resp.Roles) != 1 {
-// 		t.Fatalf("project role length is not 1")
-// 	}
-
-// 	if resp.Roles[0].Kind != models.RoleAdmin {
-// 		t.Errorf("project role kind is incorrect: expected %s, got %s\n", models.RoleAdmin, resp.Roles[0].Kind)
-// 	}
-
-// 	if resp.Roles[0].UserID != user.ID {
-// 		t.Errorf("project role user_id is incorrect: expected %d, got %d\n", user.ID, resp.Roles[0].UserID)
-// 	}
-// }
-
-// func TestGetProject(t *testing.T) {
-// 	email := "get_project_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_get_project_test.json")
-// 	user := initUser(email, client, t)
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-// 	project := initProject("project-test", client, t)
-
-// 	resp, err := client.GetProject(context.Background(), project.ID)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	// make sure user is admin and project name is correct
-// 	if resp.Name != "project-test" {
-// 		t.Errorf("project name incorrect: expected %s, got %s\n", "project-test", resp.Name)
-// 	}
-
-// 	if len(resp.Roles) != 1 {
-// 		t.Fatalf("project role length is not 1")
-// 	}
-
-// 	if resp.Roles[0].Kind != models.RoleAdmin {
-// 		t.Errorf("project role kind is incorrect: expected %s, got %s\n", models.RoleAdmin, resp.Roles[0].Kind)
-// 	}
-
-// 	if resp.Roles[0].UserID != user.ID {
-// 		t.Errorf("project role user_id is incorrect: expected %d, got %d\n", user.ID, resp.Roles[0].UserID)
-// 	}
-// }
-
-// func TestGetProjectServiceAccount(t *testing.T) {
-// 	email := "get_project_sa_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_get_project_sa_test.json")
-// 	user := initUser(email, client, t)
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-// 	project := initProject("project-test", client, t)
-// 	cc := initProjectCandidate(project.ID, OIDCAuthWithoutData, client, t)
-// 	cluster := initProjectCluster(project.ID, cc.ID, client, t)
-
-// 	resp, err := client.GetProjectCluster(context.Background(), project.ID, cluster.ID)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	// ensure project id and metadata is correct
-// 	if resp.ProjectID != project.ID {
-// 		t.Errorf("project id incorrect: expected %d, got %d\n", project.ID, resp.ProjectID)
-// 	}
-
-// 	// verify clusters
-// 	if resp.Name != "cluster-test" {
-// 		t.Errorf("cluster's name is incorrect: expected %s, got %s\n", "cluster-test", resp.Name)
-// 	}
-
-// 	if resp.Server != "https://10.10.10.10" {
-// 		t.Errorf("cluster's server is incorrect: expected %s, got %s\n", "https://10.10.10.10", resp.Server)
-// 	}
-// }
-
-// func TestCreateProjectCandidates(t *testing.T) {
-// 	email := "create_project_candidates_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_create_project_candidates_test.json")
-// 	user := initUser(email, client, t)
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-// 	project := initProject("project-test", client, t)
-
-// 	resp, err := client.CreateProjectCandidates(
-// 		context.Background(),
-// 		project.ID,
-// 		&api.CreateProjectCandidatesRequest{
-// 			Kubeconfig: OIDCAuthWithoutData,
-// 		},
-// 	)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	// make sure length is 1
-// 	if len(resp) != 1 {
-// 		t.Fatalf("candidates length is not 1\n")
-// 	}
-
-// 	// make sure auth mechanism is OIDC, project id is correct, and cluster info is correct
-// 	if resp[0].ProjectID != project.ID {
-// 		t.Errorf("project id incorrect: expected %d, got %d\n", project.ID, resp[0].ProjectID)
-// 	}
-
-// 	if resp[0].Name != "cluster-test" {
-// 		t.Errorf("cluster name incorrect: expected %s, got %s\n", "cluster-test", resp[0].Name)
-// 	}
-
-// 	if resp[0].Server != "https://10.10.10.10" {
-// 		t.Errorf("cluster endpoint incorrect: expected %s, got %s\n", "https://10.10.10.10", resp[0].Server)
-// 	}
-
-// 	// make sure correct resolvers need to be performed
-// 	if len(resp[0].Resolvers) != 1 {
-// 		t.Fatalf("actions length is not 1\n")
-// 	}
-// }
-
-// func TestGetProjectCandidates(t *testing.T) {
-// 	email := "get_project_candidates_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_get_project_candidates_test.json")
-// 	user := initUser(email, client, t)
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-// 	project := initProject("project-test", client, t)
-// 	initProjectCandidate(project.ID, OIDCAuthWithoutData, client, t)
-
-// 	resp, err := client.GetProjectCandidates(context.Background(), project.ID)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	// make sure length is 1
-// 	if len(resp) != 1 {
-// 		t.Fatalf("candidates length is not 1\n")
-// 	}
-
-// 	// make sure auth mechanism is OIDC, project id is correct, and cluster info is correct
-// 	// if resp[0].Integration != models.OIDC {
-// 	// 	t.Errorf("oidc auth mechanism incorrect: expected %s, got %s\n", models.OIDC, resp[0].Integration)
-// 	// }
-
-// 	// if resp[0].ProjectID != project.ID {
-// 	// 	t.Errorf("project id incorrect: expected %d, got %d\n", project.ID, resp[0].ProjectID)
-// 	// }
-
-// 	// if resp[0].ClusterName != "cluster-test" {
-// 	// 	t.Errorf("cluster name incorrect: expected %s, got %s\n", "cluster-test", resp[0].ClusterName)
-// 	// }
-
-// 	// if resp[0].ClusterEndpoint != "https://10.10.10.10" {
-// 	// 	t.Errorf("cluster endpoint incorrect: expected %s, got %s\n", "https://10.10.10.10", resp[0].ClusterEndpoint)
-// 	// }
-
-// 	// // make sure correct actions need to be performed
-// 	// if len(resp[0].Actions) != 1 {
-// 	// 	t.Fatalf("actions length is not 1\n")
-// 	// }
-
-// 	// if resp[0].Actions[0].Name != models.OIDCIssuerDataAction {
-// 	// 	t.Errorf("action name incorrect: expected %s, got %s\n", models.OIDCIssuerDataAction, resp[0].Actions[0].Name)
-// 	// }
-
-// 	// if resp[0].Actions[0].Filename != "/fake/path/to/ca.pem" {
-// 	// 	t.Errorf("action filename incorrect: expected %s, got %s\n", "/fake/path/to/ca.pem", resp[0].Actions[0].Filename)
-// 	// }
-// }
-
-// func TestCreateProjectServiceAccount(t *testing.T) {
-// 	email := "create_project_sa_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_create_project_sa_test.json")
-// 	user := initUser(email, client, t)
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-// 	project := initProject("project-test", client, t)
-// 	saCandidate := initProjectCandidate(project.ID, OIDCAuthWithoutData, client, t)
-
-// 	resp, err := client.CreateProjectCluster(
-// 		context.Background(),
-// 		project.ID,
-// 		saCandidate.ID,
-// 		&models.ClusterResolverAll{
-// 			OIDCIssuerCAData: "LS0tLS1CRUdJTiBDRVJ=",
-// 		},
-// 	)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	// ensure project id and metadata is correct
-// 	if resp.ProjectID != project.ID {
-// 		t.Errorf("project id incorrect: expected %d, got %d\n", project.ID, resp.ProjectID)
-// 	}
-
-// 	// if resp.Kind != "connector" {
-// 	// 	t.Errorf("service account kind incorrect: expected %s, got %s\n", "connector", resp.Kind)
-// 	// }
-
-// 	// if resp.Integration != models.OIDC {
-// 	// 	t.Errorf("service account auth mechanism incorrect: expected %s, got %s\n", models.OIDC, resp.Integration)
-// 	// }
-
-// 	// // verify clusters
-// 	// if len(resp.Clusters) != 1 {
-// 	// 	t.Fatalf("length of clusters is not 1")
-// 	// }
-
-// 	// if resp.Clusters[0].ServiceAccountID != resp.ID {
-// 	// 	t.Errorf("cluster's sa id is incorrect: expected %d, got %d\n", resp.ID, resp.Clusters[0].ServiceAccountID)
-// 	// }
-
-// 	// if resp.Clusters[0].Name != "cluster-test" {
-// 	// 	t.Errorf("cluster's name is incorrect: expected %s, got %s\n", "cluster-test", resp.Clusters[0].Name)
-// 	// }
-
-// 	// if resp.Clusters[0].Server != "https://10.10.10.10" {
-// 	// 	t.Errorf("cluster's name is incorrect: expected %s, got %s\n", "https://10.10.10.10", resp.Clusters[0].Server)
-// 	// }
-// }
-
-// func TestListProjectClusters(t *testing.T) {
-// 	email := "list_project_clusters_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_list_project_clusters_test.json")
-// 	user := initUser(email, client, t)
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-// 	project := initProject("project-test", client, t)
-// 	cc := initProjectCandidate(project.ID, OIDCAuthWithoutData, client, t)
-// 	initProjectCluster(project.ID, cc.ID, client, t)
-
-// 	resp, err := client.ListProjectClusters(
-// 		context.Background(),
-// 		project.ID,
-// 	)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	// verify clusters
-// 	if len(resp) != 1 {
-// 		t.Fatalf("length of clusters is not 1")
-// 	}
-
-// 	if resp[0].Name != "cluster-test" {
-// 		t.Errorf("cluster's name is incorrect: expected %s, got %s\n", "cluster-test", resp[0].Name)
-// 	}
-
-// 	if resp[0].Server != "https://10.10.10.10" {
-// 		t.Errorf("cluster's name is incorrect: expected %s, got %s\n", "https://10.10.10.10", resp[0].Server)
-// 	}
-// }
-
-// func TestDeleteProject(t *testing.T) {
-// 	email := "delete_project_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_delete_project_test.json")
-// 	user := initUser(email, client, t)
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-// 	project := initProject("project-test", client, t)
-
-// 	resp, err := client.DeleteProject(context.Background(), project.ID)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	// make sure user is admin and project name is correct
-// 	if resp.Name != "project-test" {
-// 		t.Errorf("project name incorrect: expected %s, got %s\n", "project-test", resp.Name)
-// 	}
-
-// 	if len(resp.Roles) != 1 {
-// 		t.Fatalf("project role length is not 1")
-// 	}
-
-// 	if resp.Roles[0].Kind != models.RoleAdmin {
-// 		t.Errorf("project role kind is incorrect: expected %s, got %s\n", models.RoleAdmin, resp.Roles[0].Kind)
-// 	}
-
-// 	if resp.Roles[0].UserID != user.ID {
-// 		t.Errorf("project role user_id is incorrect: expected %d, got %d\n", user.ID, resp.Roles[0].UserID)
-// 	}
-
-// 	// make sure that project can no longer be found
-// 	_, err = client.GetProject(context.Background(), project.ID)
-
-// 	if err == nil {
-// 		t.Fatalf("no error returned\n")
-// 	}
-// }
-
-// const OIDCAuthWithoutData string = `
-// apiVersion: v1
-// clusters:
-// - cluster:
-//     server: https://10.10.10.10
-//     certificate-authority-data: LS0tLS1CRUdJTiBDRVJ=
-//   name: cluster-test
-// contexts:
-// - context:
-//     cluster: cluster-test
-//     user: test-admin
-//   name: context-test
-// current-context: context-test
-// kind: Config
-// preferences: {}
-// users:
-// - name: test-admin
-//   user:
-//     auth-provider:
-//       config:
-//         client-id: porter-api
-//         id-token: token
-//         idp-issuer-url: https://10.10.10.10
-//         idp-certificate-authority: /fake/path/to/ca.pem
-//       name: oidc
-// `

+ 110 - 420
api/client/registry.go

@@ -2,234 +2,48 @@ package client
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
-	"net/http"
-	"strings"
-	"time"
 
-	"github.com/porter-dev/porter/internal/registry"
-
-	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/api/types"
 )
 
-// CreateECRRequest represents the accepted fields for creating
-// an ECR registry
-type CreateECRRequest struct {
-	Name             string `json:"name"`
-	AWSIntegrationID uint   `json:"aws_integration_id"`
-}
-
-// CreateECRResponse is the resulting registry after creation
-type CreateECRResponse models.RegistryExternal
-
-// CreateECR creates an Elastic Container Registry integration
-func (c *Client) CreateECR(
-	ctx context.Context,
-	projectID uint,
-	createECR *CreateECRRequest,
-) (*CreateECRResponse, error) {
-	data, err := json.Marshal(createECR)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/registries", c.BaseURL, projectID),
-		strings.NewReader(string(data)),
-	)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &CreateECRResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
-}
-
-// CreatePrivateRegistryRequest represents the accepted fields for creating
-// a private registry
-type CreatePrivateRegistryRequest struct {
-	Name               string `json:"name"`
-	URL                string `json:"url"`
-	BasicIntegrationID uint   `json:"basic_integration_id"`
-}
-
-// CreatePrivateRegistryResponse is the resulting registry after creation
-type CreatePrivateRegistryResponse models.RegistryExternal
-
-// CreatePrivateRegistry creates a private registry integration
-func (c *Client) CreatePrivateRegistry(
-	ctx context.Context,
-	projectID uint,
-	createPR *CreatePrivateRegistryRequest,
-) (*CreatePrivateRegistryResponse, error) {
-	data, err := json.Marshal(createPR)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/registries", c.BaseURL, projectID),
-		strings.NewReader(string(data)),
-	)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &CreatePrivateRegistryResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
-}
-
-// CreateGCRRequest represents the accepted fields for creating
-// a GCR registry
-type CreateGCRRequest struct {
-	Name             string `json:"name"`
-	GCPIntegrationID uint   `json:"gcp_integration_id"`
-	URL              string `json:"url"`
-}
-
-// CreateGCRResponse is the resulting registry after creation
-type CreateGCRResponse models.RegistryExternal
-
-// CreateGCR creates an Google Container Registry integration
-func (c *Client) CreateGCR(
+// CreateRegistry creates a new registry integration
+func (c *Client) CreateRegistry(
 	ctx context.Context,
 	projectID uint,
-	createGCR *CreateGCRRequest,
-) (*CreateGCRResponse, error) {
-	data, err := json.Marshal(createGCR)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/registries", c.BaseURL, projectID),
-		strings.NewReader(string(data)),
+	req *types.CreateRegistryRequest,
+) (*types.Registry, error) {
+	resp := &types.Registry{}
+
+	err := c.postRequest(
+		fmt.Sprintf(
+			"/projects/%d/registries",
+			projectID,
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &CreateGCRResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// CreateDOCRRequest represents the accepted fields for creating
-// a DOCR registry
-type CreateDOCRRequest struct {
-	Name            string `json:"name"`
-	DOIntegrationID uint   `json:"do_integration_id"`
-	URL             string `json:"url"`
-}
-
-// CreateDOCRResponse is the resulting registry after creation
-type CreateDOCRResponse models.RegistryExternal
-
-// CreateDOCR creates an Digital Ocean Container Registry integration
-func (c *Client) CreateDOCR(
-	ctx context.Context,
-	projectID uint,
-	createDOCR *CreateDOCRRequest,
-) (*CreateDOCRResponse, error) {
-	data, err := json.Marshal(createDOCR)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/registries", c.BaseURL, projectID),
-		strings.NewReader(string(data)),
-	)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &CreateDOCRResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
-}
-
-// ListRegistryResponse is the list of registries for a project
-type ListRegistryResponse []models.RegistryExternal
-
 // ListRegistries returns a list of registries for a project
 func (c *Client) ListRegistries(
 	ctx context.Context,
 	projectID uint,
-) (ListRegistryResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/registries", c.BaseURL, projectID),
+) (*types.RegistryListResponse, error) {
+	resp := &types.RegistryListResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/registries",
+			projectID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &ListRegistryResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return *bodyResp, nil
+	return resp, err
 }
 
 // DeleteProjectRegistry deletes a registry given a project id and registry id
@@ -238,276 +52,152 @@ func (c *Client) DeleteProjectRegistry(
 	projectID uint,
 	registryID uint,
 ) error {
-	req, err := http.NewRequest(
-		"DELETE",
-		fmt.Sprintf("%s/projects/%d/registries/%d", c.BaseURL, projectID, registryID),
+	return c.deleteRequest(
+		fmt.Sprintf(
+			"/projects/%d/registries/%d",
+			projectID,
+			registryID,
+		),
+		nil,
 		nil,
 	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return err
-	}
-
-	return nil
-}
-
-// GetTokenResponse blah
-type GetTokenResponse struct {
-	Token     string     `json:"token"`
-	ExpiresAt *time.Time `json:"expires_at"`
 }
 
 // GetECRAuthorizationToken gets an ECR authorization token
 func (c *Client) GetECRAuthorizationToken(
 	ctx context.Context,
 	projectID uint,
-	region string,
-) (*GetTokenResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/registries/ecr/token?region=%s", c.BaseURL, projectID, region),
-		nil,
+	req *types.GetRegistryECRTokenRequest,
+) (*types.GetRegistryTokenResponse, error) {
+	resp := &types.GetRegistryTokenResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/registries/ecr/token",
+			projectID,
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	bodyResp := &GetTokenResponse{}
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
-}
-
-type GetGCRTokenRequest struct {
-	ServerURL string `json:"server_url"`
+	return resp, err
 }
 
 // GetGCRAuthorizationToken gets a GCR authorization token
 func (c *Client) GetGCRAuthorizationToken(
 	ctx context.Context,
 	projectID uint,
-	gcrRequest *GetGCRTokenRequest,
-) (*GetTokenResponse, error) {
-	data, err := json.Marshal(gcrRequest)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/registries/gcr/token?server_url=%s", c.BaseURL, projectID, gcrRequest.ServerURL),
-		nil,
+	req *types.GetRegistryGCRTokenRequest,
+) (*types.GetRegistryTokenResponse, error) {
+	resp := &types.GetRegistryTokenResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/registries/gcr/token",
+			projectID,
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	bodyResp := &GetTokenResponse{}
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
 // GetDockerhubAuthorizationToken gets a Docker Hub authorization token
 func (c *Client) GetDockerhubAuthorizationToken(
 	ctx context.Context,
 	projectID uint,
-) (*GetTokenResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/registries/dockerhub/token", c.BaseURL, projectID),
+) (*types.GetRegistryTokenResponse, error) {
+	resp := &types.GetRegistryTokenResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/registries/dockerhub/token",
+			projectID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	bodyResp := &GetTokenResponse{}
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
-}
-
-type GetDOCRTokenRequest struct {
-	ServerURL string `json:"server_url"`
+	return resp, err
 }
 
 // GetDOCRAuthorizationToken gets a DOCR authorization token
 func (c *Client) GetDOCRAuthorizationToken(
 	ctx context.Context,
 	projectID uint,
-	docrRequest *GetDOCRTokenRequest,
-) (*GetTokenResponse, error) {
-	data, err := json.Marshal(docrRequest)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/registries/docr/token?server_url=%s", c.BaseURL, projectID, docrRequest.ServerURL),
-		nil,
+	req *types.GetRegistryGCRTokenRequest,
+) (*types.GetRegistryTokenResponse, error) {
+	resp := &types.GetRegistryTokenResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/registries/docr/token",
+			projectID,
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	bodyResp := &GetTokenResponse{}
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// ListRegistryRepositoryResponse is the list of repositories in a registry
-type ListRegistryRepositoryResponse []registry.Repository
-
 // ListRegistryRepositories lists the repositories in a registry
 func (c *Client) ListRegistryRepositories(
 	ctx context.Context,
 	projectID uint,
 	registryID uint,
-) (ListRegistryRepositoryResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/registries/%d/repositories", c.BaseURL, projectID, registryID),
+) (*types.ListRegistryRepositoryResponse, error) {
+	resp := &types.ListRegistryRepositoryResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/registries/%d/repositories",
+			projectID,
+			registryID,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &ListRegistryRepositoryResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return *bodyResp, nil
+	return resp, err
 }
 
-// ListImagesResponse is the list of images in a repository
-type ListImagesResponse []registry.Image
-
 // ListImages lists the images (repository+tag) in a repository
 func (c *Client) ListImages(
 	ctx context.Context,
 	projectID uint,
 	registryID uint,
 	repoName string,
-) (ListImagesResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects/%d/registries/%d/repositories/%s", c.BaseURL, projectID, registryID, repoName),
+) (*types.ListImageResponse, error) {
+	resp := &types.ListImageResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects/%d/registries/%d/repositories/%s",
+			projectID,
+			registryID,
+			repoName,
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &ListImagesResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return *bodyResp, nil
+	return resp, err
 }
 
-type CreateRepositoryRequest struct {
-	ImageRepoURI string `json:"image_repo_uri"`
-}
-
-// CreateECR creates an Elastic Container Registry integration
 func (c *Client) CreateRepository(
 	ctx context.Context,
 	projectID, regID uint,
-	createRepo *CreateRepositoryRequest,
+	req *types.CreateRegistryRepositoryRequest,
 ) error {
-	data, err := json.Marshal(createRepo)
-
-	if err != nil {
-		return err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/projects/%d/registries/%d/repository", c.BaseURL, projectID, regID),
-		strings.NewReader(string(data)),
+	return c.postRequest(
+		fmt.Sprintf(
+			"/projects/%d/registries/%d/repository",
+			projectID,
+			regID,
+		),
+		req,
+		nil,
 	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return err
-	}
-
-	return nil
 }

+ 24 - 46
api/client/template.go

@@ -3,64 +3,42 @@ package client
 import (
 	"context"
 	"fmt"
-	"net/http"
 
-	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/api/types"
 )
 
 func (c *Client) ListTemplates(
 	ctx context.Context,
-) ([]*models.PorterChartList, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/templates", c.BaseURL),
-		nil,
+	req *types.ListTemplatesRequest,
+) (*types.ListTemplatesResponse, error) {
+	resp := &types.ListTemplatesResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/templates",
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-
-	bodyResp := make([]*models.PorterChartList, 0)
-
-	if httpErr, err := c.sendRequest(req, &bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
 func (c *Client) GetTemplate(
 	ctx context.Context,
 	name, version string,
-) (*models.PorterChartRead, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/templates/%s/%s", c.BaseURL, name, version),
-		nil,
+	req *types.GetTemplateRequest,
+) (*types.GetTemplateResponse, error) {
+	resp := &types.GetTemplateResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/templates/%s/%s",
+			name, version,
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-
-	bodyResp := &models.PorterChartRead{}
-
-	if httpErr, err := c.sendRequest(req, &bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }

+ 51 - 181
api/client/user.go

@@ -2,224 +2,94 @@ package client
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
-	"net/http"
-	"strings"
 
 	"github.com/porter-dev/porter/api/types"
-	"github.com/porter-dev/porter/internal/models"
 )
 
-// AuthCheckResponse is the user model response that is returned if the
-// user is logged in
-type AuthCheckResponse models.UserExternal
-
 // AuthCheck performs a check to ensure that the user is logged in
-func (c *Client) AuthCheck(ctx context.Context) (*AuthCheckResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/users/current", c.BaseURL),
+func (c *Client) AuthCheck(ctx context.Context) (*types.GetAuthenticatedUserResponse, error) {
+	resp := &types.GetAuthenticatedUserResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/users/current",
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-
-	bodyResp := &AuthCheckResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
-}
-
-// LoginRequest is the email/password associated with a login request
-type LoginRequest struct {
-	Email    string `json:"email"`
-	Password string `json:"password"`
+	return resp, err
 }
 
-// LoginResponse is the user model response that is returned after successfully
-// logging in
-type LoginResponse models.UserExternal
-
 // Login authorizes the user and grants them a cookie-based session
-func (c *Client) Login(ctx context.Context, loginRequest *LoginRequest) (*LoginResponse, error) {
-	data, err := json.Marshal(loginRequest)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/login", c.BaseURL),
-		strings.NewReader(string(data)),
+func (c *Client) Login(ctx context.Context, req *types.LoginUserRequest) (*types.GetAuthenticatedUserResponse, error) {
+	resp := &types.GetAuthenticatedUserResponse{}
+
+	err := c.postRequest(
+		fmt.Sprintf(
+			"/login",
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &LoginResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, false); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
 // Logout logs the user out and deauthorizes the cookie-based session
 func (c *Client) Logout(ctx context.Context) error {
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/logout", c.BaseURL),
+	return c.postRequest(
+		fmt.Sprintf(
+			"/logout",
+		),
+		nil,
 		nil,
 	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return err
-	}
-
-	return nil
-}
-
-// CreateUserRequest is the email/password associated with creating a user
-type CreateUserRequest struct {
-	Email    string `json:"email"`
-	Password string `json:"password"`
 }
 
-// CreateUserResponse is the user model response that is returned after successfully
-// creating a user
-type CreateUserResponse models.UserExternal
-
 // CreateUser will create the user, authorize the user and grant them a cookie-based session
 func (c *Client) CreateUser(
 	ctx context.Context,
-	createUserRequest *CreateUserRequest,
-) (*CreateUserResponse, error) {
-	data, err := json.Marshal(createUserRequest)
-
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest(
-		"POST",
-		fmt.Sprintf("%s/users", c.BaseURL),
-		strings.NewReader(string(data)),
+	req *types.CreateUserRequest,
+) (*types.CreateUserResponse, error) {
+	resp := &types.CreateUserResponse{}
+
+	err := c.postRequest(
+		fmt.Sprintf(
+			"/users",
+		),
+		req,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-	bodyResp := &CreateUserResponse{}
-
-	if httpErr, err := c.sendRequest(req, bodyResp, false); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// ListUserProjectsResponse is the list of projects returned
-type ListUserProjectsResponse []*types.Project
-
 // ListUserProjects returns a list of projects associated with a user
-func (c *Client) ListUserProjects(ctx context.Context, userID uint) (ListUserProjectsResponse, error) {
-	req, err := http.NewRequest(
-		"GET",
-		fmt.Sprintf("%s/projects", c.BaseURL),
+func (c *Client) ListUserProjects(ctx context.Context) (*types.ListUserProjectsResponse, error) {
+	resp := &types.ListUserProjectsResponse{}
+
+	err := c.getRequest(
+		fmt.Sprintf(
+			"/projects",
+		),
 		nil,
+		resp,
 	)
 
-	if err != nil {
-		return nil, err
-	}
-
-	req = req.WithContext(ctx)
-
-	bodyResp := ListUserProjectsResponse{}
-
-	if httpErr, err := c.sendRequest(req, &bodyResp, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return nil, err
-	}
-
-	return bodyResp, nil
+	return resp, err
 }
 
-// DeleteUserRequest is the password needed to verify a user should be deleted
-type DeleteUserRequest struct {
-	Password string `json:"password"`
-}
-
-// DeleteUser deletes a user of a given user id
+// DeleteUser deletes the current user
 func (c *Client) DeleteUser(
 	ctx context.Context,
-	userID uint,
-	deleteUserRequest *DeleteUserRequest,
 ) error {
-	data, err := json.Marshal(deleteUserRequest)
-
-	if err != nil {
-		return err
-	}
-
-	req, err := http.NewRequest(
-		"DELETE",
-		fmt.Sprintf("%s/users/%d", c.BaseURL, userID),
-		strings.NewReader(string(data)),
+	return c.deleteRequest(
+		fmt.Sprintf(
+			"/users/current",
+		),
+		nil,
+		nil,
 	)
-
-	if err != nil {
-		return err
-	}
-
-	req = req.WithContext(ctx)
-
-	if httpErr, err := c.sendRequest(req, nil, true); httpErr != nil || err != nil {
-		if httpErr != nil {
-			return fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
-		}
-
-		return err
-	}
-
-	return nil
 }

+ 0 - 164
api/client/user_test.go

@@ -1,164 +0,0 @@
-package client_test
-
-// import (
-// 	"context"
-// 	"strings"
-// 	"testing"
-
-// 	api "github.com/porter-dev/porter/api/client"
-// 	"github.com/porter-dev/porter/internal/models"
-// )
-
-// func initUser(email string, client *api.Client, t *testing.T) *api.CreateUserResponse {
-// 	t.Helper()
-
-// 	resp, err := client.CreateUser(context.Background(), &api.CreateUserRequest{
-// 		Email:    email,
-// 		Password: "hello1234",
-// 	})
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	return resp
-// }
-
-// func TestLogin(t *testing.T) {
-// 	email := "login_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_login_test.json")
-// 	user := initUser(email, client, t)
-
-// 	resp, err := client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	if resp.Email != user.Email {
-// 		t.Errorf("incorrect email: expected %s, got %s\n", user.Email, resp.Email)
-// 	}
-// }
-
-// func TestLogout(t *testing.T) {
-// 	email := "logout_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_logout_test.json")
-// 	user := initUser(email, client, t)
-
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-
-// 	err := client.Logout(context.Background())
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	// try to get the user and ensure 403
-// 	_, err = client.AuthCheck(context.Background())
-
-// 	if err != nil && !strings.Contains(err.Error(), "403") {
-// 		t.Fatalf("%v\n", err)
-// 	}
-// }
-
-// func TestAuthCheck(t *testing.T) {
-// 	email := "auth_check_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_auth_check_test.json")
-// 	user := initUser(email, client, t)
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-
-// 	resp, err := client.AuthCheck(context.Background())
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	if resp.Email != user.Email {
-// 		t.Errorf("incorrect email: expected %s, got %s\n", user.Email, resp.Email)
-// 	}
-// }
-
-// func TestGetUser(t *testing.T) {
-// 	email := "get_user_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_get_user_test.json")
-// 	user := initUser(email, client, t)
-
-// 	resp, err := client.GetUser(context.Background(), user.ID)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	if resp.Email != user.Email {
-// 		t.Errorf("incorrect email: expected %s, got %s\n", user.Email, resp.Email)
-// 	}
-// }
-
-// func TestListUserProjects(t *testing.T) {
-// 	email := "list_user_projects@example.com"
-// 	client := api.NewClient(baseURL, "cookie_list_user_projects.json")
-// 	user := initUser(email, client, t)
-// 	client.Login(context.Background(), &api.LoginRequest{
-// 		Email:    user.Email,
-// 		Password: "hello1234",
-// 	})
-// 	project := initProject("project-test", client, t)
-
-// 	projects, err := client.ListUserProjects(context.Background(), user.ID)
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	if len(projects) != 1 {
-// 		t.Fatalf("length of projects is not 1")
-// 	}
-
-// 	resp := projects[0]
-
-// 	// make sure user is admin and project name is correct
-// 	if resp.Name != project.Name {
-// 		t.Errorf("project name incorrect: expected %s, got %s\n", project.Name, resp.Name)
-// 	}
-
-// 	if len(resp.Roles) != 1 {
-// 		t.Fatalf("project role length is not 1")
-// 	}
-
-// 	if resp.Roles[0].Kind != models.RoleAdmin {
-// 		t.Errorf("project role kind is incorrect: expected %s, got %s\n", models.RoleAdmin, resp.Roles[0].Kind)
-// 	}
-
-// 	if resp.Roles[0].UserID != user.ID {
-// 		t.Errorf("project role user_id is incorrect: expected %d, got %d\n", user.ID, resp.Roles[0].UserID)
-// 	}
-// }
-
-// func TestDeleteUser(t *testing.T) {
-// 	email := "delete_user_test@example.com"
-// 	client := api.NewClient(baseURL, "cookie_delete_user_test.json")
-// 	user := initUser(email, client, t)
-
-// 	err := client.DeleteUser(context.Background(), user.ID, &api.DeleteUserRequest{
-// 		Password: "hello1234",
-// 	})
-
-// 	if err != nil {
-// 		t.Fatalf("%v\n", err)
-// 	}
-
-// 	_, err = client.GetUser(context.Background(), user.ID)
-
-// 	if err != nil && !strings.Contains(err.Error(), "could not find requested object") {
-// 		t.Fatalf("%v\n", err)
-// 	}
-// }

+ 1 - 1
api/server/handlers/cluster/create_candidate.go

@@ -45,7 +45,7 @@ func (c *CreateClusterCandidateHandler) ServeHTTP(w http.ResponseWriter, r *http
 		return
 	}
 
-	res := make([]*types.ClusterCandidate, 0)
+	res := make(types.CreateClusterCandidateResponse, 0)
 
 	for _, cc := range ccs {
 		// handle write to the database

+ 1 - 1
api/server/handlers/cluster/list.go

@@ -36,7 +36,7 @@ func (p *ClusterListHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	res := make([]*types.Cluster, len(clusters))
+	res := make(types.ListClusterResponse, len(clusters))
 
 	for i, cluster := range clusters {
 		res[i] = cluster.ToClusterType()

+ 1 - 1
api/server/handlers/cluster/list_candidates.go

@@ -34,7 +34,7 @@ func (c *ListClusterCandidatesHandler) ServeHTTP(w http.ResponseWriter, r *http.
 		return
 	}
 
-	res := make([]*types.ClusterCandidate, 0)
+	res := make(types.ListClusterCandidateResponse, 0)
 
 	for _, cc := range ccs {
 		res = append(res, cc.ToClusterCandidateType())

+ 8 - 0
api/server/handlers/release/create.go

@@ -56,6 +56,14 @@ func (c *CreateReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	if request.RepoURL == "" {
+		request.RepoURL = c.Config().ServerConf.DefaultApplicationHelmRepoURL
+	}
+
+	if request.TemplateVersion == "latest" {
+		request.TemplateVersion = ""
+	}
+
 	chart, err := loader.LoadChartPublic(request.RepoURL, request.TemplateName, request.TemplateVersion)
 
 	if err != nil {

+ 1 - 1
api/server/handlers/release/update_steps.go

@@ -81,7 +81,7 @@ func (c *UpdateReleaseStepsHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
 
 	if err := c.Repo().Event().AppendEvent(container, &models.SubEvent{
 		EventContainerID: container.ID,
-		EventID:          request.Event.ID,
+		EventID:          request.Event.EventID,
 		Name:             request.Event.Name,
 		Index:            request.Event.Index,
 		Status:           request.Event.Status,

+ 6 - 0
api/types/cluster.go

@@ -241,3 +241,9 @@ type CreateClusterCandidateRequest struct {
 type UpdateClusterRequest struct {
 	Name string `json:"name" form:"required"`
 }
+
+type ListClusterResponse []*Cluster
+
+type CreateClusterCandidateResponse []*ClusterCandidate
+
+type ListClusterCandidateResponse []*ClusterCandidate

+ 34 - 0
api/types/registry.go

@@ -25,6 +25,36 @@ type Registry struct {
 	InfraID uint `json:"infra_id"`
 }
 
+// Repository is a collection of images
+type RegistryRepository struct {
+	// Name of the repository
+	Name string `json:"name"`
+
+	// When the repository was created
+	CreatedAt time.Time `json:"created_at,omitempty"`
+
+	// The URI of the repository
+	URI string `json:"uri"`
+}
+
+// Image is a Docker image type
+type Image struct {
+	// The sha256 digest of the image manifest.
+	Digest string `json:"digest"`
+
+	// The tag used for the image.
+	Tag string `json:"tag"`
+
+	// The image manifest associated with the image.
+	Manifest string `json:"manifest"`
+
+	// The name of the repository associated with the image.
+	RepositoryName string `json:"repository_name"`
+
+	// When the image was pushed
+	PushedAt *time.Time `json:"pushed_at"`
+}
+
 type RegistryService string
 
 const (
@@ -71,3 +101,7 @@ type GetRegistryECRTokenRequest struct {
 type GetRegistryDOCRTokenRequest struct {
 	ServerURL string `schema:"server_url"`
 }
+
+type ListRegistryRepositoryResponse []*RegistryRepository
+
+type ListImageResponse []*Image

+ 5 - 7
api/types/release.go

@@ -2,6 +2,7 @@ package types
 
 import (
 	"helm.sh/helm/v3/pkg/release"
+	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -17,6 +18,7 @@ type PorterRelease struct {
 	ID              uint             `json:"id"`
 	WebhookToken    string           `json:"webhook_token"`
 	GitActionConfig *GitActionConfig `json:"git_action_config,omitempty"`
+	ImageRepoURI    string           `json:"image_repo_uri"`
 }
 
 type GetReleaseResponse Release
@@ -100,13 +102,7 @@ const (
 )
 
 type UpdateReleaseStepsRequest struct {
-	Event struct {
-		ID     string      `json:"event_id" form:"required"`
-		Name   string      `json:"name" form:"required"`
-		Index  int64       `json:"index" form:"required"`
-		Status EventStatus `json:"status" form:"required"`
-		Info   string      `json:"info" form:"required"`
-	} `json:"event" form:"required"`
+	Event SubEvent `json:"event" form:"required"`
 }
 
 type NotificationConfig struct {
@@ -127,3 +123,5 @@ type DNSRecord struct {
 
 	ClusterID uint `json:"cluster_id"`
 }
+
+type GetReleaseAllPodsResponse []v1.Pod

+ 2 - 0
api/types/user.go

@@ -58,3 +58,5 @@ type FinalizeResetUserPasswordRequest struct {
 
 	NewPassword string `json:"new_password" form:"required,max=255"`
 }
+
+type ListUserProjectsResponse []*Project

+ 15 - 10
cli/cmd/auth.go

@@ -8,6 +8,7 @@ import (
 	"github.com/fatih/color"
 
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	loginBrowser "github.com/porter-dev/porter/cli/cmd/login"
 	"github.com/porter-dev/porter/cli/cmd/utils"
 	"github.com/spf13/cobra"
@@ -25,7 +26,7 @@ var loginCmd = &cobra.Command{
 		err := login()
 
 		if err != nil {
-			color.Red("Error logging in:", err.Error())
+			color.Red("Error logging in: %s\n", err.Error())
 			os.Exit(1)
 		}
 	},
@@ -38,7 +39,7 @@ var registerCmd = &cobra.Command{
 		err := register()
 
 		if err != nil {
-			color.Red("Error registering:", err.Error())
+			color.Red("Error registering: %s\n", err.Error())
 			os.Exit(1)
 		}
 	},
@@ -76,9 +77,9 @@ func init() {
 func login() error {
 	client := api.NewClientWithToken(config.Host+"/api", config.Token)
 
-	user, _ := client.AuthCheck(context.Background())
+	user, err := client.AuthCheck(context.Background())
 
-	if user != nil {
+	if err == nil {
 		// set the token if the user calls login with the --token flag or the PORTER_TOKEN env
 		if config.Token != "" {
 			config.SetToken(config.Token)
@@ -143,7 +144,7 @@ func login() error {
 
 	user, err = client.AuthCheck(context.Background())
 
-	if user == nil {
+	if err != nil {
 		color.Red("Invalid token.")
 		return err
 	}
@@ -155,12 +156,14 @@ func login() error {
 
 func setProjectForUser(client *api.Client, userID uint) error {
 	// get a list of projects, and set the current project
-	projects, err := client.ListUserProjects(context.Background(), userID)
+	resp, err := client.ListUserProjects(context.Background())
 
 	if err != nil {
 		return err
 	}
 
+	projects := *resp
+
 	if len(projects) > 0 {
 		config.SetProject(projects[0].ID)
 
@@ -193,7 +196,7 @@ func loginManual() error {
 		return err
 	}
 
-	_user, err := client.Login(context.Background(), &api.LoginRequest{
+	_, err = client.Login(context.Background(), &types.LoginUserRequest{
 		Email:    username,
 		Password: pw,
 	})
@@ -208,12 +211,14 @@ func loginManual() error {
 	color.New(color.FgGreen).Println("Successfully logged in!")
 
 	// get a list of projects, and set the current project
-	projects, err := client.ListUserProjects(context.Background(), _user.ID)
+	resp, err := client.ListUserProjects(context.Background())
 
 	if err != nil {
 		return err
 	}
 
+	projects := *resp
+
 	if len(projects) > 0 {
 		config.SetProject(projects[0].ID)
 
@@ -244,7 +249,7 @@ func register() error {
 
 	client := GetAPIClient(config)
 
-	resp, err := client.CreateUser(context.Background(), &api.CreateUserRequest{
+	resp, err := client.CreateUser(context.Background(), &types.CreateUserRequest{
 		Email:    username,
 		Password: pw,
 	})
@@ -258,7 +263,7 @@ func register() error {
 	return nil
 }
 
-func logout(user *api.AuthCheckResponse, client *api.Client, args []string) error {
+func logout(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	err := client.Logout(context.Background())
 
 	if err != nil {

+ 7 - 4
cli/cmd/cluster.go

@@ -10,6 +10,7 @@ import (
 
 	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/utils"
 	"github.com/spf13/cobra"
 )
@@ -75,13 +76,15 @@ func init() {
 	clusterNamespaceCmd.AddCommand(clusterNamespaceListCmd)
 }
 
-func listClusters(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	clusters, err := client.ListProjectClusters(context.Background(), config.Project)
+func listClusters(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	resp, err := client.ListProjectClusters(context.Background(), config.Project)
 
 	if err != nil {
 		return err
 	}
 
+	clusters := *resp
+
 	w := new(tabwriter.Writer)
 	w.Init(os.Stdout, 3, 8, 0, '\t', tabwriter.AlignRight)
 
@@ -102,7 +105,7 @@ func listClusters(user *api.AuthCheckResponse, client *api.Client, args []string
 	return nil
 }
 
-func deleteCluster(user *api.AuthCheckResponse, client *api.Client, args []string) error {
+func deleteCluster(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	userResp, err := utils.PromptPlaintext(
 		fmt.Sprintf(
 			`Are you sure you'd like to delete the cluster with id %s? %s `,
@@ -134,7 +137,7 @@ func deleteCluster(user *api.AuthCheckResponse, client *api.Client, args []strin
 	return nil
 }
 
-func listNamespaces(user *api.AuthCheckResponse, client *api.Client, args []string) error {
+func listNamespaces(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	pID := config.Project
 
 	// get the service account based on the cluster id

+ 7 - 33
cli/cmd/connect.go

@@ -4,6 +4,7 @@ import (
 	"os"
 
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/connect"
 	"github.com/spf13/cobra"
 )
@@ -91,19 +92,6 @@ var connectDOCRCmd = &cobra.Command{
 	},
 }
 
-var connectHRCmd = &cobra.Command{
-	Use:     "helmrepo",
-	Aliases: []string{"helm", "helmrepos"},
-	Short:   "Adds a Helm repository to a project",
-	Run: func(cmd *cobra.Command, args []string) {
-		err := checkLoginAndRun(args, runConnectHelmRepoBasic)
-
-		if err != nil {
-			os.Exit(1)
-		}
-	},
-}
-
 func init() {
 	rootCmd.AddCommand(connectCmd)
 
@@ -128,10 +116,9 @@ func init() {
 	connectCmd.AddCommand(connectDockerhubCmd)
 	connectCmd.AddCommand(connectGCRCmd)
 	connectCmd.AddCommand(connectDOCRCmd)
-	connectCmd.AddCommand(connectHRCmd)
 }
 
-func runConnectKubeconfig(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
+func runConnectKubeconfig(_ *types.GetAuthenticatedUserResponse, client *api.Client, _ []string) error {
 	isLocal := false
 
 	if config.Driver == "local" {
@@ -153,7 +140,7 @@ func runConnectKubeconfig(_ *api.AuthCheckResponse, client *api.Client, _ []stri
 	return config.SetCluster(id)
 }
 
-func runConnectECR(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
+func runConnectECR(_ *types.GetAuthenticatedUserResponse, client *api.Client, _ []string) error {
 	regID, err := connect.ECR(
 		client,
 		config.Project,
@@ -166,7 +153,7 @@ func runConnectECR(_ *api.AuthCheckResponse, client *api.Client, _ []string) err
 	return config.SetRegistry(regID)
 }
 
-func runConnectGCR(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
+func runConnectGCR(_ *types.GetAuthenticatedUserResponse, client *api.Client, _ []string) error {
 	regID, err := connect.GCR(
 		client,
 		config.Project,
@@ -179,7 +166,7 @@ func runConnectGCR(_ *api.AuthCheckResponse, client *api.Client, _ []string) err
 	return config.SetRegistry(regID)
 }
 
-func runConnectDOCR(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
+func runConnectDOCR(_ *types.GetAuthenticatedUserResponse, client *api.Client, _ []string) error {
 	regID, err := connect.DOCR(
 		client,
 		config.Project,
@@ -192,7 +179,7 @@ func runConnectDOCR(_ *api.AuthCheckResponse, client *api.Client, _ []string) er
 	return config.SetRegistry(regID)
 }
 
-func runConnectDockerhub(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
+func runConnectDockerhub(_ *types.GetAuthenticatedUserResponse, client *api.Client, _ []string) error {
 	regID, err := connect.Dockerhub(
 		client,
 		config.Project,
@@ -205,7 +192,7 @@ func runConnectDockerhub(_ *api.AuthCheckResponse, client *api.Client, _ []strin
 	return config.SetRegistry(regID)
 }
 
-func runConnectRegistry(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
+func runConnectRegistry(_ *types.GetAuthenticatedUserResponse, client *api.Client, _ []string) error {
 	regID, err := connect.Registry(
 		client,
 		config.Project,
@@ -217,16 +204,3 @@ func runConnectRegistry(_ *api.AuthCheckResponse, client *api.Client, _ []string
 
 	return config.SetRegistry(regID)
 }
-
-func runConnectHelmRepoBasic(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
-	hrID, err := connect.Helm(
-		client,
-		config.Project,
-	)
-
-	if err != nil {
-		return err
-	}
-
-	return config.SetHelmRepo(hrID)
-}

+ 3 - 2
cli/cmd/connect/dockerhub.go

@@ -3,6 +3,7 @@ package connect
 import (
 	"context"
 	"fmt"
+
 	"github.com/porter-dev/porter/api/types"
 
 	"github.com/fatih/color"
@@ -57,10 +58,10 @@ Token:`)
 
 	color.New(color.FgGreen).Printf("created basic auth integration with id %d\n", integration.ID)
 
-	reg, err := client.CreatePrivateRegistry(
+	reg, err := client.CreateRegistry(
 		context.Background(),
 		projectID,
-		&api.CreatePrivateRegistryRequest{
+		&types.CreateRegistryRequest{
 			URL:                fmt.Sprintf("index.docker.io/%s", repoName),
 			Name:               repoName,
 			BasicIntegrationID: integration.ID,

+ 8 - 4
cli/cmd/connect/docr.go

@@ -22,12 +22,14 @@ func DOCR(
 	}
 
 	// list oauth integrations and make sure DO exists
-	oauthInts, err := client.ListOAuthIntegrations(context.TODO(), projectID)
+	resp, err := client.ListOAuthIntegrations(context.TODO(), projectID)
 
 	if err != nil {
 		return 0, err
 	}
 
+	oauthInts := *resp
+
 	linkedDO := false
 	var doAuth *types.OAuthIntegration
 
@@ -63,10 +65,10 @@ Registry URL: `))
 		return 0, err
 	}
 
-	reg, err := client.CreateDOCR(
+	reg, err := client.CreateRegistry(
 		context.Background(),
 		projectID,
-		&api.CreateDOCRRequest{
+		&types.CreateRegistryRequest{
 			Name:            regName,
 			DOIntegrationID: doAuth.ID,
 			URL:             regURL,
@@ -85,7 +87,7 @@ func triggerDigitalOceanOAuth(client *api.Client, projectID uint) (*types.OAuthI
 	utils.OpenBrowser(oauthURL)
 
 	for {
-		oauthInts, err := client.ListOAuthIntegrations(context.TODO(), projectID)
+		resp, err := client.ListOAuthIntegrations(context.TODO(), projectID)
 
 		if err != nil {
 			return doAuth, err
@@ -93,6 +95,8 @@ func triggerDigitalOceanOAuth(client *api.Client, projectID uint) (*types.OAuthI
 
 		linkedDO := false
 
+		oauthInts := *resp
+
 		// iterate through oauth integrations to find do
 		for _, oauthInt := range oauthInts {
 			if oauthInt.Client == types.OAuthDigitalOcean {

+ 4 - 3
cli/cmd/connect/ecr.go

@@ -3,10 +3,11 @@ package connect
 import (
 	"context"
 	"fmt"
-	"github.com/porter-dev/porter/api/types"
 	"strings"
 	"time"
 
+	"github.com/porter-dev/porter/api/types"
+
 	"github.com/aws/aws-sdk-go/service/ecr"
 	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
@@ -133,10 +134,10 @@ func linkRegistry(client *api.Client, projectID uint, intID uint) (uint, error)
 		return 0, err
 	}
 
-	reg, err := client.CreateECR(
+	reg, err := client.CreateRegistry(
 		context.Background(),
 		projectID,
-		&api.CreateECRRequest{
+		&types.CreateRegistryRequest{
 			Name:             regName,
 			AWSIntegrationID: intID,
 		},

+ 2 - 2
cli/cmd/connect/gcr.go

@@ -69,10 +69,10 @@ Registry URL: `))
 			return 0, err
 		}
 
-		reg, err := client.CreateGCR(
+		reg, err := client.CreateRegistry(
 			context.Background(),
 			projectID,
-			&api.CreateGCRRequest{
+			&types.CreateRegistryRequest{
 				Name:             regName,
 				GCPIntegrationID: integration.ID,
 				URL:              regURL,

+ 0 - 98
cli/cmd/connect/helm.go

@@ -1,98 +0,0 @@
-package connect
-
-import (
-	"context"
-	"fmt"
-	"github.com/porter-dev/porter/api/types"
-	"strings"
-
-	"github.com/fatih/color"
-	api "github.com/porter-dev/porter/api/client"
-	"github.com/porter-dev/porter/cli/cmd/utils"
-)
-
-// Helm connects a Helm repository using HTTP basic authentication
-func Helm(
-	client *api.Client,
-	projectID uint,
-) (uint, error) {
-	// if project ID is 0, ask the user to set the project ID or create a project
-	if projectID == 0 {
-		return 0, fmt.Errorf("no project set, please run porter project set [id]")
-	}
-
-	// query for helm repo name
-	helmName, err := utils.PromptPlaintext(fmt.Sprintf(`Give this Helm repository a name: `))
-
-	if err != nil {
-		return 0, err
-	}
-
-	repoURL, err := utils.PromptPlaintext(fmt.Sprintf(`Provide the Helm repository URL: `))
-
-	if err != nil {
-		return 0, err
-	}
-
-	userResp, err := utils.PromptPlaintext(
-		fmt.Sprintf(`Does this endpoint require a username/password to authenticate? %s `,
-			color.New(color.FgCyan).Sprintf("[y/n]"),
-		),
-	)
-
-	if err != nil {
-		return 0, err
-	}
-
-	username := ""
-	password := ""
-
-	if userResp := strings.ToLower(userResp); userResp == "y" || userResp == "yes" {
-		username, err = utils.PromptPlaintext(fmt.Sprintf(`Username: `))
-
-		if err != nil {
-			return 0, err
-		}
-
-		password, err = utils.PromptPasswordWithConfirmation()
-
-		if err != nil {
-			return 0, err
-		}
-	}
-
-	// create the basic auth integration
-	integration, err := client.CreateBasicAuthIntegration(
-		context.Background(),
-		projectID,
-		&types.CreateBasicRequest{
-			Username: username,
-			Password: password,
-		},
-	)
-
-	if err != nil {
-		return 0, err
-	}
-
-	color.New(color.FgGreen).Printf("created basic auth integration with id %d\n", integration.ID)
-
-	// create the helm repo
-	hr, err := client.CreateHelmRepo(
-		context.Background(),
-		projectID,
-		&api.CreateHelmRepoRequest{
-			Name:               helmName,
-			RepoURL:            repoURL,
-			BasicIntegrationID: integration.ID,
-		},
-	)
-
-	if err != nil {
-		return 0, err
-	}
-
-	color.New(color.FgGreen).Printf("created helm repo with id %d and name %s\n", hr.ID, hr.Name)
-
-	return hr.ID, nil
-}

+ 27 - 27
cli/cmd/connect/kubeconfig.go

@@ -16,7 +16,7 @@ import (
 	"github.com/porter-dev/porter/internal/kubernetes/local"
 
 	api "github.com/porter-dev/porter/api/client"
-	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/api/types"
 )
 
 // Kubeconfig creates a service account for a project by parsing the local
@@ -41,10 +41,10 @@ func Kubeconfig(
 	}
 
 	// send kubeconfig to client
-	ccs, err := client.CreateProjectCandidates(
+	resp, err := client.CreateProjectCandidates(
 		context.Background(),
 		projectID,
-		&api.CreateProjectCandidatesRequest{
+		&types.CreateClusterCandidateRequest{
 			Kubeconfig: string(rawBytes),
 			IsLocal:    isLocal,
 		},
@@ -54,17 +54,19 @@ func Kubeconfig(
 		return 0, err
 	}
 
+	ccs := *resp
+
 	var lastClusterID uint
 
 	for _, cc := range ccs {
-		var cluster *models.ClusterExternal
+		var cluster *types.Cluster
 
 		if len(cc.Resolvers) > 0 {
-			allResolver := &models.ClusterResolverAll{}
+			allResolver := &types.ClusterResolverAll{}
 
 			for _, resolver := range cc.Resolvers {
 				switch resolver.Name {
-				case models.ClusterCAData:
+				case types.ClusterCAData:
 					absKubeconfigPath, err := local.ResolveKubeconfigPath(kubeconfigPath)
 
 					if err != nil {
@@ -85,13 +87,13 @@ func Kubeconfig(
 					if err != nil {
 						return 0, err
 					}
-				case models.ClusterLocalhost:
+				case types.ClusterLocalhost:
 					err := resolveLocalhostAction(allResolver)
 
 					if err != nil {
 						return 0, err
 					}
-				case models.ClientCertData:
+				case types.ClientCertData:
 					absKubeconfigPath, err := local.ResolveKubeconfigPath(kubeconfigPath)
 
 					if err != nil {
@@ -112,7 +114,7 @@ func Kubeconfig(
 					if err != nil {
 						return 0, err
 					}
-				case models.ClientKeyData:
+				case types.ClientKeyData:
 					absKubeconfigPath, err := local.ResolveKubeconfigPath(kubeconfigPath)
 
 					if err != nil {
@@ -133,7 +135,7 @@ func Kubeconfig(
 					if err != nil {
 						return 0, err
 					}
-				case models.OIDCIssuerData:
+				case types.OIDCIssuerData:
 					absKubeconfigPath, err := local.ResolveKubeconfigPath(kubeconfigPath)
 
 					if err != nil {
@@ -154,7 +156,7 @@ func Kubeconfig(
 					if err != nil {
 						return 0, err
 					}
-				case models.TokenData:
+				case types.TokenData:
 					absKubeconfigPath, err := local.ResolveKubeconfigPath(kubeconfigPath)
 
 					if err != nil {
@@ -175,7 +177,7 @@ func Kubeconfig(
 					if err != nil {
 						return 0, err
 					}
-				case models.GCPKeyData:
+				case types.GCPKeyData:
 					err := resolveGCPKeyAction(
 						cc.Server,
 						cc.Name,
@@ -185,7 +187,7 @@ func Kubeconfig(
 					if err != nil {
 						return 0, err
 					}
-				case models.AWSData:
+				case types.AWSData:
 					err := resolveAWSAction(
 						cc.Server,
 						cc.Name,
@@ -212,7 +214,7 @@ func Kubeconfig(
 				return 0, err
 			}
 
-			clExt := models.ClusterExternal(*resp)
+			clExt := types.Cluster(*resp)
 
 			cluster = &clExt
 		} else {
@@ -226,9 +228,7 @@ func Kubeconfig(
 				return 0, err
 			}
 
-			clExt := models.ClusterExternal(*resp)
-
-			cluster = &clExt
+			cluster = resp.Cluster
 		}
 
 		color.New(color.FgGreen).Printf("created cluster %s with id %d\n", cluster.Name, cluster.ID)
@@ -241,7 +241,7 @@ func Kubeconfig(
 // resolves a cluster ca data action
 func resolveClusterCAAction(
 	filename string,
-	resolver *models.ClusterResolverAll,
+	resolver *types.ClusterResolverAll,
 ) error {
 	fileBytes, err := ioutil.ReadFile(filename)
 
@@ -255,7 +255,7 @@ func resolveClusterCAAction(
 }
 
 func resolveLocalhostAction(
-	resolver *models.ClusterResolverAll,
+	resolver *types.ClusterResolverAll,
 ) error {
 	resolver.ClusterHostname = "host.docker.internal"
 
@@ -265,7 +265,7 @@ func resolveLocalhostAction(
 // resolves a client cert data action
 func resolveClientCertAction(
 	filename string,
-	resolver *models.ClusterResolverAll,
+	resolver *types.ClusterResolverAll,
 ) error {
 	fileBytes, err := ioutil.ReadFile(filename)
 
@@ -281,7 +281,7 @@ func resolveClientCertAction(
 // resolves a client key data action
 func resolveClientKeyAction(
 	filename string,
-	resolver *models.ClusterResolverAll,
+	resolver *types.ClusterResolverAll,
 ) error {
 	fileBytes, err := ioutil.ReadFile(filename)
 
@@ -297,7 +297,7 @@ func resolveClientKeyAction(
 // resolves an oidc issuer data action
 func resolveOIDCIssuerAction(
 	filename string,
-	resolver *models.ClusterResolverAll,
+	resolver *types.ClusterResolverAll,
 ) error {
 	fileBytes, err := ioutil.ReadFile(filename)
 
@@ -313,7 +313,7 @@ func resolveOIDCIssuerAction(
 // resolves a token data action
 func resolveTokenDataAction(
 	filename string,
-	resolver *models.ClusterResolverAll,
+	resolver *types.ClusterResolverAll,
 ) error {
 	fileBytes, err := ioutil.ReadFile(filename)
 
@@ -330,7 +330,7 @@ func resolveTokenDataAction(
 func resolveGCPKeyAction(
 	endpoint string,
 	clusterName string,
-	resolver *models.ClusterResolverAll,
+	resolver *types.ClusterResolverAll,
 ) error {
 	userResp, err := utils.PromptPlaintext(
 		fmt.Sprintf(
@@ -400,7 +400,7 @@ Would you like to proceed? %s `,
 func resolveGCPKeyActionManual(
 	endpoint string,
 	clusterName string,
-	resolver *models.ClusterResolverAll,
+	resolver *types.ClusterResolverAll,
 ) error {
 	keyFileLocation, err := utils.PromptPlaintext(fmt.Sprintf(`Please provide the full path to a service account key file.
 Key file location: `))
@@ -433,7 +433,7 @@ func resolveAWSAction(
 	awsClusterIDGuess string,
 	kubeconfigPath string,
 	contextName string,
-	resolver *models.ClusterResolverAll,
+	resolver *types.ClusterResolverAll,
 ) error {
 	userResp, err := utils.PromptPlaintext(
 		fmt.Sprintf(
@@ -480,7 +480,7 @@ func resolveAWSActionManual(
 	endpoint string,
 	clusterName string,
 	awsClusterIDGuess string,
-	resolver *models.ClusterResolverAll,
+	resolver *types.ClusterResolverAll,
 ) error {
 	// query to see if the AWS cluster ID guess is correct
 	var clusterID string

+ 3 - 2
cli/cmd/connect/registry.go

@@ -3,6 +3,7 @@ package connect
 import (
 	"context"
 	"fmt"
+
 	"github.com/porter-dev/porter/api/types"
 
 	"github.com/fatih/color"
@@ -57,10 +58,10 @@ Username: `))
 
 	color.New(color.FgGreen).Printf("created basic auth integration with id %d\n", integration.ID)
 
-	reg, err := client.CreatePrivateRegistry(
+	reg, err := client.CreateRegistry(
 		context.Background(),
 		projectID,
-		&api.CreatePrivateRegistryRequest{
+		&types.CreateRegistryRequest{
 			URL:                repoURL,
 			Name:               repoURL,
 			BasicIntegrationID: integration.ID,

+ 2 - 1
cli/cmd/create.go

@@ -8,6 +8,7 @@ import (
 
 	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/deploy"
 	"github.com/porter-dev/porter/cli/cmd/gitutils"
 	"github.com/spf13/cobra"
@@ -149,7 +150,7 @@ func init() {
 
 var supportedKinds = map[string]string{"web": "", "job": "", "worker": ""}
 
-func createFull(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+func createFull(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	// check the kind
 	if _, exists := supportedKinds[args[0]]; !exists {
 		return fmt.Errorf("%s is not a supported type: specify web, job, or worker", args[0])

+ 78 - 77
cli/cmd/deploy.go

@@ -6,6 +6,7 @@ import (
 
 	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/deploy"
 	"github.com/spf13/cobra"
 )
@@ -289,7 +290,7 @@ func init() {
 	updateCmd.AddCommand(updateConfigCmd)
 }
 
-func updateFull(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+func updateFull(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	color.New(color.FgGreen).Println("Deploying app:", app)
 
 	updateAgent, err := updateGetAgent(client)
@@ -319,7 +320,7 @@ func updateFull(resp *api.AuthCheckResponse, client *api.Client, args []string)
 	return nil
 }
 
-func updateGetEnv(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+func updateGetEnv(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	updateAgent, err := updateGetAgent(client)
 
 	if err != nil {
@@ -343,7 +344,7 @@ func updateGetEnv(resp *api.AuthCheckResponse, client *api.Client, args []string
 	return updateAgent.WriteBuildEnv(getEnvFileDest)
 }
 
-func updateBuild(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+func updateBuild(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	updateAgent, err := updateGetAgent(client)
 
 	if err != nil {
@@ -353,7 +354,7 @@ func updateBuild(resp *api.AuthCheckResponse, client *api.Client, args []string)
 	return updateBuildWithAgent(updateAgent)
 }
 
-func updatePush(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+func updatePush(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	updateAgent, err := updateGetAgent(client)
 
 	if err != nil {
@@ -363,7 +364,7 @@ func updatePush(resp *api.AuthCheckResponse, client *api.Client, args []string)
 	return updatePushWithAgent(updateAgent)
 }
 
-func updateUpgrade(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+func updateUpgrade(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	updateAgent, err := updateGetAgent(client)
 
 	if err != nil {
@@ -401,12 +402,12 @@ func updateBuildWithAgent(updateAgent *deploy.DeployAgent) error {
 	color.New(color.FgGreen).Println("Building docker image for", app)
 
 	if stream {
-		updateAgent.StreamEvent(api.Event{
-			ID:     "build",
-			Name:   "Build",
-			Index:  100,
-			Status: api.EventStatusInProgress,
-			Info:   "",
+		updateAgent.StreamEvent(types.SubEvent{
+			EventID: "build",
+			Name:    "Build",
+			Index:   100,
+			Status:  types.EventStatusInProgress,
+			Info:    "",
 		})
 	}
 
@@ -415,12 +416,12 @@ func updateBuildWithAgent(updateAgent *deploy.DeployAgent) error {
 	if err != nil {
 		if stream {
 			// another concern: is it safe to ignore the error here?
-			updateAgent.StreamEvent(api.Event{
-				ID:     "build",
-				Name:   "Build",
-				Index:  110,
-				Status: api.EventStatusFailed,
-				Info:   err.Error(),
+			updateAgent.StreamEvent(types.SubEvent{
+				EventID: "build",
+				Name:    "Build",
+				Index:   110,
+				Status:  types.EventStatusFailed,
+				Info:    err.Error(),
 			})
 		}
 		return err
@@ -431,12 +432,12 @@ func updateBuildWithAgent(updateAgent *deploy.DeployAgent) error {
 
 	if err != nil {
 		if stream {
-			updateAgent.StreamEvent(api.Event{
-				ID:     "build",
-				Name:   "Build",
-				Index:  120,
-				Status: api.EventStatusFailed,
-				Info:   err.Error(),
+			updateAgent.StreamEvent(types.SubEvent{
+				EventID: "build",
+				Name:    "Build",
+				Index:   120,
+				Status:  types.EventStatusFailed,
+				Info:    err.Error(),
 			})
 		}
 		return err
@@ -444,24 +445,24 @@ func updateBuildWithAgent(updateAgent *deploy.DeployAgent) error {
 
 	if err := updateAgent.Build(); err != nil {
 		if stream {
-			updateAgent.StreamEvent(api.Event{
-				ID:     "build",
-				Name:   "Build",
-				Index:  130,
-				Status: api.EventStatusFailed,
-				Info:   err.Error(),
+			updateAgent.StreamEvent(types.SubEvent{
+				EventID: "build",
+				Name:    "Build",
+				Index:   130,
+				Status:  types.EventStatusFailed,
+				Info:    err.Error(),
 			})
 		}
 		return err
 	}
 
 	if stream {
-		updateAgent.StreamEvent(api.Event{
-			ID:     "build",
-			Name:   "Build",
-			Index:  140,
-			Status: api.EventStatusSuccess,
-			Info:   "",
+		updateAgent.StreamEvent(types.SubEvent{
+			EventID: "build",
+			Name:    "Build",
+			Index:   140,
+			Status:  types.EventStatusSuccess,
+			Info:    "",
 		})
 	}
 
@@ -473,35 +474,35 @@ func updatePushWithAgent(updateAgent *deploy.DeployAgent) error {
 	color.New(color.FgGreen).Println("Pushing new image for", app)
 
 	if stream {
-		updateAgent.StreamEvent(api.Event{
-			ID:     "push",
-			Name:   "Push",
-			Index:  200,
-			Status: api.EventStatusInProgress,
-			Info:   "",
+		updateAgent.StreamEvent(types.SubEvent{
+			EventID: "push",
+			Name:    "Push",
+			Index:   200,
+			Status:  types.EventStatusInProgress,
+			Info:    "",
 		})
 	}
 
 	if err := updateAgent.Push(); err != nil {
 		if stream {
-			updateAgent.StreamEvent(api.Event{
-				ID:     "push",
-				Name:   "Push",
-				Index:  210,
-				Status: api.EventStatusFailed,
-				Info:   err.Error(),
+			updateAgent.StreamEvent(types.SubEvent{
+				EventID: "push",
+				Name:    "Push",
+				Index:   210,
+				Status:  types.EventStatusFailed,
+				Info:    err.Error(),
 			})
 		}
 		return err
 	}
 
 	if stream {
-		updateAgent.StreamEvent(api.Event{
-			ID:     "push",
-			Name:   "Push",
-			Index:  220,
-			Status: api.EventStatusSuccess,
-			Info:   "",
+		updateAgent.StreamEvent(types.SubEvent{
+			EventID: "push",
+			Name:    "Push",
+			Index:   220,
+			Status:  types.EventStatusSuccess,
+			Info:    "",
 		})
 	}
 
@@ -513,12 +514,12 @@ func updateUpgradeWithAgent(updateAgent *deploy.DeployAgent) error {
 	color.New(color.FgGreen).Println("Upgrading configuration for", app)
 
 	if stream {
-		updateAgent.StreamEvent(api.Event{
-			ID:     "upgrade",
-			Name:   "Upgrade",
-			Index:  300,
-			Status: api.EventStatusInProgress,
-			Info:   "",
+		updateAgent.StreamEvent(types.SubEvent{
+			EventID: "upgrade",
+			Name:    "Upgrade",
+			Index:   300,
+			Status:  types.EventStatusInProgress,
+			Info:    "",
 		})
 	}
 
@@ -527,12 +528,12 @@ func updateUpgradeWithAgent(updateAgent *deploy.DeployAgent) error {
 
 	if err != nil {
 		if stream {
-			updateAgent.StreamEvent(api.Event{
-				ID:     "upgrade",
-				Name:   "Upgrade",
-				Index:  310,
-				Status: api.EventStatusFailed,
-				Info:   err.Error(),
+			updateAgent.StreamEvent(types.SubEvent{
+				EventID: "upgrade",
+				Name:    "Upgrade",
+				Index:   310,
+				Status:  types.EventStatusFailed,
+				Info:    err.Error(),
 			})
 		}
 		return err
@@ -542,24 +543,24 @@ func updateUpgradeWithAgent(updateAgent *deploy.DeployAgent) error {
 
 	if err != nil {
 		if stream {
-			updateAgent.StreamEvent(api.Event{
-				ID:     "upgrade",
-				Name:   "Upgrade",
-				Index:  320,
-				Status: api.EventStatusFailed,
-				Info:   err.Error(),
+			updateAgent.StreamEvent(types.SubEvent{
+				EventID: "upgrade",
+				Name:    "Upgrade",
+				Index:   320,
+				Status:  types.EventStatusFailed,
+				Info:    err.Error(),
 			})
 		}
 		return err
 	}
 
 	if stream {
-		updateAgent.StreamEvent(api.Event{
-			ID:     "upgrade",
-			Name:   "Upgrade",
-			Index:  330,
-			Status: api.EventStatusSuccess,
-			Info:   "",
+		updateAgent.StreamEvent(types.SubEvent{
+			EventID: "upgrade",
+			Name:    "Upgrade",
+			Index:   330,
+			Status:  types.EventStatusSuccess,
+			Info:    "",
 		})
 	}
 

+ 58 - 51
cli/cmd/deploy/create.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/docker"
 	"github.com/porter-dev/porter/internal/templater/utils"
 )
@@ -44,7 +45,7 @@ func (c *CreateAgent) CreateFromGithub(
 	opts := c.CreateOpts
 
 	// get all linked github repos and find matching repo
-	gitRepos, err := c.Client.ListGitRepos(
+	resp, err := c.Client.ListGitInstallationIDs(
 		context.Background(),
 		c.CreateOpts.ProjectID,
 	)
@@ -53,23 +54,27 @@ func (c *CreateAgent) CreateFromGithub(
 		return "", err
 	}
 
-	var gitRepoMatch uint
+	gitInstallations := *resp
 
-	for _, gitRepo := range gitRepos {
+	var gitRepoMatch int64
+
+	for _, gitInstallationID := range gitInstallations {
 		// for each git repo, search for a matching username/owner
-		githubRepos, err := c.Client.ListGithubRepos(
+		resp, err := c.Client.ListGitRepos(
 			context.Background(),
 			c.CreateOpts.ProjectID,
-			gitRepo,
+			gitInstallationID,
 		)
 
 		if err != nil {
 			return "", err
 		}
 
+		githubRepos := *resp
+
 		for _, githubRepo := range githubRepos {
 			if githubRepo.FullName == ghOpts.Repo {
-				gitRepoMatch = gitRepo
+				gitRepoMatch = gitInstallationID
 				break
 			}
 		}
@@ -107,12 +112,6 @@ func (c *CreateAgent) CreateFromGithub(
 		return "", err
 	}
 
-	env, err := GetEnvFromConfig(mergedValues)
-
-	if err != nil {
-		env = map[string]string{}
-	}
-
 	subdomain, err := c.CreateSubdomainIfRequired(mergedValues)
 
 	if err != nil {
@@ -123,23 +122,24 @@ func (c *CreateAgent) CreateFromGithub(
 		context.Background(),
 		opts.ProjectID,
 		opts.ClusterID,
-		opts.Kind,
-		latestVersion,
-		&api.DeployTemplateRequest{
-			TemplateName: opts.Kind,
-			ImageURL:     imageURL,
-			FormValues:   mergedValues,
-			Namespace:    opts.Namespace,
-			Name:         opts.ReleaseName,
-			GitAction: &api.DeployTemplateGitAction{
-				GitRepo:        ghOpts.Repo,
-				GitBranch:      ghOpts.Branch,
-				ImageRepoURI:   imageURL,
-				DockerfilePath: opts.LocalDockerfile,
-				FolderPath:     ".",
-				GitRepoID:      gitRepoMatch,
-				BuildEnv:       env,
-				RegistryID:     regID,
+		opts.Namespace,
+		&types.CreateReleaseRequest{
+			CreateReleaseBaseRequest: &types.CreateReleaseBaseRequest{
+				TemplateName:    opts.Kind,
+				TemplateVersion: latestVersion,
+				Values:          mergedValues,
+				Name:            opts.ReleaseName,
+			},
+			ImageURL: imageURL,
+			GithubActionConfig: &types.CreateGitActionConfigRequest{
+				GitRepo:              ghOpts.Repo,
+				GitBranch:            ghOpts.Branch,
+				ImageRepoURI:         imageURL,
+				DockerfilePath:       opts.LocalDockerfile,
+				FolderPath:           ".",
+				GitRepoID:            uint(gitRepoMatch),
+				RegistryID:           regID,
+				ShouldCreateWorkflow: true,
 			},
 		},
 	)
@@ -190,14 +190,15 @@ func (c *CreateAgent) CreateFromRegistry(
 		context.Background(),
 		opts.ProjectID,
 		opts.ClusterID,
-		opts.Kind,
-		latestVersion,
-		&api.DeployTemplateRequest{
-			TemplateName: opts.Kind,
-			ImageURL:     imageSpl[0],
-			FormValues:   mergedValues,
-			Namespace:    opts.Namespace,
-			Name:         opts.ReleaseName,
+		opts.Namespace,
+		&types.CreateReleaseRequest{
+			CreateReleaseBaseRequest: &types.CreateReleaseBaseRequest{
+				TemplateName:    opts.Kind,
+				TemplateVersion: latestVersion,
+				Values:          mergedValues,
+				Name:            opts.ReleaseName,
+			},
+			ImageURL: imageSpl[0],
 		},
 	)
 
@@ -294,7 +295,7 @@ func (c *CreateAgent) CreateFromDocker(
 		context.Background(),
 		opts.ProjectID,
 		regID,
-		&api.CreateRepositoryRequest{
+		&types.CreateRegistryRepositoryRequest{
 			ImageRepoURI: imageURL,
 		},
 	)
@@ -319,14 +320,15 @@ func (c *CreateAgent) CreateFromDocker(
 		context.Background(),
 		opts.ProjectID,
 		opts.ClusterID,
-		opts.Kind,
-		latestVersion,
-		&api.DeployTemplateRequest{
-			TemplateName: opts.Kind,
-			ImageURL:     imageURL,
-			FormValues:   mergedValues,
-			Namespace:    opts.Namespace,
-			Name:         opts.ReleaseName,
+		opts.Namespace,
+		&types.CreateReleaseRequest{
+			CreateReleaseBaseRequest: &types.CreateReleaseBaseRequest{
+				TemplateName:    opts.Kind,
+				TemplateVersion: latestVersion,
+				Values:          mergedValues,
+				Name:            opts.ReleaseName,
+			},
+			ImageURL: imageURL,
 		},
 	)
 
@@ -352,11 +354,13 @@ func (c *CreateAgent) HasDefaultDockerfile(buildPath string) bool {
 func (c *CreateAgent) GetImageRepoURL(name, namespace string) (uint, string, error) {
 	// get all image registries linked to the project
 	// get the list of namespaces
-	registries, err := c.Client.ListRegistries(
+	resp, err := c.Client.ListRegistries(
 		context.Background(),
 		c.CreateOpts.ProjectID,
 	)
 
+	registries := *resp
+
 	if err != nil {
 		return 0, "", err
 	} else if len(registries) == 0 {
@@ -387,14 +391,17 @@ func (c *CreateAgent) GetImageRepoURL(name, namespace string) (uint, string, err
 // GetLatestTemplateVersion retrieves the latest template version for a specific
 // Porter template from the chart repository.
 func (c *CreateAgent) GetLatestTemplateVersion(templateName string) (string, error) {
-	templates, err := c.Client.ListTemplates(
+	resp, err := c.Client.ListTemplates(
 		context.Background(),
+		&types.ListTemplatesRequest{},
 	)
 
 	if err != nil {
 		return "", err
 	}
 
+	templates := *resp
+
 	var version string
 	// find the matching template name
 	for _, template := range templates {
@@ -418,6 +425,7 @@ func (c *CreateAgent) GetLatestTemplateDefaultValues(templateName, templateVersi
 		context.Background(),
 		templateName,
 		templateVersion,
+		&types.GetTemplateRequest{},
 	)
 
 	if err != nil {
@@ -471,9 +479,8 @@ func (c *CreateAgent) CreateSubdomainIfRequired(mergedValues map[string]interfac
 						context.Background(),
 						c.CreateOpts.ProjectID,
 						c.CreateOpts.ClusterID,
-						&api.CreateDNSRecordRequest{
-							ReleaseName: c.CreateOpts.ReleaseName,
-						},
+						c.CreateOpts.Namespace,
+						c.CreateOpts.ReleaseName,
 					)
 
 					if err != nil {

+ 28 - 10
cli/cmd/deploy/deploy.go

@@ -9,7 +9,8 @@ import (
 	"path/filepath"
 	"strings"
 
-	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/docker"
 	"github.com/porter-dev/porter/cli/cmd/github"
 	"github.com/porter-dev/porter/internal/templater/utils"
@@ -31,8 +32,8 @@ const (
 type DeployAgent struct {
 	App string
 
-	client         *api.Client
-	release        *api.GetReleaseResponse
+	client         *client.Client
+	release        *types.GetReleaseResponse
 	agent          *docker.Agent
 	opts           *DeployOpts
 	tag            string
@@ -52,7 +53,7 @@ type DeployOpts struct {
 
 // NewDeployAgent creates a new DeployAgent given a Porter API client, application
 // name, and DeployOpts.
-func NewDeployAgent(client *api.Client, app string, opts *DeployOpts) (*DeployAgent, error) {
+func NewDeployAgent(client *client.Client, app string, opts *DeployOpts) (*DeployAgent, error) {
 	deployAgent := &DeployAgent{
 		App:    app,
 		opts:   opts,
@@ -197,10 +198,20 @@ func (d *DeployAgent) Build() error {
 	var err error
 
 	if !d.opts.Local {
+		repoSplit := strings.Split(d.release.GitActionConfig.GitRepo, "/")
+
+		if len(repoSplit) != 2 {
+			return fmt.Errorf("invalid formatting of repo name")
+		}
+
 		zipResp, err := d.client.GetRepoZIPDownloadURL(
 			context.Background(),
 			d.opts.ProjectID,
-			d.release.GitActionConfig,
+			int64(d.release.GitActionConfig.GitRepoID),
+			"github",
+			repoSplit[0],
+			repoSplit[1],
+			d.release.GitActionConfig.GitBranch,
 		)
 
 		if err != nil {
@@ -313,10 +324,10 @@ func (d *DeployAgent) UpdateImageAndValues(overrideValues map[string]interface{}
 		context.Background(),
 		d.opts.ProjectID,
 		d.opts.ClusterID,
+		d.release.Namespace,
 		d.release.Name,
-		&api.UpgradeReleaseRequest{
-			Values:    string(bytes),
-			Namespace: d.release.Namespace,
+		&types.UpgradeReleaseRequest{
+			Values: string(bytes),
 		},
 	)
 }
@@ -447,8 +458,15 @@ func (d *DeployAgent) downloadRepoToDir(downloadURL string) (string, error) {
 	return res, nil
 }
 
-func (d *DeployAgent) StreamEvent(event api.Event) error {
-	return d.client.StreamEvent(event, d.opts.ProjectID, d.opts.ClusterID, d.release.Name, d.release.Namespace)
+func (d *DeployAgent) StreamEvent(event types.SubEvent) error {
+	return d.client.CreateEvent(
+		context.Background(),
+		d.opts.ProjectID, d.opts.ClusterID,
+		d.release.Namespace, d.release.Name,
+		&types.UpdateReleaseStepsRequest{
+			Event: event,
+		},
+	)
 }
 
 type NestedMapFieldNotFoundError struct {

+ 5 - 2
cli/cmd/docker.go

@@ -14,6 +14,7 @@ import (
 
 	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
+	ptypes "github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/github"
 	"github.com/spf13/cobra"
 
@@ -44,14 +45,14 @@ func init() {
 	dockerCmd.AddCommand(configureCmd)
 }
 
-func dockerConfig(user *api.AuthCheckResponse, client *api.Client, args []string) error {
+func dockerConfig(user *ptypes.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	pID := config.Project
 
 	// get all registries that should be added
 	regToAdd := make([]string, 0)
 
 	// get the list of namespaces
-	registries, err := client.ListRegistries(
+	resp, err := client.ListRegistries(
 		context.Background(),
 		pID,
 	)
@@ -60,6 +61,8 @@ func dockerConfig(user *api.AuthCheckResponse, client *api.Client, args []string
 		return err
 	}
 
+	registries := *resp
+
 	for _, registry := range registries {
 		if registry.URL != "" {
 			rURL := registry.URL

+ 6 - 3
cli/cmd/docker/auth.go

@@ -13,6 +13,7 @@ import (
 	"time"
 
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"k8s.io/client-go/util/homedir"
 )
 
@@ -72,7 +73,7 @@ func (a *AuthGetter) GetGCRCredentials(serverURL string, projID uint) (user stri
 		token = cachedEntry.AuthorizationToken
 	} else {
 		// get a token from the server
-		tokenResp, err := a.Client.GetGCRAuthorizationToken(context.Background(), projID, &api.GetGCRTokenRequest{
+		tokenResp, err := a.Client.GetGCRAuthorizationToken(context.Background(), projID, &types.GetRegistryGCRTokenRequest{
 			ServerURL: serverURL,
 		})
 
@@ -104,7 +105,7 @@ func (a *AuthGetter) GetDOCRCredentials(serverURL string, projID uint) (user str
 	} else {
 
 		// get a token from the server
-		tokenResp, err := a.Client.GetDOCRAuthorizationToken(context.Background(), projID, &api.GetDOCRTokenRequest{
+		tokenResp, err := a.Client.GetDOCRAuthorizationToken(context.Background(), projID, &types.GetRegistryGCRTokenRequest{
 			ServerURL: serverURL,
 		})
 
@@ -152,7 +153,9 @@ func (a *AuthGetter) GetECRCredentials(serverURL string, projID uint) (user stri
 		token = cachedEntry.AuthorizationToken
 	} else {
 		// get a token from the server
-		tokenResp, err := a.Client.GetECRAuthorizationToken(context.Background(), projID, matches[3])
+		tokenResp, err := a.Client.GetECRAuthorizationToken(context.Background(), projID, &types.GetRegistryECRTokenRequest{
+			Region: matches[3],
+		})
 
 		if err != nil {
 			return "", "", err

+ 3 - 2
cli/cmd/errors.go

@@ -7,12 +7,13 @@ import (
 
 	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 )
 
 var ErrNotLoggedIn error = errors.New("You are not logged in.")
 var ErrCannotConnect error = errors.New("Unable to connect to the Porter server.")
 
-func checkLoginAndRun(args []string, runner func(user *api.AuthCheckResponse, client *api.Client, args []string) error) error {
+func checkLoginAndRun(args []string, runner func(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error) error {
 	client := GetAPIClient(config)
 
 	user, err := client.AuthCheck(context.Background())
@@ -20,7 +21,7 @@ func checkLoginAndRun(args []string, runner func(user *api.AuthCheckResponse, cl
 	if err != nil {
 		red := color.New(color.FgRed)
 
-		if strings.Contains(err.Error(), "403") {
+		if strings.Contains(err.Error(), "Forbidden") {
 			red.Print("You are not logged in. Log in using \"porter auth login\"\n")
 			return ErrNotLoggedIn
 		} else if strings.Contains(err.Error(), "connection refused") {

+ 0 - 124
cli/cmd/helm_repo.go

@@ -1,124 +0,0 @@
-package cmd
-
-import (
-	"context"
-	"fmt"
-	"os"
-	"strings"
-	"text/tabwriter"
-
-	"github.com/fatih/color"
-	api "github.com/porter-dev/porter/api/client"
-	"github.com/spf13/cobra"
-)
-
-// helmRepoCmd represents the "porter helmrepo" base command when called
-// without any subcommands
-var helmRepoCmd = &cobra.Command{
-	Use:     "helmrepo",
-	Aliases: []string{"helm", "helmrepos"},
-	Short:   "Commands that read from a connected Helm repository",
-}
-
-var helmRepoListCmd = &cobra.Command{
-	Use:   "list",
-	Short: "Lists the Helm repositories linked to a project",
-	Run: func(cmd *cobra.Command, args []string) {
-		err := checkLoginAndRun(args, listHelmRepos)
-
-		if err != nil {
-			os.Exit(1)
-		}
-	},
-}
-
-var helmRepoChartCmd = &cobra.Command{
-	Use:     "chart",
-	Aliases: []string{"charts"},
-	Short:   "Commands for interacting with Helm repository charts",
-}
-
-var helmRepoChartListCmd = &cobra.Command{
-	Use:   "list",
-	Short: "Lists charts in the default Helm repository",
-	Run: func(cmd *cobra.Command, args []string) {
-		err := checkLoginAndRun(args, listHelmRepoCharts)
-
-		if err != nil {
-			os.Exit(1)
-		}
-	},
-}
-
-func init() {
-	rootCmd.AddCommand(helmRepoCmd)
-
-	helmRepoCmd.PersistentFlags().AddFlagSet(helmRepoFlagSet)
-
-	helmRepoCmd.AddCommand(helmRepoListCmd)
-	helmRepoCmd.AddCommand(helmRepoChartCmd)
-
-	helmRepoChartCmd.AddCommand(helmRepoChartListCmd)
-}
-
-func listHelmRepos(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	pID := config.Project
-
-	hrs, err := client.ListHelmRepos(
-		context.Background(),
-		pID,
-	)
-
-	if err != nil {
-		return err
-	}
-
-	w := new(tabwriter.Writer)
-	w.Init(os.Stdout, 3, 8, 0, '\t', tabwriter.AlignRight)
-
-	fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "ID", "NAME", "URL", "SERVICE")
-
-	currHelmID := config.HelmRepo
-
-	for _, hr := range hrs {
-		if currHelmID == hr.ID {
-			color.New(color.FgGreen).Fprintf(w, "%d\t%s\t%s\t%s (current helm repo)\n", hr.ID, hr.Name, hr.RepoURL, hr.Service)
-		} else {
-			fmt.Fprintf(w, "%d\t%s\t%s\t%s\n", hr.ID, hr.Name, hr.RepoURL, hr.Service)
-		}
-	}
-
-	w.Flush()
-
-	return nil
-}
-
-func listHelmRepoCharts(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	pID := config.Project
-	hrID := config.HelmRepo
-
-	charts, err := client.ListCharts(
-		context.Background(),
-		pID,
-		hrID,
-	)
-
-	if err != nil {
-		return err
-	}
-
-	w := new(tabwriter.Writer)
-	w.Init(os.Stdout, 3, 8, 0, '\t', tabwriter.AlignRight)
-
-	fmt.Fprintf(w, "%s\t%s\n", "NAME", "VERSION")
-
-	for _, chart := range charts {
-		for _, version := range chart.Versions {
-			fmt.Fprintf(w, "%s\t%s\n", strings.ToLower(chart.Name), version)
-		}
-	}
-
-	w.Flush()
-
-	return nil
-}

+ 3 - 2
cli/cmd/job.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/spf13/cobra"
 )
 
@@ -73,7 +74,7 @@ func init() {
 	batchImageUpdateCmd.MarkPersistentFlagRequired("tag")
 }
 
-func batchImageUpdate(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+func batchImageUpdate(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	color.New(color.FgGreen).Println("Updating all jobs which use the image:", imageRepoURI)
 
 	return client.UpdateBatchImage(
@@ -81,7 +82,7 @@ func batchImageUpdate(resp *api.AuthCheckResponse, client *api.Client, args []st
 		config.Project,
 		config.Cluster,
 		namespace,
-		&api.UpdateBatchImageRequest{
+		&types.UpdateImageBatchRequest{
 			ImageRepoURI: imageRepoURI,
 			Tag:          tag,
 		},

+ 2 - 1
cli/cmd/logs.go

@@ -5,6 +5,7 @@ import (
 	"os"
 
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/utils"
 	"github.com/spf13/cobra"
 )
@@ -45,7 +46,7 @@ func init() {
 	)
 }
 
-func logs(_ *api.AuthCheckResponse, client *api.Client, args []string) error {
+func logs(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	podsSimple, err := getPods(client, namespace, args[0])
 
 	if err != nil {

+ 13 - 8
cli/cmd/project.go

@@ -10,6 +10,7 @@ import (
 
 	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/utils"
 	"github.com/spf13/cobra"
 )
@@ -68,8 +69,8 @@ func init() {
 	projectCmd.AddCommand(listProjectCmd)
 }
 
-func createProject(_ *api.AuthCheckResponse, client *api.Client, args []string) error {
-	resp, err := client.CreateProject(context.Background(), &api.CreateProjectRequest{
+func createProject(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	resp, err := client.CreateProject(context.Background(), &types.CreateProjectRequest{
 		Name: args[0],
 	})
 
@@ -82,13 +83,15 @@ func createProject(_ *api.AuthCheckResponse, client *api.Client, args []string)
 	return config.SetProject(resp.ID)
 }
 
-func listProjects(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	projects, err := client.ListUserProjects(context.Background(), user.ID)
+func listProjects(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	resp, err := client.ListUserProjects(context.Background())
 
 	if err != nil {
 		return err
 	}
 
+	projects := *resp
+
 	w := new(tabwriter.Writer)
 	w.Init(os.Stdout, 3, 8, 0, '\t', tabwriter.AlignRight)
 
@@ -109,7 +112,7 @@ func listProjects(user *api.AuthCheckResponse, client *api.Client, args []string
 	return nil
 }
 
-func deleteProject(_ *api.AuthCheckResponse, client *api.Client, args []string) error {
+func deleteProject(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	userResp, err := utils.PromptPlaintext(
 		fmt.Sprintf(
 			`Are you sure you'd like to delete the project with id %s? %s `,
@@ -129,25 +132,27 @@ func deleteProject(_ *api.AuthCheckResponse, client *api.Client, args []string)
 			return err
 		}
 
-		resp, err := client.DeleteProject(context.Background(), uint(id))
+		err = client.DeleteProject(context.Background(), uint(id))
 
 		if err != nil {
 			return err
 		}
 
-		color.New(color.FgGreen).Printf("Deleted project with name %s and id %d\n", resp.Name, resp.ID)
+		color.New(color.FgGreen).Printf("Deleted project with id %d\n", id)
 	}
 
 	return nil
 }
 
 func setProjectCluster(client *api.Client, projectID uint) error {
-	clusters, err := client.ListProjectClusters(context.Background(), projectID)
+	resp, err := client.ListProjectClusters(context.Background(), projectID)
 
 	if err != nil {
 		return err
 	}
 
+	clusters := *resp
+
 	if len(clusters) > 0 {
 		config.SetCluster(clusters[0].ID)
 	}

+ 14 - 7
cli/cmd/registry.go

@@ -10,6 +10,7 @@ import (
 
 	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/utils"
 	"github.com/spf13/cobra"
 )
@@ -99,11 +100,11 @@ func init() {
 	registryImageCmd.AddCommand(registryImageListCmd)
 }
 
-func listRegistries(user *api.AuthCheckResponse, client *api.Client, args []string) error {
+func listRegistries(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	pID := config.Project
 
 	// get the list of namespaces
-	registries, err := client.ListRegistries(
+	resp, err := client.ListRegistries(
 		context.Background(),
 		pID,
 	)
@@ -112,6 +113,8 @@ func listRegistries(user *api.AuthCheckResponse, client *api.Client, args []stri
 		return err
 	}
 
+	registries := *resp
+
 	w := new(tabwriter.Writer)
 	w.Init(os.Stdout, 3, 8, 0, '\t', tabwriter.AlignRight)
 
@@ -132,7 +135,7 @@ func listRegistries(user *api.AuthCheckResponse, client *api.Client, args []stri
 	return nil
 }
 
-func deleteRegistry(user *api.AuthCheckResponse, client *api.Client, args []string) error {
+func deleteRegistry(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	userResp, err := utils.PromptPlaintext(
 		fmt.Sprintf(
 			`Are you sure you'd like to delete the registry with id %s? %s `,
@@ -164,12 +167,12 @@ func deleteRegistry(user *api.AuthCheckResponse, client *api.Client, args []stri
 	return nil
 }
 
-func listRepos(user *api.AuthCheckResponse, client *api.Client, args []string) error {
+func listRepos(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	pID := config.Project
 	rID := config.Registry
 
 	// get the list of namespaces
-	repos, err := client.ListRegistryRepositories(
+	resp, err := client.ListRegistryRepositories(
 		context.Background(),
 		pID,
 		rID,
@@ -179,6 +182,8 @@ func listRepos(user *api.AuthCheckResponse, client *api.Client, args []string) e
 		return err
 	}
 
+	repos := *resp
+
 	w := new(tabwriter.Writer)
 	w.Init(os.Stdout, 3, 8, 0, '\t', tabwriter.AlignRight)
 
@@ -193,13 +198,13 @@ func listRepos(user *api.AuthCheckResponse, client *api.Client, args []string) e
 	return nil
 }
 
-func listImages(user *api.AuthCheckResponse, client *api.Client, args []string) error {
+func listImages(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	pID := config.Project
 	rID := config.Registry
 	repoName := args[0]
 
 	// get the list of namespaces
-	imgs, err := client.ListImages(
+	resp, err := client.ListImages(
 		context.Background(),
 		pID,
 		rID,
@@ -210,6 +215,8 @@ func listImages(user *api.AuthCheckResponse, client *api.Client, args []string)
 		return err
 	}
 
+	imgs := *resp
+
 	w := new(tabwriter.Writer)
 	w.Init(os.Stdout, 3, 8, 0, '\t', tabwriter.AlignRight)
 

+ 5 - 2
cli/cmd/run.go

@@ -11,6 +11,7 @@ import (
 
 	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/utils"
 	"github.com/spf13/cobra"
 	v1 "k8s.io/api/core/v1"
@@ -74,7 +75,7 @@ func init() {
 	)
 }
 
-func run(_ *api.AuthCheckResponse, client *api.Client, args []string) error {
+func run(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 	color.New(color.FgGreen).Println("Running", strings.Join(args[1:], " "), "for release", args[0])
 
 	podsSimple, err := getPods(client, namespace, args[0])
@@ -219,9 +220,11 @@ func getPods(client *api.Client, namespace, releaseName string) ([]podSimple, er
 		return nil, err
 	}
 
+	pods := *resp
+
 	res := make([]podSimple, 0)
 
-	for _, pod := range resp {
+	for _, pod := range pods {
 		containerNames := make([]string, 0)
 
 		for _, container := range pod.Spec.Containers {

+ 1 - 0
internal/models/release.go

@@ -30,6 +30,7 @@ func (r *Release) ToReleaseType() *types.PorterRelease {
 	res := &types.PorterRelease{
 		ID:           r.ID,
 		WebhookToken: r.WebhookToken,
+		ImageRepoURI: r.ImageRepoURI,
 	}
 
 	if r.GitActionConfig != nil {

+ 33 - 61
internal/registry/registry.go

@@ -19,6 +19,8 @@ import (
 
 	ints "github.com/porter-dev/porter/internal/models/integrations"
 
+	ptypes "github.com/porter-dev/porter/api/types"
+
 	"github.com/digitalocean/godo"
 	"github.com/docker/cli/cli/config/configfile"
 	"github.com/docker/cli/cli/config/types"
@@ -27,36 +29,6 @@ import (
 // Registry wraps the gorm Registry model
 type Registry models.Registry
 
-// Repository is a collection of images
-type Repository struct {
-	// Name of the repository
-	Name string `json:"name"`
-
-	// When the repository was created
-	CreatedAt time.Time `json:"created_at,omitempty"`
-
-	// The URI of the repository
-	URI string `json:"uri"`
-}
-
-// Image is a Docker image type
-type Image struct {
-	// The sha256 digest of the image manifest.
-	Digest string `json:"digest"`
-
-	// The tag used for the image.
-	Tag string `json:"tag"`
-
-	// The image manifest associated with the image.
-	Manifest string `json:"manifest"`
-
-	// The name of the repository associated with the image.
-	RepositoryName string `json:"repository_name"`
-
-	// When the image was pushed
-	PushedAt *time.Time `json:"pushed_at"`
-}
-
 func GetECRRegistryURL(awsIntRepo repository.AWSIntegrationRepository, projectID, awsIntID uint) (string, error) {
 	awsInt, err := awsIntRepo.ReadAWSIntegration(projectID, awsIntID)
 
@@ -85,7 +57,7 @@ func GetECRRegistryURL(awsIntRepo repository.AWSIntegrationRepository, projectID
 func (r *Registry) ListRepositories(
 	repo repository.Repository,
 	doAuth *oauth2.Config, // only required if using DOCR
-) ([]*Repository, error) {
+) ([]*ptypes.RegistryRepository, error) {
 	// switch on the auth mechanism to get a token
 	if r.AWSIntegrationID != 0 {
 		return r.listECRRepositories(repo)
@@ -150,7 +122,7 @@ func (r *Registry) GetGCRToken(repo repository.Repository) (*ints.TokenCache, er
 
 func (r *Registry) listGCRRepositories(
 	repo repository.Repository,
-) ([]*Repository, error) {
+) ([]*ptypes.RegistryRepository, error) {
 	gcp, err := repo.GCPIntegration().ReadGCPIntegration(
 		r.ProjectID,
 		r.GCPIntegrationID,
@@ -188,7 +160,7 @@ func (r *Registry) listGCRRepositories(
 		return nil, fmt.Errorf("Could not read GCR repositories: %v", err)
 	}
 
-	res := make([]*Repository, 0)
+	res := make([]*ptypes.RegistryRepository, 0)
 
 	parsedURL, err := url.Parse("https://" + r.URL)
 
@@ -197,7 +169,7 @@ func (r *Registry) listGCRRepositories(
 	}
 
 	for _, repo := range gcrResp.Repositories {
-		res = append(res, &Repository{
+		res = append(res, &ptypes.RegistryRepository{
 			Name: repo,
 			URI:  parsedURL.Host + "/" + repo,
 		})
@@ -206,7 +178,7 @@ func (r *Registry) listGCRRepositories(
 	return res, nil
 }
 
-func (r *Registry) listECRRepositories(repo repository.Repository) ([]*Repository, error) {
+func (r *Registry) listECRRepositories(repo repository.Repository) ([]*ptypes.RegistryRepository, error) {
 	aws, err := repo.AWSIntegration().ReadAWSIntegration(
 		r.ProjectID,
 		r.AWSIntegrationID,
@@ -230,10 +202,10 @@ func (r *Registry) listECRRepositories(repo repository.Repository) ([]*Repositor
 		return nil, err
 	}
 
-	res := make([]*Repository, 0)
+	res := make([]*ptypes.RegistryRepository, 0)
 
 	for _, repo := range resp.Repositories {
-		res = append(res, &Repository{
+		res = append(res, &ptypes.RegistryRepository{
 			Name:      *repo.RepositoryName,
 			CreatedAt: *repo.CreatedAt,
 			URI:       *repo.RepositoryUri,
@@ -246,7 +218,7 @@ func (r *Registry) listECRRepositories(repo repository.Repository) ([]*Repositor
 func (r *Registry) listDOCRRepositories(
 	repo repository.Repository,
 	doAuth *oauth2.Config,
-) ([]*Repository, error) {
+) ([]*ptypes.RegistryRepository, error) {
 	oauthInt, err := repo.OAuthIntegration().ReadOAuthIntegration(
 		r.ProjectID,
 		r.DOIntegrationID,
@@ -278,10 +250,10 @@ func (r *Registry) listDOCRRepositories(
 		return nil, err
 	}
 
-	res := make([]*Repository, 0)
+	res := make([]*ptypes.RegistryRepository, 0)
 
 	for _, repo := range repos {
-		res = append(res, &Repository{
+		res = append(res, &ptypes.RegistryRepository{
 			Name: repo.Name,
 			URI:  r.URL + "/" + repo.Name,
 		})
@@ -292,13 +264,13 @@ func (r *Registry) listDOCRRepositories(
 
 func (r *Registry) listPrivateRegistryRepositories(
 	repo repository.Repository,
-) ([]*Repository, error) {
+) ([]*ptypes.RegistryRepository, error) {
 	// handle dockerhub different, as it doesn't implement the docker registry http api
 	if strings.Contains(r.URL, "docker.io") {
 		// in this case, we just return the single dockerhub repository that's linked
-		res := make([]*Repository, 0)
+		res := make([]*ptypes.RegistryRepository, 0)
 
-		res = append(res, &Repository{
+		res = append(res, &ptypes.RegistryRepository{
 			Name: strings.Split(r.URL, "docker.io/")[1],
 			URI:  r.URL,
 		})
@@ -367,14 +339,14 @@ func (r *Registry) listPrivateRegistryRepositories(
 		return nil, fmt.Errorf("Could not read private registry repositories: %v", err)
 	}
 
-	res := make([]*Repository, 0)
+	res := make([]*ptypes.RegistryRepository, 0)
 
 	if err != nil {
 		return nil, err
 	}
 
 	for _, repo := range gcrResp.Repositories {
-		res = append(res, &Repository{
+		res = append(res, &ptypes.RegistryRepository{
 			Name: repo,
 			URI:  parsedURL.Host + "/" + repo,
 		})
@@ -475,7 +447,7 @@ func (r *Registry) ListImages(
 	repoName string,
 	repo repository.Repository,
 	doAuth *oauth2.Config, // only required if using DOCR
-) ([]*Image, error) {
+) ([]*ptypes.Image, error) {
 	// switch on the auth mechanism to get a token
 	if r.AWSIntegrationID != 0 {
 		return r.listECRImages(repoName, repo)
@@ -496,7 +468,7 @@ func (r *Registry) ListImages(
 	return nil, fmt.Errorf("error listing images")
 }
 
-func (r *Registry) listECRImages(repoName string, repo repository.Repository) ([]*Image, error) {
+func (r *Registry) listECRImages(repoName string, repo repository.Repository) ([]*ptypes.Image, error) {
 	aws, err := repo.AWSIntegration().ReadAWSIntegration(
 		r.ProjectID,
 		r.AWSIntegrationID,
@@ -549,11 +521,11 @@ func (r *Registry) listECRImages(repoName string, repo repository.Repository) ([
 		imageDetails = append(imageDetails, describeResp.ImageDetails...)
 	}
 
-	res := make([]*Image, 0)
+	res := make([]*ptypes.Image, 0)
 
 	for _, img := range imageDetails {
 		for _, tag := range img.ImageTags {
-			res = append(res, &Image{
+			res = append(res, &ptypes.Image{
 				Digest:         *img.ImageDigest,
 				Tag:            *tag,
 				RepositoryName: repoName,
@@ -569,7 +541,7 @@ type gcrImageResp struct {
 	Tags []string `json:"tags"`
 }
 
-func (r *Registry) listGCRImages(repoName string, repo repository.Repository) ([]*Image, error) {
+func (r *Registry) listGCRImages(repoName string, repo repository.Repository) ([]*ptypes.Image, error) {
 	gcp, err := repo.GCPIntegration().ReadGCPIntegration(
 		r.ProjectID,
 		r.GCPIntegrationID,
@@ -614,10 +586,10 @@ func (r *Registry) listGCRImages(repoName string, repo repository.Repository) ([
 		return nil, fmt.Errorf("Could not read GCR repositories: %v", err)
 	}
 
-	res := make([]*Image, 0)
+	res := make([]*ptypes.Image, 0)
 
 	for _, tag := range gcrResp.Tags {
-		res = append(res, &Image{
+		res = append(res, &ptypes.Image{
 			RepositoryName: repoName,
 			Tag:            tag,
 		})
@@ -630,7 +602,7 @@ func (r *Registry) listDOCRImages(
 	repoName string,
 	repo repository.Repository,
 	doAuth *oauth2.Config,
-) ([]*Image, error) {
+) ([]*ptypes.Image, error) {
 	oauthInt, err := repo.OAuthIntegration().ReadOAuthIntegration(
 		r.ProjectID,
 		r.DOIntegrationID,
@@ -662,10 +634,10 @@ func (r *Registry) listDOCRImages(
 		return nil, err
 	}
 
-	res := make([]*Image, 0)
+	res := make([]*ptypes.Image, 0)
 
 	for _, tag := range tags {
-		res = append(res, &Image{
+		res = append(res, &ptypes.Image{
 			RepositoryName: repoName,
 			Tag:            tag.Tag,
 		})
@@ -674,7 +646,7 @@ func (r *Registry) listDOCRImages(
 	return res, nil
 }
 
-func (r *Registry) listPrivateRegistryImages(repoName string, repo repository.Repository) ([]*Image, error) {
+func (r *Registry) listPrivateRegistryImages(repoName string, repo repository.Repository) ([]*ptypes.Image, error) {
 	// handle dockerhub different, as it doesn't implement the docker registry http api
 	if strings.Contains(r.URL, "docker.io") {
 		return r.listDockerHubImages(repoName, repo)
@@ -720,10 +692,10 @@ func (r *Registry) listPrivateRegistryImages(repoName string, repo repository.Re
 		return nil, fmt.Errorf("Could not read private registry repositories: %v", err)
 	}
 
-	res := make([]*Image, 0)
+	res := make([]*ptypes.Image, 0)
 
 	for _, tag := range gcrResp.Tags {
-		res = append(res, &Image{
+		res = append(res, &ptypes.Image{
 			RepositoryName: repoName,
 			Tag:            tag,
 		})
@@ -749,7 +721,7 @@ type dockerHubLoginResp struct {
 	Token string `json:"token"`
 }
 
-func (r *Registry) listDockerHubImages(repoName string, repo repository.Repository) ([]*Image, error) {
+func (r *Registry) listDockerHubImages(repoName string, repo repository.Repository) ([]*ptypes.Image, error) {
 	basic, err := repo.BasicIntegration().ReadBasicIntegration(
 		r.ProjectID,
 		r.BasicIntegrationID,
@@ -820,10 +792,10 @@ func (r *Registry) listDockerHubImages(repoName string, repo repository.Reposito
 		return nil, fmt.Errorf("Could not read private registry repositories: %v", err)
 	}
 
-	res := make([]*Image, 0)
+	res := make([]*ptypes.Image, 0)
 
 	for _, result := range imageResp.Results {
-		res = append(res, &Image{
+		res = append(res, &ptypes.Image{
 			RepositoryName: repoName,
 			Tag:            result.Name,
 		})