Skip to content

Commit

Permalink
feat: Branching form to backend (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
ViBiOh authored Apr 17, 2020
1 parent 0832bde commit 1d46479
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 86 deletions.
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
Expand Down Expand Up @@ -87,6 +88,7 @@ github.com/tdewolff/parse/v2 v2.3.14/go.mod h1:+V2lSZ93xpH2Csfs/vtNY1Fjr8kcFMsZK
github.com/tdewolff/parse/v2 v2.4.2 h1:Bu2Qv6wepkc+Ou7iB/qHjAhEImlAP5vedzlQRUdj3BI=
github.com/tdewolff/parse/v2 v2.4.2/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/test v1.0.4/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand All @@ -102,8 +104,10 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
8 changes: 5 additions & 3 deletions pkg/target/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (a app) Create(ctx context.Context, o interface{}) (item interface{}, err e

release, githubErr := a.githubApp.LastRelease(target.Repository)
if githubErr != nil {
err = fmt.Errorf("unable to get latest release for %s: %s", target.Repository, githubErr)
err = githubErr
return
}

Expand Down Expand Up @@ -165,8 +165,10 @@ func (a app) Check(ctx context.Context, old, new interface{}) []crud.Error {
errors = append(errors, crud.NewError("current_version", "current version is required"))
}

if target, err := a.getTargetByRepository(item.Repository); err == nil {
errors = append(errors, crud.NewError("repository", fmt.Sprintf("repository already exists with id %d", target.ID)))
if old == nil {
if target, err := a.getTargetByRepository(item.Repository); err == nil {
errors = append(errors, crud.NewError("repository", fmt.Sprintf("repository already exists with id %d", target.ID)))
}
}

return errors
Expand Down
28 changes: 28 additions & 0 deletions pkg/ui/svg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ui

import (
"fmt"
"net/http"
"strings"

"github.com/ViBiOh/httputils/v3/pkg/httperror"
"github.com/ViBiOh/httputils/v3/pkg/templates"
)

// SVG render a svg in given coolor
func (a app) svg() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tpl := a.tpl.Lookup(fmt.Sprintf("svg-%s", strings.Trim(r.URL.Path, "/")))
if tpl == nil {
httperror.NotFound(w)
return
}

w.Header().Set("Content-Type", "image/svg+xml")

if err := templates.WriteTemplate(tpl, w, r.URL.Query().Get("fill"), "text/xml"); err != nil {
httperror.InternalServerError(w, err)
return
}
})
}
118 changes: 118 additions & 0 deletions pkg/ui/targets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package ui

import (
"fmt"
"net/http"
"strconv"
"strings"

"github.com/ViBiOh/ketchup/pkg/target"
)

// SVG render a svg in given coolor
func (a app) targets() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
a.handleError(w, http.StatusMethodNotAllowed, fmt.Errorf("invalid method %s", r.Method), nil)
return
}

if err := r.ParseForm(); err != nil {
a.handleError(w, http.StatusBadRequest, err, nil)
return
}

method := strings.ToUpper(r.FormValue("method"))

switch method {
case http.MethodPost:
a.handleCreate(w, r)
case http.MethodPut:
a.handleUpdate(w, r)
case http.MethodDelete:
a.handleDelete(w, r)
default:
a.handleError(w, http.StatusBadRequest, fmt.Errorf("invalid method %s", method), nil)
}
})
}

func (a app) handleCreate(w http.ResponseWriter, r *http.Request) {
target := target.Target{
Repository: r.FormValue("repository"),
CurrentVersion: r.FormValue("currentVersion"),
}

if errors := a.targetApp.Check(r.Context(), nil, target); len(errors) > 0 {
a.handleError(w, http.StatusBadRequest, fmt.Errorf("invalid form"), errors)
return
}

if _, err := a.targetApp.Create(r.Context(), target); err != nil {
a.handleError(w, http.StatusInternalServerError, err, nil)
return
}

http.Redirect(w, r, "/", http.StatusFound)
}

func (a app) handleUpdate(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseUint(strings.Trim(r.URL.Path, "/"), 10, 64)
if err != nil {
a.handleError(w, http.StatusBadRequest, err, nil)
return
}

rawOldTarget, err := a.targetApp.Get(r.Context(), id)
if err != nil {
a.handleError(w, http.StatusBadRequest, err, nil)
return
}

oldTarget := rawOldTarget.(target.Target)

newTarget := target.Target{
ID: id,
Repository: r.FormValue("repository"),
CurrentVersion: r.FormValue("currentVersion"),
LatestVersion: oldTarget.LatestVersion,
}

if errors := a.targetApp.Check(r.Context(), oldTarget, newTarget); len(errors) > 0 {
a.handleError(w, http.StatusBadRequest, fmt.Errorf("invalid form"), errors)
return
}

if _, err := a.targetApp.Update(r.Context(), newTarget); err != nil {
a.handleError(w, http.StatusInternalServerError, err, nil)
return
}

http.Redirect(w, r, "/", http.StatusFound)
}

