Skip to content

Commit

Permalink
Bump recommended templates to the top of the file
Browse files Browse the repository at this point in the history
* Introduces concept of sorting by recommended templates
* Adds filtering options to only view recommended or official
templates

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
  • Loading branch information
alexellis committed Apr 4, 2023
1 parent c337049 commit 526a7a0
Showing 1 changed file with 85 additions and 20 deletions.
105 changes: 85 additions & 20 deletions commands/template_store_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"io"

"net/http"
"os"
"sort"
"strings"
"text/tabwriter"
"time"
Expand All @@ -27,12 +29,16 @@ const (
var (
templateStoreURL string
inputPlatform string
recommended bool
official bool
)

func init() {
templateStoreListCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Shows additional language and platform")
templateStoreListCmd.PersistentFlags().StringVarP(&templateStoreURL, "url", "u", DefaultTemplatesStore, "Use as alternative store for templates")
templateStoreListCmd.Flags().StringVarP(&inputPlatform, "platform", "p", mainPlatform, "Shows the platform if the output is verbose")
templateStoreListCmd.Flags().BoolVarP(&recommended, "recommended", "r", false, "Shows only recommended templates")
templateStoreListCmd.Flags().BoolVarP(&official, "official", "o", false, "Shows only official templates")

templateStoreCmd.AddCommand(templateStoreListCmd)
}
Expand All @@ -42,25 +48,58 @@ var templateStoreListCmd = &cobra.Command{
Use: `list`,
Short: `List templates from OpenFaaS organizations`,
Aliases: []string{"ls"},
Long: `List templates from official store or from custom URL or set the environmental variable OPENFAAS_TEMPLATE_STORE_URL to be the default store location`,
Long: `List templates from a template store manifest file, by default the
official list maintained by the OpenFaaS community is used. You can override this.`,
Example: ` faas-cli template store list
faas-cli template store ls
faas-cli template store ls --url=https://raw.githubusercontent.com/openfaas/store/master/templates.json
# List only recommended templates
faas-cli template store list --recommended
# List only official templates
faas-cli template store list --official
# Override the store via a flag
faas-cli template store ls \
--url=https://raw.githubusercontent.com/openfaas/store/master/templates.json
# Specify an alternative store via environment variable
export OPENFAAS_TEMPLATE_STORE_URL=https://example.com/templates.json
# See additional language and platform
faas-cli template store ls --verbose=true
faas-cli template store list --platform arm64`,
# Filter by platform for arm64 only
faas-cli template store list --platform arm64
`,
RunE: runTemplateStoreList,
}

func runTemplateStoreList(cmd *cobra.Command, args []string) error {
envTemplateRepoStore := os.Getenv(templateStoreURLEnvironment)
storeURL := getTemplateStoreURL(templateStoreURL, envTemplateRepoStore, DefaultTemplatesStore)

templatesInfo, templatesErr := getTemplateInfo(storeURL)
if templatesErr != nil {
return fmt.Errorf("error while getting templates info: %s", templatesErr)
templatesInfo, err := getTemplateInfo(storeURL)
if err != nil {
return fmt.Errorf("error while getting templates info: %s", err)
}
list := []TemplateInfo{}

formattedOutput := formatTemplatesOutput(templatesInfo, verbose, inputPlatform)
if recommended {
for i := 0; i < len(templatesInfo); i++ {
if templatesInfo[i].Recommended {
list = append(list, templatesInfo[i])
}
}
} else if official {
for i := 0; i < len(templatesInfo); i++ {
if templatesInfo[i].Official == "true" {
list = append(list, templatesInfo[i])
}
}
} else {
list = templatesInfo
}

formattedOutput := formatTemplatesOutput(list, verbose, inputPlatform)

fmt.Fprintf(cmd.OutOrStdout(), "%s", formattedOutput)

Expand All @@ -78,9 +117,9 @@ func getTemplateInfo(repository string) ([]TemplateInfo, error) {
req = req.WithContext(reqContext)

client := http.DefaultClient
res, clientErr := client.Do(req)
if clientErr != nil {
return nil, fmt.Errorf("error while requesting template list: %s", clientErr.Error())
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error while requesting template list: %s", err.Error())
}

if res.Body == nil {
Expand All @@ -92,19 +131,37 @@ func getTemplateInfo(repository string) ([]TemplateInfo, error) {
return nil, fmt.Errorf("unexpected status code wanted: %d got: %d", http.StatusOK, res.StatusCode)
}

body, bodyErr := ioutil.ReadAll(res.Body)
if bodyErr != nil {
return nil, fmt.Errorf("error while reading data from templates body: %s", bodyErr.Error())
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("error while reading response: %s", err.Error())
}

templatesInfo := []TemplateInfo{}
unmarshallErr := json.Unmarshal(body, &templatesInfo)
if unmarshallErr != nil {
return nil, fmt.Errorf("error while unmarshalling into templates struct: %s", unmarshallErr.Error())
if err := json.Unmarshal(body, &templatesInfo); err != nil {
return nil, fmt.Errorf("can't unmarshal text: %s", err.Error())
}

sortTemplates(templatesInfo)

return templatesInfo, nil
}

func sortTemplates(templatesInfo []TemplateInfo) {
sort.Slice(templatesInfo, func(i, j int) bool {
if templatesInfo[i].Recommended == templatesInfo[j].Recommended {
if templatesInfo[i].Official == templatesInfo[j].Official {
return strings.ToLower(templatesInfo[i].TemplateName) < strings.ToLower(templatesInfo[j].TemplateName)
} else {
return templatesInfo[i].Official < templatesInfo[j].Official
}
} else if templatesInfo[i].Recommended {
return true
} else {
return false
}
})
}

func formatTemplatesOutput(templates []TemplateInfo, verbose bool, platform string) string {

if platform != mainPlatform {
Expand Down Expand Up @@ -135,10 +192,17 @@ func formatTemplatesOutput(templates []TemplateInfo, verbose bool, platform stri

func formatBasicOutput(lineWriter *tabwriter.Writer, templates []TemplateInfo) {

fmt.Fprintf(lineWriter, "NAME\tSOURCE\tDESCRIPTION\n")
fmt.Fprintf(lineWriter, "NAME\tRECOMMENDED\tDESCRIPTION\tSOURCE\n")
for _, template := range templates {
fmt.Fprintf(lineWriter, "%s\t%s\t%s\n",

recommended := "[ ]"
if template.Recommended {
recommended = "[x]"
}

fmt.Fprintf(lineWriter, "%s\t%s\t%s\t%s\n",
template.TemplateName,
recommended,
template.Source,
template.Description)
}
Expand Down Expand Up @@ -166,6 +230,7 @@ type TemplateInfo struct {
Description string `json:"description"`
Repository string `json:"repo"`
Official string `json:"official"`
Recommended bool `json:"recommended"`
}

func filterTemplate(templates []TemplateInfo, platform string) []TemplateInfo {
Expand Down

0 comments on commit 526a7a0

Please sign in to comment.