Skip to content

Commit

Permalink
#42: refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
iignatevich committed Apr 23, 2024
1 parent a4b67e9 commit 58e4d07
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 57 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
dist/
/swagger-ui
/.husky/_
.idea
82 changes: 47 additions & 35 deletions server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"os"
"sort"

"github.com/launchrctl/launchr/pkg/log"

"github.com/launchrctl/launchr"
"github.com/launchrctl/launchr/pkg/action"
"gopkg.in/yaml.v3"
Expand All @@ -35,27 +37,27 @@ func (l *launchrServer) GetOneRunningActionByID(w http.ResponseWriter, _ *http.R
})
}

func (l *launchrServer) GetRunningActionStreams(w http.ResponseWriter, _ *http.Request, id ActionId, runID ActionRunInfoId, _ GetRunningActionStreamsParams) {
_, ok := l.actionMngr.RunInfoByID(runID)
func (l *launchrServer) GetRunningActionStreams(w http.ResponseWriter, _ *http.Request, id ActionId, runID ActionRunInfoId, params GetRunningActionStreamsParams) {
ri, ok := l.actionMngr.RunInfoByID(runID)
if !ok {
sendError(w, http.StatusNotFound, fmt.Sprintf("action run info with id %q is not found", id))
return
}
outputFile, err := os.ReadFile(fmt.Sprintf("%s-out.txt", id))
streams := ri.Action.GetInput().IO
fStreams, ok := streams.(fileStreams)
if !ok {
panic("not supported")
}
sd, err := fStreams.GetStreamData(params)
if err != nil {
if os.IsNotExist(err) {
sendError(w, http.StatusNotFound, fmt.Sprintf("Output file associated with actionId %q not found", id))
}
sendError(w, http.StatusInternalServerError, "Error accessing file")
log.Debug(err.Error())
sendError(w, http.StatusInternalServerError, "Error reading streams")
}

// @todo: care about error file aswell.
// @todo: care about error file as well

w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(ActionRunStreamData{
Type: "stdOut",
Content: string(outputFile),
})
_ = json.NewEncoder(w).Encode(sd[0])
}

func (l *launchrServer) basePath() string {
Expand Down Expand Up @@ -88,8 +90,14 @@ func (l *launchrServer) GetActionByID(w http.ResponseWriter, _ *http.Request, id
return
}

afull, err := apiActionFull(l.basePath(), a)
if err != nil {
sendError(w, http.StatusInternalServerError, fmt.Sprintf("error on building actionFull %q", id))
return
}

w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(apiActionFull(l.basePath(), a))
_ = json.NewEncoder(w).Encode(afull)
}

func (l *launchrServer) GetActionJSONSchema(w http.ResponseWriter, _ *http.Request, id string) {
Expand All @@ -102,22 +110,27 @@ func (l *launchrServer) GetActionJSONSchema(w http.ResponseWriter, _ *http.Reque
sendError(w, http.StatusInternalServerError, fmt.Sprintf("error on loading action %q", id))
return
}
afull := apiActionFull(l.basePath(), a)

afull, err := apiActionFull(l.basePath(), a)
if err != nil {
sendError(w, http.StatusInternalServerError, fmt.Sprintf("error on building actionFull %q", id))
return
}

w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(afull.JSONSchema)
}

func (l *launchrServer) GetRunningActionsByID(w http.ResponseWriter, _ *http.Request, id string) {
runningActions := l.actionMngr.RunInfoByAction(id)

sortFunc := func(i, j int) bool {
if runningActions[i].Status != runningActions[j].Status {
return runningActions[i].Status < runningActions[j].Status
sort.Slice(runningActions, func(i, j int) bool {
if runningActions[i].Status == runningActions[j].Status {
return runningActions[i].ID < runningActions[j].ID
}
return runningActions[i].ID < runningActions[j].ID
}

sort.Slice(runningActions, sortFunc)
return runningActions[i].Status < runningActions[j].Status
})

var result = make([]ActionRunInfo, 0, len(runningActions))
for _, ri := range runningActions {
Expand Down Expand Up @@ -147,9 +160,10 @@ func (l *launchrServer) RunAction(w http.ResponseWriter, r *http.Request, id str

// Prepare action for run.
// Can we fetch directly json?
streams, err := fileStreams(id)
streams, err := createFileStreams(id)
if err != nil {
sendError(w, http.StatusBadRequest, "Error creation files")
log.Debug(err.Error())
sendError(w, http.StatusInternalServerError, "Error preparing streams")
}

defer func() {
Expand Down Expand Up @@ -182,37 +196,35 @@ func (l *launchrServer) RunAction(w http.ResponseWriter, r *http.Request, id str
})
}

func apiActionFull(baseURL string, a *action.Action) ActionFull {
func apiActionFull(baseURL string, a *action.Action) (ActionFull, error) {
jsonschema := a.JSONSchema()
jsonschema.ID = fmt.Sprintf("%s/actions/%s/schema.json", baseURL, url.QueryEscape(a.ID))
def := a.ActionDef()

var resultMap map[string]interface{}
var uiSchema map[string]interface{}

yamlData, err := os.ReadFile(fmt.Sprintf("%s/ui-schema.yaml", a.Dir()))
if err != nil {
if os.IsNotExist(err) {
fmt.Println("Info: ui-schema.yaml not found, using empty UISchema")
resultMap = map[string]interface{}{}
} else {
panic(err)
if !os.IsNotExist(err) {
return ActionFull{}, err
}

fmt.Println("Info: ui-schema.yaml not found, using empty UISchema")
uiSchema = map[string]interface{}{}
} else {
var data interface{}
err = yaml.Unmarshal(yamlData, &data)
err = yaml.Unmarshal(yamlData, &uiSchema)
if err != nil {
panic(err)
return ActionFull{}, err
}
resultMap, _ = data.(map[string]interface{})
}

return ActionFull{
ID: a.ID,
Title: def.Title,
Description: def.Description,
JSONSchema: jsonschema,
UISchema: resultMap,
}
UISchema: uiSchema,
}, nil
}

func apiActionShort(a *action.Action) (ActionShort, error) {
Expand Down
44 changes: 23 additions & 21 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,27 +74,7 @@ func Run(ctx context.Context, app launchr.App, opts *RunOptions) error {
}

// Serve frontend files.
if opts.ProxyClient != "" {
target, _ := url.Parse(opts.ProxyClient)
proxy := httputil.NewSingleHostReverseProxy(target)

// @todo: Add same SPA serving for proxy aswell.
r.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {
proxy.ServeHTTP(w, r)
})
} else {
r.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {
f, err := opts.ClientFS.Open(strings.TrimPrefix(path.Clean(r.URL.Path), "/"))
if err == nil {
defer f.Close()
}
if os.IsNotExist(err) {
r.URL.Path = "/"
}
http.FileServer(http.FS(opts.ClientFS)).ServeHTTP(w, r)
})

}
r.HandleFunc("/*", spaHandler(opts))

// Use the validation middleware to check all requests against the OpenAPI schema on Api subroutes.
r.Route(opts.APIPrefix, func(r chi.Router) {
Expand Down Expand Up @@ -123,6 +103,28 @@ func Run(ctx context.Context, app launchr.App, opts *RunOptions) error {
return s.ListenAndServe()
}

func spaHandler(opts *RunOptions) http.HandlerFunc {
if opts.ProxyClient != "" {
target, _ := url.Parse(opts.ProxyClient)
proxy := httputil.NewSingleHostReverseProxy(target)

return func(w http.ResponseWriter, r *http.Request) {
proxy.ServeHTTP(w, r)
}
}

return func(w http.ResponseWriter, r *http.Request) {
f, err := opts.ClientFS.Open(strings.TrimPrefix(path.Clean(r.URL.Path), "/"))
if err == nil {
defer f.Close()
}
if os.IsNotExist(err) {
r.URL.Path = "/"
}
http.FileServer(http.FS(opts.ClientFS)).ServeHTTP(w, r)
}
}

func serveSwaggerUI(swagger *openapi3.T, r chi.Router, opts *RunOptions) {
pathUI := opts.APIPrefix + swaggerUIPath
r.Route(pathUI, func(r chi.Router) {
Expand Down
44 changes: 43 additions & 1 deletion server/streams.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package server

import (
"bufio"
"fmt"
"io"
"os"
Expand All @@ -9,6 +10,10 @@ import (
"github.com/launchrctl/launchr/pkg/cli"
)

type fileStreams interface {
GetStreamData(GetRunningActionStreamsParams) ([]*ActionRunStreamData, error)
}

// webCli implements Streams interface.
// @todo Maybe refactor original streams.
type webCli struct {
Expand Down Expand Up @@ -41,6 +46,43 @@ func (cli *webCli) Close() (err error) {
return nil
}

// GetStreamData implements fileStreams.
func (cli *webCli) GetStreamData(_ GetRunningActionStreamsParams) ([]*ActionRunStreamData, error) {
// @todo include GetRunningActionStreamsParams
_, err := cli.files[0].Seek(0, io.SeekStart)
if err != nil {
return nil, err
}
reader := bufio.NewReader(cli.files[0])
outData, err := io.ReadAll(reader)
if err != nil {
return nil, err
}

outSd := &ActionRunStreamData{
Type: StdOut,
Content: string(outData),
}

_, err = cli.files[1].Seek(0, io.SeekStart)
if err != nil {
return nil, err
}
reader = bufio.NewReader(cli.files[1])
errData, err := io.ReadAll(reader)
if err != nil {
return nil, err
}

errSd := &ActionRunStreamData{
Type: StdErr,
Content: string(errData),
}

result := []*ActionRunStreamData{outSd, errSd}
return result, nil
}

type wrappedWriter struct {
p ActionRunStreamDataType
w io.Writer
Expand All @@ -50,7 +92,7 @@ func (w *wrappedWriter) Write(p []byte) (int, error) {
return w.w.Write(p)
}

func fileStreams(actionId ActionId) (*webCli, error) {
func createFileStreams(actionId ActionId) (*webCli, error) {
outfile, err := os.Create(fmt.Sprintf("%s-out.txt", actionId))
if err != nil {
return nil, fmt.Errorf("error creating output file: %w", err)
Expand Down

0 comments on commit 58e4d07

Please sign in to comment.