Skip to content

Commit

Permalink
Add command index and command page
Browse files Browse the repository at this point in the history
  • Loading branch information
bhou-crto committed Nov 11, 2023
1 parent c59ef24 commit e68c933
Show file tree
Hide file tree
Showing 9 changed files with 411 additions and 29 deletions.
3 changes: 2 additions & 1 deletion cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/criteo/command-launcher/internal/backend"
"github.com/criteo/command-launcher/internal/config"
"github.com/criteo/command-launcher/internal/context"
"github.com/criteo/command-launcher/internal/server"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand All @@ -30,7 +31,7 @@ func AddServeCmd(rootCmd *cobra.Command, appCtx context.LauncherContext, back ba
port = serveFlags.port
}
fmt.Printf("Starting server on port %d", port)
if err := back.Serve(port); err != nil {
if err := server.Serve(&back, port); err != nil {
fmt.Printf("Failed to start server: %s\n", err)
os.Exit(1)
}
Expand Down
3 changes: 0 additions & 3 deletions internal/backend/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,6 @@ type Backend interface {
// Return dropin local repsository
DropinRepository() repository.PackageRepository

// start the server as daemon
Serve(port int) error

// Print out the command resolution details
Debug()
}
25 changes: 0 additions & 25 deletions internal/backend/server.go

This file was deleted.

168 changes: 168 additions & 0 deletions internal/server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package server

import (
"bytes"
"embed"
"fmt"
"html/template"
"net/http"
"sort"
"strings"

. "github.com/criteo/command-launcher/internal/backend"
"github.com/criteo/command-launcher/internal/command"
)

type Server struct {
backend *Backend
}

//go:embed static
var fs embed.FS

//go:embed templates
var templates embed.FS

func (server *Server) IndexHandler(w http.ResponseWriter, r *http.Request) {
index, _ := fs.ReadFile("static/index.html")
w.Write(index)
}

type Command struct {
FullName string
Group string
Name string
Package string
Registry string
Short string
Long string
Examples []command.ExampleEntry
Flags []command.Flag
SubCmds []*Command
}

type CommandIndex struct {
Commands []*Command
}

func (server *Server) CommandIndexHandler(w http.ResponseWriter, r *http.Request) {
repos := (*(server.backend)).AllRepositories()
cmdMap := map[string]*Command{}

for _, repo := range repos {
pkgs := repo.InstalledPackages()
for _, pkg := range pkgs {
cmds := pkg.Commands()
for _, cmd := range cmds {
if cmd.Name() == "__setup__" {
continue
}
if cmd.Group() == "" { // this is top level command
// add group command
if groupCmd, ok := cmdMap[cmd.FullName()]; !ok {
groupCmd = &Command{
FullName: cmd.FullName(),
Group: cmd.Group(),
Name: cmd.Name(),
Package: pkg.Name(),
Registry: repo.Name(),
SubCmds: []*Command{},
}
cmdMap[cmd.FullName()] = groupCmd
}
} else { // this is a sub command
// get the group command full name
groupCmdFullName := fmt.Sprintf("%s@@%s@%s", cmd.Group(), pkg.Name(), repo.Name())
if groupCmd, ok := cmdMap[groupCmdFullName]; !ok {
groupCmd = &Command{
FullName: groupCmdFullName,
Group: "",
Name: cmd.Group(),
Package: pkg.Name(),
Registry: repo.Name(),
SubCmds: []*Command{
&Command{
FullName: cmd.FullName(),
Group: cmd.Group(),
Name: cmd.Name(),
Package: pkg.Name(),
Registry: repo.Name(),
SubCmds: []*Command{},
},
},
}
cmdMap[groupCmdFullName] = groupCmd
} else {
groupCmd.SubCmds = append(groupCmd.SubCmds, &Command{
FullName: cmd.FullName(),
Group: cmd.Group(),
Name: cmd.Name(),
Package: pkg.Name(),
Registry: repo.Name(),
SubCmds: []*Command{},
})
}

}
}
}
}

// load the template
tmpl_text, _ := templates.ReadFile("templates/cmd_index.html")
tmpl, _ := template.New("command_index").Parse(string(tmpl_text))

groups := make([]*Command, 0, len(cmdMap))
for _, cmd := range cmdMap {
groups = append(groups, cmd)
}

sort.Slice(groups, func(i, j int) bool {
// return fmt.Sprintf("%s@%s", groups[i].Package, groups[i].Registry) < fmt.Sprintf("%s@%s", groups[j].Package, groups[j].Registry)
return groups[i].FullName < groups[j].FullName
})

var tpl bytes.Buffer
tmpl.Execute(&tpl, CommandIndex{Commands: groups})

html_wrapper, _ := templates.ReadFile("templates/html_wrapper.html")
content := strings.ReplaceAll(string(html_wrapper), "#INPUT#", tpl.String())
w.Write([]byte(content))
}

func (server *Server) CommandHandler(w http.ResponseWriter, r *http.Request) {
fullName := strings.TrimPrefix(r.URL.Path, "/command/")
fmt.Println(r.URL.Path, fullName)
cmd, _ := (*(server.backend)).FindCommandByFullName(fullName)

tmpl_text, _ := templates.ReadFile("templates/command.html")
tmpl, _ := template.New("command").Parse(string(tmpl_text))
command := Command{
FullName: cmd.FullName(),
Group: cmd.Group(),
Name: cmd.Name(),
Package: cmd.PackageName(),
Registry: cmd.RepositoryID(),
Short: cmd.ShortDescription(),
Long: cmd.LongDescription(),
Examples: cmd.Examples(),
Flags: cmd.Flags(),
}

tmpl.Execute(w, command)
}

func (server *Server) HealthHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
}

