Skip to content

Commit

Permalink
Refactor and add support for host via cli args
Browse files Browse the repository at this point in the history
  • Loading branch information
matronator committed Apr 10, 2024
1 parent 5749408 commit 00af4a2
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 59 deletions.
1 change: 1 addition & 0 deletions .idea/active-tab-highlighter-v2.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .idea/dictionaries/matronator.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ Alternatively you can define environment variables or an `.env` file to configur
```dotenv
AMOCK_HOST=localhost
AMOCK_PORT=8080
AMOCK_DIR=path/to/entities# default is empty
AMOCK_ENTITIES='[user.json, post.json]'# default is empty
AMOCK_DIR='path/to/entities' # default is empty
AMOCK_ENTITIES='[user.json, post.json]' # default is empty
AMOCK_INIT_COUNT=20
```

Expand Down
90 changes: 78 additions & 12 deletions database.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,49 @@ import (
"github.com/jwalton/gchalk"
)

type Database struct {
Tables map[string]Table
}

type Table struct {
Name string
File string
DefinitionFile string
Definition map[string]*Field
SchemaFile string
LastAutoID uint
}

type Entity map[string]any

type EntityCollection []Entity

type EntityJSON map[string]string

type Filter struct {
Field string
Operator string
Value any
Apply func(EntityCollection) EntityCollection
}

type Sort struct {
Field string
Order string
}

type PaginatedItems struct {
First int
Last int
Prev int
Next int
Pages int
Count int
Items EntityCollection
}

type EntityIds map[string]uint

