Jelajahi Sumber

add get template endpoints + improve types

Alexander Belanger 4 tahun lalu
induk
melakukan
b56d555f94

+ 79 - 0
api/server/handlers/template/get.go

@@ -0,0 +1,79 @@
+package template
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/go-chi/chi"
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/helm/loader"
+	"github.com/porter-dev/porter/internal/templater/parser"
+)
+
+type TemplateGetHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewTemplateGetHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *TemplateGetHandler {
+	return &TemplateGetHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (t *TemplateGetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	request := &types.GetTemplateRequest{}
+
+	ok := t.DecodeAndValidate(w, r, request)
+
+	if !ok {
+		return
+	}
+
+	// TODO: get url params elegantly
+	name := chi.URLParam(r, "name")
+	version := chi.URLParam(r, "version")
+
+	// if version passed as latest, pass empty string to loader to get latest
+	if version == "latest" {
+		version = ""
+	}
+
+	chart, err := loader.LoadChartPublic(request.RepoURL, name, version)
+
+	if err != nil {
+		t.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	parserDef := &parser.ClientConfigDefault{
+		HelmChart: chart,
+	}
+
+	res := &types.GetTemplateResponse{}
+	res.Metadata = chart.Metadata
+	res.Values = chart.Values
+
+	for _, file := range chart.Files {
+		if strings.Contains(file.Name, "form.yaml") {
+			formYAML, err := parser.FormYAMLFromBytes(parserDef, file.Data, "declared")
+
+			if err != nil {
+				break
+			}
+
+			res.Form = formYAML
+		} else if strings.Contains(file.Name, "README.md") {
+			res.Markdown = string(file.Data)
+		}
+	}
+
+	t.WriteResult(w, r, res)
+}

+ 25 - 0
api/server/router/user.go

@@ -257,5 +257,30 @@ func getUserRoutes(
 		Router:   r,
 	})
 
+	// GET /api/templates/{name}/{version} -> template.NewTemplateGetHandler
+	getTemplateEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: "/templates/{name}/{version}",
+			},
+			Scopes: []types.PermissionScope{types.UserScope},
+		},
+	)
+
+	getTemplateRequest := template.NewTemplateGetHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: getTemplateEndpoint,
+		Handler:  getTemplateRequest,
+		Router:   r,
+	})
+
 	return routes
 }

+ 29 - 1
api/types/template.go

@@ -1,5 +1,33 @@
 package types
 