// Implement the serve funcation in default backend
func Serve(backend *Backend, port int) error {
server := Server{backend: backend}

http.HandleFunc("/", server.CommandIndexHandler)
http.HandleFunc("/test", server.CommandIndexHandler)
http.HandleFunc("/command/", server.CommandHandler)
http.HandleFunc("/health", server.HealthHandler)
return http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}
112 changes: 112 additions & 0 deletions internal/server/static/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>Command Launcher</title>
<link rel="stylesheet" href="https://unpkg.com/mvp.css">
</head>
<body>
<main>
<nav>
<ul>
<li><a href="/">Commands</a></li>
<li>Registries</li>
<li>Packages</li>
</ul>
</nav>
<article>
<h1>Index</h1>
<div>
<b>moab:</b>
<a href="#moab-init">init</a>
<a href="#moab-build">build</a>
</div>
<div>
<b>doc:</b>
<a href="#moab-init">html</a>
<a href="#moab-build">confluence</a>
</div>
</article>

<article>
<command>
<hr>
<h2 id="moab-init">moab init <a href="#moab-init" style="text-decoration:none">🔗</a></h2>
<p><sup>full name:init@moab@moab@dropin</sup><sup>package:moab</sup><sup>registry:dropin</sup></p>
<p>Initiate a moab workspace</p>
<details>
<summary>Examples</summary>
<div>
<h4>Initiate a java moab workspace and checkout one repository</h4>
<pre><samp>moab init java software-factory/example-projects</samp></pre>
</div>
</details>
<details>
<summary>Try it out</summary>
<form>
<label for="work-dir">work directory<sup>required</sup></label>
<label><small><em>Which directory the command will run from</em></small></label>
<input id="work-dir" type="text" name="work-dir" value="/Users/b.hou/.cdt">

<label for="moab-name">moab name<sup>required</sup></label>
<label><small><em>MOAB name: java or csharp</em></small></label>
<input id="moab-name" type="text" name="moab-name" value="java">
<label for="args">repositories</label>
<label><small><em>repositories to checkout</em></small></label>
<textarea id="args" name="args" rows="4" cols="50"></textarea>
<label for="exclude-ide">exclude-ide</label>
<label><small><em>do not prepare ide</em></small></label>
<input id="exclude-ide" type="checkbox" name="exclude-ide"><b>enable</b></input>
<br>
<button type="submit">Run</button>
</form>
<div>Output</div>
<pre><samp>
npm install -g @command-launcher/cli
another line
$ls -la
.
..
.git
</samp></pre>
</details>
</command>
<command>
<hr>
<h2 id="moab-build">moab build</h2>
<p><sup>package:moab</sup><sup>registry:dropin</sup></p>
<p>Build all the projects in current folder</p>
<details>
<summary>Examples</summary>
<div>
<h4>Initiate a java moab workspace and checkout one repository</h4>
<pre><samp>moab init java software-factory/example-projects</samp></pre>
</div>
</details>
<details>
<summary>Try it out</summary>
<form>
<label for="moab-name">moab name<sup>required</sup></label>
<input id="moab-name" type="text" name="moab-name" value="java">
<label for="args">repositories</label>
<textarea id="args" name="args" rows="4" cols="50"></textarea>
<input id="exclude-ide" type="checkbox" name="exclude-ide"><b>exclude-ide</b></input>
<br>
<button type="submit">Run</button>
</form>
<div>Output</div>
<pre><samp>
npm install -g @command-launcher/cli
another line
$ls -la
.
..
.git
</samp></pre>
</details>
</command>
</article>
</main>
<footer>
</footer>
</body>
</html>
13 changes: 13 additions & 0 deletions internal/server/templates/cmd_index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<article>
<h1>Index</h1>
{{range .Commands}}
<div>
<sup>Registry: {{.Registry}}</sup><sup>Package: {{.Package}}</sup><br><br>
<a href="/command/{{.FullName}}">{{.Group}} {{.Name}}</a><br>
{{range .SubCmds}}
<a href="/command/{{.FullName}}">{{.Group}} {{.Name}}</a><br>
{{end}}
<br><br>
</div>
{{end}}
</article>
Loading

0 comments on commit e68c933

Please sign in to comment.