func GenerateEntity(entity EntityJSON, table *Table) (Entity, *Table) {
fields := make(Entity, len(entity))

Expand Down Expand Up @@ -111,6 +154,10 @@ func CreateTable(table *Table, entityJSON EntityJSON) *Table {
return table
}

// func SearchTable(table *Table, filters map[string]any) (EntityCollection, error) {
//
// }

func GetTable(table *Table) ([]byte, error) {
raw, err := os.ReadFile(table.File)

Expand All @@ -121,27 +168,46 @@ func GetTable(table *Table) ([]byte, error) {
return raw, err
}

func GetEntity(table *Table, id string) ([]byte, error) {
func GetEntityById(table *Table, id string) ([]byte, error) {
collection, err := ReadTable(table)

if err != nil {
return nil, err
}

for _, entity := range collection {
newId, _ := strconv.ParseFloat(id, 64)
if entity["id"] == newId {
b, err2 := json.Marshal(entity)
if err2 != nil {
return nil, fmt.Errorf("could not marshal entity: %w", err2)
}
var (
entity *Entity
found bool
)

return b, err
}
newId, err := strconv.ParseFloat(id, 64)
if err != nil {
entity, found = FindBy[string](&collection, "id", id)
} else {
entity, found = FindBy[float64](&collection, "id", newId)
}

if !found {
return nil, errors.New("entity not found, id: " + id)
}

return nil, errors.New("entity not found")
b, err := json.Marshal(entity)
if err != nil {
return nil, fmt.Errorf("could not marshal entity: %w", err)
}

return b, err
}

func FindBy[T comparable](collection *EntityCollection, key string, search T) (*Entity, bool) {
for _, entity := range *collection {
Debug("Comparing", "key", key, "search", search, "entity", entity[key])
if entity[key] == search {
Debug("Found entity", "entity", entity)
return &entity, true
}
}

return nil, false
}

func ReadTable(table *Table) (EntityCollection, error) {
Expand Down
140 changes: 98 additions & 42 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package main

import (
"encoding/json"
"errors"
"flag"
"fmt"
"log"
"net/http"
Expand Down Expand Up @@ -30,19 +32,7 @@ var ConfigPaths = []string{

var DataDir = path.Join(".amock", "data")
var SchemaDir = path.Join(".amock", "schema")

type Database struct {
Tables map[string]Table
}

type Table struct {
Name string
File string
DefinitionFile string
Definition map[string]*Field
SchemaFile string
LastAutoID uint
}
var TablesDir = path.Join(".amock", "tables")

type Config struct {
Host string `yaml:"host" env:"AMOCK_HOST" env-default:"localhost"`
Expand All @@ -52,12 +42,6 @@ type Config struct {
InitCount int `yaml:"initCount" env:"AMOCK_INIT_COUNT" env-default:"20"`
}

type Entity map[string]any

type EntityCollection []Entity

type EntityJSON map[string]string

var config *Config

var db Database
Expand All @@ -66,12 +50,14 @@ func init() {
InitLogger()
Debug("Creating database from config...")

config, _ = ParseConfigFiles(ConfigPaths...)
config, _ = parseConfigFiles(ConfigPaths...)

if config == nil {
log.Fatal("No configuration file found")
}

getHostFromArgs()

buildTablesFromConfig()

if _, err := os.Stat(DataDir); errors.Is(err, os.ErrNotExist) {
Expand All @@ -93,6 +79,35 @@ func init() {
db = *HydrateDatabase(&db)
}

func getHostFromArgs() {
host := flag.Arg(0)
if host != "" {
var noPrefix string
var prefix string
if strings.Contains(host, "http://") {
noPrefix = strings.TrimPrefix(host, "http://")
prefix = "http://"
} else if strings.Contains(host, "https://") {
noPrefix = strings.TrimPrefix(host, "https://")
prefix = "https://"
} else {
noPrefix = host
}
if strings.Contains(noPrefix, ":") {
parts := strings.Split(noPrefix, ":")
config.Host = prefix + parts[0]

port, err := strconv.Atoi(parts[1])
if err != nil {
log.Fatal(err)
}
config.Port = port
} else {
config.Host = host
}
}
}

func main() {
StartServer()
}
Expand All @@ -112,17 +127,25 @@ func StartServer() {
log.Fatal(http.ListenAndServe(config.Host+":"+strconv.Itoa(config.Port), LogRequest(router)))
}

func ParseConfigFiles(files ...string) (*Config, error) {
func parseConfigFiles(files ...string) (*Config, error) {
var cfg Config
fileRead := false

for i := 0; i < len(files); i++ {
if _, err := os.Stat(files[i]); errors.Is(err, os.ErrNotExist) {
continue
}

err := cleanenv.ReadConfig(files[i], &cfg)
if err == nil {
fileRead = true
}
}

if !fileRead {
err := cleanenv.ReadEnv(&cfg)
if err != nil {
log.Printf("Error reading configuration from file:%v", files[i])
log.Printf("Error reading configuration from file or environment: %v\n", err)
return nil, err
}
}
Expand All @@ -131,6 +154,13 @@ func ParseConfigFiles(files ...string) (*Config, error) {
}

func buildTablesFromConfig() {
if _, err := os.Stat(TablesDir); errors.Is(err, os.ErrNotExist) {
err = os.MkdirAll(TablesDir, os.ModePerm)
if err != nil {
log.Fatal(err)
}
}

db.Tables = make(map[string]Table)

if config.Dir != "" {
Expand All @@ -142,34 +172,60 @@ func buildTablesFromConfig() {

for _, entry := range dir {
filename := entry.Name()

if path.Ext(filename) == ".json" {
name := strings.ToLower(filename[:len(filename)-5])
db.Tables[name] = Table{
Name: name,
DefinitionFile: path.Join(config.Dir, filename),
Definition: make(map[string]*Field),
File: filename,
LastAutoID: 1,
}
}
table, name := getOrCreateTable(filename, path.Join(config.Dir, filename))
db.Tables[name] = *table
}
}

if len(config.Entities) > 0 {
for _, entity := range config.Entities {
if path.Ext(entity) == ".json" {
name := entity[:len(entity)-5]
db.Tables[name] = Table{
Name: name,
DefinitionFile: entity,
Definition: make(map[string]*Field),
File: path.Base(entity),
LastAutoID: 1,
}
table, name := getOrCreateTable(entity, entity)
db.Tables[name] = *table
}
}
}

func getOrCreateTable(filename string, definitionFile string) (*Table, string) {
createNew := false
tempTable := Table{}
var name string

if path.Ext(filename) == ".json" {
tableFilePath := path.Join(TablesDir, filename+".table")
name = strings.ToLower(filename[:len(filename)-5])

if _, err := os.Stat(tableFilePath); errors.Is(err, os.ErrNotExist) {
createNew = true
} else {
tableFile, err := os.ReadFile(tableFilePath)
if err != nil {
createNew = true
}

Debug("Table "+gchalk.Bold(name)+" found at "+gchalk.Italic(tableFilePath)+" - skipping...", "table", name, "file", tableFilePath)

err = json.Unmarshal(tableFile, &tempTable)
if err != nil {
createNew = true
}
}
}

if createNew {
tempTable = createNewTable(name, filename, definitionFile)
}

return &tempTable, name
}

func createNewTable(name string, filename string, definitionFile string) Table {
return Table{
Name: name,
DefinitionFile: definitionFile,
Definition: make(map[string]*Field),
File: filename,
LastAutoID: 1,
}
}

func constructUrl() string {
Expand Down
5 changes: 5 additions & 0 deletions post-user.http
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ Content-Type: application/json
PUT localhost:8000/user

###

### GET request to example server
GET localhost:8000/user/1

###
Loading

0 comments on commit 00af4a2

Please sign in to comment.