Skip to content

Commit

Permalink
feat(config): add basic validation for api config
Browse files Browse the repository at this point in the history
  • Loading branch information
ramfox committed Apr 9, 2018
1 parent 873e7e5 commit c353200
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 10 deletions.
67 changes: 58 additions & 9 deletions config/api.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,77 @@
package config

import (
"github.com/qri-io/jsonschema"
)

// DefaultAPIPort is local the port webapp serves on by default
var DefaultAPIPort = "2503"

// API holds configuration for the qri JSON api
type API struct {
Enabled bool
Enabled bool `json:"enabled"`
// APIPort specifies the port to listen for JSON API calls
Port string
Port string `json:"port"`
// read-only mode
ReadOnly bool
ReadOnly bool `json:"readonly"`
// URLRoot is the base url for this server
URLRoot string
URLRoot string `json:"urlroot"`
// TLS enables https via letsEyncrypt
TLS bool
TLS bool `json:"tls"`
// if true, requests that have X-Forwarded-Proto: http will be redirected
// to their https variant
ProxyForceHTTPS bool
ProxyForceHTTPS bool `json:"proxyforcehttps"`
// support CORS signing from a list of origins
AllowedOrigins []string
AllowedOrigins []string `json:"allowedorigins"`
}

// Validate validates all fields of api returning all errors found.
func (a API) Validate() error {
schema := jsonschema.Must(`{
"$schema": "http://json-schema.org/draft-06/schema#",
"title": "api",
"description": "Config for the api",
"type": "object",
"required": ["enabled", "port", "readonly", "urlroot", "tls", "proxyforcehttps", "allowedorigins"],
"properties": {
"enabled": {
"description": "When false, the api port does not listen for calls",
"type": "boolean"
},
"port": {
"description": "The port that listens for JSON API calls",
"type": "string"
},
"readonly": {
"description": "When true, api port limits the accepted calls to certain GET requests",
"type": "boolean"
},
"urlroot": {
"description": "The base url for this server",
"type": "string"
},
"tls": {
"description": "Enables https via letsEncrypt",
"type": "boolean"
},
"proxyforcehttps": {
"description": "When true, requests that have X-Forwarded-Proto: http will be redirected to their https variant",
"type": "boolean"
},
"allowedorigins": {
"description": "Support CORS signing from a list of origins",
"type": "array",
"items": {
"type": "string"
}
}
}
}`)
return validate(schema, &a)
}

// Default returns the default configuration details
func (API) Default() *API {
// DefaultAPI returns the default configuration details
func DefaultAPI() *API {
return &API{
Enabled: true,
Port: DefaultAPIPort,
Expand Down
21 changes: 21 additions & 0 deletions config/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package config

import (
"testing"
)

func TestAPIValidate(t *testing.T) {
apiDefault := API{
Enabled: true,
Port: "2503",
ReadOnly: false,
URLRoot: "",
TLS: false,
ProxyForceHTTPS: false,
AllowedOrigins: []string{"http://localhost:2505"},
}
err := apiDefault.Validate()
if err != nil {
t.Errorf("error validating apiDefault: %s", err)
}
}
19 changes: 18 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
package config

import (
"encoding/json"
"fmt"
"io/ioutil"
"reflect"
"strconv"
"strings"

"github.com/qri-io/jsonschema"
"gopkg.in/yaml.v2"
)

Expand All @@ -34,7 +36,7 @@ func (Config) Default() *Config {
Store: Store{}.Default(),

CLI: CLI{}.Default(),
API: API{}.Default(),
API: DefaultAPI(),
P2P: P2P{}.Default(),
Webapp: Webapp{}.Default(),
RPC: RPC{}.Default(),
Expand Down Expand Up @@ -163,3 +165,18 @@ func (cfg Config) path(path string) (elem reflect.Value, err error) {

return elem, nil
}

// valiate is a helper function that wraps json.Marshal an ValidateBytes
// it is used by each struct that is in a Config field (eg API, Profile, etc)
func validate(rs *jsonschema.RootSchema, s interface{}) error {
strct, err := json.Marshal(s)
if err != nil {
return fmt.Errorf("error marshaling profile to json: %s", err)
}
if errors, err := rs.ValidateBytes(strct); len(errors) > 0 {
return fmt.Errorf("%s", errors[0])
} else if err != nil {
return err
}
return nil
}

0 comments on commit c353200

Please sign in to comment.