mirror of
https://github.com/Bornholm/formidable.git
synced 2024-12-22 14:59:34 +01:00
feat: values updaters
This commit is contained in:
parent
08476e5346
commit
0e7e955a58
54
README.md
54
README.md
@ -30,17 +30,59 @@ It will download `frmd` to your current directory.
|
||||
|
||||
Formidable uses URLs to define how to handle schemas/defaults/values.
|
||||
|
||||
For example, to edit a web available schema (in YAML), defaults from `stdin` (in JSON) and values from the local file system (in HCL):
|
||||
For example, to edit a schema (in YAML) from an HTTPS server, while readig default values from `stdin` (in JSON) and using effective values from the local file system (in HCL), outputing updates to `stdout`:
|
||||
|
||||
```shell
|
||||
```bash
|
||||
echo '{}' | frmd \
|
||||
edit
|
||||
--schema https://example.com/my-schema.yml \
|
||||
--defaults stdin://local?format=json \
|
||||
--values file:///my/file/absolute/path.hcl
|
||||
--schema 'https://example.com/my-schema.yml' \
|
||||
--defaults 'stdin://local?format=json' \
|
||||
--values 'file:///my/file/absolute/path.hcl' \
|
||||
--output 'stdout://local?format=json'
|
||||
```
|
||||
|
||||
The `?format=<json|yaml|hcl>` query variable allows to specify the file format when no file extension is available (for example when reading from `stdin`).
|
||||
### Available loaders
|
||||
|
||||
#### `stdin://`
|
||||
|
||||
> TODO: Write doc + example
|
||||
#### `http://` and `https://`
|
||||
|
||||
> TODO: Write doc + example
|
||||
#### `file://`
|
||||
|
||||
> TODO: Write doc + example
|
||||
|
||||
### Available formats
|
||||
|
||||
#### JSON
|
||||
|
||||
- URL Query: `?format=json`
|
||||
- File extension: `.json`
|
||||
|
||||
#### YAML
|
||||
|
||||
- URL Query: `?format=yaml`
|
||||
- File extension: `.yaml` or `.yml`
|
||||
|
||||
#### HCL
|
||||
|
||||
- URL Query: `?format=hcl`
|
||||
- File extension: `.hcl`
|
||||
|
||||
### Available outputs
|
||||
|
||||
#### `stdout://` (default)
|
||||
|
||||
> TODO: Write doc + example
|
||||
|
||||
#### `file://`
|
||||
|
||||
> TODO: Write doc + example
|
||||
|
||||
#### `exec://`
|
||||
|
||||
> TODO: Write doc + example
|
||||
|
||||
## Licence
|
||||
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
encjson "encoding/json"
|
||||
|
||||
@ -15,6 +14,9 @@ import (
|
||||
"forge.cadoles.com/wpetit/formidable/internal/data/scheme/file"
|
||||
"forge.cadoles.com/wpetit/formidable/internal/data/scheme/http"
|
||||
"forge.cadoles.com/wpetit/formidable/internal/data/scheme/stdin"
|
||||
"forge.cadoles.com/wpetit/formidable/internal/data/updater/exec"
|
||||
fileUpdater "forge.cadoles.com/wpetit/formidable/internal/data/updater/file"
|
||||
"forge.cadoles.com/wpetit/formidable/internal/data/updater/stdout"
|
||||
"forge.cadoles.com/wpetit/formidable/internal/def"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/santhosh-tekuri/jsonschema/v5"
|
||||
@ -50,8 +52,8 @@ func commonFlags() []cli.Flag {
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Aliases: []string{"o", "out"},
|
||||
Value: "-",
|
||||
Usage: "Output modified values to `output_file` (or '-' for stdout, the default)",
|
||||
Value: "stdout://local?format=json",
|
||||
Usage: "Output modified values to specified URL",
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -147,29 +149,39 @@ func loadSchema(ctx *cli.Context) (*jsonschema.Schema, error) {
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
const OutputStdout = "-"
|
||||
func outputValues(ctx *cli.Context, values interface{}) error {
|
||||
outputFlag := ctx.String("output")
|
||||
|
||||
type noopWriteCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (c *noopWriteCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func outputWriter(ctx *cli.Context) (io.WriteCloser, error) {
|
||||
output := ctx.String("output")
|
||||
|
||||
if output == OutputStdout {
|
||||
return &noopWriteCloser{ctx.App.Writer}, nil
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(output, os.O_WRONLY|os.O_CREATE, 0o644)
|
||||
url, err := url.Parse(outputFlag)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return file, nil
|
||||
encoder := newEncoder()
|
||||
|
||||
reader, err := encoder.Encode(url, values)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
updater := newUpdater()
|
||||
|
||||
writer, err := updater.Update(url)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := writer.Close(); err != nil {
|
||||
panic(errors.WithStack(err))
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err := io.Copy(writer, reader); err != nil && !errors.Is(err, io.EOF) {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newLoader() *data.Loader {
|
||||
@ -187,3 +199,18 @@ func newDecoder() *data.Decoder {
|
||||
yaml.NewDecoderHandler(),
|
||||
)
|
||||
}
|
||||
|
||||
func newUpdater() *data.Updater {
|
||||
return data.NewUpdater(
|
||||
stdout.NewUpdaterHandler(),
|
||||
fileUpdater.NewUpdaterHandler(),
|
||||
exec.NewUpdaterHandler(),
|
||||
)
|
||||
}
|
||||
|
||||
func newEncoder() *data.Encoder {
|
||||
return data.NewEncoder(
|
||||
json.NewEncoderHandler(),
|
||||
yaml.NewEncoderHandler(),
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
@ -49,17 +48,8 @@ func Delete() *cli.Command {
|
||||
return errors.Wrap(err, "could not validate resulting json")
|
||||
}
|
||||
|
||||
output, err := outputWriter(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create output writer")
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(output)
|
||||
|
||||
encoder.SetIndent("", " ")
|
||||
|
||||
if err := encoder.Encode(updatedValues); err != nil {
|
||||
return errors.Wrap(err, "could not write to output")
|
||||
if err := outputValues(ctx, updatedValues); err != nil {
|
||||
return errors.Wrap(err, "could not output updated values")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -52,6 +52,13 @@ func Edit() *cli.Command {
|
||||
server.WithSchema(schema),
|
||||
server.WithValues(values),
|
||||
server.WithDefaults(defaults),
|
||||
server.WithOnUpdate(func(values interface{}) error {
|
||||
if err := outputValues(ctx, values); err != nil {
|
||||
return errors.Wrap(err, "could not output updated values")
|
||||
}
|
||||
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
|
||||
addrs, srvErrs := srv.Start(srvCtx)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
@ -50,17 +49,8 @@ func Get() *cli.Command {
|
||||
return errors.Wrapf(err, "could not get value from pointer '%v'", rawPointer)
|
||||
}
|
||||
|
||||
output, err := outputWriter(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create output writer")
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(output)
|
||||
|
||||
encoder.SetIndent("", " ")
|
||||
|
||||
if err := encoder.Encode(value); err != nil {
|
||||
return errors.Wrap(err, "could not write to output")
|
||||
if err := outputValues(ctx, value); err != nil {
|
||||
return errors.Wrap(err, "could not output updated values")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -1,7 +1,6 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
@ -44,23 +43,17 @@ func Set() *cli.Command {
|
||||
|
||||
pointer := jsonpointer.New(rawPointer)
|
||||
|
||||
var value interface{}
|
||||
|
||||
if err := json.Unmarshal([]byte(rawValue), &value); err != nil {
|
||||
return errors.Wrapf(err, "could not parse json '%s'", rawValue)
|
||||
}
|
||||
|
||||
var updatedValues interface{}
|
||||
|
||||
force := ctx.Bool("force")
|
||||
|
||||
if force {
|
||||
updatedValues, err = pointer.Force(values, value)
|
||||
updatedValues, err = pointer.Force(values, rawValue)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not force value '%v' to pointer '%v'", rawValue, rawPointer)
|
||||
}
|
||||
} else {
|
||||
updatedValues, err = pointer.Set(values, value)
|
||||
updatedValues, err = pointer.Set(values, rawValue)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not set value '%v' to pointer '%v'", rawValue, rawPointer)
|
||||
}
|
||||
@ -76,17 +69,8 @@ func Set() *cli.Command {
|
||||
return errors.Wrap(err, "could not validate resulting json")
|
||||
}
|
||||
|
||||
output, err := outputWriter(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create output writer")
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(output)
|
||||
|
||||
encoder.SetIndent("", " ")
|
||||
|
||||
if err := encoder.Encode(updatedValues); err != nil {
|
||||
return errors.Wrap(err, "could not write to output")
|
||||
if err := outputValues(ctx, updatedValues); err != nil {
|
||||
return errors.Wrap(err, "could not output updated values")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
40
internal/data/format/json/encoder_handler.go
Normal file
40
internal/data/format/json/encoder_handler.go
Normal file
@ -0,0 +1,40 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"forge.cadoles.com/wpetit/formidable/internal/data/format"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type EncoderHandler struct{}
|
||||
|
||||
func (d *EncoderHandler) Match(url *url.URL) bool {
|
||||
ext := filepath.Ext(path.Join(url.Host, url.Path))
|
||||
|
||||
return ext == ExtensionJSON ||
|
||||
format.MatchURLQueryFormat(url, FormatJSON)
|
||||
}
|
||||
|
||||
func (d *EncoderHandler) Encode(url *url.URL, data interface{}) (io.Reader, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
encoder := json.NewEncoder(&buf)
|
||||
|
||||
encoder.SetIndent("", " ")
|
||||
|
||||
if err := encoder.Encode(data); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return &buf, nil
|
||||
}
|
||||
|
||||
func NewEncoderHandler() *EncoderHandler {
|
||||
return &EncoderHandler{}
|
||||
}
|
38
internal/data/format/yaml/encoder_handler.go
Normal file
38
internal/data/format/yaml/encoder_handler.go
Normal file
@ -0,0 +1,38 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"forge.cadoles.com/wpetit/formidable/internal/data/format"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type EncoderHandler struct{}
|
||||
|
||||
func (d *EncoderHandler) Match(url *url.URL) bool {
|
||||
ext := filepath.Ext(path.Join(url.Host, url.Path))
|
||||
|
||||
return ExtensionYAML.MatchString(ext) ||
|
||||
format.MatchURLQueryFormat(url, FormatYAML)
|
||||
}
|
||||
|
||||
func (d *EncoderHandler) Encode(url *url.URL, data interface{}) (io.Reader, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
encoder := yaml.NewEncoder(&buf)
|
||||
|
||||
if err := encoder.Encode(data); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return &buf, nil
|
||||
}
|
||||
|
||||
func NewEncoderHandler() *EncoderHandler {
|
||||
return &EncoderHandler{}
|
||||
}
|
38
internal/data/updater.go
Normal file
38
internal/data/updater.go
Normal file
@ -0,0 +1,38 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type UpdaterHandler interface {
|
||||
URLMatcher
|
||||
Update(url *url.URL) (io.WriteCloser, error)
|
||||
}
|
||||
|
||||
type Updater struct {
|
||||
handlers []UpdaterHandler
|
||||
}
|
||||
|
||||
func (u *Updater) Update(url *url.URL) (io.WriteCloser, error) {
|
||||
for _, h := range u.handlers {
|
||||
if !h.Match(url) {
|
||||
continue
|
||||
}
|
||||
|
||||
wr, err := h.Update(url)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return wr, nil
|
||||
}
|
||||
|
||||
return nil, errors.Wrapf(ErrHandlerNotFound, "could not find matching handler for url '%s'", url.String())
|
||||
}
|
||||
|
||||
func NewUpdater(handlers ...UpdaterHandler) *Updater {
|
||||
return &Updater{handlers}
|
||||
}
|
57
internal/data/updater/exec/updater_handler.go
Normal file
57
internal/data/updater/exec/updater_handler.go
Normal file
@ -0,0 +1,57 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const SchemeExec = "exec"
|
||||
|
||||
type UpdaterHandler struct{}
|
||||
|
||||
func (h *UpdaterHandler) Match(url *url.URL) bool {
|
||||
return url.Scheme == SchemeExec
|
||||
}
|
||||
|
||||
func (u *UpdaterHandler) Update(url *url.URL) (io.WriteCloser, error) {
|
||||
path := filepath.Join(url.Host, url.Path)
|
||||
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(absPath)
|
||||
|
||||
if url.Query().Get("env") == "yes" {
|
||||
cmd.Env = os.Environ()
|
||||
}
|
||||
|
||||
if url.Query().Get("stdout") == "yes" {
|
||||
cmd.Stdout = os.Stdout
|
||||
}
|
||||
|
||||
if url.Query().Get("stderr") == "yes" {
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
writer, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
panic(errors.WithStack(err))
|
||||
}
|
||||
|
||||
return writer, nil
|
||||
}
|
||||
|
||||
func NewUpdaterHandler() *UpdaterHandler {
|
||||
return &UpdaterHandler{}
|
||||
}
|
33
internal/data/updater/file/updater_handler.go
Normal file
33
internal/data/updater/file/updater_handler.go
Normal file
@ -0,0 +1,33 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const SchemeFile = "file"
|
||||
|
||||
type UpdaterHandler struct{}
|
||||
|
||||
func (h *UpdaterHandler) Match(url *url.URL) bool {
|
||||
return url.Scheme == SchemeFile
|
||||
}
|
||||
|
||||
func (u *UpdaterHandler) Update(url *url.URL) (io.WriteCloser, error) {
|
||||
name := filepath.Join(url.Host, url.Path)
|
||||
|
||||
file, err := os.Create(name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not open file '%s'", name)
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func NewUpdaterHandler() *UpdaterHandler {
|
||||
return &UpdaterHandler{}
|
||||
}
|
23
internal/data/updater/stdout/updater_handler.go
Normal file
23
internal/data/updater/stdout/updater_handler.go
Normal file
@ -0,0 +1,23 @@
|
||||
package stdout
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
const SchemeStdout = "stdout"
|
||||
|
||||
type UpdaterHandler struct{}
|
||||
|
||||
func (h *UpdaterHandler) Match(url *url.URL) bool {
|
||||
return url.Scheme == SchemeStdout
|
||||
}
|
||||
|
||||
func (u *UpdaterHandler) Update(url *url.URL) (io.WriteCloser, error) {
|
||||
return os.Stdout, nil
|
||||
}
|
||||
|
||||
func NewUpdaterHandler() *UpdaterHandler {
|
||||
return &UpdaterHandler{}
|
||||
}
|
@ -11,6 +11,7 @@ type Option struct {
|
||||
Schema *jsonschema.Schema
|
||||
Values interface{}
|
||||
Defaults interface{}
|
||||
OnUpdate OnUpdateFunc
|
||||
}
|
||||
|
||||
type OptionFunc func(*Option)
|
||||
@ -47,3 +48,9 @@ func WithDefaults(defaults interface{}) OptionFunc {
|
||||
opt.Defaults = defaults
|
||||
}
|
||||
}
|
||||
|
||||
func WithOnUpdate(onUpdate OnUpdateFunc) OptionFunc {
|
||||
return func(opt *Option) {
|
||||
opt.OnUpdate = onUpdate
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package route
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
@ -12,68 +12,72 @@ import (
|
||||
"github.com/santhosh-tekuri/jsonschema/v5"
|
||||
)
|
||||
|
||||
func createRenderFormHandlerFunc(schema *jsonschema.Schema, defaults, values interface{}) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
data := &template.FormItemData{
|
||||
Parent: nil,
|
||||
Schema: schema,
|
||||
Property: "",
|
||||
Defaults: defaults,
|
||||
Values: values,
|
||||
func (s *Server) serveFormReq(w http.ResponseWriter, r *http.Request) {
|
||||
data := &template.FormItemData{
|
||||
Parent: nil,
|
||||
Schema: s.schema,
|
||||
Property: "",
|
||||
Defaults: s.defaults,
|
||||
Values: s.values,
|
||||
}
|
||||
|
||||
if err := s.schema.Validate(data.Values); err != nil {
|
||||
validationErr, ok := err.(*jsonschema.ValidationError)
|
||||
if !ok {
|
||||
panic(errors.Wrap(err, "could not validate values"))
|
||||
}
|
||||
|
||||
if err := schema.Validate(data.Values); err != nil {
|
||||
validationErr, ok := err.(*jsonschema.ValidationError)
|
||||
if !ok {
|
||||
panic(errors.Wrap(err, "could not validate values"))
|
||||
}
|
||||
data.Error = validationErr
|
||||
}
|
||||
|
||||
data.Error = validationErr
|
||||
}
|
||||
|
||||
if err := template.Exec("index.html.tmpl", w, data); err != nil {
|
||||
panic(errors.WithStack(err))
|
||||
}
|
||||
if err := template.Exec("index.html.tmpl", w, data); err != nil {
|
||||
panic(errors.WithStack(err))
|
||||
}
|
||||
}
|
||||
|
||||
func createHandleFormHandlerFunc(schema *jsonschema.Schema, defaults, values interface{}) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
data := &template.FormItemData{
|
||||
Parent: nil,
|
||||
Schema: schema,
|
||||
Property: "",
|
||||
Defaults: defaults,
|
||||
Values: values,
|
||||
}
|
||||
func (s *Server) handleFormReq(w http.ResponseWriter, r *http.Request) {
|
||||
data := &template.FormItemData{
|
||||
Parent: nil,
|
||||
Schema: s.schema,
|
||||
Property: "",
|
||||
Defaults: s.defaults,
|
||||
Values: s.values,
|
||||
}
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
panic(errors.WithStack(err))
|
||||
} else {
|
||||
values, err = handleForm(r.Form, schema, values)
|
||||
if err != nil {
|
||||
panic(errors.WithStack(err))
|
||||
}
|
||||
var values interface{}
|
||||
|
||||
data.Values = values
|
||||
}
|
||||
|
||||
if err := schema.Validate(data.Values); err != nil {
|
||||
validationErr, ok := err.(*jsonschema.ValidationError)
|
||||
if !ok {
|
||||
panic(errors.Wrap(err, "could not validate values"))
|
||||
}
|
||||
|
||||
data.Error = validationErr
|
||||
}
|
||||
|
||||
if data.Error == nil {
|
||||
data.SuccessMessage = "Data updated."
|
||||
}
|
||||
|
||||
if err := template.Exec("index.html.tmpl", w, data); err != nil {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
panic(errors.WithStack(err))
|
||||
} else {
|
||||
values, err = handleForm(r.Form, s.schema, values)
|
||||
if err != nil {
|
||||
panic(errors.WithStack(err))
|
||||
}
|
||||
|
||||
data.Values = values
|
||||
}
|
||||
|
||||
if err := s.schema.Validate(data.Values); err != nil {
|
||||
validationErr, ok := err.(*jsonschema.ValidationError)
|
||||
if !ok {
|
||||
panic(errors.Wrap(err, "could not validate values"))
|
||||
}
|
||||
|
||||
data.Error = validationErr
|
||||
}
|
||||
|
||||
if data.Error == nil {
|
||||
if s.onUpdate != nil {
|
||||
if err := s.onUpdate(data.Values); err != nil {
|
||||
panic(errors.Wrap(err, "could not update values"))
|
||||
}
|
||||
}
|
||||
|
||||
data.SuccessMessage = "Data updated."
|
||||
}
|
||||
|
||||
if err := template.Exec("index.html.tmpl", w, data); err != nil {
|
||||
panic(errors.WithStack(err))
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/santhosh-tekuri/jsonschema/v5"
|
||||
)
|
||||
|
||||
func NewHandler(schema *jsonschema.Schema, defaults, values interface{}, assetsHandler http.Handler) (*chi.Mux, error) {
|
||||
router := chi.NewRouter()
|
||||
|
||||
router.Use(middleware.RequestID)
|
||||
// router.Use(middleware.Logger)
|
||||
|
||||
router.Get("/", createRenderFormHandlerFunc(schema, defaults, values))
|
||||
router.Post("/", createHandleFormHandlerFunc(schema, defaults, values))
|
||||
|
||||
router.Handle("/assets/*", assetsHandler)
|
||||
|
||||
return router, nil
|
||||
}
|
@ -7,8 +7,8 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"forge.cadoles.com/wpetit/formidable/internal/server/route"
|
||||
"forge.cadoles.com/wpetit/formidable/internal/server/template"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/santhosh-tekuri/jsonschema/v5"
|
||||
)
|
||||
@ -19,8 +19,11 @@ type Server struct {
|
||||
schema *jsonschema.Schema
|
||||
defaults interface{}
|
||||
values interface{}
|
||||
onUpdate OnUpdateFunc
|
||||
}
|
||||
|
||||
type OnUpdateFunc func(values interface{}) error
|
||||
|
||||
func (s *Server) Start(ctx context.Context) (<-chan net.Addr, <-chan error) {
|
||||
errs := make(chan error)
|
||||
addrs := make(chan net.Addr)
|
||||
@ -71,16 +74,15 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
|
||||
assets := getEmbeddedAssets()
|
||||
assetsHandler := http.FileServer(http.FS(assets))
|
||||
|
||||
handler, err := route.NewHandler(s.schema, s.defaults, s.values, assetsHandler)
|
||||
if err != nil {
|
||||
errs <- errors.WithStack(err)
|
||||
router := chi.NewRouter()
|
||||
|
||||
return
|
||||
}
|
||||
router.Get("/", s.serveFormReq)
|
||||
router.Post("/", s.handleFormReq)
|
||||
router.Handle("/assets/*", assetsHandler)
|
||||
|
||||
log.Println("http server listening")
|
||||
|
||||
if err := http.Serve(listener, handler); err != nil && !errors.Is(err, net.ErrClosed) {
|
||||
if err := http.Serve(listener, router); err != nil && !errors.Is(err, net.ErrClosed) {
|
||||
errs <- errors.WithStack(err)
|
||||
}
|
||||
|
||||
@ -99,5 +101,6 @@ func New(funcs ...OptionFunc) *Server {
|
||||
schema: opt.Schema,
|
||||
defaults: opt.Defaults,
|
||||
values: opt.Values,
|
||||
onUpdate: opt.OnUpdate,
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user