diff --git a/.amock.json b/.amock.json new file mode 100644 index 0000000..6f228eb --- /dev/null +++ b/.amock.json @@ -0,0 +1,7 @@ +{ + "host": "localhost", + "port": 8080, + "entities": [ + "user.json" + ] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bcfa99 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +### Go template +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +vendor/ +pkg/ + +# Go workspace file +go.work diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..a9d7db9 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# GitHub Copilot persisted chat sessions +/copilot/chatSessions diff --git a/.idea/amock.iml b/.idea/amock.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/amock.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/developer-tools.xml b/.idea/developer-tools.xml new file mode 100644 index 0000000..69a9b9c --- /dev/null +++ b/.idea/developer-tools.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/golinter.xml b/.idea/golinter.xml new file mode 100644 index 0000000..b7207b2 --- /dev/null +++ b/.idea/golinter.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml new file mode 100644 index 0000000..646c3a2 --- /dev/null +++ b/.idea/jsonSchemas.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c96f213 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/amock b/amock new file mode 100755 index 0000000..6ad82b8 Binary files /dev/null and b/amock differ diff --git a/amock.db b/amock.db new file mode 100644 index 0000000..0b745ff Binary files /dev/null and b/amock.db differ diff --git a/generator.go b/generator.go new file mode 100644 index 0000000..6d4481a --- /dev/null +++ b/generator.go @@ -0,0 +1,65 @@ +package main + +import ( + "encoding/json" + "log" + "os" + + "github.com/timshannon/bolthold" +) + +func GenerateEntity(entity EntityJSON, table *Table) (Entity, *Table) { + fields := make(Entity, len(entity)) + + for key, value := range entity { + fields[key], table = GenerateField(value, table) + } + + return fields, table +} + +func HydrateDatabase(db Database) Database { + var entityJSON EntityJSON + + store, err := bolthold.Open("amock.db", 0666, nil) + if err != nil { + log.Fatal(err) + } + + for key, table := range db.Tables { + var updated Table + updated, store = CreateTable(&table, entityJSON, store) + db.Tables[key] = updated + } + + return db +} + +func CreateTable(table *Table, entityJSON EntityJSON, store *bolthold.Store) (Table, *bolthold.Store) { + raw, err := os.ReadFile(table.Definition) + + if err != nil { + log.Fatal(err) + } + + err = json.Unmarshal(raw, &entityJSON) + if err != nil { + log.Fatal(err) + } + + entities := make([]Entity, config.InitCount) + + for i := 0; i < config.InitCount; i++ { + entities[i], table = GenerateEntity(entityJSON, table) + } + + b, _ := json.Marshal(entities) + + filename := table.Name + ".amock.json" + + _ = os.WriteFile(filename, b, os.ModePerm) + + table.File = filename + + return *table, store +} diff --git a/generator/date.go b/generator/date.go new file mode 100644 index 0000000..1f63ab0 --- /dev/null +++ b/generator/date.go @@ -0,0 +1,124 @@ +package generator + +import ( + "strings" + "time" + + "github.com/brianvoe/gofakeit/v7" +) + +type DateGenerator struct { + Generator +} + +func (g DateGenerator) Date(args ...string) any { + if len(args) > 0 { + format := args[0] + switch format { + case "ANSIC": + return gofakeit.Date().Format(time.ANSIC) + case "UnixDate": + return gofakeit.Date().Format(time.UnixDate) + case "RubyDate": + return gofakeit.Date().Format(time.RubyDate) + case "RFC822": + return gofakeit.Date().Format(time.RFC822) + case "RFC822Z": + return gofakeit.Date().Format(time.RFC822Z) + case "RFC850": + return gofakeit.Date().Format(time.RFC850) + case "RFC1123": + return gofakeit.Date().Format(time.RFC1123) + case "RFC1123Z": + return gofakeit.Date().Format(time.RFC1123Z) + case "RFC3339": + return gofakeit.Date().Format(time.RFC3339) + case "RFC3339Nano": + return gofakeit.Date().Format(time.RFC3339Nano) + default: + return gofakeit.Date().Format(formatToGoFormat(format)) + } + } + + return gofakeit.Date() +} + +func (g DateGenerator) Timestamp() int64 { + return gofakeit.DateRange(time.Unix(0, 0), gofakeit.FutureDate()).Unix() +} + +func (g DateGenerator) Day() int { + return gofakeit.Day() +} + +func (g DateGenerator) Month(args ...string) any { + if len(args) > 0 { + if args[0] == "string" { + return gofakeit.MonthString() + } + } + + return gofakeit.Month() +} + +func (g DateGenerator) Year() int { + return gofakeit.Year() +} + +func (g DateGenerator) WeekDay() string { + return gofakeit.WeekDay() +} + +func (g DateGenerator) Future() time.Time { + return gofakeit.FutureDate() +} + +func (g DateGenerator) Past() time.Time { + return gofakeit.PastDate() +} + +func formatToGoFormat(format string) string { + format = strings.Replace(format, "ddd", "_2", -1) + format = strings.Replace(format, "dd", "02", -1) + format = strings.Replace(format, "d", "2", -1) + + format = strings.Replace(format, "HH", "15", -1) + + format = strings.Replace(format, "hh", "03", -1) + format = strings.Replace(format, "h", "3", -1) + + format = strings.Replace(format, "mm", "04", -1) + format = strings.Replace(format, "m", "4", -1) + + format = strings.Replace(format, "ss", "05", -1) + format = strings.Replace(format, "s", "5", -1) + + format = strings.Replace(format, "yyyy", "2006", -1) + format = strings.Replace(format, "yy", "06", -1) + format = strings.Replace(format, "y", "06", -1) + + format = strings.Replace(format, "SSS", "000", -1) + + format = strings.Replace(format, "a", "pm", -1) + format = strings.Replace(format, "aa", "PM", -1) + + format = strings.Replace(format, "MMMM", "January", -1) + format = strings.Replace(format, "MMM", "Jan", -1) + format = strings.Replace(format, "MM", "01", -1) + format = strings.Replace(format, "M", "1", -1) + + format = strings.Replace(format, "ZZ", "-0700", -1) + + if !strings.Contains(format, "Z07") { + format = strings.Replace(format, "Z", "-07", -1) + } + + format = strings.Replace(format, "zz:zz", "Z07:00", -1) + format = strings.Replace(format, "zzzz", "Z0700", -1) + format = strings.Replace(format, "z", "MST", -1) + + format = strings.Replace(format, "EEEE", "Monday", -1) + format = strings.Replace(format, "E", "Mon", -1) + + return format +} diff --git a/generator/enum.go b/generator/enum.go new file mode 100644 index 0000000..d4a5feb --- /dev/null +++ b/generator/enum.go @@ -0,0 +1,11 @@ +package generator + +import "github.com/brianvoe/gofakeit/v7" + +type EnumGenerator struct { + Generator +} + +func (g EnumGenerator) Enum(args ...string) string { + return gofakeit.RandomString(args) +} diff --git a/generator/generator.go b/generator/generator.go new file mode 100644 index 0000000..915f89e --- /dev/null +++ b/generator/generator.go @@ -0,0 +1,13 @@ +package generator + +import "github.com/brianvoe/gofakeit/v7" + +type Generator struct{} + +type BoolGenerator struct { + Generator +} + +func (g BoolGenerator) Bool() bool { + return gofakeit.Bool() +} diff --git a/generator/id.go b/generator/id.go new file mode 100644 index 0000000..6efb9eb --- /dev/null +++ b/generator/id.go @@ -0,0 +1,25 @@ +package generator + +import ( + "strconv" + + "github.com/brianvoe/gofakeit/v7" +) + +type IDGenerator struct { + Generator +} + +func (g IDGenerator) UUID() string { + return gofakeit.UUID() +} + +func (g IDGenerator) Sequence(args ...string) uint { + if len(args) > 0 { + id, _ := strconv.Atoi(args[0]) + + return uint(id) + } + + return 1 +} diff --git a/generator/number.go b/generator/number.go new file mode 100644 index 0000000..65b2191 --- /dev/null +++ b/generator/number.go @@ -0,0 +1,82 @@ +package generator + +import ( + "math" + "strconv" + + "github.com/brianvoe/gofakeit/v7" +) + +type NumberGenerator struct { + Generator +} + +func randRangeInt(min int, max int) int { + return gofakeit.IntRange(min, max) +} +func randRangeFloat(min float64, max float64) float64 { + return gofakeit.Float64Range(min, max) +} + +func (g NumberGenerator) Number() int { + return gofakeit.Int() +} + +func (g NumberGenerator) Float() float64 { + return gofakeit.Float64() +} + +func (g NumberGenerator) FloatRange(min string, max string) float64 { + if min == "x" && max == "x" { + return g.Float() + } + + if min == "x" { + fMax, _ := strconv.ParseFloat(max, 64) + return randRangeFloat(0, fMax) + } else if max == "x" { + fMin, _ := strconv.ParseFloat(min, 64) + return randRangeFloat(fMin, math.MaxInt) + } else { + fMin, _ := strconv.ParseFloat(min, 64) + fMax, _ := strconv.ParseFloat(max, 64) + return randRangeFloat(fMin, fMax) + } +} + +func (g NumberGenerator) Decimal(p string, min string, max string) string { + prec, _ := strconv.Atoi(p) + if (min == "" && max == "") || (min == "x" && max == "x") { + return strconv.FormatFloat(g.Float(), 'f', prec, 32) + } + + if min == "x" { + fMax, _ := strconv.ParseFloat(max, 32) + return strconv.FormatFloat(g.FloatRange("x", strconv.FormatFloat(fMax, 'f', -1, 32)), 'f', prec, 32) + } else if max == "x" { + fMin, _ := strconv.ParseFloat(min, 32) + return strconv.FormatFloat(g.FloatRange(strconv.FormatFloat(fMin, 'f', -1, 32), "x"), 'f', prec, 32) + } else { + fMin, _ := strconv.ParseFloat(min, 32) + fMax, _ := strconv.ParseFloat(max, 32) + return strconv.FormatFloat(g.FloatRange(strconv.FormatFloat(fMin, 'f', -1, 32), strconv.FormatFloat(fMax, 'f', -1, 32)), 'f', prec, 32) + } +} + +func (g NumberGenerator) Int(min string, max string) int { + if min == "x" && max == "x" { + return g.Number() + } + + if min == "x" { + iMax, _ := strconv.Atoi(max) + return randRangeInt(0, iMax) + } else if max == "x" { + iMin, _ := strconv.Atoi(min) + return randRangeInt(iMin, math.MaxInt) + } else { + iMin, _ := strconv.Atoi(min) + iMax, _ := strconv.Atoi(max) + return randRangeInt(iMin, iMax) + } +} diff --git a/generator/string.go b/generator/string.go new file mode 100644 index 0000000..239f35e --- /dev/null +++ b/generator/string.go @@ -0,0 +1,143 @@ +package generator + +import ( + "fmt" + "strconv" + + "github.com/brianvoe/gofakeit/v7" +) + +type StringGenerator struct { + Generator +} + +func (g StringGenerator) String() string { + return gofakeit.Regex("[A-z\\-_+?&*$@/!=#]{3,16}") +} + +func (g StringGenerator) FullName() string { + return gofakeit.Name() +} + +func (g StringGenerator) FirstName() string { + return gofakeit.FirstName() +} + +func (g StringGenerator) LastName() string { + return gofakeit.LastName() +} + +func (g StringGenerator) Email() string { + return gofakeit.Email() +} + +func (g StringGenerator) Url() string { + return gofakeit.URL() +} + +func (g StringGenerator) Ip() string { + return gofakeit.IPv4Address() +} + +func (g StringGenerator) Ipv6() string { + return gofakeit.IPv6Address() +} + +func (g StringGenerator) Username() string { + return gofakeit.Username() +} + +func (g StringGenerator) Password() string { + return gofakeit.Password(true, true, true, true, false, 16) +} + +func (g StringGenerator) Phone() string { + return gofakeit.Phone() +} + +func (g StringGenerator) Zip() string { + return gofakeit.Zip() +} + +func (g StringGenerator) Country(args ...string) string { + if len(args) > 0 { + if args[0] == "short" { + return gofakeit.CountryAbr() + } + } + + return gofakeit.Country() +} + +func (g StringGenerator) City() string { + return gofakeit.City() +} + +func (g StringGenerator) Street() string { + return gofakeit.Street() +} + +func (g StringGenerator) StreetName() string { + return gofakeit.StreetName() +} + +func (g StringGenerator) State(args ...string) string { + if len(args) > 0 { + if args[0] == "short" { + return gofakeit.StateAbr() + } + } + + return gofakeit.State() +} + +func (g StringGenerator) Company() string { + return gofakeit.Company() +} + +func (g StringGenerator) Bitcoin() string { + return gofakeit.BitcoinAddress() +} + +func (g StringGenerator) Color(args ...string) string { + if len(args) > 0 { + if args[0] == "hex" { + return gofakeit.HexColor() + } + + if args[0] == "safe" { + return gofakeit.SafeColor() + } + + if args[0] == "rgb" { + rgb := gofakeit.RGBColor() + return fmt.Sprintf("rgb(%d, %d, %d)", rgb[0], rgb[1], rgb[2]) + } + } + + return gofakeit.Color() +} + +func (g StringGenerator) Word() string { + return gofakeit.Word() +} + +func (g StringGenerator) Sentence(args ...string) string { + if len(args) > 0 { + n, _ := strconv.Atoi(args[0]) + + return gofakeit.Sentence(n) + } + + return gofakeit.Sentence(3) +} + +func (g StringGenerator) Paragraph(args ...string) string { + if len(args) <= 0 { + return gofakeit.Paragraph(3, 4, 12, "\n\n") + } + + n, _ := strconv.Atoi(args[0]) + + return gofakeit.Paragraph(n, 4, 12, "\n\n") +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1569d35 --- /dev/null +++ b/go.mod @@ -0,0 +1,18 @@ +module amock + +go 1.22 + +require ( + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/brianvoe/gofakeit/v7 v7.0.2 // indirect + github.com/ilyakaznacheev/cleanenv v1.5.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/julienschmidt/httprouter v1.3.0 // indirect + github.com/oriser/regroup v0.0.0-20230527212431-1b00c9bdbc5b // indirect + github.com/timshannon/bolthold v0.0.0-20231129192944-dca5178aa629 // indirect + go.etcd.io/bbolt v1.3.8 // indirect + golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect + golang.org/x/sys v0.15.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..30201c0 --- /dev/null +++ b/go.sum @@ -0,0 +1,40 @@ +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/brianvoe/gofakeit/v7 v7.0.2 h1:jzYT7Ge3RDHw7J1CM1kwu0OQywV9vbf2qSGxBS72TCY= +github.com/brianvoe/gofakeit/v7 v7.0.2/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= +github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4= +github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/oriser/regroup v0.0.0-20230527212431-1b00c9bdbc5b h1:9L56kn3D7E9jd2R9U9p7tfzaBaLTVPt4HgnrP+g2VGk= +github.com/oriser/regroup v0.0.0-20230527212431-1b00c9bdbc5b/go.mod h1:6eb1+OYHjOvThrtgEVue70NTfmzkalZgohRtndAUUbI= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/timshannon/bolthold v0.0.0-20231129192944-dca5178aa629 h1:6JyscwjLxdI0S7GTDtcQXpxjsldBYwNXi4jfSpZEMzE= +github.com/timshannon/bolthold v0.0.0-20231129192944-dca5178aa629/go.mod h1:PCFYfAEfKT+Nd6zWvUpsXduMR1bXFLf0uGSlEF05MCI= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ= +olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= diff --git a/main.go b/main.go new file mode 100644 index 0000000..71d4979 --- /dev/null +++ b/main.go @@ -0,0 +1,146 @@ +package main + +import ( + "errors" + "log" + "os" + "path" + "strings" + + "github.com/ilyakaznacheev/cleanenv" +) + +var ConfigPaths = []string{ + ".amock.json", + ".amockrc", + ".amock.json.json", + ".amock.json.yml", + ".amock.json.yaml", + ".amock.json.toml", + "amock.config", + "amock.json", + "amock.yml", + "amock.yaml", + "amock.toml", +} + +type Database struct { + Tables map[string]Table +} + +type Table struct { + Name string + File string + Definition string + LastAutoID uint +} + +type Config struct { + Host string `yaml:"host" env:"HOST" env-default:"localhost"` + Port int `yaml:"port" env:"PORT" env-default:"8080"` + Dir string `yaml:"dir" env:"DIR"` + Entities []string `yaml:"entities" env:"ENTITIES"` + InitCount int `yaml:"init_count" env:"INIT_COUNT" env-default:"20"` +} + +type Entity map[string]interface{} + +type EntityJSON map[string]string + +// func PostRequest(url string, data Entity) { +// store, _ := bh.Open("db", os.ModePerm, nil) +// +// defer func(store *bh.Store) { +// err := store.Close() +// if err != nil { +// log.Fatal(err) +// } +// }(store) +// +// _ = store.Insert(data["id"], data["value"]) +// } +// +// func GetRequest(url string) Entity { +// +// } +// +// func PostHandler(w http.ResponseWriter, r *http.Request) { +// +// _, _ = fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) +// } + +// var api EntityJSON + +var config *Config + +var db Database + +func Init() { + config, _ = ParseConfigFiles(ConfigPaths...) + + if config == nil { + log.Fatal("No configuration file found") + } + + db.Tables = make(map[string]Table) + + if config.Dir != "" { + dir, err := os.ReadDir(config.Dir) + + if err != nil { + log.Fatal(err) + } + + 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, + Definition: path.Join(config.Dir, filename), + File: filename, + LastAutoID: 1, + } + } + } + } + + 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, + Definition: entity, + File: path.Base(entity), + LastAutoID: 1, + } + } + } + } + + db = HydrateDatabase(db) +} + +func main() { + Init() +} + +func ParseConfigFiles(files ...string) (*Config, error) { + var cfg Config + + 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 { + log.Printf("Error reading configuration from file:%v", files[i]) + return nil, err + } + } + + return &cfg, nil +} diff --git a/parser.go b/parser.go new file mode 100644 index 0000000..6a9cf08 --- /dev/null +++ b/parser.go @@ -0,0 +1,146 @@ +package main + +import ( + "reflect" + "strconv" + "strings" + + "amock/generator" + "github.com/oriser/regroup" +) + +var FieldPattern = regroup.MustCompile(`(?P[a-z]+)(?P\.[a-z]+)?(?P:.*)?`) +var NumberRangePattern = regroup.MustCompile(`(?P(-?[0-9]+(\.[0-9]+)?)|x)?-(?P(-?[0-9]+(\.[0-9]+)?)|x)?`) + +type Field struct { + Type string `regroup:"type"` + Subtype string `regroup:"subtype"` + Params string `regroup:"params"` +} + +func GenerateField(field string, table *Table) (any, *Table) { + f := &Field{} + + err := FieldPattern.MatchToTarget(field, f) + + if err != nil { + panic(err) + } + + var subtype, paramStr string + var params []string + var gen any + + t := f.Type + if len(f.Subtype) > 1 { + subtype = strings.TrimLeft(f.Subtype, ".") + } + + gen = GetGenerator(t, subtype) + + if len(f.Params) > 1 { + paramStr = strings.TrimLeft(f.Params, ":") + + if f.Type == "number" { + params = strings.Split(paramStr, ",") + for i, p := range params { + if strings.Contains(p, "-") { + groups, _ := NumberRangePattern.Groups(p) + if groups["min"] == "" && groups["max"] == "" { + params[i] = "x-x" + } else { + params[i] = groups["min"] + params = append(params, groups["max"]) + } + } + } + } else { + params = strings.Split(paramStr, ",") + } + } else if f.Type == "id" && subtype != "uuid" { + params = []string{strconv.Itoa(int(table.LastAutoID))} + table.LastAutoID = table.LastAutoID + 1 + } + + if len(params) > 0 { + params2 := make([]reflect.Value, len(params)) + for i, p := range params { + params2[i] = reflect.ValueOf(p) + } + return reflect.ValueOf(gen).Call(params2)[0].Interface(), table + } + + return reflect.ValueOf(gen).Call([]reflect.Value{})[0].Interface(), table +} + +type GeneratorFunc any + +type GeneratorMap map[string]GeneratorFunc + +var Generators = map[string]GeneratorMap{ + "string": { + "root": generator.StringGenerator{}.String, + "name": generator.StringGenerator{}.FullName, + "firstname": generator.StringGenerator{}.FirstName, + "lastname": generator.StringGenerator{}.LastName, + "email": generator.StringGenerator{}.Email, + "url": generator.StringGenerator{}.Url, + "ip": generator.StringGenerator{}.Ip, + "ipv6": generator.StringGenerator{}.Ipv6, + "username": generator.StringGenerator{}.Username, + "password": generator.StringGenerator{}.Password, + "phone": generator.StringGenerator{}.Phone, + "zip": generator.StringGenerator{}.Zip, + "country": generator.StringGenerator{}.Country, + "city": generator.StringGenerator{}.City, + "street": generator.StringGenerator{}.Street, + "streetName": generator.StringGenerator{}.StreetName, + "state": generator.StringGenerator{}.State, + "company": generator.StringGenerator{}.Company, + "bitcoin": generator.StringGenerator{}.Bitcoin, + "color": generator.StringGenerator{}.Color, + "word": generator.StringGenerator{}.Word, + "sentence": generator.StringGenerator{}.Sentence, + "paragraph": generator.StringGenerator{}.Paragraph, + }, + "number": { + "root": generator.NumberGenerator{}.Number, + "int": generator.NumberGenerator{}.Int, + "decimal": generator.NumberGenerator{}.Decimal, + "float": generator.NumberGenerator{}.Float, + "range": generator.NumberGenerator{}.FloatRange, + }, + "date": { + "root": generator.DateGenerator{}.Date, + "timestamp": generator.DateGenerator{}.Timestamp, + "day": generator.DateGenerator{}.Day, + "month": generator.DateGenerator{}.Month, + "year": generator.DateGenerator{}.Year, + "weekday": generator.DateGenerator{}.WeekDay, + "future": generator.DateGenerator{}.Future, + "past": generator.DateGenerator{}.Past, + }, + "bool": { + "root": generator.BoolGenerator{}.Bool, + }, + "enum": { + "root": generator.EnumGenerator{}.Enum, + }, + "id": { + "root": generator.IDGenerator{}.Sequence, + "sequence": generator.IDGenerator{}.Sequence, + "uuid": generator.IDGenerator{}.UUID, + }, +} + +func GetGenerator(t string, subtype string) any { + var gen any + + if subtype == "" { + gen = Generators[t]["root"] + } else { + gen = Generators[t][subtype] + } + + return gen +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..69f87ff --- /dev/null +++ b/server.go @@ -0,0 +1,18 @@ +package main + +import ( + "net/http" + + "github.com/julienschmidt/httprouter" +) + +func InitHandlers(config *Config, db Database) { + router := httprouter.New() + + for _, table := range db.Tables { + router.GET("/"+table.Name, func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + + _, _ = w.Write([]byte("Hello, " + ps.ByName("name") + "!")) + }) + } +} diff --git a/user.amock.json b/user.amock.json new file mode 100755 index 0000000..1aa11c1 --- /dev/null +++ b/user.amock.json @@ -0,0 +1,382 @@ +[ + { + "age": 33, + "birthday": "1935-07-15", + "city": "Scottsdale", + "country": "UY", + "created_at": 1206091307, + "email": "maybellkunde@schiller.name", + "id": 1, + "is_active": false, + "money": "4609.88", + "name": "Nedra", + "password": "O45IZWZMLS.S5b0f", + "role": "user", + "street": "25224 New Commonbury", + "surname": "Fay", + "token": "L[zn\u0026iZ_", + "updated_at": 291548669, + "username": "Bernhard4193" + }, + { + "age": 22, + "birthday": "1901-05-22", + "city": "Chesapeake", + "country": "TH", + "created_at": 908138472, + "email": "michaelosinski@rodriguez.biz", + "id": 2, + "is_active": true, + "money": "9385.28", + "name": "Peter", + "password": "02saQY4z\u0026q@.\u0026ZK.", + "role": "moderator", + "street": "96456 Brookview", + "surname": "Dickinson", + "token": "srwafNp[", + "updated_at": 1232303408, + "username": "Langosh6406" + }, + { + "age": 40, + "birthday": "2011-11-12", + "city": "Miami", + "country": "GU", + "created_at": 1341845598, + "email": "violetcremin@hettinger.name", + "id": 3, + "is_active": true, + "money": "4259.82", + "name": "Norval", + "password": "orLtl$#A2D6Oof08", + "role": "moderator", + "street": "86636 South Expresswaymouth", + "surname": "Waters", + "token": "IvN_q", + "updated_at": 435270625, + "username": "Kertzmann4208" + }, + { + "age": 59, + "birthday": "1923-08-08", + "city": "Scottsdale", + "country": "KG", + "created_at": 1332018087, + "email": "aprilcole@crooks.org", + "id": 4, + "is_active": false, + "money": "9016.02", + "name": "Joyce", + "password": "!\u0026uOD7HQEoTJJXsD", + "role": "admin", + "street": "469 Port Parksmouth", + "surname": "Wuckert", + "token": "vs#VoFd#", + "updated_at": 943664159, + "username": "Graham5455" + }, + { + "age": 32, + "birthday": "1908-12-20", + "city": "Stockton", + "country": "IE", + "created_at": 854693590, + "email": "jamilcruickshank@hane.biz", + "id": 5, + "is_active": false, + "money": "4235.92", + "name": "Isom", + "password": "*vU9BEr5P*5uysmp", + "role": "moderator", + "street": "152 South Forestfurt", + "surname": "Bailey", + "token": "TEuy-_l", + "updated_at": 1192321334, + "username": "Spinka3404" + }, + { + "age": 50, + "birthday": "1908-01-31", + "city": "San Francisco", + "country": "KI", + "created_at": 1550621657, + "email": "kristianmurazik@ortiz.io", + "id": 6, + "is_active": false, + "money": "27.76", + "name": "Amari", + "password": "a?6d9FJsh_z\u00266r*2", + "role": "admin", + "street": "715 Spurshaven", + "surname": "Abernathy", + "token": "Ym??d", + "updated_at": 1261506506, + "username": "Yundt1500" + }, + { + "age": 48, + "birthday": "1926-11-04", + "city": "Long Beach", + "country": "TM", + "created_at": 1034774350, + "email": "salmalarkin@ferry.net", + "id": 7, + "is_active": true, + "money": "9534.51", + "name": "Allan", + "password": "UWWCE@5GR!R373wl", + "role": "admin", + "street": "805 Lightstown", + "surname": "Schmitt", + "token": "mFJ*_O", + "updated_at": 1424365721, + "username": "Little1607" + }, + { + "age": 64, + "birthday": "1982-07-10", + "city": "Portland", + "country": "VI", + "created_at": 1466252331, + "email": "kianavonrueden@rohan.net", + "id": 8, + "is_active": false, + "money": "7503.30", + "name": "Hilda", + "password": "8@HBy\u0026R5jsYQO00q", + "role": "user", + "street": "121 Curveville", + "surname": "Bosco", + "token": "\u003d\u003dRj+KNYj", + "updated_at": 1068951714, + "username": "Gibson3260" + }, + { + "age": 31, + "birthday": "1925-02-13", + "city": "Anaheim", + "country": "IS", + "created_at": 1120258952, + "email": "kylerbrakus@hermiston.name", + "id": 9, + "is_active": true, + "money": "928.46", + "name": "Mozelle", + "password": "8YnI.f_y8z6#I5?$", + "role": "admin", + "street": "375 East Forgesberg", + "surname": "Hackett", + "token": "OdMAaM", + "updated_at": 1356776590, + "username": "Barrows4929" + }, + { + "age": 34, + "birthday": "1943-01-14", + "city": "Denver", + "country": "PS", + "created_at": 693936427, + "email": "madonnakautzer@koelpin.info", + "id": 10, + "is_active": false, + "money": "2981.66", + "name": "Sydnee", + "password": "uQ_!8-Zqv66M-X?J", + "role": "user", + "street": "120 New Plainsburgh", + "surname": "Mayer", + "token": "t@!]RI$s", + "updated_at": 880459894, + "username": "Pollich1684" + }, + { + "age": 42, + "birthday": "1918-09-30", + "city": "Colorado Springs", + "country": "SG", + "created_at": 673194548, + "email": "jackelinekeeling@kertzmann.name", + "id": 11, + "is_active": false, + "money": "7372.15", + "name": "Rhiannon", + "password": "KLO3iw!l\u0026X#aCSg9", + "role": "moderator", + "street": "8188 New Millberg", + "surname": "Larson", + "token": "ogxSQfzH$", + "updated_at": 95891321, + "username": "Bosco3097" + }, + { + "age": 22, + "birthday": "2021-08-05", + "city": "Charlotte", + "country": "RU", + "created_at": 53542959, + "email": "zariamayer@willms.biz", + "id": 12, + "is_active": true, + "money": "695.16", + "name": "Brian", + "password": "*6jKSL-Jb9Xa9D-7", + "role": "user", + "street": "980 South Summitchester", + "surname": "Schultz", + "token": "^V-?t", + "updated_at": 517839523, + "username": "Brekke2461" + }, + { + "age": 23, + "birthday": "1988-06-25", + "city": "El Paso", + "country": "AL", + "created_at": 1572357702, + "email": "dorthyschoen@breitenberg.io", + "id": 13, + "is_active": false, + "money": "8838.26", + "name": "Sterling", + "password": "UVfCWb4Dv5O6*TAX", + "role": "admin", + "street": "32224 New Capemouth", + "surname": "Ebert", + "token": "EgTa", + "updated_at": 240610002, + "username": "Heathcote7111" + }, + { + "age": 42, + "birthday": "1908-04-29", + "city": "Austin", + "country": "SA", + "created_at": 1037206047, + "email": "montanaharber@crist.io", + "id": 14, + "is_active": false, + "money": "4479.90", + "name": "Amparo", + "password": "?02v48*_W?2CNz7M", + "role": "admin", + "street": "5452 Port Meadowchester", + "surname": "Champlin", + "token": "/zHk", + "updated_at": 412049759, + "username": "Lang1245" + }, + { + "age": 59, + "birthday": "2012-07-22", + "city": "Raleigh", + "country": "FM", + "created_at": 1641447373, + "email": "denisebert@tillman.info", + "id": 15, + "is_active": false, + "money": "502.29", + "name": "Natalie", + "password": "RqFzm4SJ8ip?2#rG", + "role": "moderator", + "street": "5197 Missionville", + "surname": "Bartell", + "token": "Yy*U", + "updated_at": 195871529, + "username": "Smitham7834" + }, + { + "age": 42, + "birthday": "1947-01-22", + "city": "Arlington", + "country": "CD", + "created_at": 1366447398, + "email": "cordelianader@oreilly.org", + "id": 16, + "is_active": true, + "money": "2120.37", + "name": "Elna", + "password": "HQVXi.LDl7TNoc6N", + "role": "admin", + "street": "7808 New Lightland", + "surname": "Johns", + "token": "rqL$", + "updated_at": 532160132, + "username": "Haag6886" + }, + { + "age": 43, + "birthday": "1992-06-30", + "city": "San Bernardino", + "country": "ML", + "created_at": 1403744305, + "email": "reeceveum@beier.biz", + "id": 17, + "is_active": false, + "money": "4421.14", + "name": "Dax", + "password": "bYU$E8vV52r-5x9$", + "role": "moderator", + "street": "18079 South Vistaburgh", + "surname": "Larson", + "token": "zbNzFW", + "updated_at": 336614920, + "username": "Wilderman5042" + }, + { + "age": 24, + "birthday": "1939-07-18", + "city": "Lincoln", + "country": "VC", + "created_at": 1535157542, + "email": "markkoch@kessler.org", + "id": 18, + "is_active": false, + "money": "2868.76", + "name": "Rhiannon", + "password": "VDI_GKrp\u0026dJASQO7", + "role": "user", + "street": "801 South Plainsland", + "surname": "Ledner", + "token": "KF^", + "updated_at": 1393119774, + "username": "Stanton6153" + }, + { + "age": 43, + "birthday": "1984-07-23", + "city": "Durham", + "country": "EH", + "created_at": 552308313, + "email": "masonstehr@green.org", + "id": 19, + "is_active": false, + "money": "8077.05", + "name": "Lauren", + "password": "Rb95!04W57-5N!-N", + "role": "user", + "street": "119 North Curvefort", + "surname": "Heaney", + "token": "V+Y", + "updated_at": 166887067, + "username": "Kessler5971" + }, + { + "age": 46, + "birthday": "1959-06-27", + "city": "Baton Rouge", + "country": "EH", + "created_at": 892589738, + "email": "tamarakassulke@conroy.com", + "id": 20, + "is_active": false, + "money": "4827.46", + "name": "Delphia", + "password": "Ge#aJf826H*YyWi0", + "role": "moderator", + "street": "7611 Commonmouth", + "surname": "Koelpin", + "token": "zsVtw", + "updated_at": 1135341110, + "username": "Kemmer9957" + } +] diff --git a/user.json b/user.json new file mode 100644 index 0000000..329ad73 --- /dev/null +++ b/user.json @@ -0,0 +1,19 @@ +{ + "id": "id.sequence", + "name": "string.firstname", + "surname": "string.lastname", + "age": "number.int:18-64", + "birthday": "date:yyyy-MM-dd", + "money": "number.decimal:2,0-10000", + "email": "string.email", + "password": "string.password", + "username": "string.username", + "city": "string.city", + "street": "string.street", + "country": "string.country:short", + "role": "enum:admin,user,moderator", + "is_active": "bool", + "token": "string", + "created_at": "date.timestamp", + "updated_at": "date.timestamp" +}