func (a app) handleDelete(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseUint(strings.Trim(r.URL.Path, "/"), 10, 64)
if err != nil {
a.handleError(w, http.StatusBadRequest, err, nil)
return
}

target, err := a.targetApp.Get(r.Context(), id)
if err != nil {
a.handleError(w, http.StatusBadRequest, err, nil)
return
}

if errors := a.targetApp.Check(r.Context(), target, nil); len(errors) > 0 {
a.handleError(w, http.StatusBadRequest, fmt.Errorf("invalid form"), errors)
return
}

if err := a.targetApp.Delete(r.Context(), target); err != nil {
a.handleError(w, http.StatusInternalServerError, err, nil)
return
}

http.Redirect(w, r, "/", http.StatusFound)
}
74 changes: 43 additions & 31 deletions pkg/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,27 @@ import (
"os"
"strings"

"github.com/ViBiOh/httputils/v3/pkg/crud"
"github.com/ViBiOh/httputils/v3/pkg/httperror"
"github.com/ViBiOh/httputils/v3/pkg/logger"
"github.com/ViBiOh/httputils/v3/pkg/templates"
"github.com/ViBiOh/ketchup/pkg/model"
"github.com/ViBiOh/ketchup/pkg/target"
)

const (
svgPath = "/svg"
targetsPath = "/targets"
)

// App of package
type App interface {
Handler() http.Handler
}

type app struct {
tpl *template.Template
tpl *template.Template
version string

targetApp target.App
}
Expand All @@ -33,54 +41,58 @@ func New(targetApp target.App) (App, error) {

return &app{
tpl: template.Must(template.New("ketchup").ParseFiles(filesTemplates...)),
version: os.Getenv("VERSION"),
targetApp: targetApp,
}, nil
}

// SVG render a svg in given coolor
func (a app) SVG(w http.ResponseWriter, name, fill string) {
tpl := a.tpl.Lookup(fmt.Sprintf("svg-%s", name))
if tpl == nil {
httperror.NotFound(w)
return
}

w.Header().Set("Content-Type", "image/svg+xml")

if err := templates.WriteTemplate(tpl, w, fill, "text/xml"); err != nil {
httperror.InternalServerError(w, err)
return
}
}

// Handler for request. Should be use with net/http
func (a app) Handler() http.Handler {
version := os.Getenv("VERSION")
svgHandler := http.StripPrefix(svgPath, a.svg())
targetsHandler := http.StripPrefix(targetsPath, a.targets())

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/svg") {
a.SVG(w, strings.TrimPrefix(r.URL.Path, "/svg/"), r.URL.Query().Get("fill"))
if strings.HasPrefix(r.URL.Path, svgPath) {
svgHandler.ServeHTTP(w, r)
return
}

content := map[string]interface{}{
"Version": version,
if strings.HasPrefix(r.URL.Path, targetsPath) {
targetsHandler.ServeHTTP(w, r)
return
}
status := http.StatusOK

targets, _, err := a.targetApp.List(r.Context(), 1, 100, "", false, nil)
if err != nil {
content["Message"] = model.Message{
Level: "error",
Content: err.Error(),
}
status = http.StatusInternalServerError
} else {
content["Targets"] = targets
a.handleError(w, http.StatusInternalServerError, err, nil)
return
}

content := map[string]interface{}{
"Version": a.version,
"Targets": targets,
}

if err := templates.ResponseHTMLTemplate(a.tpl.Lookup("ketchup"), w, content, status); err != nil {
if err := templates.ResponseHTMLTemplate(a.tpl.Lookup("app"), w, content, http.StatusOK); err != nil {
httperror.InternalServerError(w, err)
}
})
}

func (a app) handleError(w http.ResponseWriter, status int, err error, errors []crud.Error) {
logger.Error("%s", err)

content := map[string]interface{}{
"Version": a.version,
"Message": model.Message{
Level: "error",
Content: err.Error(),
},
"Errors": errors,
}

if err := templates.ResponseHTMLTemplate(a.tpl.Lookup("error"), w, content, status); err != nil {
httperror.InternalServerError(w, err)
return
}
}
Loading

0 comments on commit 1d46479

Please sign in to comment.