Add Config test

This commit is contained in:
Trevor Slocum 2019-04-16 05:48:32 -07:00
parent 5ac9e23f3f
commit 181c163669
7 changed files with 195 additions and 51 deletions

View File

@ -3,8 +3,9 @@ package main
const DefaultAuthorEmail = "stick@notes.app"
type Author struct {
Key string
Username string
Name string
Key string
Name string
Email string
Notebooks []*Notebook
}

119
config.go
View File

@ -1,36 +1,75 @@
package main
import (
"fmt"
"io/ioutil"
"sort"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)
const (
AuthorAccessRead = 0
AuthorAccessCheck = 1
AuthorAccessWrite = 2
)
const DefaultConfigPath = "~/.config/stick/stick.yml"
type Config struct {
Salt string
Serve string
Authors []*AuthorConfig
Notebooks []*NotebookConfig
Debug bool
}
func (c *Config) load(path string) {
var err error
type AuthorConfig struct {
Email string
Name string
}
type NotebookConfig struct {
Label string
Repo string
Serve map[string]int
}
func ReadConfigFile(path string) (*Config, error) {
var err error
if path == "" {
path = DefaultConfigPath
}
path, err = expandPath(path)
CheckError(err)
if err != nil {
return nil, err
}
data, err := ioutil.ReadFile(path)
CheckError(err)
if err != nil {
return nil, err
}
c := &Config{}
err = c.load(data)
return c, err
}
func ReadConfigData(data []byte) (*Config, error) {
c := &Config{}
err := c.load(data)
return c, err
}
func (c *Config) load(data []byte) error {
var config map[string]interface{}
err = yaml.Unmarshal(data, &config)
CheckError(err)
err := yaml.Unmarshal(data, &config)
if err != nil {
return err
}
if salt, ok := config["salt"]; ok {
c.Salt = salt.(string)
@ -40,16 +79,13 @@ func (c *Config) load(path string) {
c.Serve = serve.(string)
}
stick.Authors = map[string]*Author{}
if as, ok := config["authors"]; ok {
auths := as.(map[interface{}]interface{})
for author, name := range auths {
stick.Authors[hash(author.(string))] = &Author{Key: hash(author.(string)), Username: author.(string), Name: name.(string)}
for aemail, aname := range auths {
c.Authors = append(c.Authors, &AuthorConfig{Email: aemail.(string), Name: aname.(string)})
}
}
stick.Notebooks = map[string]*Notebook{}
if nbsv, ok := config["notebooks"]; ok {
nbs := nbsv.(map[interface{}]interface{})
var labels []string
@ -57,51 +93,52 @@ func (c *Config) load(path string) {
labels = append(labels, label.(string))
}
sort.Strings(labels)
for _, label := range labels {
notebookrepo := ""
var notebookserve []string
nbconfig := &NotebookConfig{Label: label, Serve: make(map[string]int)}
nbo := nbs[label].(map[interface{}]interface{})
for option, v := range nbo {
switch option.(string) {
case "repo":
notebookrepo = v.(string)
break
nbconfig.Repo = v.(string)
case "serve":
nbsv := v.([]interface{})
for _, nbserve := range nbsv {
notebookserve = append(notebookserve, hash(nbserve.(string)))
for _, nbsvi := range nbsv {
switch nbsvi.(type) {
case string:
nbconfig.Serve[nbsvi.(string)] = AuthorAccessWrite
case map[interface{}]interface{}:
nbsvx := nbsvi.(map[interface{}]interface{})
for nbsvxi, nbsvxa := range nbsvx {
access := -1
switch nbsvxa.(string) {
case "read":
access = AuthorAccessRead
case "check":
access = AuthorAccessCheck
case "write":
access = AuthorAccessWrite
}
if access == -1 {
return errors.New(fmt.Sprintf("invalid serve configuration: invalid access level specified for %s (%s should be read/check/write)", nbsvxi, nbsvxa.(string)))
}
nbconfig.Serve[nbsvxi.(string)] = access
}
default:
return errors.New(fmt.Sprintf("invalid serve configuration: %T", nbsvi))
}
}
break
}
}
if notebookrepo == "" {
panic("invalid notebook configuration: no repo path supplied")
if nbconfig.Repo == "" {
return errors.New("invalid notebook configuration: no repo path supplied")
}
notebook, err := LoadNotebook(notebookrepo)
CheckError(err)
notebook.ID = hash(label)
notebook.Label = label
notebook.Serve = notebookserve
sort.Strings(notebook.Serve)
stick.Notebooks[hash(label)] = notebook
c.Notebooks = append(c.Notebooks, nbconfig)
}
}
var author *Author
for _, notebook := range stick.Notebooks {
for _, serveauth := range notebook.Serve {
author = stick.getAuthor(serveauth)
if author == nil {
continue
}
author.Notebooks = append(author.Notebooks, notebook)
}
}
return nil
}

49
config_test.go Normal file
View File

@ -0,0 +1,49 @@
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestConfig(t *testing.T) {
t.Parallel()
var configData = []byte(`
salt: 'xXxN4CLxXx'
authors:
read@stick.rocketnine.space: 'Read Access'
check@stick.rocketnine.space: 'Check Access'
write@stick.rocketnine.space: 'Write Access'
notebooks:
Test Notebook A:
repo: /home/stick/repo/a
serve:
- read@stick.rocketnine.space: read
- write@stick.rocketnine.space: write
Test Notebook B:
repo: /home/stick/repo/b
serve:
- check@stick.rocketnine.space: check
- write@stick.rocketnine.space
`)
c, err := ReadConfigData(configData)
if err != nil {
t.Errorf("failed to load config data: %+v", err)
}
assert.Equal(t, false, c.Debug)
assert.Equal(t, "xXxN4CLxXx", c.Salt)
assert.Equal(t, []*AuthorConfig{
{Email: "read@stick.rocketnine.space", Name: "Read Access"},
{Email: "check@stick.rocketnine.space", Name: "Check Access"},
{Email: "write@stick.rocketnine.space", Name: "Write Access"}},
c.Authors)
assert.Equal(t, []*NotebookConfig{
{Label: "Test Notebook A", Repo: "/home/stick/repo/a", Serve: map[string]int{"read@stick.rocketnine.space": AuthorAccessRead, "write@stick.rocketnine.space": AuthorAccessWrite}},
{Label: "Test Notebook B", Repo: "/home/stick/repo/b", Serve: map[string]int{"check@stick.rocketnine.space": AuthorAccessCheck, "write@stick.rocketnine.space": AuthorAccessWrite}}},
c.Notebooks)
}

View File

@ -41,7 +41,7 @@ const DefaultSubmoduleDepth = 10
type Notebook struct {
ID string
Label string
Serve []string `json:"-"`
Serve map[string]int `json:"-"`
Repository *git.Repository `json:"-"`
}

View File

@ -1,6 +1,8 @@
package main
import "sync"
import (
"sync"
)
type Stick struct {
Config *Config
@ -13,10 +15,59 @@ type Stick struct {
}
func Initialize(configPath string, debug bool) {
var err error
stick = &Stick{}
stick.Config = &Config{}
stick.Config.load(configPath)
stick.Config, err = ReadConfigFile(configPath)
CheckError(err)
stick.Config.Debug = debug
StickSalt = []byte(stick.Config.Salt)
stick.loadAuthors()
stick.loadNotebooks()
}
func (s *Stick) loadAuthors() {
// TODO: Allow reloading config
stick.Authors = map[string]*Author{}
for _, author := range stick.Config.Authors {
stick.Authors[hash(author.Email)] = &Author{Key: hash(author.Email), Email: author.Email, Name: author.Name}
}
}
func (s *Stick) loadNotebooks() {
// TODO: Allow reloading config
stick.Notebooks = make(map[string]*Notebook)
for _, nbconfig := range stick.Config.Notebooks {
notebook, err := LoadNotebook(nbconfig.Repo)
CheckError(err)
notebook.ID = hash(nbconfig.Label)
notebook.Label = nbconfig.Label
notebook.Serve = make(map[string]int)
for serveauthor, serveaccess := range nbconfig.Serve {
notebook.Serve[hash(serveauthor)] = serveaccess
}
stick.Notebooks[hash(nbconfig.Label)] = notebook
}
var author *Author
for _, notebook := range stick.Notebooks {
for serveauth := range notebook.Serve {
author = stick.getAuthor(serveauth)
if author == nil {
continue
}
author.Notebooks = append(author.Notebooks, notebook)
}
}
}
func (s *Stick) getAuthor(key string) *Author {

View File

@ -10,6 +10,8 @@ import (
"time"
)
var StickSalt []byte
func expandPath(path string) (string, error) {
if !strings.HasPrefix(path, "~") {
return path, nil
@ -23,12 +25,16 @@ func expandPath(path string) (string, error) {
return homedir + path[1:], nil
}
func hash(str string) string {
h := hmac.New(sha256.New, []byte(stick.Config.Salt))
func hashWithSalt(str string, salt []byte) string {
h := hmac.New(sha256.New, salt)
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
func hash(str string) string {
return hashWithSalt(str, StickSalt)
}
func elapsed(what string) func() {
start := time.Now()
return func() {

2
web.go
View File

@ -196,7 +196,7 @@ func printServedNotebooks() {
}
log.Println()
log.Printf("%-10s http://%s/#%s", author.Name[0:int(math.Min(10, float64(len(author.Name))))], stick.Config.Serve, authkey)
log.Printf("%-10s http://%s/#login/%s", author.Name[0:int(math.Min(10, float64(len(author.Name))))], stick.Config.Serve, authkey)
for _, notebook := range author.Notebooks {
log.Printf("- %s", notebook.Label[0:int(math.Min(20, float64(len(notebook.Label))))])
}