-type ListTemplatesRequest struct {
+import "helm.sh/helm/v3/pkg/chart"
+
+type TemplateGetBaseRequest struct {
 	RepoURL string `schema:"repo_url"`
 }
+
+type ListTemplatesRequest struct {
+	TemplateGetBaseRequest
+}
+
+type PorterTemplateSimple struct {
+	Name        string   `json:"name"`
+	Versions    []string `json:"versions"`
+	Description string   `json:"description"`
+	Icon        string   `json:"icon"`
+}
+
+// ListTemplatesResponse is how a chart gets displayed when listed
+type ListTemplatesResponse []PorterTemplateSimple
+
+type GetTemplateRequest struct {
+	TemplateGetBaseRequest
+}
+
+// GetTemplateResponse is a chart with detailed information and a form for reading
+type GetTemplateResponse struct {
+	Markdown string                 `json:"markdown"`
+	Metadata *chart.Metadata        `json:"metadata"`
+	Values   map[string]interface{} `json:"values"`
+	Form     *FormYAML              `json:"form"`
+}

+ 1 - 1
dashboard/src/main/Main.tsx

@@ -56,7 +56,7 @@ export default class Main extends Component<PropsType, StateType> {
       .catch((err) => this.setState({ isLoggedIn: false, loading: false }));
 
     api
-      .getCapabilities("", {}, {})
+      .getMetadata("", {}, {})
       .then((res) => {
         this.setState({ local: !res.data?.provisioner });
       })

+ 1 - 1
dashboard/src/main/auth/Login.tsx

@@ -48,7 +48,7 @@ export default class Login extends Component<PropsType, StateType> {
 
     // get capabilities to case on github
     api
-      .getCapabilities("", {}, {})
+      .getMetadata("", {}, {})
       .then((res) => {
         this.setState({
           hasBasic: res.data?.basic_login,

+ 1 - 1
dashboard/src/main/auth/Register.tsx

@@ -44,7 +44,7 @@ export default class Register extends Component<PropsType, StateType> {
 
     // get capabilities to case on github
     api
-      .getCapabilities("", {}, {})
+      .getMetadata("", {}, {})
       .then((res) => {
         this.setState({
           hasGithub: res.data?.github_login,

+ 4 - 4
dashboard/src/main/home/Home.tsx

@@ -111,9 +111,9 @@ class Home extends Component<PropsType, StateType> {
       });
   };
 
-  getCapabilities = () => {
+  getMetadata = () => {
     api
-      .getCapabilities("<token>", {}, {})
+      .getMetadata("<token>", {}, {})
       .then((res) => {
         this.context.setCapabilities(res.data);
       })
@@ -286,7 +286,7 @@ class Home extends Component<PropsType, StateType> {
     this.setState({ ghRedirect: urlParams.get("gh_oauth") !== null });
     urlParams.delete("gh_oauth");
     this.getProjects(defaultProjectId);
-    this.getCapabilities();
+    this.getMetadata();
   }
 
   // TODO: Need to handle the following cases. Do a deep rearchitecture (Prov -> Dashboard?) if need be:
@@ -302,7 +302,7 @@ class Home extends Component<PropsType, StateType> {
         this.checkDO();
       } else {
         this.initializeView();
-        this.getCapabilities();
+        this.getMetadata();
       }
     }
   }

+ 3 - 3
dashboard/src/shared/api.tsx

@@ -768,8 +768,8 @@ const getTemplates = baseApi<
   {}
 >("GET", "/api/templates");
 
-const getCapabilities = baseApi<{}, {}>("GET", () => {
-  return `/api/capabilities`;
+const getMetadata = baseApi<{}, {}>("GET", () => {
+  return `/api/metadata`;
 });
 
 const linkGithubProject = baseApi<
@@ -1087,7 +1087,7 @@ export default {
   detectBuildpack,
   getBranchContents,
   getBranches,
-  getCapabilities,
+  getMetadata,
   getChart,
   getCharts,
   getChartComponents,

+ 6 - 6
internal/helm/loader/loader.go

@@ -7,20 +7,20 @@ import (
 	"net/http"
 	"strings"
 
-	"github.com/porter-dev/porter/internal/models"
 	"k8s.io/helm/pkg/repo"
 	"sigs.k8s.io/yaml"
 
+	"github.com/porter-dev/porter/api/types"
 	"helm.sh/helm/v3/pkg/chart"
 	chartloader "helm.sh/helm/v3/pkg/chart/loader"
 )
 
 // RepoIndexToPorterChartList converts an index file to a list of porter charts
-func RepoIndexToPorterChartList(index *repo.IndexFile) []*models.PorterChartList {
+func RepoIndexToPorterChartList(index *repo.IndexFile) types.ListTemplatesResponse {
 	// sort the entries before parsing
 	index.SortEntries()
 
-	porterCharts := make([]*models.PorterChartList, 0)
+	porterCharts := make(types.ListTemplatesResponse, 0)
 
 	for _, entryVersions := range index.Entries {
 		indexChart := entryVersions[0]
@@ -30,7 +30,7 @@ func RepoIndexToPorterChartList(index *repo.IndexFile) []*models.PorterChartList
 			versions = append(versions, entryVersion.Version)
 		}
 
-		porterChart := &models.PorterChartList{
+		porterChart := types.PorterTemplateSimple{
 			Name:        indexChart.Name,
 			Description: indexChart.Description,
 			Icon:        indexChart.Icon,
@@ -44,7 +44,7 @@ func RepoIndexToPorterChartList(index *repo.IndexFile) []*models.PorterChartList
 }
 
 // FindPorterChartInIndexList finds a chart by name given an index file and returns it
-func FindPorterChartInIndexList(index *repo.IndexFile, name string) *models.PorterChartList {
+func FindPorterChartInIndexList(index *repo.IndexFile, name string) *types.PorterTemplateSimple {
 	// sort the entries before parsing
 	index.SortEntries()
 
@@ -58,7 +58,7 @@ func FindPorterChartInIndexList(index *repo.IndexFile, name string) *models.Port
 				versions = append(versions, entryVersion.Version)
 			}
 
-			return &models.PorterChartList{
+			return &types.PorterTemplateSimple{
 				Name:        indexChart.Name,
 				Description: indexChart.Description,
 				Icon:        indexChart.Icon,

+ 3 - 2
internal/helm/repo/repo.go

@@ -3,6 +3,7 @@ package repo
 import (
 	"fmt"
 
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/helm/loader"
 	"github.com/porter-dev/porter/internal/models"
 	"helm.sh/helm/v3/pkg/chart"
@@ -14,7 +15,7 @@ import (
 type HelmRepo models.HelmRepo
 
 // ListCharts lists Porter charts for a given helm repo
-func (hr *HelmRepo) ListCharts(repo repository.Repository) ([]*models.PorterChartList, error) {
+func (hr *HelmRepo) ListCharts(repo repository.Repository) (types.ListTemplatesResponse, error) {
 	if hr.BasicAuthIntegrationID != 0 {
 		return hr.listChartsBasic(repo)
 	}
@@ -36,7 +37,7 @@ func (hr *HelmRepo) GetChart(
 
 func (hr *HelmRepo) listChartsBasic(
 	repo repository.Repository,
-) ([]*models.PorterChartList, error) {
+) (types.ListTemplatesResponse, error) {
 	// get the basic auth integration
 	basic, err := repo.BasicIntegration().ReadBasicIntegration(
 		hr.BasicAuthIntegrationID,

+ 0 - 18
internal/models/templates.go

@@ -1,23 +1,5 @@
 package models
 
-import "helm.sh/helm/v3/pkg/chart"
-
-// PorterChartList is how a chart gets displayed when listed
-type PorterChartList struct {
-	Name        string   `json:"name"`
-	Versions    []string `json:"versions"`
-	Description string   `json:"description"`
-	Icon        string   `json:"icon"`
-}
-
-// PorterChartRead is a chart with detailed information and a form for reading
-type PorterChartRead struct {
-	Markdown string                 `json:"markdown"`
-	Metadata *chart.Metadata        `json:"metadata"`
-	Values   map[string]interface{} `json:"values"`
-	Form     *FormYAML              `json:"form"`
-}
-
 // FormContext is the target context
 type FormContext struct {
 	Type   string            `yaml:"type" json:"type"`